Skip to content

Commit cee4289

Browse files
author
Andy
authored
Add code fix to convert 'require' in a '.ts' file to an 'import' (#23711)
* Add code fix to convert 'require' in a '.ts' file to an 'import' * Only add suggestion for modules * Revert "Only add suggestion for modules" This reverts commit b1a728f.
1 parent 5ea4d3b commit cee4289

11 files changed

+101
-2
lines changed

src/compiler/diagnosticMessages.json

+12
Original file line numberDiff line numberDiff line change
@@ -3902,6 +3902,10 @@
39023902
"category": "Suggestion",
39033903
"code": 80004
39043904
},
3905+
"'require' call may be converted to an import.": {
3906+
"category": "Suggestion",
3907+
"code": 80005
3908+
},
39053909

39063910
"Add missing 'super()' call": {
39073911
"category": "Message",
@@ -4178,5 +4182,13 @@
41784182
"Generate 'get' and 'set' accessors": {
41794183
"category": "Message",
41804184
"code": 95046
4185+
},
4186+
"Convert 'require' to 'import'": {
4187+
"category": "Message",
4188+
"code": 95047
4189+
},
4190+
"Convert all 'require' to 'import'": {
4191+
"category": "Message",
4192+
"code": 95048
41814193
}
41824194
}

src/harness/fourslash.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,10 @@ namespace FourSlash {
324324
this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true);
325325
}
326326
});
327-
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
328-
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
327+
if (!compilationOptions.noLib) {
328+
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
329+
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
330+
}
329331
}
330332

331333
for (const file of testData.files) {

src/harness/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"../services/codefixes/inferFromUsage.ts",
113113
"../services/codefixes/fixInvalidImportSyntax.ts",
114114
"../services/codefixes/fixStrictClassInitialization.ts",
115+
"../services/codefixes/requireInTs.ts",
115116
"../services/codefixes/useDefaultImport.ts",
116117
"../services/refactors/extractSymbol.ts",
117118
"../services/refactors/generateGetAccessorAndSetAccessor.ts",

src/server/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
"../services/codefixes/inferFromUsage.ts",
109109
"../services/codefixes/fixInvalidImportSyntax.ts",
110110
"../services/codefixes/fixStrictClassInitialization.ts",
111+
"../services/codefixes/requireInTs.ts",
111112
"../services/codefixes/useDefaultImport.ts",
112113
"../services/refactors/extractSymbol.ts",
113114
"../services/refactors/generateGetAccessorAndSetAccessor.ts",

src/server/tsconfig.library.json

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
"../services/codefixes/inferFromUsage.ts",
115115
"../services/codefixes/fixInvalidImportSyntax.ts",
116116
"../services/codefixes/fixStrictClassInitialization.ts",
117+
"../services/codefixes/requireInTs.ts",
117118
"../services/codefixes/useDefaultImport.ts",
118119
"../services/refactors/extractSymbol.ts",
119120
"../services/refactors/generateGetAccessorAndSetAccessor.ts",

src/services/codefixes/requireInTs.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
const fixId = "requireInTs";
4+
const errorCodes = [Diagnostics.require_call_may_be_converted_to_an_import.code];
5+
registerCodeFix({
6+
errorCodes,
7+
getCodeActions(context) {
8+
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.program));
9+
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_require_to_import, fixId, Diagnostics.Convert_all_require_to_import)];
10+
},
11+
fixIds: [fixId],
12+
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start, context.program)),
13+
});
14+
15+
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, program: Program) {
16+
const { statement, name, required } = getInfo(sourceFile, pos);
17+
changes.replaceNode(sourceFile, statement, getAllowSyntheticDefaultImports(program.getCompilerOptions())
18+
? createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createImportClause(name, /*namedBindings*/ undefined), required)
19+
: createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, name, createExternalModuleReference(required)));
20+
}
21+
22+
interface Info { readonly statement: VariableStatement; readonly name: Identifier; readonly required: StringLiteralLike; }
23+
function getInfo(sourceFile: SourceFile, pos: number): Info {
24+
const { parent } = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false);
25+
if (!isRequireCall(parent, /*checkArgumentIsStringLiteralLike*/ true)) throw Debug.failBadSyntaxKind(parent);
26+
const decl = cast(parent.parent, isVariableDeclaration);
27+
return { statement: cast(decl.parent.parent, isVariableStatement), name: cast(decl.name, isIdentifier), required: parent.arguments[0] };
28+
}
29+
}

src/services/suggestionDiagnostics.ts

+13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ namespace ts {
3232
}
3333
check(sourceFile);
3434

35+
if (!isJsFile) {
36+
for (const statement of sourceFile.statements) {
37+
if (isVariableStatement(statement) &&
38+
statement.declarationList.flags & NodeFlags.Const &&
39+
statement.declarationList.declarations.length === 1) {
40+
const init = statement.declarationList.declarations[0].initializer;
41+
if (init && isRequireCall(init, /*checkArgumentIsStringLiteralLike*/ true)) {
42+
diags.push(createDiagnosticForNode(init, Diagnostics.require_call_may_be_converted_to_an_import));
43+
}
44+
}
45+
}
46+
}
47+
3548
if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) {
3649
for (const moduleSpecifier of sourceFile.imports) {
3750
const importNode = importFromModuleSpecifier(moduleSpecifier);

src/services/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"codefixes/inferFromUsage.ts",
106106
"codefixes/fixInvalidImportSyntax.ts",
107107
"codefixes/fixStrictClassInitialization.ts",
108+
"codefixes/requireInTs.ts",
108109
"codefixes/useDefaultImport.ts",
109110
"refactors/extractSymbol.ts",
110111
"refactors/generateGetAccessorAndSetAccessor.ts",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /a.ts
4+
////const a = [|require("a")|];
5+
6+
verify.getSuggestionDiagnostics([{
7+
message: "'require' call may be converted to an import.",
8+
code: 80005,
9+
}]);
10+
11+
verify.codeFix({
12+
description: "Convert 'require' to 'import'",
13+
newFileContent:
14+
`import a = require("a");`,
15+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /a.ts
4+
////const a = [|require("a")|];
5+
////const b = [|require("b")|];
6+
7+
verify.codeFixAll({
8+
fixId: "requireInTs",
9+
fixAllDescription: "Convert all 'require' to 'import'",
10+
newFileContent:
11+
`import a = require("a");
12+
import b = require("b");`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @allowSyntheticDefaultImports: true
4+
5+
// @Filename: /a.ts
6+
////const a = [|require("a")|];
7+
8+
verify.codeFix({
9+
description: "Convert 'require' to 'import'",
10+
newFileContent: `import a from "a";`,
11+
});

0 commit comments

Comments
 (0)