|
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