From fa27e5838ce7a9bb37b507e33c22a226a0d01983 Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Mon, 8 May 2017 01:09:52 +1000 Subject: [PATCH] feat(typescript): add TSTypeAssertionExpression and naive TSX detection (#1545) --- src/fast-path.js | 2 + src/parser.js | 14 ++- src/printer.js | 7 ++ src/typescript-ast-nodes.js | 6 + .../compiler/__snapshots__/jsfmt.spec.js.snap | 113 ++++++++++++++++++ tests/typescript/compiler/castOfAwait.ts | 8 ++ tests/typescript/compiler/castParentheses.ts | 11 ++ tests/typescript/compiler/castTest.ts | 30 +++++ .../__snapshots__/jsfmt.spec.js.snap | 10 ++ .../conformance/es6/templates/jsfmt.spec.js | 1 + ...gWithEmbeddedTypeAssertionOnAdditionES6.ts | 2 + .../__snapshots__/jsfmt.spec.js.snap | 106 ++++++++++++++++ .../functionCalls/callWithSpreadES6.ts | 51 ++++++++ .../expressions/functionCalls/jsfmt.spec.js | 1 + 14 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 tests/typescript/compiler/castOfAwait.ts create mode 100644 tests/typescript/compiler/castParentheses.ts create mode 100644 tests/typescript/compiler/castTest.ts create mode 100644 tests/typescript/conformance/es6/templates/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/es6/templates/jsfmt.spec.js create mode 100644 tests/typescript/conformance/es6/templates/templateStringWithEmbeddedTypeAssertionOnAdditionES6.ts create mode 100644 tests/typescript/conformance/expressions/functionCalls/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/expressions/functionCalls/callWithSpreadES6.ts create mode 100644 tests/typescript/conformance/expressions/functionCalls/jsfmt.spec.js diff --git a/src/fast-path.js b/src/fast-path.js index 9ee25cb3..35c4105d 100644 --- a/src/fast-path.js +++ b/src/fast-path.js @@ -313,6 +313,7 @@ FPp.needsParens = function() { return true; } // else fall through + case "TSTypeAssertionExpression": case "TSAsExpression": case "LogicalExpression": switch (parent.type) { @@ -320,6 +321,7 @@ FPp.needsParens = function() { case "NewExpression": return name === "callee" && parent.callee === node; + case "TSTypeAssertionExpression": case "TaggedTemplateExpression": case "UnaryExpression": case "SpreadElement": diff --git a/src/parser.js b/src/parser.js index fca734bf..bfa7b100 100644 --- a/src/parser.js +++ b/src/parser.js @@ -64,7 +64,7 @@ function parseWithTypeScript(text) { tokens: true, attachComment: true, ecmaFeatures: { - jsx: true + jsx: isJsx(text) } }); } catch(e) { @@ -76,4 +76,16 @@ function parseWithTypeScript(text) { } } +/** + * Use a naive regular expression until we address + * https://github.com/prettier/prettier/issues/1538 + */ +function isJsx(text) { + return new RegExp([ + "()" // Contains "/>" on line not starting with "//" + ].join(""), "m").test(text); +} + module.exports = { parseWithFlow, parseWithBabylon, parseWithTypeScript }; diff --git a/src/printer.js b/src/printer.js index 23d9c333..52228264 100644 --- a/src/printer.js +++ b/src/printer.js @@ -257,6 +257,13 @@ function genericPrintNoParens(path, options, print, args) { " = ", path.call(print, "right") ]); + case "TSTypeAssertionExpression": + return concat([ + "<", + path.call(print, "typeAnnotation"), + ">", + path.call(print, "expression") + ]); case "MemberExpression": { const parent = path.getParentNode(); let firstNonMemberParent; diff --git a/src/typescript-ast-nodes.js b/src/typescript-ast-nodes.js index 93651dc2..7c435359 100644 --- a/src/typescript-ast-nodes.js +++ b/src/typescript-ast-nodes.js @@ -180,4 +180,10 @@ module.exports = function(fork) { def("TSTypeParameter").build("name").field("name", def("Identifier")); def("TSParameterProperty").build("accessibility", "isReadonly", "parameters"); + + def("TSTypeAssertionExpression") + .build("expression", "typeAnnotation") + .field("expression", def("Identifier")) + .field("typeAnnotation", def("TSType")) + .bases("Expression"); }; diff --git a/tests/typescript/compiler/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/compiler/__snapshots__/jsfmt.spec.js.snap index 14a72374..f3607dd1 100644 --- a/tests/typescript/compiler/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/compiler/__snapshots__/jsfmt.spec.js.snap @@ -20,6 +20,119 @@ var results = number[]; `; +exports[`castOfAwait.ts 1`] = ` +// @target: es6 +async function f() { + await 0; + typeof await 0; + void await 0; + await void typeof void await 0; + await await 0; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @target: es6 +async function f() { + await 0; + typeof await 0; + void await 0; + await void (typeof (void await 0)); + await await 0; +} + +`; + +exports[`castParentheses.ts 1`] = ` +class a { + static b: any; +} + +var b = (a); +var b = (a).b; +var b = (a.b).c; +var b = (a.b()).c; +var b = (new a); +var b = (new a.b); +var b = (new a).b +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class a { + static b: any; +} + +var b = a; +var b = (a).b; +var b = (a.b).c; +var b = (a.b()).c; +var b = new a(); +var b = new a.b(); +var b = (new a()).b; + +`; + +exports[`castTest.ts 1`] = ` + +var x : any = 0; +var z = x; +var y = x + z; + +var a = 0; +var b = true; +var s = ""; + +var ar = null; + +var f = <(res : number) => void>null; + +declare class Point +{ + x: number; + y: number; + add(dx: number, dy: number): Point; + mult(p: Point): Point; + constructor(x: number, y: number); +} + +var p_cast = ({ + x: 0, + y: 0, + add: function(dx, dy) { + return new Point(this.x + dx, this.y + dy); + }, + mult: function(p) { return p; } +}) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: any = 0; +var z = x; +var y = x + z; + +var a = 0; +var b = true; +var s = ""; + +var ar = null; + +var f = <(res: number) => void>null; + +declare class Point { + x: number; + y: number; + add(dx: number, dy: number): Point; + mult(p: Point): Point; + constructor(x: number, y: number); +} + +var p_cast = { + x: 0, + y: 0, + add: function(dx, dy) { + return new Point(this.x + dx, this.y + dy); + }, + mult: function(p) { + return p; + } +}; + +`; + exports[`checkInfiniteExpansionTermination.ts 1`] = ` // Regression test for #1002 // Before fix this code would cause infinite loop diff --git a/tests/typescript/compiler/castOfAwait.ts b/tests/typescript/compiler/castOfAwait.ts new file mode 100644 index 00000000..fcd8f999 --- /dev/null +++ b/tests/typescript/compiler/castOfAwait.ts @@ -0,0 +1,8 @@ +// @target: es6 +async function f() { + await 0; + typeof await 0; + void await 0; + await void typeof void await 0; + await await 0; +} diff --git a/tests/typescript/compiler/castParentheses.ts b/tests/typescript/compiler/castParentheses.ts new file mode 100644 index 00000000..18c6fcdd --- /dev/null +++ b/tests/typescript/compiler/castParentheses.ts @@ -0,0 +1,11 @@ +class a { + static b: any; +} + +var b = (a); +var b = (a).b; +var b = (a.b).c; +var b = (a.b()).c; +var b = (new a); +var b = (new a.b); +var b = (new a).b diff --git a/tests/typescript/compiler/castTest.ts b/tests/typescript/compiler/castTest.ts new file mode 100644 index 00000000..87d66daa --- /dev/null +++ b/tests/typescript/compiler/castTest.ts @@ -0,0 +1,30 @@ + +var x : any = 0; +var z = x; +var y = x + z; + +var a = 0; +var b = true; +var s = ""; + +var ar = null; + +var f = <(res : number) => void>null; + +declare class Point +{ + x: number; + y: number; + add(dx: number, dy: number): Point; + mult(p: Point): Point; + constructor(x: number, y: number); +} + +var p_cast = ({ + x: 0, + y: 0, + add: function(dx, dy) { + return new Point(this.x + dx, this.y + dy); + }, + mult: function(p) { return p; } +}) diff --git a/tests/typescript/conformance/es6/templates/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/es6/templates/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..20414ca7 --- /dev/null +++ b/tests/typescript/conformance/es6/templates/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`templateStringWithEmbeddedTypeAssertionOnAdditionES6.ts 1`] = ` +// @target: ES6 +var x = \`abc\${ (10 + 10) }def\`; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @target: ES6 +var x = \`abc\${(10 + 10)}def\`; + +`; diff --git a/tests/typescript/conformance/es6/templates/jsfmt.spec.js b/tests/typescript/conformance/es6/templates/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/es6/templates/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/es6/templates/templateStringWithEmbeddedTypeAssertionOnAdditionES6.ts b/tests/typescript/conformance/es6/templates/templateStringWithEmbeddedTypeAssertionOnAdditionES6.ts new file mode 100644 index 00000000..2a6d8446 --- /dev/null +++ b/tests/typescript/conformance/es6/templates/templateStringWithEmbeddedTypeAssertionOnAdditionES6.ts @@ -0,0 +1,2 @@ +// @target: ES6 +var x = `abc${ (10 + 10) }def`; diff --git a/tests/typescript/conformance/expressions/functionCalls/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/expressions/functionCalls/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..16502cb8 --- /dev/null +++ b/tests/typescript/conformance/expressions/functionCalls/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`callWithSpreadES6.ts 1`] = ` +// @target: ES6 + +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @target: ES6 + +interface X { + foo(x: number, y: number, ...z: string[]) +} + +function foo(x: number, y: number, ...z: string[]) {} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) {} +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} + +`; diff --git a/tests/typescript/conformance/expressions/functionCalls/callWithSpreadES6.ts b/tests/typescript/conformance/expressions/functionCalls/callWithSpreadES6.ts new file mode 100644 index 00000000..accb6df1 --- /dev/null +++ b/tests/typescript/conformance/expressions/functionCalls/callWithSpreadES6.ts @@ -0,0 +1,51 @@ +// @target: ES6 + +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} diff --git a/tests/typescript/conformance/expressions/functionCalls/jsfmt.spec.js b/tests/typescript/conformance/expressions/functionCalls/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/expressions/functionCalls/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" });