Skip to content

Instantly share code, notes, and snippets.

@walkerdb
Last active August 14, 2022 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save walkerdb/08187fff6d2defee0891923c0b97c332 to your computer and use it in GitHub Desktop.
Save walkerdb/08187fff6d2defee0891923c0b97c332 to your computer and use it in GitHub Desktop.
/*
This codemod migrates usage of default exports to named exports instead
See migrate-default-exports-to-named-exports.test.js for before/after examples.
*/
export const parser = 'tsx';
const generateNamedExportDeclaration = (jscodeshift, inlineDefaultExport, filePath) => {
const declarationType = inlineDefaultExport.node.declaration.type;
if (declarationType === 'FunctionDeclaration' || declarationType === 'ClassDeclaration') {
return jscodeshift.exportNamedDeclaration(inlineDefaultExport.node.declaration);
}
const dynamicExportName = filePath.split('/').pop().split('.')[0];
const exportName = jscodeshift.identifier(dynamicExportName);
return jscodeshift.exportNamedDeclaration(
jscodeshift.variableDeclaration('const', [
jscodeshift.variableDeclarator(exportName, inlineDefaultExport.node.declaration),
])
);
};
function migrateDefaultExports(source, jscodeshift, filePath) {
if (filePath.includes('.story.') || filePath.includes('.stories.')) {
return;
}
// migrate inline default exports, giving them an export name of the filename
source
.find(jscodeshift.ExportDefaultDeclaration, (t) => t.declaration.type !== 'Identifier')
.replaceWith((inlineDefaultExport) => generateNamedExportDeclaration(jscodeshift, inlineDefaultExport, filePath));
// migrate default exports that just reference a variable
source
.find(jscodeshift.ExportDefaultDeclaration, { declaration: { type: 'Identifier' } })
.forEach((defaultExport) => {
const constThatWasBeingDefaultExported = defaultExport.value.declaration.name;
const filter = { id: { name: constThatWasBeingDefaultExported } };
// remove the old default export
defaultExport.get().prune();
// check if it's already being exported as a const
if (source.find(jscodeshift.ExportNamedDeclaration, { declaration: { declarations: [filter] } }).length) {
return;
}
// check if it's already being exported as a function or a class
if (source.find(jscodeshift.ExportNamedDeclaration, { declaration: filter }).length) {
return;
}
// update original declarations with export keyword
const namedExportGenerator = (original) => jscodeshift.exportNamedDeclaration(original.node);
source.find(jscodeshift.VariableDeclaration, { declarations: [filter] }).replaceWith(namedExportGenerator);
source.find(jscodeshift.FunctionDeclaration, filter).replaceWith(namedExportGenerator);
});
}
function migrateExportDefaultAsStatements(source, jscodeshift) {
// delete manual type exports since they'll be covered by next change in this chain
source
.find(jscodeshift.ExportNamedDeclaration, {
exportKind: 'type',
source: { type: 'StringLiteral' },
})
.forEach((typeExport) => typeExport.prune());
// migrate "export { default as SomeName } from './thing'" to "export * from './thing'"
source
.find(jscodeshift.ExportNamedDeclaration, {
specifiers: [{ type: 'ExportSpecifier', local: { name: 'default' } }],
})
.forEach((t) => t.replace(jscodeshift.exportAllDeclaration(t.value.source, null)));
}
const filetypesToIgnore = ['.png', '.jpg', '.jpeg', '.md'];
function migrateDefaultImports(source, jscodeshift) {
source.find(jscodeshift.ImportDefaultSpecifier).forEach((defaultImport) => {
const importPath = defaultImport.parent.value.source.value;
if (importPath.includes('./') && !filetypesToIgnore.some((filetype) => importPath.endsWith(filetype))) {
defaultImport.replace(jscodeshift.importSpecifier(defaultImport.value.local, null));
}
});
}
export default function transformer(file, api) {
const { jscodeshift } = api;
const source = jscodeshift.withParser(parser)(file.source);
migrateDefaultExports(source, jscodeshift, file.path);
migrateDefaultImports(source, jscodeshift);
migrateExportDefaultAsStatements(source, jscodeshift);
return source.toSource();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment