diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2776c6595a8..2300c4fad5f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -745,7 +745,10 @@ module ts { forEach(symbol.declarations, node => { if (node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.ModuleDeclaration) { forEach((node).exportStars, exportStar => { - visit(resolveExternalModuleName(exportStar, exportStar.moduleSpecifier)); + var moduleSymbol = resolveExternalModuleName(exportStar, exportStar.moduleSpecifier); + if (moduleSymbol) { + visit(moduleSymbol); + } }); } }); diff --git a/src/services/services.ts b/src/services/services.ts index ececc1082ca..71248b007b6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1795,33 +1795,140 @@ module ts { }); } + function recordModuleName() { + var importPath = scanner.getTokenValue(); + var pos = scanner.getTokenPos(); + importedFiles.push({ + fileName: importPath, + pos: pos, + end: pos + importPath.length + }); + } + function processImport(): void { scanner.setText(sourceText); var token = scanner.scan(); // Look for: - // import foo = module("foo"); + // import "mod"; + // import d from "mod" + // import {a as A } from "mod"; + // import * as NS from "mod" + // import d, {a, b as B} from "mod" + // import i = require("mod"); + // + // export * from "mod" + // export {a as b} from "mod" + while (token !== SyntaxKind.EndOfFileToken) { if (token === SyntaxKind.ImportKeyword) { token = scanner.scan(); - if (token === SyntaxKind.Identifier) { - token = scanner.scan(); - if (token === SyntaxKind.EqualsToken) { + if (token === SyntaxKind.StringLiteral) { + // import "mod"; + recordModuleName(); + continue; + } + else { + if (token === SyntaxKind.Identifier) { token = scanner.scan(); - if (token === SyntaxKind.RequireKeyword) { + if (token === SyntaxKind.FromKeyword) { token = scanner.scan(); - if (token === SyntaxKind.OpenParenToken) { + if (token === SyntaxKind.StringLiteral) { + // import d from "mod"; + recordModuleName(); + continue + } + } + else if (token === SyntaxKind.EqualsToken) { + token = scanner.scan(); + if (token === SyntaxKind.RequireKeyword) { token = scanner.scan(); - if (token === SyntaxKind.StringLiteral) { - var importPath = scanner.getTokenValue(); - var pos = scanner.getTokenPos(); - importedFiles.push({ - fileName: importPath, - pos: pos, - end: pos + importPath.length - }); + if (token === SyntaxKind.OpenParenToken) { + token = scanner.scan(); + if (token === SyntaxKind.StringLiteral) { + // import i = require("mod"); + recordModuleName(); + continue; + } } } } + else if (token === SyntaxKind.CommaToken) { + // consume comma and keep going + token = scanner.scan(); + } + else { + // unknown syntax + continue; + } + } + + if (token === SyntaxKind.OpenBraceToken) { + token = scanner.scan(); + // consume "{ a as B, c, d as D}" clauses + while (token !== SyntaxKind.CloseBraceToken) { + token = scanner.scan(); + } + + if (token === SyntaxKind.CloseBraceToken) { + token = scanner.scan(); + if (token === SyntaxKind.FromKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.StringLiteral) { + // import {a as A} from "mod"; + // import d, {a, b as B} from "mod" + recordModuleName(); + } + } + } + } + else if (token === SyntaxKind.AsteriskToken) { + token = scanner.scan(); + if (token === SyntaxKind.AsKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.Identifier) { + token = scanner.scan(); + if (token === SyntaxKind.FromKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.StringLiteral) { + // import * as NS from "mod" + // import d, * as NS from "mod" + recordModuleName(); + } + } + } + } + } + } + } + else if (token === SyntaxKind.ExportKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.OpenBraceToken) { + token = scanner.scan(); + // consume "{ a as B, c, d as D}" clauses + while (token !== SyntaxKind.CloseBraceToken) { + token = scanner.scan(); + } + + if (token === SyntaxKind.CloseBraceToken) { + token = scanner.scan(); + if (token === SyntaxKind.FromKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.StringLiteral) { + // export {a as A} from "mod"; + // export {a, b as B} from "mod" + recordModuleName(); + } + } + } + } + else if (token === SyntaxKind.AsteriskToken) { + token = scanner.scan(); + if (token === SyntaxKind.FromKeyword) { + token = scanner.scan(); + if (token === SyntaxKind.StringLiteral) { + // export * from "mod" + recordModuleName(); + } } } } diff --git a/tests/cases/unittests/services/preProcessFile.ts b/tests/cases/unittests/services/preProcessFile.ts index e0d397838e0..c504f444f18 100644 --- a/tests/cases/unittests/services/preProcessFile.ts +++ b/tests/cases/unittests/services/preProcessFile.ts @@ -26,7 +26,7 @@ describe('PreProcessFile:', function () { var expectedImportedFile = expectedImportedFiles[i]; assert.equal(resultImportedFile.fileName, expectedImportedFile.fileName, "Imported file path does not match expected. Expected: " + expectedImportedFile.fileName + ". Actual: " + resultImportedFile.fileName + "."); - + assert.equal(resultImportedFile.pos, expectedImportedFile.pos, "Imported file position does not match expected. Expected: " + expectedImportedFile.pos + ". Actual: " + resultImportedFile.pos + "."); assert.equal(resultImportedFile.end, expectedImportedFile.end, "Imported file length does not match expected. Expected: " + expectedImportedFile.end + ". Actual: " + resultImportedFile.end + "."); @@ -37,7 +37,7 @@ describe('PreProcessFile:', function () { var expectedReferencedFile = expectedReferencedFiles[i]; assert.equal(resultReferencedFile.fileName, expectedReferencedFile.fileName, "Referenced file path does not match expected. Expected: " + expectedReferencedFile.fileName + ". Actual: " + resultReferencedFile.fileName + "."); - + assert.equal(resultReferencedFile.pos, expectedReferencedFile.pos, "Referenced file position does not match expected. Expected: " + expectedReferencedFile.pos + ". Actual: " + resultReferencedFile.pos + "."); assert.equal(resultReferencedFile.end, expectedReferencedFile.end, "Referenced file length does not match expected. Expected: " + expectedReferencedFile.end + ". Actual: " + resultReferencedFile.end + "."); @@ -108,6 +108,48 @@ describe('PreProcessFile:', function () { isLibFile: false }) }); + + it("Correctly return ES6 imports", function () { + test("import * as ns from \"m1\";" + "\n" + + "import def, * as ns from \"m2\";" + "\n" + + "import def from \"m3\";" + "\n" + + "import {a} from \"m4\";" + "\n" + + "import {a as A} from \"m5\";" + "\n" + + "import {a as A, b, c as C} from \"m6\";" + "\n" + + "import def , {a, b, c as C} from \"m7\";" + "\n", + true, + { + referencedFiles: [], + importedFiles: [ + { fileName: "m1", pos: 20, end: 22 }, + { fileName: "m2", pos: 51, end: 53 }, + { fileName: "m3", pos: 73, end: 75 }, + { fileName: "m4", pos: 95, end: 97 }, + { fileName: "m5", pos: 122, end: 124 }, + { fileName: "m6", pos: 160, end: 162 }, + { fileName: "m7", pos: 199, end: 201 } + ], + isLibFile: false + }) + }); + + it("Correctly return ES6 exports", function () { + test("export * from \"m1\";" + "\n" + + "export {a} from \"m2\";" + "\n" + + "export {a as A} from \"m3\";" + "\n" + + "export {a as A, b, c as C} from \"m4\";" + "\n", + true, + { + referencedFiles: [], + importedFiles: [ + { fileName: "m1", pos: 14, end: 16 }, + { fileName: "m2", pos: 36, end: 38 }, + { fileName: "m3", pos: 63, end: 65 }, + { fileName: "m4", pos: 101, end: 103 }, + ], + isLibFile: false + }) + }); }); });