Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3130,7 +3130,7 @@ Actual: ${stringify(fullActual)}`);
const action = ts.first(refactor.actions);
assert(action.name === "Move to a new file" && action.description === "Move to a new file");

const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, ts.defaultPreferences)!;
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.defaultPreferences)!;
for (const edit of editInfo.edits) {
const newContent = options.newFileContents[edit.fileName];
if (newContent === undefined) {
Expand Down Expand Up @@ -4836,5 +4836,6 @@ namespace FourSlashInterface {

export interface MoveToNewFileOptions {
readonly newFileContents: { readonly [fileName: string]: string };
readonly preferences?: ts.UserPreferences;
}
}
49 changes: 25 additions & 24 deletions src/services/codefixes/convertToEs6Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module.code],
getCodeActions(context) {
const { sourceFile, program } = context;
const { sourceFile, program, preferences } = context;
const changes = textChanges.ChangeTracker.with(context, changes => {
const moduleExportsChangedToDefault = convertFileToEs6Module(sourceFile, program.getTypeChecker(), changes, program.getCompilerOptions().target!);
const moduleExportsChangedToDefault = convertFileToEs6Module(sourceFile, program.getTypeChecker(), changes, program.getCompilerOptions().target!, preferences);
if (moduleExportsChangedToDefault) {
for (const importingFile of program.getSourceFiles()) {
fixImportOfModuleExports(importingFile, sourceFile, changes);
fixImportOfModuleExports(importingFile, sourceFile, changes, preferences);
}
}
});
Expand All @@ -17,7 +17,7 @@ namespace ts.codefix {
},
});

function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker) {
function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker, preferences: UserPreferences) {
for (const moduleSpecifier of importingFile.imports) {
const imported = getResolvedModule(importingFile, moduleSpecifier.text);
if (!imported || imported.resolvedFileName !== exportingFile.fileName) {
Expand All @@ -27,7 +27,7 @@ namespace ts.codefix {
const importNode = importFromModuleSpecifier(moduleSpecifier);
switch (importNode.kind) {
case SyntaxKind.ImportEqualsDeclaration:
changes.replaceNode(importingFile, importNode, makeImport(importNode.name, /*namedImports*/ undefined, moduleSpecifier));
changes.replaceNode(importingFile, importNode, makeImport(importNode.name, /*namedImports*/ undefined, moduleSpecifier, preferences));
break;
case SyntaxKind.CallExpression:
if (isRequireCall(importNode, /*checkArgumentIsStringLiteralLike*/ false)) {
Expand All @@ -39,13 +39,13 @@ namespace ts.codefix {
}

/** @returns Whether we converted a `module.exports =` to a default export. */
function convertFileToEs6Module(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, target: ScriptTarget): ModuleExportsChanged {
function convertFileToEs6Module(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, target: ScriptTarget, preferences: UserPreferences): ModuleExportsChanged {
const identifiers: Identifiers = { original: collectFreeIdentifiers(sourceFile), additional: createMap<true>() };
const exports = collectExportRenames(sourceFile, checker, identifiers);
convertExportsAccesses(sourceFile, exports, changes);
let moduleExportsChangedToDefault = false;
for (const statement of sourceFile.statements) {
const moduleExportsChanged = convertStatement(sourceFile, statement, checker, changes, identifiers, target, exports);
const moduleExportsChanged = convertStatement(sourceFile, statement, checker, changes, identifiers, target, exports, preferences);
moduleExportsChangedToDefault = moduleExportsChangedToDefault || moduleExportsChanged;
}
return moduleExportsChangedToDefault;
Expand Down Expand Up @@ -98,18 +98,18 @@ namespace ts.codefix {
/** Whether `module.exports =` was changed to `export default` */
type ModuleExportsChanged = boolean;

function convertStatement(sourceFile: SourceFile, statement: Statement, checker: TypeChecker, changes: textChanges.ChangeTracker, identifiers: Identifiers, target: ScriptTarget, exports: ExportRenames): ModuleExportsChanged {
function convertStatement(sourceFile: SourceFile, statement: Statement, checker: TypeChecker, changes: textChanges.ChangeTracker, identifiers: Identifiers, target: ScriptTarget, exports: ExportRenames, preferences: UserPreferences): ModuleExportsChanged {
switch (statement.kind) {
case SyntaxKind.VariableStatement:
convertVariableStatement(sourceFile, statement as VariableStatement, changes, checker, identifiers, target);
convertVariableStatement(sourceFile, statement as VariableStatement, changes, checker, identifiers, target, preferences);
return false;
case SyntaxKind.ExpressionStatement: {
const { expression } = statement as ExpressionStatement;
switch (expression.kind) {
case SyntaxKind.CallExpression: {
if (isRequireCall(expression, /*checkArgumentIsStringLiteralLike*/ true)) {
// For side-effecting require() call, just make a side-effecting import.
changes.replaceNode(sourceFile, statement, makeImport(/*name*/ undefined, /*namedImports*/ undefined, expression.arguments[0]));
changes.replaceNode(sourceFile, statement, makeImport(/*name*/ undefined, /*namedImports*/ undefined, expression.arguments[0], preferences));
}
return false;
}
Expand All @@ -125,7 +125,7 @@ namespace ts.codefix {
}
}

function convertVariableStatement(sourceFile: SourceFile, statement: VariableStatement, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers, target: ScriptTarget): void {
function convertVariableStatement(sourceFile: SourceFile, statement: VariableStatement, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers, target: ScriptTarget, preferences: UserPreferences): void {
const { declarationList } = statement;
let foundImport = false;
const newNodes = flatMap(declarationList.declarations, decl => {
Expand All @@ -138,11 +138,11 @@ namespace ts.codefix {
}
else if (isRequireCall(initializer, /*checkArgumentIsStringLiteralLike*/ true)) {
foundImport = true;
return convertSingleImport(sourceFile, name, initializer.arguments[0], changes, checker, identifiers, target);
return convertSingleImport(sourceFile, name, initializer.arguments[0], changes, checker, identifiers, target, preferences);
}
else if (isPropertyAccessExpression(initializer) && isRequireCall(initializer.expression, /*checkArgumentIsStringLiteralLike*/ true)) {
foundImport = true;
return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0], identifiers);
return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0], identifiers, preferences);
}
}
// Move it out to its own variable statement. (This will not be used if `!foundImport`)
Expand All @@ -155,20 +155,20 @@ namespace ts.codefix {
}

/** Converts `const name = require("moduleSpecifier").propertyName` */
function convertPropertyAccessImport(name: BindingName, propertyName: string, moduleSpecifier: StringLiteralLike, identifiers: Identifiers): ReadonlyArray<Node> {
function convertPropertyAccessImport(name: BindingName, propertyName: string, moduleSpecifier: StringLiteralLike, identifiers: Identifiers, preferences: UserPreferences): ReadonlyArray<Node> {
switch (name.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern: {
// `const [a, b] = require("c").d` --> `import { d } from "c"; const [a, b] = d;`
const tmp = makeUniqueName(propertyName, identifiers);
return [
makeSingleImport(tmp, propertyName, moduleSpecifier),
makeSingleImport(tmp, propertyName, moduleSpecifier, preferences),
makeConst(/*modifiers*/ undefined, name, createIdentifier(tmp)),
];
}
case SyntaxKind.Identifier:
// `const a = require("b").c` --> `import { c as a } from "./b";
return [makeSingleImport(name.text, propertyName, moduleSpecifier)];
return [makeSingleImport(name.text, propertyName, moduleSpecifier, preferences)];
default:
return Debug.assertNever(name);
}
Expand Down Expand Up @@ -340,6 +340,7 @@ namespace ts.codefix {
checker: TypeChecker,
identifiers: Identifiers,
target: ScriptTarget,
preferences: UserPreferences,
): ReadonlyArray<Node> {
switch (name.kind) {
case SyntaxKind.ObjectBindingPattern: {
Expand All @@ -348,7 +349,7 @@ namespace ts.codefix {
? undefined
: makeImportSpecifier(e.propertyName && (e.propertyName as Identifier).text, e.name.text)); // tslint:disable-line no-unnecessary-type-assertion (TODO: GH#18217)
if (importSpecifiers) {
return [makeImport(/*name*/ undefined, importSpecifiers, moduleSpecifier)];
return [makeImport(/*name*/ undefined, importSpecifiers, moduleSpecifier, preferences)];
}
}
// falls through -- object destructuring has an interesting pattern and must be a variable declaration
Expand All @@ -359,12 +360,12 @@ namespace ts.codefix {
*/
const tmp = makeUniqueName(moduleSpecifierToValidIdentifier(moduleSpecifier.text, target), identifiers);
return [
makeImport(createIdentifier(tmp), /*namedImports*/ undefined, moduleSpecifier),
makeImport(createIdentifier(tmp), /*namedImports*/ undefined, moduleSpecifier, preferences),
makeConst(/*modifiers*/ undefined, getSynthesizedDeepClone(name), createIdentifier(tmp)),
];
}
case SyntaxKind.Identifier:
return convertSingleIdentifierImport(file, name, moduleSpecifier, changes, checker, identifiers);
return convertSingleIdentifierImport(file, name, moduleSpecifier, changes, checker, identifiers, preferences);
default:
return Debug.assertNever(name);
}
Expand All @@ -374,7 +375,7 @@ namespace ts.codefix {
* Convert `import x = require("x").`
* Also converts uses like `x.y()` to `y()` and uses a named import.
*/
function convertSingleIdentifierImport(file: SourceFile, name: Identifier, moduleSpecifier: StringLiteralLike, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers): ReadonlyArray<Node> {
function convertSingleIdentifierImport(file: SourceFile, name: Identifier, moduleSpecifier: StringLiteralLike, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers, preferences: UserPreferences): ReadonlyArray<Node> {
const nameSymbol = checker.getSymbolAtLocation(name);
// Maps from module property name to name actually used. (The same if there isn't shadowing.)
const namedBindingsNames = createMap<string>();
Expand Down Expand Up @@ -409,7 +410,7 @@ namespace ts.codefix {
// If it was unused, ensure that we at least import *something*.
needDefaultImport = true;
}
return [makeImport(needDefaultImport ? getSynthesizedDeepClone(name) : undefined, namedBindings, moduleSpecifier)];
return [makeImport(needDefaultImport ? getSynthesizedDeepClone(name) : undefined, namedBindings, moduleSpecifier, preferences)];
}

// Identifiers helpers
Expand Down Expand Up @@ -481,10 +482,10 @@ namespace ts.codefix {
getSynthesizedDeepClones(cls.members));
}

function makeSingleImport(localName: string, propertyName: string, moduleSpecifier: StringLiteralLike): ImportDeclaration {
function makeSingleImport(localName: string, propertyName: string, moduleSpecifier: StringLiteralLike, preferences: UserPreferences): ImportDeclaration {
return propertyName === "default"
? makeImport(createIdentifier(localName), /*namedImports*/ undefined, moduleSpecifier)
: makeImport(/*name*/ undefined, [makeImportSpecifier(propertyName, localName)], moduleSpecifier);
? makeImport(createIdentifier(localName), /*namedImports*/ undefined, moduleSpecifier, preferences)
: makeImport(/*name*/ undefined, [makeImportSpecifier(propertyName, localName)], moduleSpecifier, preferences);
}

function makeImportSpecifier(propertyName: string | undefined, name: string): ImportSpecifier {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/fixInvalidImportSyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace ts.codefix {
const variations: CodeFixAction[] = [];

// import Bluebird from "bluebird";
variations.push(createAction(context, sourceFile, node, makeImport(namespace.name, /*namedImports*/ undefined, node.moduleSpecifier)));
variations.push(createAction(context, sourceFile, node, makeImport(namespace.name, /*namedImports*/ undefined, node.moduleSpecifier, context.preferences)));

if (getEmitModuleKind(opts) === ModuleKind.CommonJS) {
// import Bluebird = require("bluebird");
Expand Down
8 changes: 4 additions & 4 deletions src/services/codefixes/useDefaultImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ namespace ts.codefix {
const { sourceFile, span: { start } } = context;
const info = getInfo(sourceFile, start);
if (!info) return undefined;
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info));
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info, context.preferences));
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_default_import, fixId, Diagnostics.Convert_all_to_default_imports)];
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
const info = getInfo(diag.file, diag.start);
if (info) doChange(changes, diag.file, info);
if (info) doChange(changes, diag.file, info, context.preferences);
}),
});

Expand All @@ -36,7 +36,7 @@ namespace ts.codefix {
}
}

function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info): void {
changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier));
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info, preferences: UserPreferences): void {
changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier, preferences));
}
}
Loading