Skip to content

Commit 70cabdd

Browse files
aluanhaddadmhegazy
authored andcommitted
fix inconsistencies in import UMD code fixes adapting to module format (#19572)
* improve import code fixes for UMD modules - use default import under --allowSyntheticDefaultImports - import..require support - make make quick fix info match resulting import - make diagnostics * Address PR feedback: - extract test for synethetic default imports into getAllowSyntheticDefaultImports in core.ts - use getAllowSyntheticDefaultImports in checker.ts and importFixes.ts - move compilerOptions to top level destructuring * add tests * remove `import =` quick fix and supporting code. * update feature tests * remove errant whitespace
1 parent 5969aef commit 70cabdd

7 files changed

+98
-12
lines changed

src/compiler/checker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ namespace ts {
6666
const languageVersion = getEmitScriptTarget(compilerOptions);
6767
const modulekind = getEmitModuleKind(compilerOptions);
6868
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
69-
const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System;
69+
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
7070
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
7171
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
7272
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");

src/compiler/core.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,13 @@ namespace ts {
19161916
return moduleResolution;
19171917
}
19181918

1919+
export function getAllowSyntheticDefaultImports(compilerOptions: CompilerOptions) {
1920+
const moduleKind = getEmitModuleKind(compilerOptions);
1921+
return compilerOptions.allowSyntheticDefaultImports !== undefined
1922+
? compilerOptions.allowSyntheticDefaultImports
1923+
: moduleKind === ModuleKind.System;
1924+
}
1925+
19191926
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "alwaysStrict";
19201927

19211928
export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {

src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -3801,5 +3801,13 @@
38013801
"Install '{0}'": {
38023802
"category": "Message",
38033803
"code": 95014
3804+
},
3805+
"Import '{0}' = require(\"{1}\").": {
3806+
"category": "Message",
3807+
"code": 95015
3808+
},
3809+
"Import * as '{0}' from \"{1}\".": {
3810+
"category": "Message",
3811+
"code": 95016
38043812
}
38053813
}

src/services/codefixes/importFixes.ts

+28-11
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ namespace ts.codefix {
180180
export const enum ImportKind {
181181
Named,
182182
Default,
183-
Namespace,
183+
Namespace
184184
}
185185

186186
export function getCodeActionForImport(moduleSymbol: Symbol, context: ImportCodeFixOptions): ImportCodeAction[] {
@@ -212,7 +212,7 @@ namespace ts.codefix {
212212

213213
function getNamespaceImportName(declaration: AnyImportSyntax): Identifier {
214214
if (declaration.kind === SyntaxKind.ImportDeclaration) {
215-
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
215+
const namedBindings = declaration.importClause && isImportClause(declaration.importClause) && declaration.importClause.namedBindings;
216216
return namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport ? namedBindings.name : undefined;
217217
}
218218
else {
@@ -237,6 +237,8 @@ namespace ts.codefix {
237237
return parent as ImportDeclaration;
238238
case SyntaxKind.ExternalModuleReference:
239239
return (parent as ExternalModuleReference).parent;
240+
case SyntaxKind.ImportEqualsDeclaration:
241+
return parent as ImportEqualsDeclaration;
240242
default:
241243
Debug.assert(parent.kind === SyntaxKind.ExportDeclaration);
242244
// Ignore these, can't add imports to them.
@@ -249,11 +251,13 @@ namespace ts.codefix {
249251
const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax);
250252

251253
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier);
254+
const quotedModuleSpecifier = createStringLiteralWithQuoteStyle(sourceFile, moduleSpecifierWithoutQuotes);
252255
const importDecl = createImportDeclaration(
253-
/*decorators*/ undefined,
254-
/*modifiers*/ undefined,
256+
/*decorators*/ undefined,
257+
/*modifiers*/ undefined,
255258
createImportClauseOfKind(kind, symbolName),
256-
createStringLiteralWithQuoteStyle(sourceFile, moduleSpecifierWithoutQuotes));
259+
quotedModuleSpecifier);
260+
257261
const changes = ChangeTracker.with(context, changeTracker => {
258262
if (lastImportDeclaration) {
259263
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: newLineCharacter });
@@ -263,11 +267,15 @@ namespace ts.codefix {
263267
}
264268
});
265269

270+
const actionFormat = kind === ImportKind.Namespace
271+
? Diagnostics.Import_Asterisk_as_0_from_1
272+
: Diagnostics.Import_0_from_1;
273+
266274
// if this file doesn't have any import statements, insert an import statement and then insert a new line
267275
// between the only import statement and user code. Otherwise just insert the statement because chances
268276
// are there are already a new line seperating code and import statements.
269277
return createCodeAction(
270-
Diagnostics.Import_0_from_1,
278+
actionFormat,
271279
[symbolName, moduleSpecifierWithoutQuotes],
272280
changes,
273281
"NewImport",
@@ -282,7 +290,7 @@ namespace ts.codefix {
282290
return literal;
283291
}
284292

285-
function createImportClauseOfKind(kind: ImportKind, symbolName: string) {
293+
function createImportClauseOfKind(kind: ImportKind.Default | ImportKind.Named | ImportKind.Namespace, symbolName: string) {
286294
const id = createIdentifier(symbolName);
287295
switch (kind) {
288296
case ImportKind.Default:
@@ -534,7 +542,7 @@ namespace ts.codefix {
534542
declarations: ReadonlyArray<AnyImportSyntax>): ImportCodeAction {
535543
const fromExistingImport = firstDefined(declarations, declaration => {
536544
if (declaration.kind === SyntaxKind.ImportDeclaration && declaration.importClause) {
537-
const changes = tryUpdateExistingImport(ctx, declaration.importClause);
545+
const changes = tryUpdateExistingImport(ctx, isImportClause(declaration.importClause) && declaration.importClause || undefined);
538546
if (changes) {
539547
const moduleSpecifierWithoutQuotes = stripQuotes(declaration.moduleSpecifier.getText());
540548
return createCodeAction(
@@ -564,9 +572,10 @@ namespace ts.codefix {
564572
return expression && isStringLiteral(expression) ? expression.text : undefined;
565573
}
566574

567-
function tryUpdateExistingImport(context: SymbolContext & { kind: ImportKind }, importClause: ImportClause): FileTextChanges[] | undefined {
575+
function tryUpdateExistingImport(context: SymbolContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined {
568576
const { symbolName, sourceFile, kind } = context;
569-
const { name, namedBindings } = importClause;
577+
const { name } = importClause;
578+
const { namedBindings } = importClause.kind !== SyntaxKind.ImportEqualsDeclaration && importClause;
570579
switch (kind) {
571580
case ImportKind.Default:
572581
return name ? undefined : ChangeTracker.with(context, t =>
@@ -627,7 +636,7 @@ namespace ts.codefix {
627636
}
628637

629638
function getActionsForUMDImport(context: ImportCodeFixContext): ImportCodeAction[] {
630-
const { checker, symbolToken } = context;
639+
const { checker, symbolToken, compilerOptions } = context;
631640
const umdSymbol = checker.getSymbolAtLocation(symbolToken);
632641
let symbol: ts.Symbol;
633642
let symbolName: string;
@@ -644,6 +653,14 @@ namespace ts.codefix {
644653
Debug.fail("Either the symbol or the JSX namespace should be a UMD global if we got here");
645654
}
646655

656+
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
657+
658+
// Import a synthetic `default` if enabled.
659+
if (allowSyntheticDefaultImports) {
660+
return getCodeActionForImport(symbol, { ...context, symbolName, kind: ImportKind.Default });
661+
}
662+
663+
// Fall back to the `import * as ns` style import.
647664
return getCodeActionForImport(symbol, { ...context, symbolName, kind: ImportKind.Namespace });
648665
}
649666

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path="fourslash.ts" />
2+
// @AllowSyntheticDefaultImports: true
3+
4+
// @Filename: a/f1.ts
5+
//// [|export var x = 0;
6+
//// bar/*0*/();|]
7+
8+
// @Filename: a/foo.d.ts
9+
//// declare function bar(): number;
10+
//// export = bar;
11+
//// export as namespace bar;
12+
13+
verify.importFixAtPosition([
14+
`import bar from "./foo";
15+
16+
export var x = 0;
17+
bar();`
18+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path="fourslash.ts" />
2+
// @Module: system
3+
4+
// @Filename: a/f1.ts
5+
//// [|export var x = 0;
6+
//// bar/*0*/();|]
7+
8+
// @Filename: a/foo.d.ts
9+
//// declare function bar(): number;
10+
//// export = bar;
11+
//// export as namespace bar;
12+
13+
verify.importFixAtPosition([
14+
`import bar from "./foo";
15+
16+
export var x = 0;
17+
bar();`
18+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path="fourslash.ts" />
2+
// @AllowSyntheticDefaultImports: false
3+
4+
// @Filename: a/f1.ts
5+
//// [|export var x = 0;
6+
//// bar/*0*/();|]
7+
8+
// @Filename: a/foo.d.ts
9+
//// declare function bar(): number;
10+
//// export = bar;
11+
//// export as namespace bar;
12+
13+
verify.importFixAtPosition([
14+
`import * as bar from "./foo";
15+
16+
export var x = 0;
17+
bar();`
18+
]);

0 commit comments

Comments
 (0)