|
import { |
|
ExportAllDeclaration, |
|
ExportNamedDeclaration, |
|
FileInfo, |
|
ImportDeclaration, |
|
Transform, |
|
} from 'jscodeshift'; |
|
import { dirname, relative, resolve } from 'path'; |
|
|
|
function maybeResolvedDeclaration< |
|
T extends ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration, |
|
>(file: FileInfo, node: T, fn: ({ path: string }) => T) { |
|
if ( |
|
typeof node.source?.value === 'string' && |
|
node.source.value.startsWith('.') && |
|
!node.source.value.endsWith('.js') |
|
) { |
|
const base = process.cwd(); |
|
|
|
const importLocation = node.source.value; |
|
const absoluteSourcePath = resolve(base, file.path); |
|
|
|
const absoluteImportPath = resolve( |
|
dirname(absoluteSourcePath), |
|
importLocation.endsWith('/') ? `${importLocation}index` : importLocation, |
|
); |
|
|
|
// in case resolve throws (missing dep) |
|
try { |
|
const absoluteResolvedImportPath = require.resolve(absoluteImportPath); |
|
|
|
const relativeResolvedImportPath = relative( |
|
dirname(absoluteSourcePath), |
|
// TS specific hack |
|
absoluteResolvedImportPath.match(/\.tsx?$/) |
|
? absoluteResolvedImportPath.replace(/\.tsx?$/, '.js') |
|
: absoluteResolvedImportPath, |
|
); |
|
|
|
// path/relative will strip the ./ prefix if they are in same location |
|
const path = relativeResolvedImportPath.startsWith('.') |
|
? relativeResolvedImportPath |
|
: `./${relativeResolvedImportPath}`; |
|
|
|
return fn({ |
|
path, |
|
}); |
|
} catch (err) { |
|
process.exitCode = 1; |
|
console.warn(err, { |
|
importLocation, |
|
absoluteSourcePath, |
|
absoluteImportPath, |
|
}); |
|
} |
|
} |
|
|
|
return node; |
|
} |
|
|
|
const transform: Transform = (file, api) => { |
|
const j = api.jscodeshift; |
|
const root = j(file.source); |
|
|
|
// https://github.com/facebook/jscodeshift/blob/master/recipes/retain-first-comment.md |
|
// Save the comments attached to the first node |
|
const getFirstNode = () => root.find(j.Program).get('body', 0).node; |
|
const firstNode = getFirstNode(); |
|
const { comments } = firstNode; |
|
|
|
root.find(j.ImportDeclaration).replaceWith((astPath) => { |
|
const { node } = astPath; |
|
|
|
return maybeResolvedDeclaration(file, node, ({ path }) => { |
|
node.source = j.literal(path); |
|
return node; |
|
// return j.importDeclaration(node.specifiers, j.literal(path)); |
|
}); |
|
}); |
|
|
|
root.find(j.ExportNamedDeclaration).replaceWith((astPath) => { |
|
const { node } = astPath; |
|
|
|
return maybeResolvedDeclaration(file, node, ({ path }) => { |
|
node.source = j.literal(path); |
|
return node; |
|
|
|
// return j.exportNamedDeclaration( |
|
// node.declaration || null, |
|
// node.specifiers, |
|
// j.literal(path), |
|
// ); |
|
}); |
|
}); |
|
|
|
root.find(j.ExportAllDeclaration).replaceWith((astPath) => { |
|
const { node } = astPath; |
|
|
|
return maybeResolvedDeclaration(file, node, ({ path }) => { |
|
node.source = j.literal(path); |
|
return node; |
|
// return j.exportAllDeclaration(j.literal(path), node.exported || null); |
|
}); |
|
}); |
|
|
|
// https://github.com/facebook/jscodeshift/blob/master/recipes/retain-first-comment.md |
|
// If the first node has been modified or deleted, reattach the comments |
|
const maybeFirstNode = getFirstNode(); |
|
if (maybeFirstNode !== firstNode) { |
|
maybeFirstNode.comments = comments; |
|
} |
|
|
|
return root.toSource(); |
|
}; |
|
|
|
export default transform; |