• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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