From 7bcc0d636e51623fdf1170dbf2f6639fdd2290a3 Mon Sep 17 00:00:00 2001 From: James Henry Date: Sat, 19 Aug 2017 18:18:12 +0100 Subject: [PATCH] Fix: Ensure modifiers are applied to enums (fixes #365) (#366) --- lib/ast-node-types.js | 7 + lib/convert.js | 50 +- tests/ast-alignment/spec.js | 2 + .../typescript/basics/const-enum.src.ts | 4 + .../errorRecovery/enum-with-keywords.src.ts | 1 + tests/lib/__snapshots__/typescript.js.snap | 749 ++++++++++++++++++ 6 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/typescript/basics/const-enum.src.ts create mode 100644 tests/fixtures/typescript/errorRecovery/enum-with-keywords.src.ts diff --git a/lib/ast-node-types.js b/lib/ast-node-types.js index 3b9f20a..fcd6ab6 100644 --- a/lib/ast-node-types.js +++ b/lib/ast-node-types.js @@ -101,9 +101,11 @@ module.exports = { * TS-prefixed nodes */ TSAbstractClassProperty: "TSAbstractClassProperty", + TSAbstractKeyword: "TSAbstractKeyword", TSAbstractMethodDefinition: "TSAbstractMethodDefinition", TSAnyKeyword: "TSAnyKeyword", TSArrayType: "TSArrayType", + TSAsyncKeyword: "TSAsyncKeyword", TSBooleanKeyword: "TSBooleanKeyword", TSConstructorType: "TSConstructorType", TSConstructSignature: "TSConstructSignature", @@ -125,9 +127,14 @@ module.exports = { TSNumberKeyword: "TSNumberKeyword", TSObjectKeyword: "TSObjectKeyword", TSParameterProperty: "TSParameterProperty", + TSPrivateKeyword: "TSPrivateKeyword", TSPropertySignature: "TSPropertySignature", + TSProtectedKeyword: "TSProtectedKeyword", + TSPublicKeyword: "TSPublicKeyword", TSQualifiedName: "TSQualifiedName", TSQuestionToken: "TSQuestionToken", + TSReadonlyKeyword: "TSReadonlyKeyword", + TSStaticKeyword: "TSStaticKeyword", TSStringKeyword: "TSStringKeyword", TSTypeLiteral: "TSTypeLiteral", TSTypePredicate: "TSTypePredicate", diff --git a/lib/convert.js b/lib/convert.js index 247e162..9e6301e 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -330,6 +330,52 @@ module.exports = function convert(config) { return tagNameToken; } + /** + * Applies the given TS modifiers to the given result object. + * @param {TSNode[]} modifiers original TSNodes from the node.modifiers array + * @returns {void} (the current result object will be mutated) + */ + function applyModifiersToResult(modifiers) { + if (!modifiers || !modifiers.length) { + return; + } + /** + * Some modifiers are explicitly handled by applying them as + * boolean values on the result node. As well as adding them + * to the result, we remove them from the array, so that they + * are not handled twice. + */ + const handledModifierIndices = {}; + for (let i = 0; i < modifiers.length; i++) { + const modifier = modifiers[i]; + switch (modifier.kind) { + /** + * Ignore ExportKeyword and DefaultKeyword, they are handled + * via the fixExports utility function + */ + case SyntaxKind.ExportKeyword: + case SyntaxKind.DefaultKeyword: + handledModifierIndices[i] = true; + break; + case SyntaxKind.ConstKeyword: + result.const = true; + handledModifierIndices[i] = true; + break; + default: + } + } + /** + * If there are still valid modifiers available which have + * not been explicitly handled above, we just convert and + * add the modifiers array to the result node. + */ + const remainingModifiers = modifiers.filter((_, i) => !handledModifierIndices[i]); + if (!remainingModifiers || !remainingModifiers.length) { + return; + } + result.modifiers = remainingModifiers.map(convertChild); + } + /** * The core of the conversion logic: * Identify and convert each relevant TypeScript SyntaxKind @@ -2000,7 +2046,9 @@ module.exports = function convert(config) { id: convertChild(node.name), members: node.members.map(convertChild) }); - // check for exports + // apply modifiers first... + applyModifiersToResult(node.modifiers); + // ...then check for exports result = nodeUtils.fixExports(node, result, ast); /** * Semantically, decorators are not allowed on enum declarations, diff --git a/tests/ast-alignment/spec.js b/tests/ast-alignment/spec.js index fbae900..a98120d 100644 --- a/tests/ast-alignment/spec.js +++ b/tests/ast-alignment/spec.js @@ -422,6 +422,7 @@ const fixturePatternsToTest = [ "typescript/basics/async-function-with-var-declaration.src.ts", "typescript/basics/function-with-await.src.ts", "typescript/errorRecovery/class-extends-empty-implements.src.ts", + "typescript/basics/const-enum.src.ts", { pattern: "typescript/basics/export-named-enum.src.ts", @@ -435,6 +436,7 @@ const fixturePatternsToTest = [ // "typescript/errorRecovery/class-empty-extends.src.ts", // babylon parse errors // "typescript/errorRecovery/decorator-on-enum-declaration.src.ts", // babylon parse errors // "typescript/errorRecovery/interface-property-modifiers.src.ts", // babylon parse errors + // "typescript/errorRecovery/enum-with-keywords.src.ts" // babylon parse errors /** * Other babylon parse errors. TODO: Need to coordinate with TS Team. diff --git a/tests/fixtures/typescript/basics/const-enum.src.ts b/tests/fixtures/typescript/basics/const-enum.src.ts new file mode 100644 index 0000000..40f9491 --- /dev/null +++ b/tests/fixtures/typescript/basics/const-enum.src.ts @@ -0,0 +1,4 @@ +const enum Foo { + foo = 1, + bar +} \ No newline at end of file diff --git a/tests/fixtures/typescript/errorRecovery/enum-with-keywords.src.ts b/tests/fixtures/typescript/errorRecovery/enum-with-keywords.src.ts new file mode 100644 index 0000000..a568bf4 --- /dev/null +++ b/tests/fixtures/typescript/errorRecovery/enum-with-keywords.src.ts @@ -0,0 +1 @@ +export private public protected static readonly abstract async enum X {} \ No newline at end of file diff --git a/tests/lib/__snapshots__/typescript.js.snap b/tests/lib/__snapshots__/typescript.js.snap index aa365e0..2f23cc1 100644 --- a/tests/lib/__snapshots__/typescript.js.snap +++ b/tests/lib/__snapshots__/typescript.js.snap @@ -19403,6 +19403,338 @@ Object { } `; +exports[`typescript fixtures/basics/const-enum.src 1`] = ` +Object { + "body": Array [ + Object { + "const": true, + "id": Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "name": "Foo", + "range": Array [ + 11, + 14, + ], + "type": "Identifier", + }, + "loc": Object { + "end": Object { + "column": 1, + "line": 4, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "members": Array [ + Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 2, + }, + "start": Object { + "column": 4, + "line": 2, + }, + }, + "name": "foo", + "range": Array [ + 21, + 24, + ], + "type": "Identifier", + }, + "initializer": Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 2, + }, + "start": Object { + "column": 10, + "line": 2, + }, + }, + "range": Array [ + 27, + 28, + ], + "raw": "1", + "type": "Literal", + "value": 1, + }, + "loc": Object { + "end": Object { + "column": 11, + "line": 2, + }, + "start": Object { + "column": 4, + "line": 2, + }, + }, + "range": Array [ + 21, + 28, + ], + "type": "TSEnumMember", + }, + Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 3, + }, + "start": Object { + "column": 4, + "line": 3, + }, + }, + "name": "bar", + "range": Array [ + 34, + 37, + ], + "type": "Identifier", + }, + "loc": Object { + "end": Object { + "column": 7, + "line": 3, + }, + "start": Object { + "column": 4, + "line": 3, + }, + }, + "range": Array [ + 34, + 37, + ], + "type": "TSEnumMember", + }, + ], + "range": Array [ + 0, + 39, + ], + "type": "TSEnumDeclaration", + }, + ], + "loc": Object { + "end": Object { + "column": 1, + "line": 4, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 39, + ], + "sourceType": "script", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 5, + ], + "type": "Keyword", + "value": "const", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 10, + ], + "type": "Keyword", + "value": "enum", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "range": Array [ + 11, + 14, + ], + "type": "Identifier", + "value": "Foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 15, + "line": 1, + }, + }, + "range": Array [ + 15, + 16, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 2, + }, + "start": Object { + "column": 4, + "line": 2, + }, + }, + "range": Array [ + 21, + 24, + ], + "type": "Identifier", + "value": "foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 9, + "line": 2, + }, + "start": Object { + "column": 8, + "line": 2, + }, + }, + "range": Array [ + 25, + 26, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 2, + }, + "start": Object { + "column": 10, + "line": 2, + }, + }, + "range": Array [ + 27, + 28, + ], + "type": "Numeric", + "value": "1", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 2, + }, + "start": Object { + "column": 11, + "line": 2, + }, + }, + "range": Array [ + 28, + 29, + ], + "type": "Punctuator", + "value": ",", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 3, + }, + "start": Object { + "column": 4, + "line": 3, + }, + }, + "range": Array [ + 34, + 37, + ], + "type": "Identifier", + "value": "bar", + }, + Object { + "loc": Object { + "end": Object { + "column": 1, + "line": 4, + }, + "start": Object { + "column": 0, + "line": 4, + }, + }, + "range": Array [ + 38, + 39, + ], + "type": "Punctuator", + "value": "}", + }, + ], + "type": "Program", +} +`; + exports[`typescript fixtures/basics/declare-class-with-optional-method.src 1`] = ` Object { "body": Array [ @@ -53983,6 +54315,423 @@ Object { } `; +exports[`typescript fixtures/errorRecovery/enum-with-keywords.src 1`] = ` +Object { + "body": Array [ + Object { + "declaration": Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 69, + "line": 1, + }, + "start": Object { + "column": 68, + "line": 1, + }, + }, + "name": "X", + "range": Array [ + 68, + 69, + ], + "type": "Identifier", + }, + "loc": Object { + "end": Object { + "column": 72, + "line": 1, + }, + "start": Object { + "column": 63, + "line": 1, + }, + }, + "members": Array [], + "modifiers": Array [ + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 7, + "line": 1, + }, + }, + "range": Array [ + 7, + 14, + ], + "type": "TSPrivateKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 21, + "line": 1, + }, + "start": Object { + "column": 15, + "line": 1, + }, + }, + "range": Array [ + 15, + 21, + ], + "type": "TSPublicKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 31, + "line": 1, + }, + "start": Object { + "column": 22, + "line": 1, + }, + }, + "range": Array [ + 22, + 31, + ], + "type": "TSProtectedKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 38, + "line": 1, + }, + "start": Object { + "column": 32, + "line": 1, + }, + }, + "range": Array [ + 32, + 38, + ], + "type": "TSStaticKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 47, + "line": 1, + }, + "start": Object { + "column": 39, + "line": 1, + }, + }, + "range": Array [ + 39, + 47, + ], + "type": "TSReadonlyKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 56, + "line": 1, + }, + "start": Object { + "column": 48, + "line": 1, + }, + }, + "range": Array [ + 48, + 56, + ], + "type": "TSAbstractKeyword", + }, + Object { + "loc": Object { + "end": Object { + "column": 62, + "line": 1, + }, + "start": Object { + "column": 57, + "line": 1, + }, + }, + "range": Array [ + 57, + 62, + ], + "type": "TSAsyncKeyword", + }, + ], + "range": Array [ + 63, + 72, + ], + "type": "TSEnumDeclaration", + }, + "loc": Object { + "end": Object { + "column": 72, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 72, + ], + "source": null, + "specifiers": Array [], + "type": "ExportNamedDeclaration", + }, + ], + "loc": Object { + "end": Object { + "column": 72, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 72, + ], + "sourceType": "module", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 6, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 6, + ], + "type": "Keyword", + "value": "export", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 7, + "line": 1, + }, + }, + "range": Array [ + 7, + 14, + ], + "type": "Keyword", + "value": "private", + }, + Object { + "loc": Object { + "end": Object { + "column": 21, + "line": 1, + }, + "start": Object { + "column": 15, + "line": 1, + }, + }, + "range": Array [ + 15, + 21, + ], + "type": "Keyword", + "value": "public", + }, + Object { + "loc": Object { + "end": Object { + "column": 31, + "line": 1, + }, + "start": Object { + "column": 22, + "line": 1, + }, + }, + "range": Array [ + 22, + 31, + ], + "type": "Keyword", + "value": "protected", + }, + Object { + "loc": Object { + "end": Object { + "column": 38, + "line": 1, + }, + "start": Object { + "column": 32, + "line": 1, + }, + }, + "range": Array [ + 32, + 38, + ], + "type": "Keyword", + "value": "static", + }, + Object { + "loc": Object { + "end": Object { + "column": 47, + "line": 1, + }, + "start": Object { + "column": 39, + "line": 1, + }, + }, + "range": Array [ + 39, + 47, + ], + "type": "Identifier", + "value": "readonly", + }, + Object { + "loc": Object { + "end": Object { + "column": 56, + "line": 1, + }, + "start": Object { + "column": 48, + "line": 1, + }, + }, + "range": Array [ + 48, + 56, + ], + "type": "Identifier", + "value": "abstract", + }, + Object { + "loc": Object { + "end": Object { + "column": 62, + "line": 1, + }, + "start": Object { + "column": 57, + "line": 1, + }, + }, + "range": Array [ + 57, + 62, + ], + "type": "Identifier", + "value": "async", + }, + Object { + "loc": Object { + "end": Object { + "column": 67, + "line": 1, + }, + "start": Object { + "column": 63, + "line": 1, + }, + }, + "range": Array [ + 63, + 67, + ], + "type": "Keyword", + "value": "enum", + }, + Object { + "loc": Object { + "end": Object { + "column": 69, + "line": 1, + }, + "start": Object { + "column": 68, + "line": 1, + }, + }, + "range": Array [ + 68, + 69, + ], + "type": "Identifier", + "value": "X", + }, + Object { + "loc": Object { + "end": Object { + "column": 71, + "line": 1, + }, + "start": Object { + "column": 70, + "line": 1, + }, + }, + "range": Array [ + 70, + 71, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 72, + "line": 1, + }, + "start": Object { + "column": 71, + "line": 1, + }, + }, + "range": Array [ + 71, + 72, + ], + "type": "Punctuator", + "value": "}", + }, + ], + "type": "Program", +} +`; + exports[`typescript fixtures/errorRecovery/interface-empty-extends.src 1`] = ` Object { "body": Array [