Avoid convertExport when there's a non-identifier or a bogus one

Fixes #44105
This commit is contained in:
Eli Barzilay 2021-05-15 11:38:01 -04:00
parent 7954f0c947
commit fb5f855108
3 changed files with 43 additions and 5 deletions

View File

@ -6397,6 +6397,10 @@
"category": "Message",
"code": 95163
},
"Can only convert named export": {
"category": "Message",
"code": 95164
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",

View File

@ -57,7 +57,7 @@ namespace ts.refactor {
};
function getInfo(context: RefactorContext, considerPartialSpans = true): ExportInfo | RefactorErrorInfo | undefined {
const { file } = context;
const { file, program } = context;
const span = getRefactorContextSpan(context);
const token = getTokenAtPosition(file, span.start);
const exportNode = !!(token.parent && getSyntacticModifierFlags(token.parent) & ModifierFlags.Export) && considerPartialSpans ? token.parent : getParentNodeInSpan(token, file, span);
@ -75,6 +75,11 @@ namespace ts.refactor {
return { error: getLocaleSpecificMessage(Diagnostics.This_file_already_has_a_default_export) };
}
const checker = program.getTypeChecker();
const noSymbolError = (id: Node) =>
(isIdentifier(id) && checker.getSymbolAtLocation(id)) ? undefined
: { error: getLocaleSpecificMessage(Diagnostics.Can_only_convert_named_export) };
switch (exportNode.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
@ -83,7 +88,9 @@ namespace ts.refactor {
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ModuleDeclaration: {
const node = exportNode as FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | TypeAliasDeclaration | NamespaceDeclaration;
return node.name && isIdentifier(node.name) ? { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol } : undefined;
if (!node.name) return undefined;
return noSymbolError(node.name)
|| { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol };
}
case SyntaxKind.VariableStatement: {
const vs = exportNode as VariableStatement;
@ -94,12 +101,14 @@ namespace ts.refactor {
const decl = first(vs.declarationList.declarations);
if (!decl.initializer) return undefined;
Debug.assert(!wasDefault, "Can't have a default flag here");
return isIdentifier(decl.name) ? { exportNode: vs, exportName: decl.name, wasDefault, exportingModuleSymbol } : undefined;
return noSymbolError(decl.name)
|| { exportNode: vs, exportName: decl.name as Identifier, wasDefault, exportingModuleSymbol };
}
case SyntaxKind.ExportAssignment: {
const node = exportNode as ExportAssignment;
const exp = node.expression as Identifier;
return node.isExportEquals ? undefined : { exportNode: node, exportName: exp, wasDefault, exportingModuleSymbol };
if (node.isExportEquals) return undefined;
return noSymbolError(node.expression)
|| { exportNode: node, exportName: node.expression as Identifier, wasDefault, exportingModuleSymbol };
}
default:
return undefined;

View File

@ -0,0 +1,25 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: /a.js
/////*[| |]*/ /** x */ export default 1;
// @Filename: /b.js
/////*[| |]*/ /** x */ export default (1);
// @Filename: /c.js
/////*[| |]*/ /** x */ export default x;
goTo.eachRange(r => {
goTo.selectRange(r);
verify.not.refactorAvailable("Convert export");
});
// goTo.selectRange(test.ranges()[0]);
// edit.applyRefactor({
// refactorName: "Convert export",
// actionName: "Convert default export to named export",
// actionDescription: "Convert default export to named export",
// newContent: { "/a.js": `...` },
// });