зеркало из https://github.com/mozilla/gecko-dev.git
209 строки
5.3 KiB
JavaScript
209 строки
5.3 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// jscodeshift rule to replace EXPORTED_SYMBOLS with export declarations,
|
|
// and also convert existing ChromeUtils.importESModule to static import.
|
|
|
|
/* eslint-env node */
|
|
|
|
const _path = require("path");
|
|
const {
|
|
warnForPath,
|
|
getPrevStatement,
|
|
getNextStatement,
|
|
} = require(_path.resolve(__dirname, "./utils.js"));
|
|
const {
|
|
isImportESModuleCall,
|
|
replaceImportESModuleCall,
|
|
} = require(_path.resolve(__dirname, "./static-import.js"));
|
|
|
|
module.exports = function (fileInfo, api) {
|
|
const { jscodeshift } = api;
|
|
const root = jscodeshift(fileInfo.source);
|
|
doTranslate(fileInfo.path, jscodeshift, root);
|
|
return root.toSource({ lineTerminator: "\n" });
|
|
};
|
|
|
|
module.exports.doTranslate = doTranslate;
|
|
|
|
// Move the comment for `path.node` to adjacent statement, keeping the position
|
|
// as much as possible.
|
|
function moveComments(inputFile, path) {
|
|
const next = getNextStatement(path);
|
|
if (next) {
|
|
if (next.comments) {
|
|
next.comments = [...path.node.comments, ...next.comments];
|
|
} else {
|
|
next.comments = path.node.comments;
|
|
}
|
|
path.node.comments = [];
|
|
|
|
return;
|
|
}
|
|
|
|
const prev = getPrevStatement(path);
|
|
if (prev) {
|
|
path.node.comments.forEach(c => {
|
|
c.leading = false;
|
|
c.trailing = true;
|
|
});
|
|
|
|
if (prev.comments) {
|
|
prev.comments = [...prev.comments, ...path.node.comments];
|
|
} else {
|
|
prev.comments = path.node.comments;
|
|
}
|
|
path.node.comments = [];
|
|
|
|
return;
|
|
}
|
|
|
|
warnForPath(
|
|
inputFile,
|
|
path,
|
|
`EXPORTED_SYMBOLS has comments and it cannot be preserved`
|
|
);
|
|
}
|
|
|
|
function collectAndRemoveExportedSymbols(inputFile, root) {
|
|
const nodes = root.findVariableDeclarators("EXPORTED_SYMBOLS");
|
|
if (!nodes.length) {
|
|
throw Error(`EXPORTED_SYMBOLS not found`);
|
|
}
|
|
|
|
let path = nodes.get(0);
|
|
const obj = nodes.get(0).node.init;
|
|
if (!obj) {
|
|
throw Error(`EXPORTED_SYMBOLS is not statically known`);
|
|
}
|
|
|
|
if (path.parent.node.declarations.length !== 1) {
|
|
throw Error(`EXPORTED_SYMBOLS shouldn't be declared with other variables`);
|
|
}
|
|
|
|
if (path.parent.node.comments && path.parent.node.comments.length) {
|
|
moveComments(inputFile, path.parent);
|
|
}
|
|
|
|
path.parent.prune();
|
|
|
|
const EXPORTED_SYMBOLS = new Set();
|
|
if (obj.type !== "ArrayExpression") {
|
|
throw Error(`EXPORTED_SYMBOLS is not statically known`);
|
|
}
|
|
|
|
for (const elem of obj.elements) {
|
|
if (elem.type !== "Literal") {
|
|
throw Error(`EXPORTED_SYMBOLS is not statically known`);
|
|
}
|
|
var name = elem.value;
|
|
if (typeof name !== "string") {
|
|
throw Error(`EXPORTED_SYMBOLS item must be a string`);
|
|
}
|
|
EXPORTED_SYMBOLS.add(name);
|
|
}
|
|
|
|
return EXPORTED_SYMBOLS;
|
|
}
|
|
|
|
function isTopLevel(path) {
|
|
return path.parent.node.type === "Program";
|
|
}
|
|
|
|
function convertToExport(jscodeshift, path, name) {
|
|
const e = jscodeshift.exportNamedDeclaration(path.node);
|
|
e.comments = [];
|
|
e.comments = path.node.comments;
|
|
path.node.comments = [];
|
|
|
|
path.replace(e);
|
|
}
|
|
|
|
function doTranslate(inputFile, jscodeshift, root) {
|
|
const EXPORTED_SYMBOLS = collectAndRemoveExportedSymbols(inputFile, root);
|
|
|
|
root.find(jscodeshift.FunctionDeclaration).forEach(path => {
|
|
if (!isTopLevel(path)) {
|
|
return;
|
|
}
|
|
const name = path.node.id.name;
|
|
if (!EXPORTED_SYMBOLS.has(name)) {
|
|
return;
|
|
}
|
|
EXPORTED_SYMBOLS.delete(name);
|
|
convertToExport(jscodeshift, path, name);
|
|
});
|
|
|
|
root.find(jscodeshift.ClassDeclaration).forEach(path => {
|
|
if (!isTopLevel(path)) {
|
|
return;
|
|
}
|
|
const name = path.node.id.name;
|
|
if (!EXPORTED_SYMBOLS.has(name)) {
|
|
return;
|
|
}
|
|
EXPORTED_SYMBOLS.delete(name);
|
|
convertToExport(jscodeshift, path, name);
|
|
});
|
|
|
|
root.find(jscodeshift.VariableDeclaration).forEach(path => {
|
|
if (!isTopLevel(path)) {
|
|
return;
|
|
}
|
|
|
|
let exists = false;
|
|
let name;
|
|
for (const decl of path.node.declarations) {
|
|
if (decl.id.type === "Identifier") {
|
|
name = decl.id.name;
|
|
if (EXPORTED_SYMBOLS.has(name)) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (decl.id.type === "ObjectPattern") {
|
|
if (decl.id.properties.length === 1) {
|
|
const prop = decl.id.properties[0];
|
|
if (prop.shorthand) {
|
|
if (prop.key.type === "Identifier") {
|
|
name = prop.key.name;
|
|
if (EXPORTED_SYMBOLS.has(name)) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!exists) {
|
|
return;
|
|
}
|
|
|
|
if (path.node.declarations.length !== 1) {
|
|
throw Error(
|
|
`exported variable shouldn't be declared with other variables`
|
|
);
|
|
}
|
|
|
|
EXPORTED_SYMBOLS.delete(name);
|
|
convertToExport(jscodeshift, path, name);
|
|
});
|
|
|
|
if (EXPORTED_SYMBOLS.size !== 0) {
|
|
throw Error(
|
|
`exported symbols ${[...EXPORTED_SYMBOLS].join(", ")} not found`
|
|
);
|
|
}
|
|
|
|
root.find(jscodeshift.CallExpression).forEach(path => {
|
|
if (isImportESModuleCall(path.node)) {
|
|
// This file is not yet renamed. Skip the extension check.
|
|
// Also skip the isTargetESM.
|
|
replaceImportESModuleCall(inputFile, jscodeshift, path, true);
|
|
}
|
|
});
|
|
}
|