From 08238fc1f559323177f1eeece46996d8b168e5dd Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Sat, 29 Apr 2017 21:35:16 +1000 Subject: [PATCH] Fix: Use TSAbsractMethodDefinition for abstract constructor (fixes #228) --- lib/ast-converter.js | 47 +-- ...-class-with-abstract-constructor.result.js | 358 ++++++++++++++++++ ...act-class-with-abstract-constructor.src.ts | 3 + 3 files changed, 379 insertions(+), 29 deletions(-) create mode 100644 tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.result.js create mode 100644 tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.src.ts diff --git a/lib/ast-converter.js b/lib/ast-converter.js index 1d253e4..10a642a 100644 --- a/lib/ast-converter.js +++ b/lib/ast-converter.js @@ -106,13 +106,14 @@ function isESTreeClassMember(node) { } /** - * Returns true if the given node is an async function - * @param {TSNode} node TypeScript AST node - * @returns {boolean} is an async function + * Checks if a TSNode has a modifier + * @param {SyntaxKind} modifierKind TypeScript SyntaxKind modifier + * @param {TSNode} node TypeScript AST node + * @returns {boolean} has the modifier specified */ -function isAsyncFunction(node) { +function hasModifier(modifierKind, node) { return !!node.modifiers && !!node.modifiers.length && node.modifiers.some(function(modifier) { - return modifier.kind === SyntaxKind.AsyncKeyword; + return modifier.kind === modifierKind; }); } @@ -905,9 +906,7 @@ module.exports = function(ast, extra) { var functionDeclarationType = "FunctionDeclaration"; if (node.modifiers && node.modifiers.length) { - var isDeclareFunction = node.modifiers.some(function(modifier) { - return modifier.kind === ts.SyntaxKind.DeclareKeyword; - }); + var isDeclareFunction = hasModifier(ts.SyntaxKind.DeclareKeyword, node); if (isDeclareFunction) { functionDeclarationType = "DeclareFunction"; } @@ -925,7 +924,7 @@ module.exports = function(ast, extra) { id: convertChild(node.name), generator: !!node.asteriskToken, expression: false, - async: isAsyncFunction(node), + async: hasModifier(SyntaxKind.AsyncKeyword, node), params: node.parameters.map(convertChild), body: convertChild(node.body) }); @@ -1114,7 +1113,7 @@ module.exports = function(ast, extra) { id: null, generator: false, expression: false, - async: isAsyncFunction(node), + async: hasModifier(SyntaxKind.AsyncKeyword, node), body: convertChild(node.body), range: [ node.parameters.pos - 1, result.range[1]], loc: { @@ -1162,15 +1161,9 @@ module.exports = function(ast, extra) { /** * TypeScript class methods can be defined as "abstract" */ - var methodDefinitionType = "MethodDefinition"; - if (node.modifiers && node.modifiers.length) { - var isAbstractMethod = node.modifiers.some(function(modifier) { - return modifier.kind === ts.SyntaxKind.AbstractKeyword; - }); - if (isAbstractMethod) { - methodDefinitionType = "TSAbstractMethodDefinition"; - } - } + var methodDefinitionType = hasModifier(SyntaxKind.AbstractKeyword, node) + ? "TSAbstractMethodDefinition" + : "MethodDefinition"; assign(result, { type: methodDefinitionType, @@ -1206,6 +1199,7 @@ module.exports = function(ast, extra) { case SyntaxKind.Constructor: var constructorIsStatic = Boolean(ts.getModifierFlags(node) & ts.ModifierFlags.Static), + constructorIsAbstract = hasModifier(SyntaxKind.AbstractKeyword, node), firstConstructorToken = constructorIsStatic ? ts.findNextToken(node.getFirstToken(), ast) : node.getFirstToken(), constructorLoc = ast.getLineAndCharacterOfPosition(node.parameters.pos - 1), constructor = { @@ -1275,7 +1269,7 @@ module.exports = function(ast, extra) { } assign(result, { - type: "MethodDefinition", + type: constructorIsAbstract ? "TSAbstractMethodDefinition" : "MethodDefinition", key: constructorKey, value: constructor, computed: constructorIsComputed, @@ -1292,7 +1286,7 @@ module.exports = function(ast, extra) { generator: !!node.asteriskToken, params: node.parameters.map(convertChild), body: convertChild(node.body), - async: isAsyncFunction(node), + async: hasModifier(SyntaxKind.AbstractKeyword, node), expression: false }); // Process returnType @@ -1375,7 +1369,7 @@ module.exports = function(ast, extra) { id: null, params: node.parameters.map(convertChild), body: convertChild(node.body), - async: isAsyncFunction(node), + async: hasModifier(SyntaxKind.AsyncKeyword, node), expression: node.body.kind !== SyntaxKind.Block }); // Process returnType @@ -1501,9 +1495,7 @@ module.exports = function(ast, extra) { range: [node.getStart(), node.end], loc: getLoc(node, ast), accessibility: getTSNodeAccessibility(node), - isReadonly: node.modifiers.some(function(modifier) { - return modifier.kind === SyntaxKind.ReadonlyKeyword; - }), + isReadonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), parameter: result }; } @@ -1533,10 +1525,7 @@ module.exports = function(ast, extra) { * TypeScript class declarations can be defined as "abstract" */ if (node.kind === SyntaxKind.ClassDeclaration) { - var isAbstractClass = node.modifiers.some(function(modifier) { - return modifier.kind === ts.SyntaxKind.AbstractKeyword; - }); - if (isAbstractClass) { + if (hasModifier(SyntaxKind.AbstractKeyword, node)) { classNodeType = "TSAbstract" + classNodeType; } } diff --git a/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.result.js b/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.result.js new file mode 100644 index 0000000..a9b9530 --- /dev/null +++ b/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.result.js @@ -0,0 +1,358 @@ +module.exports = { + "type": "Program", + "range": [ + 0, + 68 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "body": [ + { + "type": "ExportNamedDeclaration", + "declaration": { + "type": "TSAbstractClassDeclaration", + "range": [ + 16, + 68 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "range": [ + 22, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 36 + } + }, + "name": "AbstractSocket" + }, + "body": { + "type": "ClassBody", + "body": [ + { + "type": "TSAbstractMethodDefinition", + "range": [ + 43, + 66 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "key": { + "type": "Identifier", + "name": "constructor", + "range": [ + 43, + 51 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 24 + } + } + }, + "value": { + "type": "FunctionExpression", + "id": null, + "params": [], + "generator": false, + "expression": false, + "async": false, + "body": null, + "range": [ + 63, + 66 + ], + "loc": { + "start": { + "line": 2, + "column": 24 + }, + "end": { + "line": 2, + "column": 27 + } + } + }, + "computed": false, + "accessibility": null, + "static": false, + "kind": "constructor" + } + ], + "range": [ + 37, + 68 + ], + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 3, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "decorators": [] + }, + "range": [ + 0, + 68 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "specifiers": [], + "source": null + } + ], + "sourceType": "module", + "tokens": [ + { + "type": "Keyword", + "value": "export", + "range": [ + 0, + 6 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 6 + } + } + }, + { + "type": "Identifier", + "value": "abstract", + "range": [ + 7, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + { + "type": "Keyword", + "value": "class", + "range": [ + 16, + 21 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 21 + } + } + }, + { + "type": "Identifier", + "value": "AbstractSocket", + "range": [ + 22, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 37, + 38 + ], + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 38 + } + } + }, + { + "type": "Identifier", + "value": "abstract", + "range": [ + 43, + 51 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + { + "type": "Identifier", + "value": "constructor", + "range": [ + 52, + 63 + ], + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 24 + } + } + }, + { + "type": "Punctuator", + "value": "(", + "range": [ + 63, + 64 + ], + "loc": { + "start": { + "line": 2, + "column": 24 + }, + "end": { + "line": 2, + "column": 25 + } + } + }, + { + "type": "Punctuator", + "value": ")", + "range": [ + 64, + 65 + ], + "loc": { + "start": { + "line": 2, + "column": 25 + }, + "end": { + "line": 2, + "column": 26 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 65, + 66 + ], + "loc": { + "start": { + "line": 2, + "column": 26 + }, + "end": { + "line": 2, + "column": 27 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 67, + 68 + ], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + } + } + ] +}; \ No newline at end of file diff --git a/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.src.ts b/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.src.ts new file mode 100644 index 0000000..66b7082 --- /dev/null +++ b/tests/fixtures/typescript/basics/abstract-class-with-abstract-constructor.src.ts @@ -0,0 +1,3 @@ +export abstract class AbstractSocket { + abstract constructor(); +} \ No newline at end of file