forked from mirrors/gecko-dev
Depends on D165819 Differential Revision: https://phabricator.services.mozilla.com/D165820
287 lines
9.2 KiB
JavaScript
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));
|
|
}
|
|
},
|
|
},
|
|
};
|
|
};
|