186 lines
7.8 KiB
JavaScript
186 lines
7.8 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.getExpressionCode = exports.getParsedExpression = void 0;
|
|
const recast_1 = require("recast");
|
|
const ast_types_1 = require("ast-types");
|
|
const VariablePolyfill_1 = require("./VariablePolyfill");
|
|
const ExpressionSplitter_1 = require("./ExpressionSplitter");
|
|
const Parser_1 = require("./Parser");
|
|
const v = ast_types_1.builders.identifier('v');
|
|
const shouldAlwaysWrapList = ['window', 'global', 'this'];
|
|
const shouldWrapInTry = (node) => {
|
|
let shouldWrap = false;
|
|
(0, recast_1.visit)(node, {
|
|
visitMemberExpression() {
|
|
shouldWrap = true;
|
|
return false;
|
|
},
|
|
visitCallExpression() {
|
|
shouldWrap = true;
|
|
return false;
|
|
},
|
|
visitIdentifier(path) {
|
|
if (shouldAlwaysWrapList.includes(path.node.name)) {
|
|
shouldWrap = true;
|
|
return false;
|
|
}
|
|
this.traverse(path);
|
|
return;
|
|
},
|
|
});
|
|
return shouldWrap;
|
|
};
|
|
const hasFunction = (node) => {
|
|
let hasFn = false;
|
|
(0, recast_1.visit)(node, {
|
|
visitFunctionExpression() {
|
|
hasFn = true;
|
|
return false;
|
|
},
|
|
visitFunctionDeclaration() {
|
|
hasFn = true;
|
|
return false;
|
|
},
|
|
visitArrowFunctionExpression() {
|
|
hasFn = true;
|
|
return false;
|
|
},
|
|
});
|
|
return hasFn;
|
|
};
|
|
const hasTemplateString = (node) => {
|
|
let hasTemp = false;
|
|
(0, recast_1.visit)(node, {
|
|
visitTemplateLiteral(path) {
|
|
if (path.node.expressions.length) {
|
|
hasTemp = true;
|
|
return false;
|
|
}
|
|
this.traverse(path);
|
|
return;
|
|
},
|
|
});
|
|
return hasTemp;
|
|
};
|
|
const wrapInErrorHandler = (node) => {
|
|
return ast_types_1.builders.tryStatement(ast_types_1.builders.blockStatement([node]), ast_types_1.builders.catchClause(ast_types_1.builders.identifier('e'), null, ast_types_1.builders.blockStatement([
|
|
ast_types_1.builders.expressionStatement(ast_types_1.builders.callExpression(ast_types_1.builders.identifier('E'), [ast_types_1.builders.identifier('e'), ast_types_1.builders.thisExpression()])),
|
|
])));
|
|
};
|
|
const maybeWrapExpr = (expr) => {
|
|
if (expr.trimStart()[0] === '{') {
|
|
return '(' + expr + ')';
|
|
}
|
|
return expr;
|
|
};
|
|
const buildFunctionBody = (expr) => {
|
|
return ast_types_1.builders.blockStatement([
|
|
ast_types_1.builders.expressionStatement(ast_types_1.builders.assignmentExpression('=', v, expr)),
|
|
ast_types_1.builders.returnStatement(ast_types_1.builders.conditionalExpression(ast_types_1.builders.logicalExpression('||', ast_types_1.builders.logicalExpression('||', v, ast_types_1.builders.binaryExpression('===', v, ast_types_1.builders.literal(0))), ast_types_1.builders.binaryExpression('===', v, ast_types_1.builders.literal(false))), v, ast_types_1.builders.literal(''))),
|
|
]);
|
|
};
|
|
const fixStringNewLines = (node) => {
|
|
const replace = (str) => {
|
|
return str.replace(/\n/g, '\\n');
|
|
};
|
|
(0, recast_1.visit)(node, {
|
|
visitTemplateElement(path) {
|
|
this.traverse(path);
|
|
const el = ast_types_1.builders.templateElement({
|
|
cooked: path.node.value.cooked === null ? null : replace(path.node.value.cooked),
|
|
raw: replace(path.node.value.raw),
|
|
}, path.node.tail);
|
|
path.replace(el);
|
|
},
|
|
});
|
|
return node;
|
|
};
|
|
const getParsedExpression = (expr) => {
|
|
return (0, ExpressionSplitter_1.splitExpression)(expr).map((chunk) => {
|
|
if (chunk.type === 'code') {
|
|
const code = maybeWrapExpr(chunk.text);
|
|
const node = (0, recast_1.parse)(code, {
|
|
parser: { parse: Parser_1.parseWithEsprimaNext },
|
|
});
|
|
return { ...chunk, parsed: node };
|
|
}
|
|
return chunk;
|
|
});
|
|
};
|
|
exports.getParsedExpression = getParsedExpression;
|
|
const getExpressionCode = (expr, dataNodeName, hooks) => {
|
|
var _a, _b;
|
|
const chunks = (0, exports.getParsedExpression)(expr);
|
|
const newProg = ast_types_1.builders.program([
|
|
ast_types_1.builders.variableDeclaration('var', [ast_types_1.builders.variableDeclarator(VariablePolyfill_1.globalIdentifier, ast_types_1.builders.objectExpression([]))]),
|
|
]);
|
|
let dataNode = ast_types_1.builders.thisExpression();
|
|
const hasFn = chunks.filter((c) => c.type === 'code').some((c) => hasFunction(c.parsed));
|
|
if (hasFn) {
|
|
dataNode = ast_types_1.builders.identifier(dataNodeName);
|
|
newProg.body.push(ast_types_1.builders.variableDeclaration('var', [ast_types_1.builders.variableDeclarator(dataNode, ast_types_1.builders.thisExpression())]));
|
|
}
|
|
const hasTempString = chunks.filter((c) => c.type === 'code').some((c) => hasTemplateString(c.parsed));
|
|
if (chunks.length > 2 ||
|
|
chunks[0].text !== '' ||
|
|
(chunks[0].text === '' && chunks.length === 1)) {
|
|
let parts = [];
|
|
for (const chunk of chunks) {
|
|
if (chunk.type === 'text') {
|
|
parts.push(ast_types_1.builders.literal(chunk.text));
|
|
}
|
|
else {
|
|
const fixed = fixStringNewLines(chunk.parsed);
|
|
for (const hook of hooks.before) {
|
|
hook(fixed, dataNode);
|
|
}
|
|
const parsed = (_a = (0, VariablePolyfill_1.jsVariablePolyfill)(fixed, dataNode)) === null || _a === void 0 ? void 0 : _a[0];
|
|
if (!parsed || parsed.type !== 'ExpressionStatement') {
|
|
throw new SyntaxError('Not a expression statement');
|
|
}
|
|
for (const hook of hooks.after) {
|
|
hook(parsed, dataNode);
|
|
}
|
|
const functionBody = buildFunctionBody(parsed.expression);
|
|
if (shouldWrapInTry(parsed)) {
|
|
functionBody.body = [
|
|
wrapInErrorHandler(functionBody.body[0]),
|
|
ast_types_1.builders.expressionStatement(ast_types_1.builders.identifier('')),
|
|
functionBody.body[1],
|
|
];
|
|
}
|
|
parts.push(ast_types_1.builders.callExpression(ast_types_1.builders.memberExpression(ast_types_1.builders.functionExpression(null, [v], functionBody), ast_types_1.builders.identifier('call')), [ast_types_1.builders.thisExpression()]));
|
|
}
|
|
}
|
|
if (chunks.length < 2) {
|
|
newProg.body.push(ast_types_1.builders.returnStatement(parts[0]));
|
|
}
|
|
else {
|
|
parts = parts.filter((i) => !(i.type === 'Literal' && i.value === ''));
|
|
newProg.body.push(ast_types_1.builders.returnStatement(ast_types_1.builders.callExpression(ast_types_1.builders.memberExpression(ast_types_1.builders.arrayExpression(parts), ast_types_1.builders.identifier('join')), [
|
|
ast_types_1.builders.literal(''),
|
|
])));
|
|
}
|
|
}
|
|
else {
|
|
const fixed = fixStringNewLines(chunks[1].parsed);
|
|
for (const hook of hooks.before) {
|
|
hook(fixed, dataNode);
|
|
}
|
|
const parsed = (_b = (0, VariablePolyfill_1.jsVariablePolyfill)(fixed, dataNode)) === null || _b === void 0 ? void 0 : _b[0];
|
|
if (!parsed || parsed.type !== 'ExpressionStatement') {
|
|
throw new SyntaxError('Not a expression statement');
|
|
}
|
|
for (const hook of hooks.after) {
|
|
hook(parsed, dataNode);
|
|
}
|
|
let retData = ast_types_1.builders.returnStatement(parsed.expression);
|
|
if (shouldWrapInTry(parsed)) {
|
|
retData = wrapInErrorHandler(retData);
|
|
}
|
|
newProg.body.push(retData);
|
|
}
|
|
return [(0, recast_1.print)(newProg).code, { has: { function: hasFn, templateString: hasTempString } }];
|
|
};
|
|
exports.getExpressionCode = getExpressionCode;
|
|
//# sourceMappingURL=ExpressionBuilder.js.map
|