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: function getCodeActionsToConvertToTypeOnlyExport(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: function getAllCodeActionsToConvertToTypeOnlyExport(context) { 15 const fixedExportDeclarations = new Map<number, 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.modifiers, 44 /*isTypeOnly*/ false, 45 factory.updateNamedExports(exportClause, filter(exportClause.elements, e => !contains(typeExportSpecifiers, e))), 46 exportDeclaration.moduleSpecifier, 47 /*assertClause*/ undefined 48 ); 49 const typeExportDeclaration = factory.createExportDeclaration( 50 /*modifiers*/ undefined, 51 /*isTypeOnly*/ true, 52 factory.createNamedExports(typeExportSpecifiers), 53 exportDeclaration.moduleSpecifier, 54 /*assertClause*/ undefined 55 ); 56 57 changes.replaceNode(context.sourceFile, exportDeclaration, valueExportDeclaration, { 58 leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, 59 trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude 60 }); 61 changes.insertNodeAfter(context.sourceFile, exportDeclaration, typeExportDeclaration); 62 } 63 } 64 65 function getTypeExportSpecifiers(originExportSpecifier: ExportSpecifier, context: CodeFixContextBase): readonly ExportSpecifier[] { 66 const exportClause = originExportSpecifier.parent; 67 if (exportClause.elements.length === 1) { 68 return exportClause.elements; 69 } 70 71 const diagnostics = getDiagnosticsWithinSpan( 72 createTextSpanFromNode(exportClause), 73 context.program.getSemanticDiagnostics(context.sourceFile, context.cancellationToken)); 74 75 return filter(exportClause.elements, element => { 76 return element === originExportSpecifier || findDiagnosticForNode(element, diagnostics)?.code === errorCodes[0]; 77 }); 78 } 79} 80