diff --git a/src/services/codefixes/requireInTs.ts b/src/services/codefixes/requireInTs.ts
index ee2a898147..ee53b3edaf 100644
--- a/src/services/codefixes/requireInTs.ts
+++ b/src/services/codefixes/requireInTs.ts
@@ -5,25 +5,68 @@ namespace ts.codefix {
registerCodeFix({
errorCodes,
getCodeActions(context) {
- const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.program));
+ const info = getInfo(context.sourceFile, context.program, context.span.start);
+ if (!info) {
+ return undefined;
+ }
+ const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, info));
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_require_to_import, fixId, Diagnostics.Convert_all_require_to_import)];
},
fixIds: [fixId],
- getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start, context.program)),
+ getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
+ const info = getInfo(diag.file, context.program, diag.start);
+ if (info) {
+ doChange(changes, context.sourceFile, info);
+ }
+ }),
});
- function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, program: Program) {
- const { statement, name, required } = getInfo(sourceFile, pos);
- changes.replaceNode(sourceFile, statement, getAllowSyntheticDefaultImports(program.getCompilerOptions())
- ? createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createImportClause(name, /*namedBindings*/ undefined), required)
- : createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, name, createExternalModuleReference(required)));
+ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info) {
+ const { allowSyntheticDefaults, defaultImportName, namedImports, statement, required } = info;
+ changes.replaceNode(sourceFile, statement, defaultImportName && !allowSyntheticDefaults
+ ? createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, defaultImportName, createExternalModuleReference(required))
+ : createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createImportClause(defaultImportName, namedImports), required));
}
- interface Info { readonly statement: VariableStatement; readonly name: Identifier; readonly required: StringLiteralLike; }
- function getInfo(sourceFile: SourceFile, pos: number): Info {
+ interface Info {
+ readonly allowSyntheticDefaults: boolean;
+ readonly defaultImportName: Identifier | undefined;
+ readonly namedImports: NamedImports | undefined;
+ readonly statement: VariableStatement;
+ readonly required: StringLiteralLike;
+ }
+
+ function getInfo(sourceFile: SourceFile, program: Program, pos: number): Info | undefined {
const { parent } = getTokenAtPosition(sourceFile, pos);
- if (!isRequireCall(parent, /*checkArgumentIsStringLiteralLike*/ true)) throw Debug.failBadSyntaxKind(parent);
+ if (!isRequireCall(parent, /*checkArgumentIsStringLiteralLike*/ true)) {
+ throw Debug.failBadSyntaxKind(parent);
+ }
+
const decl = cast(parent.parent, isVariableDeclaration);
- return { statement: cast(decl.parent.parent, isVariableStatement), name: cast(decl.name, isIdentifier), required: parent.arguments[0] };
+ const defaultImportName = tryCast(decl.name, isIdentifier);
+ const namedImports = isObjectBindingPattern(decl.name) ? tryCreateNamedImportsFromObjectBindingPattern(decl.name) : undefined;
+ if (defaultImportName || namedImports) {
+ return {
+ allowSyntheticDefaults: getAllowSyntheticDefaultImports(program.getCompilerOptions()),
+ defaultImportName,
+ namedImports,
+ statement: cast(decl.parent.parent, isVariableStatement),
+ required: first(parent.arguments)
+ };
+ }
+ }
+
+ function tryCreateNamedImportsFromObjectBindingPattern(node: ObjectBindingPattern): NamedImports | undefined {
+ const importSpecifiers: ImportSpecifier[] = [];
+ for (const element of node.elements) {
+ if (!isIdentifier(element.name) || element.initializer) {
+ return undefined;
+ }
+ importSpecifiers.push(createImportSpecifier(tryCast(element.propertyName, isIdentifier), element.name));
+ }
+
+ if (importSpecifiers.length) {
+ return createNamedImports(importSpecifiers);
+ }
}
}
diff --git a/tests/cases/fourslash/codeFixRequireInTs.ts b/tests/cases/fourslash/codeFixRequireInTs1.ts
similarity index 64%
rename from tests/cases/fourslash/codeFixRequireInTs.ts
rename to tests/cases/fourslash/codeFixRequireInTs1.ts
index 345b15f69f..773afe0eb1 100644
--- a/tests/cases/fourslash/codeFixRequireInTs.ts
+++ b/tests/cases/fourslash/codeFixRequireInTs1.ts
@@ -9,7 +9,6 @@ verify.getSuggestionDiagnostics([{
}]);
verify.codeFix({
- description: "Convert 'require' to 'import'",
- newFileContent:
-`import a = require("a");`,
+ description: ts.Diagnostics.Convert_require_to_import.message,
+ newFileContent: 'import a = require("a");',
});
diff --git a/tests/cases/fourslash/codeFixRequireInTs2.ts b/tests/cases/fourslash/codeFixRequireInTs2.ts
new file mode 100644
index 0000000000..6d6325cdfe
--- /dev/null
+++ b/tests/cases/fourslash/codeFixRequireInTs2.ts
@@ -0,0 +1,9 @@
+///
+
+// @Filename: /a.ts
+////const { a, b, c } = [|require("a")|];
+
+verify.codeFix({
+ description: ts.Diagnostics.Convert_require_to_import.message,
+ newFileContent: 'import { a, b, c } from "a";',
+});
diff --git a/tests/cases/fourslash/codeFixRequireInTs3.ts b/tests/cases/fourslash/codeFixRequireInTs3.ts
new file mode 100644
index 0000000000..3943f38fc1
--- /dev/null
+++ b/tests/cases/fourslash/codeFixRequireInTs3.ts
@@ -0,0 +1,6 @@
+///
+
+// @Filename: /a.ts
+////const { a, b: { c } } = [|require("a")|];
+
+verify.not.codeFixAvailable();
diff --git a/tests/cases/fourslash/codeFixRequireInTs_all.ts b/tests/cases/fourslash/codeFixRequireInTs_all.ts
index 9f9fe4824a..d37d2455e9 100644
--- a/tests/cases/fourslash/codeFixRequireInTs_all.ts
+++ b/tests/cases/fourslash/codeFixRequireInTs_all.ts
@@ -3,11 +3,15 @@
// @Filename: /a.ts
////const a = [|require("a")|];
////const b = [|require("b")|];
+////const { c } = [|require("c")|];
+////const { d } = [|require("d")|];
verify.codeFixAll({
fixId: "requireInTs",
- fixAllDescription: "Convert all 'require' to 'import'",
+ fixAllDescription: ts.Diagnostics.Convert_all_require_to_import.message,
newFileContent:
`import a = require("a");
-import b = require("b");`,
+import b = require("b");
+import { c } from "c";
+import { d } from "d";`,
});
diff --git a/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports.ts b/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports.ts
index 949387af2e..5fcad968e2 100644
--- a/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports.ts
+++ b/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports.ts
@@ -1,11 +1,10 @@
///
// @allowSyntheticDefaultImports: true
-
// @Filename: /a.ts
////const a = [|require("a")|];
verify.codeFix({
- description: "Convert 'require' to 'import'",
- newFileContent: `import a from "a";`,
+ description: ts.Diagnostics.Convert_require_to_import.message,
+ newFileContent: 'import a from "a";',
});
diff --git a/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports_all.ts b/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports_all.ts
new file mode 100644
index 0000000000..8e0017eb2a
--- /dev/null
+++ b/tests/cases/fourslash/codeFixRequireInTs_allowSyntheticDefaultImports_all.ts
@@ -0,0 +1,18 @@
+///
+
+// @allowSyntheticDefaultImports: true
+// @Filename: /a.ts
+////const a = [|require("a")|];
+////const b = [|require("b")|];
+////const { c } = [|require("c")|];
+////const { d } = [|require("d")|];
+
+verify.codeFixAll({
+ fixId: "requireInTs",
+ fixAllDescription: ts.Diagnostics.Convert_all_require_to_import.message,
+ newFileContent:
+`import a from "a";
+import b from "b";
+import { c } from "c";
+import { d } from "d";`,
+});