From 5ee0385d7936f40014cde3f04d591f375d612550 Mon Sep 17 00:00:00 2001 From: Danny Arnold Date: Mon, 1 May 2017 18:25:49 +0200 Subject: [PATCH] [WIP] add TSNamespaceExportDeclaration (#1459) * add TSNamespaceExportDeclaration * extract ast-types/fork * add TSEnumDeclaration and TSEnumMember * add TSImportEqualsDeclaration and TSExternalModuleReference * add TSInterfaceDeclaration and type annotation to TSMethodSignature * add TSModuleDeclaration, TSDeclareKeyword, TSModuleBlock and fix TSNamespaceExportDeclaration * remove workaround --- src/ast-types.js | 20 +++ src/comments.js | 2 +- src/fast-path.js | 7 +- src/printer.js | 131 +++++++++++++- src/typescript-ast-nodes.js | 167 ++++++++++++++++++ .../__snapshots__/jsfmt.spec.js.snap | 8 + .../types/enumDeclaration/enumDeclaration.ts | 1 + .../types/enumDeclaration/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 8 + .../importEqualsDeclaration.ts | 1 + .../importEqualsDeclaration/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 14 ++ .../interfaceDeclaration.ts | 4 + .../types/interfaceDeclaration/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 18 ++ .../types/moduleDeclaration/jsfmt.spec.js | 1 + .../moduleDeclaration/moduleDeclaration.ts | 9 + .../__snapshots__/jsfmt.spec.js.snap | 14 ++ .../exportAsNamespace.d.ts | 4 + .../namespaceExportDeclaration/jsfmt.spec.js | 1 + tests_config/run_spec.js | 2 +- 21 files changed, 406 insertions(+), 9 deletions(-) create mode 100644 src/ast-types.js create mode 100644 src/typescript-ast-nodes.js create mode 100644 tests/typescript/conformance/types/enumDeclaration/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/types/enumDeclaration/enumDeclaration.ts create mode 100644 tests/typescript/conformance/types/enumDeclaration/jsfmt.spec.js create mode 100644 tests/typescript/conformance/types/importEqualsDeclaration/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/types/importEqualsDeclaration/importEqualsDeclaration.ts create mode 100644 tests/typescript/conformance/types/importEqualsDeclaration/jsfmt.spec.js create mode 100644 tests/typescript/conformance/types/interfaceDeclaration/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/types/interfaceDeclaration/interfaceDeclaration.ts create mode 100644 tests/typescript/conformance/types/interfaceDeclaration/jsfmt.spec.js create mode 100644 tests/typescript/conformance/types/moduleDeclaration/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/types/moduleDeclaration/jsfmt.spec.js create mode 100644 tests/typescript/conformance/types/moduleDeclaration/moduleDeclaration.ts create mode 100644 tests/typescript/conformance/types/namespaceExportDeclaration/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/types/namespaceExportDeclaration/exportAsNamespace.d.ts create mode 100644 tests/typescript/conformance/types/namespaceExportDeclaration/jsfmt.spec.js diff --git a/src/ast-types.js b/src/ast-types.js new file mode 100644 index 00000000..e6f8d7dd --- /dev/null +++ b/src/ast-types.js @@ -0,0 +1,20 @@ +"use strict"; + +module.exports = require("ast-types/fork")([ + // This core module of AST types captures ES5 as it is parsed today by + // git://github.com/ariya/esprima.git#master. + require("ast-types/def/core"), + + // Feel free to add to or remove from this list of extension modules to + // configure the precise type hierarchy that you need. + require("ast-types/def/es6"), + require("ast-types/def/es7"), + require("ast-types/def/mozilla"), + require("ast-types/def/e4x"), + require("ast-types/def/jsx"), + require("ast-types/def/flow"), + require("ast-types/def/esprima"), + require("ast-types/def/babel"), + require("ast-types/def/babel6"), + require("./typescript-ast-nodes.js") +]); diff --git a/src/comments.js b/src/comments.js index 3e279cc0..66bb0576 100644 --- a/src/comments.js +++ b/src/comments.js @@ -1,7 +1,7 @@ "use strict"; var assert = require("assert"); -var types = require("ast-types"); +var types = require("./ast-types") var n = types.namedTypes; var isArray = types.builtInTypes.array; var isObject = types.builtInTypes.object; diff --git a/src/fast-path.js b/src/fast-path.js index 7eabba71..6d83ccad 100644 --- a/src/fast-path.js +++ b/src/fast-path.js @@ -1,7 +1,7 @@ "use strict"; var assert = require("assert"); -var types = require("ast-types"); +var types = require("./ast-types") var util = require("./util"); var n = types.namedTypes; var isArray = types.builtInTypes.array; @@ -67,9 +67,8 @@ function getNodeHelper(path, count) { for (var i = s.length - 1; i >= 0; i -= 2) { var value = s[i]; - // Temp: This can be removed when `ast-types` knows that TSNodes are Nodes. - var isTsNode = value && value.type && value.type.startsWith('TS'); - if ((isTsNode || n.Node.check(value)) && --count < 0) { + + if ((n.Node.check(value)) && --count < 0) { return value; } } diff --git a/src/printer.js b/src/printer.js index a90b62f2..698f3483 100644 --- a/src/printer.js +++ b/src/printer.js @@ -26,7 +26,7 @@ var willBreak = docUtils.willBreak; var isLineNext = docUtils.isLineNext; var isEmpty = docUtils.isEmpty; -var types = require("ast-types"); +var types = require("./ast-types"); var namedTypes = types.namedTypes; var isString = types.builtInTypes.string; @@ -721,9 +721,12 @@ function genericPrintNoParens(path, options, print, args) { case "ObjectExpression": case "ObjectPattern": case "ObjectTypeAnnotation": + case "TSInterfaceDeclaration": case "TSTypeLiteral": var isTypeAnnotation = n.type === "ObjectTypeAnnotation"; var isTypeScriptTypeAnnotaion = n.type === "TSTypeLiteral"; + var isTypeScriptInterfaceDeclaration = n.type === "TSInterfaceDeclaration"; + var isTypeScriptType = isTypeScriptTypeAnnotaion || isTypeScriptInterfaceDeclaration; // Leave this here because we *might* want to make this // configurable later -- flow accepts ";" for type separators, // typescript accepts ";" and newlines @@ -733,13 +736,22 @@ function genericPrintNoParens(path, options, print, args) { var rightBrace = n.exact ? "|}" : "}"; var parent = path.getParentNode(0); var parentIsUnionTypeAnnotation = parent.type === "UnionTypeAnnotation"; - var propertiesField = isTypeScriptTypeAnnotaion + var propertiesField = isTypeScriptType ? "members" : "properties"; + var prefix = "" if (isTypeAnnotation) { fields.push("indexers", "callProperties"); } + + if (isTypeScriptInterfaceDeclaration) { + prefix = concat([ + "interface ", + path.call(print, "name"), + " " + ]) + } fields.push(propertiesField); @@ -778,6 +790,7 @@ function genericPrintNoParens(path, options, print, args) { if (props.length === 0) { return group( concat([ + prefix, leftBrace, comments.printDanglingComments(path, options), softline, @@ -787,6 +800,7 @@ function genericPrintNoParens(path, options, print, args) { } else { return group( concat([ + prefix, leftBrace, indent( align( @@ -2052,12 +2066,123 @@ function genericPrintNoParens(path, options, print, args) { return concat(parts) case "TSMethodSignature": - return concat([ + parts.push( path.call(print, 'name'), "(", join(", ", path.map(print, "parameters")), ")" + ) + + if (n.typeAnnotation) { + parts.push( + ": ", + path.call(print, "typeAnnotation") + ) + } + return concat(parts) + case "TSNamespaceExportDeclaration": + if (n.declaration) { + parts.push( + "export ", + path.call(print, "declaration") + ) + } else { + parts.push( + "export as namespace ", + path.call(print, "name") + ) + + if (options.semi) { + parts.push(";") + } + } + + return concat(parts) + case "TSEnumDeclaration": + parts.push( + "enum ", + path.call(print, "name"), + " " + ) + + if (n.members.length === 0) { + parts.push( + group( + concat([ + "{", + comments.printDanglingComments(path, options), + softline, + "}" + ]) + ) + ); + } else { + parts.push( + group( + concat([ + "{", + options.bracketSpacing ? line : softline, + indent( + concat([ + softline, + printArrayItems(path, options, "members", print) + ]) + ), + comments.printDanglingComments( + path, + options, + /* sameIndent */ true + ), + softline, + options.bracketSpacing ? line : softline, + "}" + ]) + ) + ); + } + + return concat(parts); + case "TSEnumMember": + return path.call(print, "name") + case "TSImportEqualsDeclaration": + parts.push( + "import ", + path.call(print, "name"), + " = ", + path.call(print, "moduleReference") + ) + + if (options.semi) { + parts.push(";") + } + + return concat(parts) + case "TSExternalModuleReference": + return concat([ + "require(", + path.call(print, "expression"), + ")" ]) + case "TSModuleDeclaration": + if (n.modifiers) { + parts.push( + join(" ", path.map(print, "modifiers")), + " " + ) + } + parts.push( + "module ", + path.call(print, "name"), + " {", + path.call(print, "body"), + "}" + ) + + return concat(parts) + case "TSDeclareKeyword": + return "declare" + case "TSModuleBlock": + return concat(path.map(print, "body")) // TODO case "ClassHeritage": // TODO diff --git a/src/typescript-ast-nodes.js b/src/typescript-ast-nodes.js new file mode 100644 index 00000000..816a44cf --- /dev/null +++ b/src/typescript-ast-nodes.js @@ -0,0 +1,167 @@ +module.exports = function(fork) { + fork.use(require("ast-types/def/es7")); + + var types = fork.use(require("ast-types/lib/types")); + var def = types.Type.def; + var or = types.Type.or; + var defaults = fork.use(require("ast-types/lib/shared")).defaults; + + // Ambient + def("TSAmbientVariableDefinition").bases("VariableDeclaration"); + + def("TSInterfaceDeclaration") + .bases("Declaration") + .build("name", "typeParameters", "members") + .field("name", def("Identifier")) + .field( + "typeParameters", + or(def("TypeParameterDeclaration"), null), + defaults["null"] + ) + .field("members", [def("TSSignature")]); + // .field("body", def("ObjectTypeAnnotation")) + // .field("extends", [def("InterfaceExtends")]); + + def("TSKeyword").bases("Node"); + + def("TSType").bases("Node"); + + def("TypeElement").bases("Node"); + + def("TSSignature") + .bases("TypeElement") + .build("typeParameters", "parameters", "typeAnnotation") + .field( + "typeParameters", + or(def("TypeParameterDeclaration"), null), + defaults["null"] + ) + .field("parameters", [def("Identifier")]) + .field("typeAnnotation", def("TSType")); + + def("TSAnyKeyword").bases("TSKeyword"); + + def("TSBooleanKeyword").bases("TSKeyword"); + + def("TSNeverKeyword").bases("TSKeyword"); + + def("TSNumberKeyword").bases("TSKeyword"); + + def("TSObjectKeyword").bases("TSKeyword"); + + def("TSReadonlyKeyword").bases("TSKeyword"); + + def("TSStringKeyword").bases("TSKeyword"); + + def("TSSymbolKeyword").bases("TSKeyword"); + + def("TSUndefinedKeyword").bases("TSKeyword"); + + def("TSVoidKeyword").bases("TSKeyword"); + + // Types + def("TSConstructorType").bases("TSType"); + + def("TSFunctionType").bases("TSType"); + + def("TSIntersectionType") + .bases("TSType") + .build("types") + .field("types", [def("TSType")]); + + def("TSParenthesizedType").bases("TSType"); + + def("TSThisType").bases("TSType"); + + def("TSUnionType") + .bases("TSType") + .build("types") + .field("types", [def("TSType")]); + + def("TSTypeLiteral") + .bases("TSType") + .build("members") + .field("members", [def("TSSignature")]); + + def("TSTypeOperator").bases("TSType"); + + def("TSTypeReference") + .bases("TSType") + .build("typeName", "typeParameters") + .field("typeName", def("Identifier")) + .field("typeParameters", def("TSType")); + + def("TSFirstTypeNode") + .bases("Node") + .build("id", "typeAnnotation") + .field("id", def("Identifier")) + .field("typeAnnotation", def("TSType")); + + // Signatures + def("TSCallSignature") + .bases("TSSignature") + .build("typeParameters", "parameters", "typeAnnotation"); + + def("TSConstructSignature") + .bases("TSSignature") + .build("typeParameters", "parameters", "typeAnnotation"); + + def("TSIndexSignature") + .bases("TSSignature") + .build("typeParameters", "parameters", "typeAnnotation"); + + def("TSMethodSignature") + .bases("TSSignature") + .build("name", "typeParameters", "parameters", "typeAnnotation") + .field("name", def("Identifier")); + + def("TSPropertySignature") + .bases("TSSignature") + .build("name", "typeAnnotation", "initializer") + .field("name", def("Identifier")) + .field("typeAnnotation", def("TSType")) + .field("initializer", def("Expression")); + + def("TSAsExpression").bases("Expression"); + + def("TSNamespaceExportDeclaration") + .bases("Declaration") + // needs more like `modefiers` and `decorators` + .build("name"); + + def("TSEnumDeclaration") + .bases("Declaration") + .build("name", "members") + .field("name", def("Identifier")); + + def("TSEnumMember").build("name").field("name", def("Identifier")); + + def("TSImportEqualsDeclaration") + .build("name", "moduleReference") + .field("name", def("Identifier")) + .field("moduleReference", def("TSExternalModuleReference")); + + def("TSImportEqualsDeclaration") + .build("expression") + .field("expression", def("Literal")); + + def("TSInterfaceDeclaration") + .build("name", "members") + .field("name", def("Identifier")) + .field("members", [def("TSMethodSignature")]); + + def("TSModuleDeclaration") + .build("modifiers", "name", "body") + .bases("Node") + .field("name", or(def("Identifier"), def("Literal"))); + + def("TSDeclareKeyword").build(); + + def("TSModuleBlock").build("body"); + + def("TSAbstractMethodDefinition").build().bases("Node"); + + def("TSAbstractClassProperty").build("key", "value").bases("Node"); + + def("TSAbstractClassDeclaration").build().bases("Node"); +}; diff --git a/tests/typescript/conformance/types/enumDeclaration/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/enumDeclaration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..2b45eff7 --- /dev/null +++ b/tests/typescript/conformance/types/enumDeclaration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`enumDeclaration.ts 1`] = ` +enum E { A, B, C } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +enum E { A, B, C } + +`; diff --git a/tests/typescript/conformance/types/enumDeclaration/enumDeclaration.ts b/tests/typescript/conformance/types/enumDeclaration/enumDeclaration.ts new file mode 100644 index 00000000..f00df588 --- /dev/null +++ b/tests/typescript/conformance/types/enumDeclaration/enumDeclaration.ts @@ -0,0 +1 @@ +enum E { A, B, C } diff --git a/tests/typescript/conformance/types/enumDeclaration/jsfmt.spec.js b/tests/typescript/conformance/types/enumDeclaration/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/types/enumDeclaration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/types/importEqualsDeclaration/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/importEqualsDeclaration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..e30aacb9 --- /dev/null +++ b/tests/typescript/conformance/types/importEqualsDeclaration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`importEqualsDeclaration.ts 1`] = ` +import glo_m4 = require("glo_m4"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import glo_m4 = require("glo_m4"); + +`; diff --git a/tests/typescript/conformance/types/importEqualsDeclaration/importEqualsDeclaration.ts b/tests/typescript/conformance/types/importEqualsDeclaration/importEqualsDeclaration.ts new file mode 100644 index 00000000..071edd02 --- /dev/null +++ b/tests/typescript/conformance/types/importEqualsDeclaration/importEqualsDeclaration.ts @@ -0,0 +1 @@ +import glo_m4 = require("glo_m4"); diff --git a/tests/typescript/conformance/types/importEqualsDeclaration/jsfmt.spec.js b/tests/typescript/conformance/types/importEqualsDeclaration/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/types/importEqualsDeclaration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/types/interfaceDeclaration/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/interfaceDeclaration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..4bf120c1 --- /dev/null +++ b/tests/typescript/conformance/types/interfaceDeclaration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`interfaceDeclaration.ts 1`] = ` +interface abstract { + abstract(): void, + concrete(): number +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +interface abstract { + abstract(): void, + concrete(): number +} + +`; diff --git a/tests/typescript/conformance/types/interfaceDeclaration/interfaceDeclaration.ts b/tests/typescript/conformance/types/interfaceDeclaration/interfaceDeclaration.ts new file mode 100644 index 00000000..f452959d --- /dev/null +++ b/tests/typescript/conformance/types/interfaceDeclaration/interfaceDeclaration.ts @@ -0,0 +1,4 @@ +interface abstract { + abstract(): void, + concrete(): number +} diff --git a/tests/typescript/conformance/types/interfaceDeclaration/jsfmt.spec.js b/tests/typescript/conformance/types/interfaceDeclaration/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/types/interfaceDeclaration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/types/moduleDeclaration/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/moduleDeclaration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..cea52ebb --- /dev/null +++ b/tests/typescript/conformance/types/moduleDeclaration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`moduleDeclaration.ts 1`] = ` +module A { + export class A { + } +} + +declare module "B" { + export class B { + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module A {export class A {}} + +declare module "B" {export class B {}} + +`; diff --git a/tests/typescript/conformance/types/moduleDeclaration/jsfmt.spec.js b/tests/typescript/conformance/types/moduleDeclaration/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/types/moduleDeclaration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/types/moduleDeclaration/moduleDeclaration.ts b/tests/typescript/conformance/types/moduleDeclaration/moduleDeclaration.ts new file mode 100644 index 00000000..bca9ee2c --- /dev/null +++ b/tests/typescript/conformance/types/moduleDeclaration/moduleDeclaration.ts @@ -0,0 +1,9 @@ +module A { + export class A { + } +} + +declare module "B" { + export class B { + } +} diff --git a/tests/typescript/conformance/types/namespaceExportDeclaration/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/namespaceExportDeclaration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..4c57469a --- /dev/null +++ b/tests/typescript/conformance/types/namespaceExportDeclaration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`exportAsNamespace.d.ts 1`] = ` +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +export as namespace N +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +export as namespace N; + +`; diff --git a/tests/typescript/conformance/types/namespaceExportDeclaration/exportAsNamespace.d.ts b/tests/typescript/conformance/types/namespaceExportDeclaration/exportAsNamespace.d.ts new file mode 100644 index 00000000..7e595f78 --- /dev/null +++ b/tests/typescript/conformance/types/namespaceExportDeclaration/exportAsNamespace.d.ts @@ -0,0 +1,4 @@ +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +export as namespace N diff --git a/tests/typescript/conformance/types/namespaceExportDeclaration/jsfmt.spec.js b/tests/typescript/conformance/types/namespaceExportDeclaration/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/types/namespaceExportDeclaration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js index 174bd857..12249c5b 100644 --- a/tests_config/run_spec.js +++ b/tests_config/run_spec.js @@ -2,7 +2,7 @@ const fs = require("fs"); const prettier = require("../"); -const types = require("ast-types"); +const types = require("../src/ast-types"); const parser = require("../src/parser"); const RUN_AST_TESTS = process.env["AST_COMPARE"];