1/* @internal */ 2namespace ts.codefix { 3 const errorCodes = [Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type.code]; 4 const fixId = "convertToTypeOnlyExport"; 5 registerCodeFix({ 6 errorCodes, 7 getCodeActions: context => { 8 const changes = textChanges.ChangeTracker.with(context, t => fixSingleExportDeclaration(t, getExportSpecifierForDiagnosticSpan(context.span, context.sourceFile), context)); 9 if (changes.length) { 10 return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_export, fixId, Diagnostics.Convert_all_re_exported_types_to_type_only_exports)]; 11 } 12 }, 13 fixIds: [fixId], 14 getAllCodeActions: context => { 15 const fixedExportDeclarations = new Map<string, true>(); 16 return codeFixAll(context, errorCodes, (changes, diag) => { 17 const exportSpecifier = getExportSpecifierForDiagnosticSpan(diag, context.sourceFile); 18 if (exportSpecifier && addToSeen(fixedExportDeclarations, getNodeId(exportSpecifier.parent.parent))) { 19 fixSingleExportDeclaration(changes, exportSpecifier, context); 20 } 21 }); 22 } 23 }); 24 25 function getExportSpecifierForDiagnosticSpan(span: TextSpan, sourceFile: SourceFile) { 26 return tryCast(getTokenAtPosition(sourceFile, span.start).parent, isExportSpecifier); 27 } 28 29 function fixSingleExportDeclaration(changes: textChanges.ChangeTracker, exportSpecifier: ExportSpecifier | undefined, context: CodeFixContextBase) { 30 if (!exportSpecifier) { 31 return; 32 } 33 34 const exportClause = exportSpecifier.parent; 35 const exportDeclaration = exportClause.parent; 36 const typeExportSpecifiers = getTypeExportSpecifiers(exportSpecifier, context); 37 if (typeExportSpecifiers.length === exportClause.elements.length) { 38 changes.insertModifierBefore(context.sourceFile, SyntaxKind.TypeKeyword, exportClause); 39 } 40 else { 41 const valueExportDeclaration = factory.updateExportDeclaration( 42 exportDeclaration, 43 exportDeclaration.decorators, 44 exportDeclaration.modifiers, 45 /*isTypeOnly*/ false, 46 factory.updateNamedExports(exportClause, filter(exportClause.elements, e => !contains(typeExportSpecifiers, e))), 47 exportDeclaration.moduleSpecifier); 48 const typeExportDeclaration = factory.createExportDeclaration( 49 /*decorators*/ undefined, 50 /*modifiers*/ undefined, 51 /*isTypeOnly*/ true, 52 factory.createNamedExports(typeExportSpecifiers), 53 exportDeclaration.moduleSpecifier); 54 55 changes.replaceNode(context.sourceFile, exportDeclaration, valueExportDeclaration, { 56 leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, 57 trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude 58 }); 59 changes.insertNodeAfter(context.sourceFile, exportDeclaration, typeExportDeclaration); 60 } 61 } 62 63 function getTypeExportSpecifiers(originExportSpecifier: ExportSpecifier, context: CodeFixContextBase): readonly ExportSpecifier[] { 64 const exportClause = originExportSpecifier.parent; 65 if (exportClause.elements.length === 1) { 66 return exportClause.elements; 67 } 68 69 const diagnostics = getDiagnosticsWithinSpan( 70 createTextSpanFromNode(exportClause), 71 context.program.getSemanticDiagnostics(context.sourceFile, context.cancellationToken)); 72 73 return filter(exportClause.elements, element => { 74 return element === originExportSpecifier || findDiagnosticForNode(element, diagnostics)?.code === errorCodes[0]; 75 }); 76 } 77} 78