fix(38081): allow transforming object binding to named imports
This commit is contained in:
parent
1d1c1673bf
commit
bc502c8c3c
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");',
|
||||
});
|
9
tests/cases/fourslash/codeFixRequireInTs2.ts
Normal file
9
tests/cases/fourslash/codeFixRequireInTs2.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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";',
|
||||
});
|
6
tests/cases/fourslash/codeFixRequireInTs3.ts
Normal file
6
tests/cases/fourslash/codeFixRequireInTs3.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: /a.ts
|
||||
////const { a, b: { c } } = [|require("a")|];
|
||||
|
||||
verify.not.codeFixAvailable();
|
|
@ -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";`,
|
||||
});
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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";',
|
||||
});
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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";`,
|
||||
});
|
Loading…
Reference in a new issue