Last active
August 31, 2020 05:58
-
-
Save itsrifat/5035d92e7403164be9b1ae5f1349d21b to your computer and use it in GitHub Desktop.
jscodeshift script to replace an import with another. should be called like `find $1 -iname '*.tsx' -print | xargs ./node_modules/.bin/jscodeshift -t import-replacer.ts -fromModule react-apollo -fromImport graphql -toModule @apollo/client -toImport graphql`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* tslint:disable */ | |
// @ts-nocheck | |
/* | |
* This codemod can replace imports | |
* for exmalple it can change | |
* import {compose} from 'react-apollo' | |
* into | |
* import {flowRight as compose} from 'lodash' | |
* | |
Should be called like: | |
find $1 -iname '*.tsx' -print | xargs ./node_modules/.bin/jscodeshift -t import-replacer.ts -fromModule react-apollo -fromImport graphql -toModule @apollo/client -toImport graphql | |
* */ | |
import { Transform } from "jscodeshift"; | |
import { ImportDeclaration, JSCodeshift } from "jscodeshift"; | |
import { ImportDeclarationBuilder } from "ast-types/gen/builders"; | |
const codeContainsImport = ( | |
j: JSCodeshift, | |
source: string, | |
options: { [key: string]: string } | |
) => { | |
return ( | |
j(source) | |
.find<ImportDeclaration>(j.ImportDeclaration) | |
.filter(imp => { | |
const importLine = imp.node; | |
return importLine.source.value === options.fromModule; | |
}) | |
.filter(imp => { | |
return imp.node.specifiers.some( | |
s => s.imported && s.imported.name === options.fromImport | |
); | |
}).length > 0 | |
); | |
}; | |
const containsReplacer = ( | |
j: JSCodeshift, | |
source: string, | |
options: { [key: string]: string } | |
) => { | |
return ( | |
j(source) | |
.find<ImportDeclaration>(j.ImportDeclaration) | |
.filter(imp => { | |
const importLine = imp.node; | |
return importLine.source.value === options.toModule; | |
}).length > 0 | |
); | |
}; | |
const transform: Transform = (fileInfo, api, options) => { | |
const { fromModule, fromImport, toModule, toImport } = options; | |
if (![fromModule, fromImport, toModule, toImport].every(i => i)) { | |
throw new Error( | |
"Didn't receive all the arguments: fromModule, fromImport, toModule, toImport" | |
); | |
} | |
const j = api.jscodeshift; | |
const importDeclaration: ImportDeclarationBuilder = j.importDeclaration as any; | |
// does the current file contains a reference to import of compose from react-apollo? | |
const hasFromImport = codeContainsImport(j, fileInfo.source, options); | |
if (!hasFromImport) { | |
return; | |
} | |
// remove instances of compose from react-apollo import | |
const sourceAfterRemovingSourceImport = j(fileInfo.source) | |
.find<ImportDeclaration>(j.ImportDeclaration) | |
.filter(imp => { | |
const importLine = imp.node; | |
return importLine.source.value === options.fromModule; | |
}) | |
.replaceWith(p => { | |
const specifiers = p.node.specifiers.filter( | |
s => | |
s.type === "ImportDefaultSpecifier" || | |
(s.imported && s.imported.name !== options.fromImport) | |
); | |
if (!specifiers.length) return; | |
return importDeclaration(specifiers, { | |
value: options.fromModule, | |
type: "Literal" | |
}); | |
}) | |
.toSource(); | |
const replacerSpecifier = { | |
imported: { | |
name: options.toImport, | |
type: "Identifier" | |
}, | |
type: "ImportSpecifier" | |
}; | |
const importFromReplacer = importDeclaration([replacerSpecifier], { | |
value: options.toModule, | |
type: "Literal" | |
}); | |
const hasReplacer = containsReplacer(j, sourceAfterRemovingSourceImport, options); | |
// do we already have an import from microscopekit in this file? them add a new import statement for microscopekit | |
if (!hasReplacer) { | |
const decl = j(sourceAfterRemovingSourceImport) | |
.find<ImportDeclaration>(j.ImportDeclaration) | |
.at(0) | |
.insertAfter(importFromReplacer); | |
return decl.toSource(); | |
} | |
// otherwise amend a compose to the existing one | |
const decl = j(sourceAfterRemovingSourceImport) | |
.find<ImportDeclaration>(j.ImportDeclaration, node => { | |
return node.source.value === options.toModule; | |
}) | |
.at(0) | |
.replaceWith(p => { | |
return importDeclaration( | |
[...(p.node.specifiers as any), replacerSpecifier], | |
{ | |
value: options.toModule, | |
type: "Literal" | |
} | |
); | |
}); | |
return decl.toSource(); | |
}; | |
export const parser = "tsx"; | |
export default transform; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment