1/* @internal */ 2namespace ts.codefix { 3 const fixId = "requireInTs"; 4 const errorCodes = [Diagnostics.require_call_may_be_converted_to_an_import.code]; 5 registerCodeFix({ 6 errorCodes, 7 getCodeActions(context) { 8 const info = getInfo(context.sourceFile, context.program, context.span.start); 9 if (!info) { 10 return undefined; 11 } 12 const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, info)); 13 return [createCodeFixAction(fixId, changes, Diagnostics.Convert_require_to_import, fixId, Diagnostics.Convert_all_require_to_import)]; 14 }, 15 fixIds: [fixId], 16 getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { 17 const info = getInfo(diag.file, context.program, diag.start); 18 if (info) { 19 doChange(changes, context.sourceFile, info); 20 } 21 }), 22 }); 23 24 function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info) { 25 const { allowSyntheticDefaults, defaultImportName, namedImports, statement, required } = info; 26 changes.replaceNode(sourceFile, statement, defaultImportName && !allowSyntheticDefaults 27 ? factory.createImportEqualsDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, defaultImportName, factory.createExternalModuleReference(required)) 28 : factory.createImportDeclaration(/*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, defaultImportName, namedImports), required, /*assertClause*/ undefined)); 29 } 30 31 interface Info { 32 readonly allowSyntheticDefaults: boolean; 33 readonly defaultImportName: Identifier | undefined; 34 readonly namedImports: NamedImports | undefined; 35 readonly statement: VariableStatement; 36 readonly required: StringLiteralLike; 37 } 38 39 function getInfo(sourceFile: SourceFile, program: Program, pos: number): Info | undefined { 40 const { parent } = getTokenAtPosition(sourceFile, pos); 41 if (!isRequireCall(parent, /*checkArgumentIsStringLiteralLike*/ true)) { 42 throw Debug.failBadSyntaxKind(parent); 43 } 44 45 const decl = cast(parent.parent, isVariableDeclaration); 46 const defaultImportName = tryCast(decl.name, isIdentifier); 47 const namedImports = isObjectBindingPattern(decl.name) ? tryCreateNamedImportsFromObjectBindingPattern(decl.name) : undefined; 48 if (defaultImportName || namedImports) { 49 return { 50 allowSyntheticDefaults: getAllowSyntheticDefaultImports(program.getCompilerOptions()), 51 defaultImportName, 52 namedImports, 53 statement: cast(decl.parent.parent, isVariableStatement), 54 required: first(parent.arguments) 55 }; 56 } 57 } 58 59 function tryCreateNamedImportsFromObjectBindingPattern(node: ObjectBindingPattern): NamedImports | undefined { 60 const importSpecifiers: ImportSpecifier[] = []; 61 for (const element of node.elements) { 62 if (!isIdentifier(element.name) || element.initializer) { 63 return undefined; 64 } 65 importSpecifiers.push(factory.createImportSpecifier(/*isTypeOnly*/ false, tryCast(element.propertyName, isIdentifier), element.name)); 66 } 67 68 if (importSpecifiers.length) { 69 return factory.createNamedImports(importSpecifiers); 70 } 71 } 72} 73