codemods: implement core-imports codemod

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-06-01 23:01:02 +02:00
parent 20a5c789a4
commit b74d558369
3 changed files with 98 additions and 5 deletions
+3 -1
View File
@@ -26,9 +26,11 @@
"dependencies": {
"@backstage/cli-common": "0.1.1",
"chalk": "^4.0.0",
"jscodeshift": "^0.12.0"
"jscodeshift": "^0.12.0",
"jscodeshift-add-imports": "^1.0.10"
},
"devDependencies": {
"@types/jscodeshift": "^0.11.0",
"@types/node": "^14.14.32",
"commander": "^6.1.0",
"ts-node": "^9.1.1"
+71 -1
View File
@@ -13,4 +13,74 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
console.log('hello');
const addImports = require('jscodeshift-add-imports');
const { resolve: resolvePath } = require('path');
const fs = require('fs');
function findExports(packageName) {
const packagePath = require.resolve(`${packageName}/package.json`);
const typesPath = resolvePath(packagePath, '../dist/index.d.ts');
const content = fs.readFileSync(typesPath, 'utf8');
const [, exportSymbols] = content.match(/export \{ (.*) \};/);
return exportSymbols
.split(', ')
.map(exported => exported.match(/.* as (.*)/)?.[1] || exported);
}
const symbolTable = {
'@backstage/core-app-api': findExports('@backstage/core-app-api'),
'@backstage/core-components': findExports('@backstage/core-components'),
'@backstage/core-plugin-api': findExports('@backstage/core-plugin-api'),
};
const reverseSymbolTable = Object.entries(symbolTable).reduce(
(table, [pkg, symbols]) => {
for (const symbol of symbols) {
table[symbol] = pkg;
}
return table;
},
{},
);
module.exports = (file, /** @type {import('jscodeshift').API} */ api) => {
const j = api.jscodeshift;
const root = j(file.source);
// Find all import statements of @backstage/core
const imports = root.find(j.ImportDeclaration, {
source: {
value: '@backstage/core',
},
});
// Then loop through all the import statement and collects each imported symbol
const importedSymbols = imports.nodes().flatMap(node =>
(node.specifiers || []).flatMap(specifier => ({
name: specifier.imported.name, // The symbol we're importing
local: specifier.local.name, // The local name, usually this is the same
})),
);
// Now that we gathered all the imports we want to add, we get rid of the old one
imports.remove();
// The convert the imports into actual import statements
const newImportStatements = importedSymbols.map(({ name, local }) => {
const targetPackage = reverseSymbolTable[name];
if (!targetPackage) {
throw new Error(`No target package found for import of ${name}`);
}
return j.importDeclaration(
[j.importSpecifier(j.identifier(name), j.identifier(local))],
j.literal(targetPackage),
);
});
// And add the new imports. `addImports` will take care of resolving duplicates
addImports(root, newImportStatements);
return root.toSource();
};