fune/browser/components/newtab/tools/babel-jsm-to-commonjs.js

287 lines
9.2 KiB
JavaScript

// The MIT License
// Copyright (c) Kate Hudson <k88hudson@gmail.com>
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
"use strict";
const DEFAULT_OPTIONS = {
// Only Cu.imports matching the following pattern will be rewritten as import statements.
basePath: /^resource:\/\//,
// Should the import path be rewritten to exclude the basePath?
// e.g. if the basePath is "resource://}, "resource://foo.jsm" becomes "foo.jsm"
replace: false,
};
module.exports = function plugin(babel) {
const t = babel.types;
let exportItems = [];
// module.exports = {foo, bar, baz};
function createModuleExports(exportedIdentifiers) {
const left = t.memberExpression(
t.identifier("module"),
t.identifier("exports")
);
const right = t.objectExpression(
exportedIdentifiers.map(item => {
return t.objectProperty(
t.identifier(item),
t.identifier(item),
false,
true
);
// return typeof item === "string" ? t.objectProperty(item, item) : t.objectProperty(item[0], item[1]);
})
);
return t.expressionStatement(t.assignmentExpression("=", left, right));
}
function replaceExports(nodes) {
nodes.forEach(path => {
if (
path.isVariableDeclaration() &&
path.get("declarations")[0] &&
path.get("declarations")[0].node.id.name === "EXPORTED_SYMBOLS"
) {
exportItems = path
.get("declarations")[0]
.get("init.elements")
.map(e => e.node.value);
path.remove();
} else if (
path.isExpressionStatement() &&
path.get("expression").isAssignmentExpression() &&
path.get("expression.left.object").isThisExpression() &&
path.get("expression.left.property").isIdentifier()
) {
const left = path.node.expression.left.property;
const { right } = path.node.expression;
if (left.name === "EXPORTED_SYMBOLS") {
// const names = right.elements.map(el => el.value);
// path.replaceWith(createModuleExports(names));
exportItems = right.elements.map(el => el.value);
path.remove();
} else {
const decl = t.variableDeclaration("var", [
t.variableDeclarator(left, right),
]);
if (left.name === right.name) {
path.remove();
} else {
path.replaceWith(decl);
}
}
}
});
}
function checkForDeclarations(nodes, id, initFinalResults = []) {
const results = [];
let finalResults = [...initFinalResults];
nodes.forEach(parentPath => {
if (!parentPath.isVariableDeclaration()) return;
parentPath.traverse({
VariableDeclarator(path) {
if (!path.get("id").isIdentifier()) return;
const init = path.get("init");
if (init.isIdentifier() && init.node.name === id) {
results.push(path.node.id.name);
}
},
});
});
if (results.length) {
finalResults.push(...results);
results.forEach(name => {
finalResults = checkForDeclarations(nodes, name, finalResults);
});
return finalResults;
}
return finalResults;
}
function checkForUtilsDeclarations(nodes, ids) {
const results = [];
nodes.forEach(parentPath => {
if (!parentPath.isVariableDeclaration()) return;
parentPath.traverse({
VariableDeclarator(path) {
const id = path.get("id");
const init = path.get("init");
// const {utils} = Components;
if (
id.isObjectPattern() &&
init.isIdentifier() &&
ids.includes(init.node.name)
) {
id.node.properties.forEach(prop => {
if (prop.key.name === "utils") {
results.push(prop.value.name);
}
});
}
// const foo = Components.utils;
else if (
id.isIdentifier() &&
init.isMemberExpression() &&
init.get("object").isIdentifier() &&
ids.includes(init.get("object").node.name) &&
init.get("property").isIdentifier() &&
init.get("property").node.name === "utils"
) {
results.push(id.node.name);
}
},
});
});
return results;
}
function replaceImports(
nodes,
ComponentNames,
CuNames,
basePath,
replacePath,
removeOtherImports
) {
nodes.forEach(p => {
if (!p.isVariableDeclaration()) return;
p.traverse({
CallExpression(path) {
if (
t.isStringLiteral(path.node.arguments[0]) &&
// path.node.arguments[0].value.match(basePath) &&
t.isObjectPattern(path.parentPath.node.id) &&
// Check if actually Components.utils.import
path.get("callee").isMemberExpression() &&
(path.get("callee.property").node.name === "import" ||
path.get("callee.property").node.name === "importESModule")
) {
const callee = path.get("callee");
if (callee.get("object").isMemberExpression()) {
if (
!ComponentNames.includes(
callee.get("object.object").node.name
) ||
callee.get("object.property").node.name !== "utils"
) {
return;
}
} else {
const objectName = callee.get("object").node.name;
if (
objectName !== "ChromeUtils" &&
!CuNames.includes(objectName)
) {
return;
}
}
let filePath = path.node.arguments[0].value;
if (
!removeOtherImports ||
(replacePath && filePath.match(basePath))
) {
if (replacePath) filePath = filePath.replace(basePath, "");
const requireStatement = t.callExpression(
t.identifier("require"),
[t.stringLiteral(filePath)]
);
path.replaceWith(requireStatement);
} else if (removeOtherImports) {
path.parentPath.parentPath.remove();
}
}
},
});
});
}
function replaceModuleGetters(paths, basePath, replacePath) {
paths.forEach(path => {
if (
path.isExpressionStatement() &&
path.get("expression").isCallExpression() &&
["XPCOMUtils", "ChromeUtils"].includes(
path.get("expression.callee.object.name").node
) &&
["defineLazyModuleGetter", "defineModuleGetter"].includes(
path.get("expression.callee.property.name").node
)
) {
const argPaths = path.get("expression.arguments");
const idName = argPaths[1].node.value;
let filePath = argPaths[2].node.value;
if (!filePath.match(basePath)) return;
if (replacePath) filePath = filePath.replace(basePath, "");
const requireStatement = t.callExpression(t.identifier("require"), [
t.stringLiteral(filePath),
]);
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(
t.objectPattern([
t.objectProperty(
t.identifier(idName),
t.identifier(idName),
false,
true
),
]),
requireStatement
),
]);
path.replaceWith(varDecl);
}
});
}
return {
visitor: {
Program(path, state) {
const opts = Object.assign({}, DEFAULT_OPTIONS, state.opts);
const topLevelNodes = path.get("body");
const ids = checkForDeclarations(topLevelNodes, "Components", [
"Components",
]);
const utils = checkForUtilsDeclarations(topLevelNodes, ids);
replaceImports(
topLevelNodes,
ids,
utils,
opts.basePath,
opts.replace,
opts.removeOtherImports
);
replaceModuleGetters(topLevelNodes, opts.basePath, opts.replace);
replaceExports(topLevelNodes);
if (exportItems.length) {
path.pushContainer("body", createModuleExports(exportItems));
}
},
},
};
};