Skip to content

Instantly share code, notes, and snippets.

@maksimr
Last active July 2, 2020 11:41
Show Gist options
  • Save maksimr/e24236c284d40f4f1b571710b81171a4 to your computer and use it in GitHub Desktop.
Save maksimr/e24236c284d40f4f1b571710b81171a4 to your computer and use it in GitHub Desktop.
Programmatically updat imports inside javascript code
/**
* Example of how to programmatically update imports inside javascript code
* when you move files
*/
function main() {
const assert = require('assert');
const filePath = '/a/b/c.js';
const fromPath = '../foo/foo.js';
const toPath = '../foo/bar.js';
assert.equal(updateImports('require("./foo");', filePath, '/a/b/foo.js', '/a/b/bar.js'), 'require("./bar");');
assert.equal(updateImports('require("./foo/foo/foo");', filePath, '/a/b/foo/foo/foo.js', '/a/b/foo/foo/index.js'), 'require("./foo/foo");');
assert.equal(updateImports('require("../foo/foo.js");', filePath, fromPath, toPath), 'require("../foo/bar");');
assert.equal(updateImports('require("foo");', filePath, 'foo', 'bar'), 'require("bar");');
assert.equal(updateImports('require("foo/foo");', filePath, 'foo/foo', 'foo/bar'), 'require("foo/bar");');
assert.equal(updateImports('const r = require; r("../foo/foo.js");', filePath, fromPath, toPath), 'const r = require; r("../foo/bar");');
assert.equal(updateImports('import foo from "../foo/foo.js";', filePath, fromPath, toPath), 'import foo from "../foo/bar";');
assert.equal(updateImports('import("../foo/foo.js");', filePath, fromPath, toPath), 'import("../foo/bar");');
assert.equal(updateImports('require("../foo/foo");', filePath, fromPath, toPath), 'require("../foo/bar");');
assert.equal(updateImports('require("../foo/foo");', filePath, fromPath, '../foo/index.js'), 'require("../foo");');
assert.equal(updateImports('require("../foo");', filePath, '../foo/index.js', '../foo/foo.js'), 'require("../foo/foo");');
assert.equal(updateImports('require(["../foo/foo", "../foo/foo"], () => null);', filePath, fromPath, toPath), 'require(["../foo/bar", "../foo/bar"], () => null);');
assert.equal(updateImports('require(["../foo/foo", "../foo/foo"], () => require("../foo/foo"));', filePath, fromPath, toPath), 'require(["../foo/bar", "../foo/bar"], () => require("../foo/bar"));');
assert.equal(updateImports('require(require("../foo/foo"));', filePath, fromPath, toPath), 'require(require("../foo/bar"));');
assert.equal(updateImports('require();', filePath, fromPath, toPath), 'require();');
assert.equal(updateImports('require(foo);', filePath, fromPath, toPath), 'require(foo);');
assert.equal(updateImports('require([]);', filePath, fromPath, toPath), 'require([]);');
assert.equal(updateImports('require(\'foo\');', filePath, 'foo', 'bar'), 'require(\'bar\');');
function updateImports(code, filePath, fromPath, toPath) {
const recast = require('recast');
const ast = recast.parse(code, {
parser: require('recast/parsers/babel')
});
return recast.print(updateImportsTransformer(ast, filePath, fromPath, toPath)).code;
}
function updateImportsTransformer(ast, filePath, fromPath, toPath) {
const recast = require('recast');
const namedTypes = recast.types.namedTypes;
return recast.visit(ast, {
visitImportDeclaration(path) {
visitLiteral(path.node);
return false;
},
visitCallExpression(path) {
let node = path.node.callee;
let scope = path.scope;
while (scope = path.scope.lookup(node.name)) {
const path = scope.bindings[node.name][0];
node = path.value;
if (namedTypes.Identifier.check(node) && namedTypes.VariableDeclarator.check(path.parentPath.value)) {
node = path.parentPath.value;
if (namedTypes.Identifier.check(node.init)) {
node = node.init;
continue;
}
}
break;
}
if (node.name === 'require' || namedTypes.Import.check(node)) {
if (path.node.arguments.length > 0) {
path.node.arguments[0] = visitLiteral(path.node.arguments[0]);
}
}
this.traverse(path);
}
});
function visitLiteral(node) {
return recast.visit(node, {
visitStringLiteral(path) {
if (resPath(path.node.value) === resPath(fromPath)) {
const modulePath = relPath(resPath(toPath));
// Preserve quote mark from source
// https://github.com/benjamn/recast/issues/171
const quote = path.node.extra.raw[0];
path.replace(recast.types.builders.stringLiteral(new String(quote + modulePath + quote)));
}
return false;
function resPath(it) {
return isModulePath(it) ? it :
normalizePath(require('path').resolve(dir(filePath), it));
}
function relPath(it) {
if (isModulePath(it)) {
return it;
}
const p = require('path').relative(dir(filePath), it);
return isModulePath(p) ? './' + p : p;
}
function isModulePath(it) {
return !/^(\.)*\//.test(it);
}
function dir(it) {
return require('path').dirname(it);
}
function normalizePath(path) {
return path
.replace(/\.js$/, '')
.replace(/\/index$/, '');
}
}
});
}
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment