diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index b01218a95ba5a..1b9c9e04c8c51 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -133,7 +133,7 @@ namespace ts.refactor { ) { const checker = program.getTypeChecker(); const prologueDirectives = takeWhile(oldFile.statements, isPrologueDirective); - if (!oldFile.externalModuleIndicator && !oldFile.commonJsModuleIndicator) { + if (oldFile.externalModuleIndicator === undefined && oldFile.commonJsModuleIndicator === undefined && usage.oldImportsNeededByNewFile.size() === 0) { deleteMovedStatements(oldFile, toMove.ranges, changes); return [...prologueDirectives, ...toMove.all]; } @@ -402,7 +402,13 @@ namespace ts.refactor { switch (name.kind) { case SyntaxKind.Identifier: if (isUnused(name)) { - changes.delete(sourceFile, name); + if (varDecl.initializer && isRequireCall(varDecl.initializer, /*requireStringLiteralLikeArgument*/ true)) { + changes.delete(sourceFile, + isVariableDeclarationList(varDecl.parent) && length(varDecl.parent.declarations) === 1 ? varDecl.parent.parent : varDecl); + } + else { + changes.delete(sourceFile, name); + } } break; case SyntaxKind.ArrayBindingPattern: @@ -641,10 +647,12 @@ namespace ts.refactor { } interface ReadonlySymbolSet { + size(): number; has(symbol: Symbol): boolean; forEach(cb: (symbol: Symbol) => void): void; forEachEntry(cb: (symbol: Symbol) => T | undefined): T | undefined; } + class SymbolSet implements ReadonlySymbolSet { private map = new Map(); add(symbol: Symbol): void { @@ -667,6 +675,9 @@ namespace ts.refactor { copyEntries(this.map, clone.map); return clone; } + size() { + return this.map.size; + } } type TopLevelExpressionStatement = ExpressionStatement & { expression: BinaryExpression & { left: PropertyAccessExpression } }; // 'exports.x = ...' @@ -775,7 +786,8 @@ namespace ts.refactor { if (useEs6Exports) { return !isExpressionStatement(decl) && hasSyntacticModifier(decl, ModifierFlags.Export) || !!(name && sourceFile.symbol.exports?.has(name.escapedText)); } - return getNamesToExportInCommonJS(decl).some(name => sourceFile.symbol.exports!.has(escapeLeadingUnderscores(name))); + return !!sourceFile.symbol && !!sourceFile.symbol.exports && + getNamesToExportInCommonJS(decl).some(name => sourceFile.symbol.exports!.has(escapeLeadingUnderscores(name))); } function addExport(decl: TopLevelDeclarationStatement, useEs6Exports: boolean): readonly Statement[] | undefined { diff --git a/tests/cases/fourslash/moveToNewFile_requireImport1.ts b/tests/cases/fourslash/moveToNewFile_requireImport1.ts new file mode 100644 index 0000000000000..37fb2b7e1a44a --- /dev/null +++ b/tests/cases/fourslash/moveToNewFile_requireImport1.ts @@ -0,0 +1,28 @@ +/// + +// @allowJs: true + +// @filename: /a.js +////module.exports = 1; + +// @filename: /b.js +////var a = require("./a"); +////[|function f() { +//// a; +////}|] + +verify.noErrors(); + +verify.moveToNewFile({ + newFileContents: { + "/b.js": "", + + "/f.js": +`var a = require("./a"); + +function f() { + a; +} +`, + }, +}); diff --git a/tests/cases/fourslash/moveToNewFile_requireImport2.ts b/tests/cases/fourslash/moveToNewFile_requireImport2.ts new file mode 100644 index 0000000000000..ebd39990ffab9 --- /dev/null +++ b/tests/cases/fourslash/moveToNewFile_requireImport2.ts @@ -0,0 +1,33 @@ +/// + +// @allowJs: true + +// @filename: /a.js +////module.exports = 1; + +// @filename: /b.js +////var a = require("./a"), +//// b = require("./a"), +//// c = require("./a"); +////[|function f() { +//// b; +////}|] + +verify.noErrors(); + +verify.moveToNewFile({ + newFileContents: { + "/b.js": +`var a = require("./a"), + c = require("./a"); +`, + + "/f.js": +`var b = require("./a"); + +function f() { + b; +} +`, + }, +}); diff --git a/tests/cases/fourslash/moveToNewFile_requireImport3.ts b/tests/cases/fourslash/moveToNewFile_requireImport3.ts new file mode 100644 index 0000000000000..88eba6b77d461 --- /dev/null +++ b/tests/cases/fourslash/moveToNewFile_requireImport3.ts @@ -0,0 +1,33 @@ +/// + +// @module: commonjs +// @moduleResolution: node + +// @filename: /node.d.ts +////declare var module: any; +////declare var require: any; + +// @filename: /a.ts +////module.exports = 1; + +// @filename: /b.ts +////var a = require("./a"); +////[|function f() { +//// a; +////}|] + +verify.noErrors(); + +verify.moveToNewFile({ + newFileContents: { + "/b.ts": "", + + "/f.ts": +`var a = require("./a"); + +function f() { + a; +} +`, + }, +});