Skip to content

Commit f290146

Browse files
author
Andy Hanson
committed
Store import nodes in SourceFile.imports instead of StringLiteral nodes
1 parent 9d6386c commit f290146

10 files changed

Lines changed: 134 additions & 146 deletions

File tree

src/compiler/builderState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ namespace ts.BuilderState {
8383
if (sourceFile.imports && sourceFile.imports.length > 0) {
8484
const checker: TypeChecker = program.getTypeChecker();
8585
for (const importName of sourceFile.imports) {
86-
const symbol = checker.getSymbolAtLocation(importName);
86+
const symbol = checker.getSymbolAtLocation(moduleSpecifierFromImport(importName));
8787
if (symbol && symbol.declarations && symbol.declarations[0]) {
8888
const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]);
8989
if (declarationSourceFile) {

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25846,8 +25846,8 @@ namespace ts {
2584625846
}
2584725847
}
2584825848

25849-
function getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile {
25850-
const specifier = getExternalModuleName(declaration);
25849+
function getExternalModuleFileFromDeclaration(declaration: AnyImportOrReExport | ModuleDeclaration): SourceFile {
25850+
const specifier = declaration.kind === SyntaxKind.ModuleDeclaration ? tryCast(declaration.name, isStringLiteral) : getExternalModuleName(declaration);
2585125851
const moduleSymbol = resolveExternalModuleNameWorker(specifier, specifier, /*moduleNotFoundError*/ undefined);
2585225852
if (!moduleSymbol) {
2585325853
return undefined;

src/compiler/program.ts

Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ namespace ts {
971971

972972
// check imports and module augmentations
973973
collectExternalModuleReferences(newSourceFile);
974-
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
974+
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, importHasSameModuleName)) {
975975
// imports has changed
976976
oldProgram.structureIsReused = StructureIsReused.SafeModules;
977977
}
@@ -1574,6 +1574,10 @@ namespace ts {
15741574
return a.fileName === b.fileName;
15751575
}
15761576

1577+
function importHasSameModuleName(a: AnyValidImportOrReExport, b: AnyValidImportOrReExport): boolean {
1578+
return moduleSpecifierFromImport(a).text === moduleSpecifierFromImport(b).text;
1579+
}
1580+
15771581
function moduleNameIsEqualTo(a: StringLiteral | Identifier, b: StringLiteral | Identifier): boolean {
15781582
return a.kind === SyntaxKind.StringLiteral
15791583
? b.kind === SyntaxKind.StringLiteral && a.text === b.text
@@ -1589,7 +1593,7 @@ namespace ts {
15891593
const isExternalModuleFile = isExternalModule(file);
15901594

15911595
// file.imports may not be undefined if there exists dynamic import
1592-
let imports: StringLiteral[];
1596+
let imports: AnyValidImportOrReExport[] | undefined;
15931597
let moduleAugmentations: (StringLiteral | Identifier)[];
15941598
let ambientModules: string[];
15951599

@@ -1599,12 +1603,11 @@ namespace ts {
15991603
&& (options.isolatedModules || isExternalModuleFile)
16001604
&& !file.isDeclarationFile) {
16011605
// synthesize 'import "tslib"' declaration
1602-
const externalHelpersModuleReference = createLiteral(externalHelpersModuleNameText);
1603-
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined);
1606+
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, createLiteral(externalHelpersModuleNameText));
16041607
addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
1605-
externalHelpersModuleReference.parent = importDecl;
1608+
importDecl.moduleSpecifier.parent = importDecl;
16061609
importDecl.parent = file;
1607-
imports = [externalHelpersModuleReference];
1610+
imports = [importDecl as ImportDeclaration & { moduleSpecifier: StringLiteral }];
16081611
}
16091612

16101613
for (const node of file.statements) {
@@ -1621,66 +1624,55 @@ namespace ts {
16211624
return;
16221625

16231626
function collectModuleReferences(node: Statement, inAmbientModule: boolean): void {
1624-
switch (node.kind) {
1625-
case SyntaxKind.ImportDeclaration:
1626-
case SyntaxKind.ImportEqualsDeclaration:
1627-
case SyntaxKind.ExportDeclaration:
1628-
const moduleNameExpr = getExternalModuleName(node);
1629-
if (!moduleNameExpr || !isStringLiteral(moduleNameExpr)) {
1630-
break;
1631-
}
1632-
if (!moduleNameExpr.text) {
1633-
break;
1634-
}
1635-
1636-
// TypeScript 1.0 spec (April 2014): 12.1.6
1637-
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
1638-
// only through top - level external module names. Relative external module names are not permitted.
1639-
if (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text)) {
1640-
(imports || (imports = [])).push(moduleNameExpr);
1627+
if (isAnyImportOrReExport(node)) {
1628+
const moduleNameExpr = getExternalModuleName(node);
1629+
// TypeScript 1.0 spec (April 2014): 12.1.6
1630+
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
1631+
// only through top - level external module names. Relative external module names are not permitted.
1632+
if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
1633+
imports = append(imports, node as AnyValidImportOrReExport);
1634+
}
1635+
}
1636+
else if (isModuleDeclaration(node)) {
1637+
if (isAmbientModule(node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
1638+
const nameText = getTextOfIdentifierOrLiteral(node.name);
1639+
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
1640+
// This will happen in two cases:
1641+
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
1642+
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
1643+
// immediately nested in top level ambient module declaration .
1644+
if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
1645+
(moduleAugmentations || (moduleAugmentations = [])).push(node.name);
16411646
}
1642-
break;
1643-
case SyntaxKind.ModuleDeclaration:
1644-
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
1645-
const moduleName = (<ModuleDeclaration>node).name;
1646-
const nameText = getTextOfIdentifierOrLiteral(moduleName);
1647-
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
1648-
// This will happen in two cases:
1649-
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
1650-
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
1651-
// immediately nested in top level ambient module declaration .
1652-
if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
1653-
(moduleAugmentations || (moduleAugmentations = [])).push(moduleName);
1647+
else if (!inAmbientModule) {
1648+
if (file.isDeclarationFile) {
1649+
// for global .d.ts files record name of ambient module
1650+
(ambientModules || (ambientModules = [])).push(nameText);
16541651
}
1655-
else if (!inAmbientModule) {
1656-
if (file.isDeclarationFile) {
1657-
// for global .d.ts files record name of ambient module
1658-
(ambientModules || (ambientModules = [])).push(nameText);
1659-
}
1660-
// An AmbientExternalModuleDeclaration declares an external module.
1661-
// This type of declaration is permitted only in the global module.
1662-
// The StringLiteral must specify a top - level external module name.
1663-
// Relative external module names are not permitted
1664-
1665-
// NOTE: body of ambient module is always a module block, if it exists
1666-
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
1667-
if (body) {
1668-
for (const statement of body.statements) {
1669-
collectModuleReferences(statement, /*inAmbientModule*/ true);
1670-
}
1652+
// An AmbientExternalModuleDeclaration declares an external module.
1653+
// This type of declaration is permitted only in the global module.
1654+
// The StringLiteral must specify a top - level external module name.
1655+
// Relative external module names are not permitted
1656+
1657+
// NOTE: body of ambient module is always a module block, if it exists
1658+
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
1659+
if (body) {
1660+
for (const statement of body.statements) {
1661+
collectModuleReferences(statement, /*inAmbientModule*/ true);
16711662
}
16721663
}
16731664
}
1665+
}
16741666
}
16751667
}
16761668

16771669
function collectDynamicImportOrRequireCalls(node: Node): void {
16781670
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
1679-
(imports || (imports = [])).push(<StringLiteral>(<CallExpression>node).arguments[0]);
1671+
imports = append(imports, node);
16801672
}
16811673
// we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
16821674
else if (isImportCall(node) && node.arguments.length === 1 && node.arguments[0].kind === SyntaxKind.StringLiteral) {
1683-
(imports || (imports = [])).push(<StringLiteral>(<CallExpression>node).arguments[0]);
1675+
imports = append(imports, node as CallExpression as RequireOrImportCall);
16841676
}
16851677
else {
16861678
forEachChild(node, collectDynamicImportOrRequireCalls);
@@ -2426,7 +2418,7 @@ namespace ts {
24262418
}
24272419

24282420
function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
2429-
const res = imports.map(i => i.text);
2421+
const res = imports.map(i => moduleSpecifierFromImport(i).text);
24302422
for (const aug of moduleAugmentations) {
24312423
if (aug.kind === SyntaxKind.StringLiteral) {
24322424
res.push(aug.text);

src/compiler/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2557,7 +2557,7 @@ namespace ts {
25572557
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
25582558
/* @internal */ resolvedModules: Map<ResolvedModuleFull | undefined>;
25592559
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
2560-
/* @internal */ imports: ReadonlyArray<StringLiteral>;
2560+
/* @internal */ imports: ReadonlyArray<AnyValidImportOrReExport>;
25612561
// Identifier only if `declare global`
25622562
/* @internal */ moduleAugmentations: ReadonlyArray<StringLiteral | Identifier>;
25632563
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
@@ -3146,6 +3146,18 @@ namespace ts {
31463146
/* @internal */
31473147
export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration;
31483148

3149+
/* @internal */
3150+
export type AnyImportOrReExport = AnyImportSyntax | ExportDeclaration;
3151+
3152+
/* @internal */
3153+
export type AnyValidImportOrReExport =
3154+
| (ImportDeclaration | ExportDeclaration) & { moduleSpecifier: StringLiteral }
3155+
| ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral } }
3156+
| RequireOrImportCall;
3157+
3158+
/* @internal */
3159+
export type RequireOrImportCall = CallExpression & { arguments: [StringLiteralLike] };
3160+
31493161
/* @internal */
31503162
export interface SymbolVisibilityResult {
31513163
accessibility: SymbolAccessibility;

src/compiler/utilities.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ namespace ts {
538538
}
539539
}
540540

541+
export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport {
542+
return isAnyImportSyntax(node) || isExportDeclaration(node);
543+
}
544+
541545
// Gets the nearest enclosing block scope container that has the provided node
542546
// as a descendant, that is not the provided node.
543547
export function getEnclosingBlockScopeContainer(node: Node): Node {
@@ -1444,7 +1448,7 @@ namespace ts {
14441448
* exactly one argument (of the form 'require("name")').
14451449
* This function does not test if the node is in a JavaScript file or not.
14461450
*/
1447-
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteral: true): callExpression is CallExpression & { expression: Identifier, arguments: [StringLiteralLike] };
1451+
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteral: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
14481452
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteral: boolean): callExpression is CallExpression;
14491453
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteral: boolean): callExpression is CallExpression {
14501454
if (callExpression.kind !== SyntaxKind.CallExpression) {
@@ -1460,14 +1464,14 @@ namespace ts {
14601464
return false;
14611465
}
14621466
const arg = args[0];
1463-
return !checkArgumentIsStringLiteral || arg.kind === SyntaxKind.StringLiteral || arg.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
1467+
return !checkArgumentIsStringLiteral || isStringLiteralLike(arg);
14641468
}
14651469

14661470
export function isSingleOrDoubleQuote(charCode: number) {
14671471
return charCode === CharacterCodes.singleQuote || charCode === CharacterCodes.doubleQuote;
14681472
}
14691473

1470-
export function isStringDoubleQuoted(str: StringLiteral, sourceFile: SourceFile): boolean {
1474+
export function isStringDoubleQuoted(str: StringLiteralLike, sourceFile: SourceFile): boolean {
14711475
return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote;
14721476
}
14731477

@@ -1650,21 +1654,30 @@ namespace ts {
16501654
!!getJSDocTypeTag(expr.parent);
16511655
}
16521656

1653-
export function getExternalModuleName(node: Node): Expression {
1654-
if (node.kind === SyntaxKind.ImportDeclaration) {
1655-
return (<ImportDeclaration>node).moduleSpecifier;
1656-
}
1657-
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
1658-
const reference = (<ImportEqualsDeclaration>node).moduleReference;
1659-
if (reference.kind === SyntaxKind.ExternalModuleReference) {
1660-
return reference.expression;
1661-
}
1662-
}
1663-
if (node.kind === SyntaxKind.ExportDeclaration) {
1664-
return (<ExportDeclaration>node).moduleSpecifier;
1657+
/* @internal */
1658+
export function moduleSpecifierFromImport(node: AnyValidImportOrReExport): StringLiteralLike {
1659+
switch (node.kind) {
1660+
case SyntaxKind.ImportDeclaration:
1661+
case SyntaxKind.ExportDeclaration:
1662+
return node.moduleSpecifier;
1663+
case SyntaxKind.ImportEqualsDeclaration:
1664+
return node.moduleReference.expression;
1665+
case SyntaxKind.CallExpression:
1666+
return first(node.arguments);
1667+
default:
1668+
return Debug.assertNever(node);
16651669
}
1666-
if (isModuleWithStringLiteralName(node)) {
1667-
return node.name;
1670+
}
1671+
1672+
export function getExternalModuleName(node: AnyImportOrReExport): Expression {
1673+
switch (node.kind) {
1674+
case SyntaxKind.ImportDeclaration:
1675+
case SyntaxKind.ExportDeclaration:
1676+
return node.moduleSpecifier;
1677+
case SyntaxKind.ImportEqualsDeclaration:
1678+
return node.moduleReference.kind === SyntaxKind.ExternalModuleReference ? node.moduleReference.expression : undefined;
1679+
default:
1680+
return Debug.assertNever(node);
16681681
}
16691682
}
16701683

src/services/codefixes/convertToEs6Module.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,22 @@ namespace ts.codefix {
1919
});
2020

2121
function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker) {
22-
for (const moduleSpecifier of importingFile.imports) {
23-
const imported = getResolvedModule(importingFile, moduleSpecifier.text);
22+
for (const importNode of importingFile.imports) {
23+
const { text } = moduleSpecifierFromImport(importNode);
24+
const imported = getResolvedModule(importingFile, text);
2425
if (!imported || imported.resolvedFileName !== exportingFile.fileName) {
2526
continue;
2627
}
2728

28-
const { parent } = moduleSpecifier;
29-
switch (parent.kind) {
30-
case SyntaxKind.ExternalModuleReference: {
31-
const importEq = (parent as ExternalModuleReference).parent;
32-
changes.replaceNode(importingFile, importEq, makeImport(importEq.name, /*namedImports*/ undefined, moduleSpecifier.text));
29+
switch (importNode.kind) {
30+
case SyntaxKind.ImportEqualsDeclaration:
31+
changes.replaceNode(importingFile, importNode, makeImport(importNode.name, /*namedImports*/ undefined, text));
3332
break;
34-
}
35-
case SyntaxKind.CallExpression: {
36-
const call = parent as CallExpression;
37-
if (isRequireCall(call, /*checkArgumentIsStringLiteral*/ false)) {
38-
changes.replaceNode(importingFile, parent, createPropertyAccess(getSynthesizedDeepClone(call), "default"));
33+
case SyntaxKind.CallExpression:
34+
if (isRequireCall(importNode, /*checkArgumentIsStringLiteral*/ false)) {
35+
changes.replaceNode(importingFile, importNode, createPropertyAccess(getSynthesizedDeepClone(importNode), "default"));
3936
}
4037
break;
41-
}
4238
}
4339
}
4440
}

0 commit comments

Comments
 (0)