|
1 | 1 | import {
|
2 |
| - CodeFixContextBase, |
3 | 2 | Diagnostics,
|
4 | 3 | factory,
|
| 4 | + getSynthesizedDeepClone, |
| 5 | + getSynthesizedDeepClones, |
5 | 6 | getTokenAtPosition,
|
| 7 | + ImportClause, |
6 | 8 | ImportDeclaration,
|
| 9 | + ImportSpecifier, |
7 | 10 | isImportDeclaration,
|
| 11 | + isImportSpecifier, |
8 | 12 | SourceFile,
|
9 | 13 | textChanges,
|
10 |
| - TextSpan, |
11 |
| - tryCast, |
12 | 14 | } from "../_namespaces/ts";
|
13 | 15 | import {
|
14 | 16 | codeFixAll,
|
15 | 17 | createCodeFixAction,
|
16 | 18 | registerCodeFix,
|
17 | 19 | } from "../_namespaces/ts.codefix";
|
18 | 20 |
|
19 |
| -const errorCodes = [Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code]; |
| 21 | +const errorCodes = [ |
| 22 | + Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code, |
| 23 | + Diagnostics._0_is_a_type_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled.code, |
| 24 | +]; |
20 | 25 | const fixId = "convertToTypeOnlyImport";
|
| 26 | + |
21 | 27 | registerCodeFix({
|
22 | 28 | errorCodes,
|
23 | 29 | getCodeActions: function getCodeActionsToConvertToTypeOnlyImport(context) {
|
24 |
| - const changes = textChanges.ChangeTracker.with(context, t => { |
25 |
| - const importDeclaration = getImportDeclarationForDiagnosticSpan(context.span, context.sourceFile); |
26 |
| - fixSingleImportDeclaration(t, importDeclaration, context); |
27 |
| - }); |
28 |
| - if (changes.length) { |
| 30 | + const declaration = getDeclaration(context.sourceFile, context.span.start); |
| 31 | + if (declaration) { |
| 32 | + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration)); |
29 | 33 | return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_import, fixId, Diagnostics.Convert_all_imports_not_used_as_a_value_to_type_only_imports)];
|
30 | 34 | }
|
| 35 | + return undefined; |
31 | 36 | },
|
32 | 37 | fixIds: [fixId],
|
33 | 38 | getAllCodeActions: function getAllCodeActionsToConvertToTypeOnlyImport(context) {
|
34 | 39 | return codeFixAll(context, errorCodes, (changes, diag) => {
|
35 |
| - const importDeclaration = getImportDeclarationForDiagnosticSpan(diag, context.sourceFile); |
36 |
| - fixSingleImportDeclaration(changes, importDeclaration, context); |
| 40 | + const declaration = getDeclaration(diag.file, diag.start); |
| 41 | + if (declaration) { |
| 42 | + doChange(changes, diag.file, declaration); |
| 43 | + } |
37 | 44 | });
|
38 | 45 | }
|
39 | 46 | });
|
40 | 47 |
|
41 |
| -function getImportDeclarationForDiagnosticSpan(span: TextSpan, sourceFile: SourceFile) { |
42 |
| - return tryCast(getTokenAtPosition(sourceFile, span.start).parent, isImportDeclaration); |
| 48 | +function getDeclaration(sourceFile: SourceFile, pos: number) { |
| 49 | + const { parent } = getTokenAtPosition(sourceFile, pos); |
| 50 | + return isImportSpecifier(parent) || isImportDeclaration(parent) && parent.importClause ? parent : undefined; |
43 | 51 | }
|
44 | 52 |
|
45 |
| -function fixSingleImportDeclaration(changes: textChanges.ChangeTracker, importDeclaration: ImportDeclaration | undefined, context: CodeFixContextBase) { |
46 |
| - if (!importDeclaration?.importClause) { |
47 |
| - return; |
| 53 | +function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ImportDeclaration | ImportSpecifier) { |
| 54 | + if (isImportSpecifier(declaration)) { |
| 55 | + changes.replaceNode(sourceFile, declaration, factory.updateImportSpecifier(declaration, /*isTypeOnly*/ true, declaration.propertyName, declaration.name)); |
48 | 56 | }
|
49 |
| - |
50 |
| - const { importClause } = importDeclaration; |
51 |
| - // `changes.insertModifierBefore` produces a range that might overlap further changes |
52 |
| - changes.insertText(context.sourceFile, importDeclaration.getStart() + "import".length, " type"); |
53 |
| - |
54 |
| - // `import type foo, { Bar }` is not allowed, so move `foo` to new declaration |
55 |
| - if (importClause.name && importClause.namedBindings) { |
56 |
| - changes.deleteNodeRangeExcludingEnd(context.sourceFile, importClause.name, importDeclaration.importClause.namedBindings); |
57 |
| - changes.insertNodeBefore(context.sourceFile, importDeclaration, factory.updateImportDeclaration( |
58 |
| - importDeclaration, |
59 |
| - /*modifiers*/ undefined, |
60 |
| - factory.createImportClause( |
61 |
| - /*isTypeOnly*/ true, |
62 |
| - importClause.name, |
63 |
| - /*namedBindings*/ undefined), |
64 |
| - importDeclaration.moduleSpecifier, |
65 |
| - /*assertClause*/ undefined)); |
| 57 | + else { |
| 58 | + const importClause = declaration.importClause as ImportClause; |
| 59 | + if (importClause.name && importClause.namedBindings) { |
| 60 | + changes.replaceNodeWithNodes(sourceFile, declaration, [ |
| 61 | + factory.createImportDeclaration( |
| 62 | + getSynthesizedDeepClones(declaration.modifiers, /*includeTrivia*/ true), |
| 63 | + factory.createImportClause(/*isTypeOnly*/ true, getSynthesizedDeepClone(importClause.name, /*includeTrivia*/ true), /*namedBindings*/ undefined), |
| 64 | + getSynthesizedDeepClone(declaration.moduleSpecifier, /*includeTrivia*/ true), |
| 65 | + getSynthesizedDeepClone(declaration.assertClause, /*includeTrivia*/ true), |
| 66 | + ), |
| 67 | + factory.createImportDeclaration( |
| 68 | + getSynthesizedDeepClones(declaration.modifiers, /*includeTrivia*/ true), |
| 69 | + factory.createImportClause(/*isTypeOnly*/ true, /*name*/ undefined, getSynthesizedDeepClone(importClause.namedBindings, /*includeTrivia*/ true)), |
| 70 | + getSynthesizedDeepClone(declaration.moduleSpecifier, /*includeTrivia*/ true), |
| 71 | + getSynthesizedDeepClone(declaration.assertClause, /*includeTrivia*/ true), |
| 72 | + ), |
| 73 | + ]); |
| 74 | + } |
| 75 | + else { |
| 76 | + const importDeclaration = factory.updateImportDeclaration(declaration, declaration.modifiers, |
| 77 | + factory.updateImportClause(importClause, /*isTypeOnly*/ true, importClause.name, importClause.namedBindings), declaration.moduleSpecifier, declaration.assertClause); |
| 78 | + changes.replaceNode(sourceFile, declaration, importDeclaration); |
| 79 | + } |
66 | 80 | }
|
67 | 81 | }
|
0 commit comments