From e1aa17a24d3e9bc4e81545a8c36703fbfe0661e7 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 20 Mar 2020 16:13:11 -0700 Subject: [PATCH] Handle import type and export type in preprocess Fixes #37338 --- src/services/preProcess.ts | 35 +++++++ .../unittests/services/preProcessFile.ts | 93 ++++++++++++++++--- 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index ca4adc00b3e77..41845616bbe4b 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -101,6 +101,21 @@ namespace ts { return true; } else { + if (token === SyntaxKind.TypeKeyword) { + const skipTypeKeyword = scanner.lookAhead(() => { + const token = scanner.scan(); + return token !== SyntaxKind.FromKeyword && ( + token === SyntaxKind.AsteriskToken || + token === SyntaxKind.OpenBraceToken || + token === SyntaxKind.Identifier || + isKeyword(token) + ); + }); + if (skipTypeKeyword) { + token = nextToken(); + } + } + if (token === SyntaxKind.Identifier || isKeyword(token)) { token = nextToken(); if (token === SyntaxKind.FromKeyword) { @@ -176,6 +191,16 @@ namespace ts { if (token === SyntaxKind.ExportKeyword) { markAsExternalModuleIfTopLevel(); token = nextToken(); + if (token === SyntaxKind.TypeKeyword) { + const skipTypeKeyword = scanner.lookAhead(() => { + const token = scanner.scan(); + return token === SyntaxKind.AsteriskToken || + token === SyntaxKind.OpenBraceToken; + }); + if (skipTypeKeyword) { + token = nextToken(); + } + } if (token === SyntaxKind.OpenBraceToken) { token = nextToken(); // consume "{ a as B, c, d as D}" clauses @@ -208,6 +233,16 @@ namespace ts { } else if (token === SyntaxKind.ImportKeyword) { token = nextToken(); + if (token === SyntaxKind.TypeKeyword) { + const skipTypeKeyword = scanner.lookAhead(() => { + const token = scanner.scan(); + return token === SyntaxKind.Identifier || + isKeyword(token); + }); + if (skipTypeKeyword) { + token = nextToken(); + } + } if (token === SyntaxKind.Identifier || isKeyword(token)) { token = nextToken(); if (token === SyntaxKind.EqualsToken) { diff --git a/src/testRunner/unittests/services/preProcessFile.ts b/src/testRunner/unittests/services/preProcessFile.ts index 57198c23c10ac..c5f5672640144 100644 --- a/src/testRunner/unittests/services/preProcessFile.ts +++ b/src/testRunner/unittests/services/preProcessFile.ts @@ -16,18 +16,7 @@ describe("unittests:: services:: PreProcessFile:", () => { if (expected === actual) { return; } - if (!expected) { - assert.isTrue(false, `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); - } - assert.equal(actual.length, expected.length, `[${kind}] Actual array's length does not match expected length. Expected files: ${JSON.stringify(expected)}, actual files: ${JSON.stringify(actual)}`); - - for (let i = 0; i < expected.length; i++) { - const actualReference = actual[i]; - const expectedReference = expected[i]; - assert.equal(actualReference.fileName, expectedReference.fileName, `[${kind}] actual file path does not match expected. Expected: "${expectedReference.fileName}". Actual: "${actualReference.fileName}".`); - assert.equal(actualReference.pos, expectedReference.pos, `[${kind}] actual file start position does not match expected. Expected: "${expectedReference.pos}". Actual: "${actualReference.pos}".`); - assert.equal(actualReference.end, expectedReference.end, `[${kind}] actual file end pos does not match expected. Expected: "${expectedReference.end}". Actual: "${actualReference.end}".`); - } + assert.deepEqual(actual, expected, `Expected [${kind}] ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); } describe("Test preProcessFiles,", () => { @@ -209,6 +198,86 @@ describe("unittests:: services:: PreProcessFile:", () => { }); }); + it("Correctly handles import types", () => { + test("import type * as ns from \"m1\";" + "\n" + + "import type def, * as ns from \"m2\";" + "\n" + + "import type def from \"m3\";" + "\n" + + "import type {a} from \"m4\";" + "\n" + + "import type {a as A} from \"m5\";" + "\n" + + "import type {a as A, b, c as C} from \"m6\";" + "\n" + + "import type def , {a, b, c as C} from \"m7\";" + "\n" + + "import type from \"m8\";" + "\n" + + "import type T = require(\"m9\");" + "\n" + + "import type = require(\"m10\");" + "\n" + + "export import type T = require(\"m11\");" + "\n" + + "export import type = require(\"m12\");" + "\n", + /*readImportFile*/ true, + /*detectJavaScriptImports*/ false, + { + referencedFiles: [], + typeReferenceDirectives: [], + libReferenceDirectives: [], + importedFiles: [ + { fileName: "m1", pos: 25, end: 27 }, + { fileName: "m2", pos: 61, end: 63 }, + { fileName: "m3", pos: 88, end: 90 }, + { fileName: "m4", pos: 115, end: 117 }, + { fileName: "m5", pos: 147, end: 149 }, + { fileName: "m6", pos: 190, end: 192 }, + { fileName: "m7", pos: 234, end: 236 }, + { fileName: "m8", pos: 257, end: 259 }, + { fileName: "m9", pos: 287, end: 289 }, + { fileName: "m10", pos: 316, end: 319 }, + { fileName: "m11", pos: 355, end: 358 }, + { fileName: "m12", pos: 392, end: 395 }, + ], + ambientExternalModules: undefined, + isLibFile: false + }); + }); + + it("Correctly handles export types", () => { + test("export type * from \"m1\";" + "\n" + + "export type {a} from \"m2\";" + "\n" + + "export type {a as A} from \"m3\";" + "\n" + + "export type {a as A, b, c as C} from \"m4\";" + "\n", + /*readImportFile*/ true, + /*detectJavaScriptImports*/ false, + { + referencedFiles: [], + typeReferenceDirectives: [], + libReferenceDirectives: [], + importedFiles: [ + { fileName: "m1", pos: 19, end: 21 }, + { fileName: "m2", pos: 46, end: 48 }, + { fileName: "m3", pos: 78, end: 80 }, + { fileName: "m4", pos: 121, end: 123 }, + ], + ambientExternalModules: undefined, + isLibFile: false + }); + }); + + it("Correctly handles import type node", () => { + test("const x: import(\"m1\") = { x: 0, y: 0 };" + "\n" + + "let y: import(\"m2\").Bar.I = { a: \"\", b: 0 };" + "\n" + + "let shim: typeof import(\"m3\") = { Bar: Bar2 };" + "\n", + /*readImportFile*/ true, + /*detectJavaScriptImports*/ false, + { + referencedFiles: [], + typeReferenceDirectives: [], + libReferenceDirectives: [], + importedFiles: [ + { fileName: "m1", pos: 16, end: 18 }, + { fileName: "m2", pos: 54, end: 56 }, + { fileName: "m3", pos: 109, end: 111 }, + ], + ambientExternalModules: undefined, + isLibFile: false + }); + }); + it("Correctly return ambient external modules", () => { test(` declare module A {}