import type { ASTPath , Collection , StringLiteral } from 'jscodeshift' ; import { Identifier } from 'jscodeshift' ; import { identifier } from 'jscodeshift' ; import { stringLiteral } from 'jscodeshift' ; import { callExpression } from 'jscodeshift' ; import { ImportSpecifier , ImportDeclaration , Program , literal , importDeclaration , CallExpression , } from 'jscodeshift' ; const isRequireExpression = ( path ) => path && path . value && path . value . callee && path . value . callee . type === 'Identifier' && path . value . callee . name === 'require' ; description param importDeclaration returns export function addImport < N > ( this , importDeclaration ) { const imports = this . find ( ImportDeclaration ) ; if ( imports . length ) { const sourceImport = imports . filter ( ( declaration ) => declaration . node . source . value === importDeclaration . source . value ) ; const sourceImportNode = sourceImport . nodes ( ) [ 0 ] ; if ( sourceImport . length ) { importDeclaration . specifiers . forEach ( ( specifier ) => { const name = specifier . local ?. name ?? ( specifier . type === 'ImportSpecifier' ? specifier . imported . name : specifier . local . name ) ; let hasChanged = false ; if ( ! sourceImport . find ( ImportSpecifier , { local : { name } } ) . length ) { sourceImportNode . specifiers . push ( specifier ) ; hasChanged = true ; } return hasChanged ; } ) ; } else { imports . at ( 0 ) . insertBefore ( importDeclaration ) ; } } else { this . find ( Program ) . get ( 'body' , 0 ) . insertBefore ( importDeclaration ) ; } return true ; } description returns export function removeImport < N > ( this , importName ) { const imports = this . find ( ImportDeclaration , { source : { value : importName , } , } ) ; if ( imports . length ) { imports . at ( 0 ) . remove ( ) ; return true ; } } description param importDeclaration returns export function findImport < N > ( this , importDeclaration ) { let hasImport = false ; this . find ( ImportDeclaration , { source : { value : importDeclaration . source . value , } , } ) . filter ( ( p ) => { return p . node . specifiers . some ( ( specifier ) => importDeclaration . specifiers . length ? importDeclaration . specifiers . some ( ( targetSpecifier ) => { if ( 'imported' in targetSpecifier ) { return specifier . local . name === targetSpecifier . imported . name ; } return false ; } ) : true ) ; } ) . forEach ( ( p ) => { hasImport = true ; } ) ; return hasImport ; } description export function renameImportSource ( this , from , to ) { const imports = this . find ( ImportDeclaration ) . filter ( ( imp ) => { return `${ imp . node . source . value }` . startsWith ( from ) ; } ) ; if ( ! imports . length ) { return false ; } const regexp = new RegExp ( `^${ from }(/.+)?$` ) ; imports . replaceWith ( ( imp ) => { const val = imp . node . source . value ; return importDeclaration ( imp . value . specifiers , literal ( `${ val }` . replace ( regexp , `${ to }$1` ) ) ) ; } ) ; return true ; } description export function renameImportSpecifier ( this , source , from , to ) { const imports = this . find ( ImportDeclaration , { source : { value : source } } ) ; if ( ! imports . length ) { return false ; } let shouldRename = false ; imports . replaceWith ( ( imp ) => { const specifiers = imp . value . specifiers . map ( ( specifier ) => { if ( 'imported' in specifier && specifier . imported . name === from ) { shouldRename = specifier . imported . name === specifier . local . name ; return { ... specifier , imported : identifier ( to ) } ; } return specifier ; } ) ; return importDeclaration ( specifiers , imp . value . source ) ; } ) ; if ( shouldRename ) { this . find ( Identifier , { name : from } ) . replaceWith ( identifier ( to ) ) ; } return true ; } description export function renameRequireSource ( this , from , to ) { const imports = this . find ( CallExpression ) . filter ( ( exp ) => { return ( isRequireExpression ( exp ) && exp . value . arguments [ 0 ] . type === 'StringLiteral' && `${ exp . value . arguments [ 0 ] . value }` . startsWith ( from ) ) ; } ) ; if ( ! imports . length ) { return false ; } const regexp = new RegExp ( `^${ from }` ) ; imports . replaceWith ( ( exp ) => { const val = ( exp . value . arguments [ 0 ] as StringLiteral ) . value ; return callExpression ( exp . value . callee , [ stringLiteral ( `${ val }` . replace ( regexp , to ) ) ] ) ; } ) ; return true ; }