From 7102de77d3b62cb7bcf344bc7d5ae3dd4c25d603 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 16 Jan 2019 15:54:08 -0800 Subject: [PATCH] Consider JSX namespace imports when moving statements between files Each of the old and new files should end up with a JSX namespace import iff it contains JSX. Fixes #27939 --- src/services/refactors/moveToNewFile.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 87973226b3c..6d2a4872842 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -460,6 +460,12 @@ namespace ts.refactor { const oldImportsNeededByNewFile = new SymbolSet(); const newFileImportsFromOldFile = new SymbolSet(); + const containsJsx = find(toMove, statement => !!(statement.transformFlags & TransformFlags.ContainsJsx)); + const jsxNamespaceSymbol = getJsxNamespaceSymbol(containsJsx); + if (jsxNamespaceSymbol) { // Might not exist (e.g. in non-compiling code) + oldImportsNeededByNewFile.add(jsxNamespaceSymbol); + } + for (const statement of toMove) { forEachTopLevelDeclaration(statement, decl => { movedSymbols.add(Debug.assertDefined(isExpressionStatement(decl) ? checker.getSymbolAtLocation(decl.expression.left) : decl.symbol)); @@ -485,6 +491,11 @@ namespace ts.refactor { for (const statement of oldFile.statements) { if (contains(toMove, statement)) continue; + // jsxNamespaceSymbol will only be set iff it is in oldImportsNeededByNewFile. + if (jsxNamespaceSymbol && !!(statement.transformFlags & TransformFlags.ContainsJsx)) { + unusedImportsFromOldFile.delete(jsxNamespaceSymbol); + } + forEachReference(statement, checker, symbol => { if (movedSymbols.has(symbol)) oldFileImportsFromNewFile.add(symbol); unusedImportsFromOldFile.delete(symbol); @@ -492,6 +503,18 @@ namespace ts.refactor { } return { movedSymbols, newFileImportsFromOldFile, oldFileImportsFromNewFile, oldImportsNeededByNewFile, unusedImportsFromOldFile }; + + function getJsxNamespaceSymbol(containsJsx: Node | undefined) { + if (containsJsx === undefined) { + return undefined; + } + + const jsxNamespace = checker.getJsxNamespace(containsJsx); + const jsxNamespaceSymbol = checker.resolveName(jsxNamespace, containsJsx, SymbolFlags.Namespace, /*excludeGlobals*/ true); + return !!jsxNamespaceSymbol && some(jsxNamespaceSymbol.declarations, isInImport) + ? jsxNamespaceSymbol + : undefined; + } } // Below should all be utilities @@ -512,7 +535,7 @@ namespace ts.refactor { } function isVariableDeclarationInImport(decl: VariableDeclaration) { return isSourceFile(decl.parent.parent.parent) && - decl.initializer && isRequireCall(decl.initializer, /*checkArgumentIsStringLiteralLike*/ true); + !!decl.initializer && isRequireCall(decl.initializer, /*checkArgumentIsStringLiteralLike*/ true); } function filterImport(i: SupportedImport, moduleSpecifier: StringLiteralLike, keep: (name: Identifier) => boolean): SupportedImportStatement | undefined {