420 lines
12 KiB
JavaScript
420 lines
12 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.detect = exports.parse = exports.visitorUseSCWD = exports.visitorMalformed = exports.visitorNonLiteral = exports.visitorSuccessful = void 0;
|
|
const babelTypes = __importStar(require("@babel/types"));
|
|
const babel = __importStar(require("@babel/parser"));
|
|
const generator_1 = __importDefault(require("@babel/generator"));
|
|
const log_1 = require("./log");
|
|
const common_1 = require("./common");
|
|
function isLiteral(node) {
|
|
if (node == null) {
|
|
return false;
|
|
}
|
|
if (!node.type.endsWith('Literal')) {
|
|
return false;
|
|
}
|
|
if (node.type === 'TemplateLiteral' && node.expressions.length !== 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function getLiteralValue(node) {
|
|
if (node.type === 'TemplateLiteral') {
|
|
return node.quasis[0].value.raw;
|
|
}
|
|
if (node.type === 'NullLiteral') {
|
|
throw new Error('Unexpected null in require expression');
|
|
}
|
|
if (node.type === 'RegExpLiteral') {
|
|
throw new Error('Unexpected regexp in require expression');
|
|
}
|
|
return node.value;
|
|
}
|
|
function reconstructSpecifiers(specs) {
|
|
if (!specs || !specs.length) {
|
|
return '';
|
|
}
|
|
const defaults = [];
|
|
for (const spec of specs) {
|
|
if (babelTypes.isImportDefaultSpecifier(spec)) {
|
|
defaults.push(spec.local.name);
|
|
}
|
|
}
|
|
const nonDefaults = [];
|
|
for (const spec of specs) {
|
|
if (babelTypes.isImportSpecifier(spec)) {
|
|
const importedName = babelTypes.isIdentifier(spec.imported)
|
|
? spec.imported.name
|
|
: spec.imported.value;
|
|
if (spec.local.name === importedName) {
|
|
nonDefaults.push(spec.local.name);
|
|
}
|
|
else {
|
|
nonDefaults.push(`${importedName} as ${spec.local.name}`);
|
|
}
|
|
}
|
|
}
|
|
if (nonDefaults.length) {
|
|
defaults.push(`{ ${nonDefaults.join(', ')} }`);
|
|
}
|
|
return defaults.join(', ');
|
|
}
|
|
function reconstruct(node) {
|
|
let v = (0, generator_1.default)(node, { comments: false }).code.replace(/\n/g, '');
|
|
let v2;
|
|
while (true) {
|
|
v2 = v.replace(/\[ /g, '[').replace(/ \]/g, ']').replace(/ {2}/g, ' ');
|
|
if (v2 === v) {
|
|
break;
|
|
}
|
|
v = v2;
|
|
}
|
|
return v2;
|
|
}
|
|
function forge(pattern, was) {
|
|
return pattern
|
|
.replace('{c1}', ', ')
|
|
.replace('{v1}', `"${was.v1}"`)
|
|
.replace('{c2}', was.v2 ? ', ' : '')
|
|
.replace('{v2}', was.v2 ? `"${was.v2}"` : '')
|
|
.replace('{c3}', was.v3 ? ' from ' : '')
|
|
.replace('{v3}', was.v3 ? was.v3 : '');
|
|
}
|
|
function valid2(v2) {
|
|
return (v2 === undefined ||
|
|
v2 === null ||
|
|
v2 === 'must-exclude' ||
|
|
v2 === 'may-exclude');
|
|
}
|
|
function visitorRequireResolve(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isMemberExpression(n.callee)) {
|
|
return null;
|
|
}
|
|
const ci = n.callee.object.type === 'Identifier' &&
|
|
n.callee.object.name === 'require' &&
|
|
n.callee.property.type === 'Identifier' &&
|
|
n.callee.property.name === 'resolve';
|
|
if (!ci) {
|
|
return null;
|
|
}
|
|
if (!n.arguments || !isLiteral(n.arguments[0])) {
|
|
return null;
|
|
}
|
|
return {
|
|
v1: getLiteralValue(n.arguments[0]),
|
|
v2: isLiteral(n.arguments[1]) ? getLiteralValue(n.arguments[1]) : null,
|
|
};
|
|
}
|
|
function visitorRequire(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isIdentifier(n.callee)) {
|
|
return null;
|
|
}
|
|
if (n.callee.name !== 'require') {
|
|
return null;
|
|
}
|
|
if (!n.arguments || !isLiteral(n.arguments[0])) {
|
|
return null;
|
|
}
|
|
return {
|
|
v1: getLiteralValue(n.arguments[0]),
|
|
v2: isLiteral(n.arguments[1]) ? getLiteralValue(n.arguments[1]) : null,
|
|
};
|
|
}
|
|
function visitorImport(n) {
|
|
if (!babelTypes.isImportDeclaration(n)) {
|
|
return null;
|
|
}
|
|
return { v1: n.source.value, v3: reconstructSpecifiers(n.specifiers) };
|
|
}
|
|
function visitorPathJoin(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isMemberExpression(n.callee)) {
|
|
return null;
|
|
}
|
|
const ci = n.callee.object &&
|
|
n.callee.object.type === 'Identifier' &&
|
|
n.callee.object.name === 'path' &&
|
|
n.callee.property &&
|
|
n.callee.property.type === 'Identifier' &&
|
|
n.callee.property.name === 'join';
|
|
if (!ci) {
|
|
return null;
|
|
}
|
|
const dn = n.arguments[0] &&
|
|
n.arguments[0].type === 'Identifier' &&
|
|
n.arguments[0].name === '__dirname';
|
|
if (!dn) {
|
|
return null;
|
|
}
|
|
const f = n.arguments && isLiteral(n.arguments[1]) && n.arguments.length === 2; // TODO concat them
|
|
if (!f) {
|
|
return null;
|
|
}
|
|
return { v1: getLiteralValue(n.arguments[1]) };
|
|
}
|
|
function visitorSuccessful(node, test = false) {
|
|
let was = visitorRequireResolve(node);
|
|
if (was) {
|
|
if (test) {
|
|
return forge('require.resolve({v1}{c2}{v2})', was);
|
|
}
|
|
if (!valid2(was.v2)) {
|
|
return null;
|
|
}
|
|
return {
|
|
alias: was.v1,
|
|
aliasType: common_1.ALIAS_AS_RESOLVABLE,
|
|
mustExclude: was.v2 === 'must-exclude',
|
|
mayExclude: was.v2 === 'may-exclude',
|
|
};
|
|
}
|
|
was = visitorRequire(node);
|
|
if (was) {
|
|
if (test) {
|
|
return forge('require({v1}{c2}{v2})', was);
|
|
}
|
|
if (!valid2(was.v2)) {
|
|
return null;
|
|
}
|
|
return {
|
|
alias: was.v1,
|
|
aliasType: common_1.ALIAS_AS_RESOLVABLE,
|
|
mustExclude: was.v2 === 'must-exclude',
|
|
mayExclude: was.v2 === 'may-exclude',
|
|
};
|
|
}
|
|
was = visitorImport(node);
|
|
if (was) {
|
|
if (test) {
|
|
return forge('import {v3}{c3}{v1}', was);
|
|
}
|
|
return { alias: was.v1, aliasType: common_1.ALIAS_AS_RESOLVABLE };
|
|
}
|
|
was = visitorPathJoin(node);
|
|
if (was) {
|
|
if (test) {
|
|
return forge('path.join(__dirname{c1}{v1})', was);
|
|
}
|
|
return { alias: was.v1, aliasType: common_1.ALIAS_AS_RELATIVE, mayExclude: false };
|
|
}
|
|
return null;
|
|
}
|
|
exports.visitorSuccessful = visitorSuccessful;
|
|
function nonLiteralRequireResolve(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isMemberExpression(n.callee)) {
|
|
return null;
|
|
}
|
|
const ci = n.callee.object.type === 'Identifier' &&
|
|
n.callee.object.name === 'require' &&
|
|
n.callee.property.type === 'Identifier' &&
|
|
n.callee.property.name === 'resolve';
|
|
if (!ci) {
|
|
return null;
|
|
}
|
|
if (isLiteral(n.arguments[0])) {
|
|
return null;
|
|
}
|
|
const m = n.arguments[1];
|
|
if (!m) {
|
|
return { v1: reconstruct(n.arguments[0]) };
|
|
}
|
|
if (!isLiteral(n.arguments[1])) {
|
|
return null;
|
|
}
|
|
return {
|
|
v1: reconstruct(n.arguments[0]),
|
|
v2: getLiteralValue(n.arguments[1]),
|
|
};
|
|
}
|
|
function nonLiteralRequire(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isIdentifier(n.callee)) {
|
|
return null;
|
|
}
|
|
if (n.callee.name !== 'require') {
|
|
return null;
|
|
}
|
|
if (isLiteral(n.arguments[0])) {
|
|
return null;
|
|
}
|
|
const m = n.arguments[1];
|
|
if (!m) {
|
|
return { v1: reconstruct(n.arguments[0]) };
|
|
}
|
|
if (!isLiteral(n.arguments[1])) {
|
|
return null;
|
|
}
|
|
return {
|
|
v1: reconstruct(n.arguments[0]),
|
|
v2: getLiteralValue(n.arguments[1]),
|
|
};
|
|
}
|
|
function visitorNonLiteral(n) {
|
|
const was = nonLiteralRequireResolve(n) || nonLiteralRequire(n);
|
|
if (was) {
|
|
if (!valid2(was.v2)) {
|
|
return null;
|
|
}
|
|
return {
|
|
alias: was.v1,
|
|
mustExclude: was.v2 === 'must-exclude',
|
|
mayExclude: was.v2 === 'may-exclude',
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
exports.visitorNonLiteral = visitorNonLiteral;
|
|
function isRequire(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isIdentifier(n.callee)) {
|
|
return null;
|
|
}
|
|
if (n.callee.name !== 'require') {
|
|
return null;
|
|
}
|
|
const f = n.arguments && n.arguments[0];
|
|
if (!f) {
|
|
return null;
|
|
}
|
|
return { v1: reconstruct(n.arguments[0]) };
|
|
}
|
|
function isRequireResolve(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isMemberExpression(n.callee)) {
|
|
return null;
|
|
}
|
|
const ci = n.callee.object.type === 'Identifier' &&
|
|
n.callee.object.name === 'require' &&
|
|
n.callee.property.type === 'Identifier' &&
|
|
n.callee.property.name === 'resolve';
|
|
if (!ci) {
|
|
return null;
|
|
}
|
|
const f = n.type === 'CallExpression' && n.arguments && n.arguments[0];
|
|
if (!f) {
|
|
return null;
|
|
}
|
|
return { v1: reconstruct(n.arguments[0]) };
|
|
}
|
|
function visitorMalformed(n) {
|
|
const was = isRequireResolve(n) || isRequire(n);
|
|
if (was) {
|
|
return { alias: was.v1 };
|
|
}
|
|
return null;
|
|
}
|
|
exports.visitorMalformed = visitorMalformed;
|
|
function visitorUseSCWD(n) {
|
|
if (!babelTypes.isCallExpression(n)) {
|
|
return null;
|
|
}
|
|
if (!babelTypes.isMemberExpression(n.callee)) {
|
|
return null;
|
|
}
|
|
const ci = n.callee.object.type === 'Identifier' &&
|
|
n.callee.object.name === 'path' &&
|
|
n.callee.property.type === 'Identifier' &&
|
|
n.callee.property.name === 'resolve';
|
|
if (!ci) {
|
|
return null;
|
|
}
|
|
const was = { v1: n.arguments.map(reconstruct).join(', ') };
|
|
if (was) {
|
|
return { alias: was.v1 };
|
|
}
|
|
return null;
|
|
}
|
|
exports.visitorUseSCWD = visitorUseSCWD;
|
|
function traverse(ast, visitor) {
|
|
// modified esprima-walk to support
|
|
// visitor return value and "trying" flag
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const stack = [[ast, false]];
|
|
for (let i = 0; i < stack.length; i += 1) {
|
|
const item = stack[i];
|
|
const [node] = item;
|
|
if (node) {
|
|
const trying = item[1] || babelTypes.isTryStatement(node);
|
|
if (visitor(node, trying)) {
|
|
for (const key in node) {
|
|
if (node[key]) {
|
|
const child = node[key];
|
|
if (child instanceof Array) {
|
|
for (let j = 0; j < child.length; j += 1) {
|
|
stack.push([child[j], trying]);
|
|
}
|
|
}
|
|
else if (child && typeof child.type === 'string') {
|
|
stack.push([child, trying]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function parse(body) {
|
|
return babel.parse(body, {
|
|
allowImportExportEverywhere: true,
|
|
allowReturnOutsideFunction: true,
|
|
});
|
|
}
|
|
exports.parse = parse;
|
|
function detect(body, visitor) {
|
|
let json;
|
|
try {
|
|
json = parse(body);
|
|
}
|
|
catch (error) {
|
|
log_1.log.warn(`Babel parse has failed: ${error.message}`);
|
|
}
|
|
if (!json) {
|
|
return;
|
|
}
|
|
traverse(json, visitor);
|
|
}
|
|
exports.detect = detect;
|
|
//# sourceMappingURL=detector.js.map
|