uni-ticket-system/node_modules/@dcloudio/uni-mp-compiler/dist/transforms/transformExpression.js
2023-12-05 10:11:10 +08:00

296 lines
13 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isBuiltInIdentifier = exports.processExpression = exports.transformExpression = void 0;
const shared_1 = require("@vue/shared");
const compiler_core_1 = require("@vue/compiler-core");
const parser_1 = require("@babel/parser");
const isLiteralWhitelisted = /*#__PURE__*/ (0, shared_1.makeMap)('true,false,null,this');
const transformExpression = (node, context) => {
if (node.type === 5 /* NodeTypes.INTERPOLATION */) {
node.content = processExpression(node.content, context);
}
else if (node.type === 1 /* NodeTypes.ELEMENT */) {
// handle directives on element
for (let i = 0; i < node.props.length; i++) {
const dir = node.props[i];
// do not process for v-on & v-for since they are special handled
if (dir.type === 7 /* NodeTypes.DIRECTIVE */ && dir.name !== 'for') {
const exp = dir.exp;
const arg = dir.arg;
// do not process exp if this is v-on:arg - we need special handling
// for wrapping inline statements.
if (exp &&
exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
!(dir.name === 'on' && arg)) {
dir.exp = processExpression(exp, context,
// slot args must be processed as function params
dir.name === 'slot');
}
if (arg && arg.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ && !arg.isStatic) {
dir.arg = processExpression(arg, context);
}
}
}
}
};
exports.transformExpression = transformExpression;
// Important: since this function uses Node.js only dependencies, it should
// always be used with a leading !__BROWSER__ check so that it can be
// tree-shaken from the browser build.
function processExpression(node, context,
// some expressions like v-slot props & v-for aliases should be parsed as
// function params
asParams = false,
// v-on handler values may contain multiple statements
asRawStatements = false, localVars = Object.create(context.identifiers)) {
if (!context.prefixIdentifiers || !node.content.trim()) {
return node;
}
const { inline, bindingMetadata } = context;
const rewriteIdentifier = (raw, parent, id) => {
const type = (0, shared_1.hasOwn)(bindingMetadata, raw) && bindingMetadata[raw];
if (inline) {
// x = y
const isAssignmentLVal = parent && parent.type === 'AssignmentExpression' && parent.left === id;
// x++
const isUpdateArg = parent && parent.type === 'UpdateExpression' && parent.argument === id;
// ({ x } = y)
const isDestructureAssignment = parent && (0, compiler_core_1.isInDestructureAssignment)(parent, parentStack);
if (type === "setup-const" /* BindingTypes.SETUP_CONST */ ||
type === "setup-reactive-const" /* BindingTypes.SETUP_REACTIVE_CONST */ ||
localVars[raw]) {
return raw;
}
else if (type === "setup-ref" /* BindingTypes.SETUP_REF */) {
return `${raw}.value`;
}
else if (type === "setup-maybe-ref" /* BindingTypes.SETUP_MAYBE_REF */) {
// const binding that may or may not be ref
// if it's not a ref, then assignments don't make sense -
// so we ignore the non-ref assignment case and generate code
// that assumes the value to be a ref for more efficiency
return isAssignmentLVal || isUpdateArg || isDestructureAssignment
? `${raw}.value`
: `${context.helperString(compiler_core_1.UNREF)}(${raw})`;
}
else if (type === "setup-let" /* BindingTypes.SETUP_LET */) {
if (isAssignmentLVal) {
// let binding.
// this is a bit more tricky as we need to cover the case where
// let is a local non-ref value, and we need to replicate the
// right hand side value.
// x = y --> isRef(x) ? x.value = y : x = y
const { right: rVal, operator } = parent;
const rExp = rawExp.slice(rVal.start - 1, rVal.end - 1);
const rExpString = stringifyExpression(processExpression((0, compiler_core_1.createSimpleExpression)(rExp, false), context, false, false, knownIds));
return `${context.helperString(compiler_core_1.IS_REF)}(${raw})${context.isTS ? ` //@ts-ignore\n` : ``} ? ${raw}.value ${operator} ${rExpString} : ${raw}`;
}
else if (isUpdateArg) {
// make id replace parent in the code range so the raw update operator
// is removed
id.start = parent.start;
id.end = parent.end;
const { prefix: isPrefix, operator } = parent;
const prefix = isPrefix ? operator : ``;
const postfix = isPrefix ? `` : operator;
// let binding.
// x++ --> isRef(a) ? a.value++ : a++
return `${context.helperString(compiler_core_1.IS_REF)}(${raw})${context.isTS ? ` //@ts-ignore\n` : ``} ? ${prefix}${raw}.value${postfix} : ${prefix}${raw}${postfix}`;
}
else if (isDestructureAssignment) {
// TODO
// let binding in a destructure assignment - it's very tricky to
// handle both possible cases here without altering the original
// structure of the code, so we just assume it's not a ref here
// for now
return raw;
}
else {
return `${context.helperString(compiler_core_1.UNREF)}(${raw})`;
}
}
else if (type === "props" /* BindingTypes.PROPS */) {
// use __props which is generated by compileScript so in ts mode
// it gets correct type
return (0, shared_1.genPropsAccessExp)(raw);
}
else if (type === "props-aliased" /* BindingTypes.PROPS_ALIASED */) {
// prop with a different local alias (from defineProps() destructure)
return (0, shared_1.genPropsAccessExp)(bindingMetadata.__propsAliases[raw]);
}
}
else {
if (type && type.startsWith('setup')) {
// setup bindings in non-inline mode
return `$setup.${raw}`;
}
else if (type === "props-aliased" /* BindingTypes.PROPS_ALIASED */) {
return `$props['${bindingMetadata.__propsAliases[raw]}']`;
}
else if (type) {
return `$${type}.${raw}`;
}
}
// fallback to ctx
return `_ctx.${raw}`;
};
// fast path if expression is a simple identifier.
const rawExp = node.content;
// bail constant on parens (function invocation) and dot (member access)
const bailConstant = rawExp.indexOf(`(`) > -1 || rawExp.indexOf('.') > 0;
if ((0, compiler_core_1.isSimpleIdentifier)(rawExp)) {
const isScopeVarReference = context.identifiers[rawExp];
const isAllowedGlobal = (0, shared_1.isGloballyWhitelisted)(rawExp);
const isLiteral = isLiteralWhitelisted(rawExp);
const isFilter = context.filters.includes(rawExp);
const isBuiltIn = isBuiltInIdentifier(rawExp);
if (!asParams &&
!isScopeVarReference &&
!isAllowedGlobal &&
!isLiteral &&
!isFilter &&
!isBuiltIn) {
// const bindings exposed from setup can be skipped for patching but
// cannot be hoisted to module scope
if (bindingMetadata[node.content] === "setup-const" /* BindingTypes.SETUP_CONST */) {
node.constType = 1 /* ConstantTypes.CAN_SKIP_PATCH */;
}
node.content = rewriteIdentifier(rawExp);
}
else if (!isScopeVarReference) {
if (isLiteral) {
node.constType = 3 /* ConstantTypes.CAN_STRINGIFY */;
}
else {
node.constType = 2 /* ConstantTypes.CAN_HOIST */;
}
}
return node;
}
let ast;
// exp needs to be parsed differently:
// 1. Multiple inline statements (v-on, with presence of `;`): parse as raw
// exp, but make sure to pad with spaces for consistent ranges
// 2. Expressions: wrap with parens (for e.g. object expressions)
// 3. Function arguments (v-for, v-slot): place in a function argument position
const source = asRawStatements
? ` ${rawExp} `
: `(${rawExp})${asParams ? `=>{}` : ``}`;
try {
ast = (0, parser_1.parse)(source, {
plugins: context.expressionPlugins,
}).program;
}
catch (e) {
context.onError((0, compiler_core_1.createCompilerError)(45 /* ErrorCodes.X_INVALID_EXPRESSION */, node.loc, undefined, '\n' + source + '\n' + e.message));
return node;
}
const ids = [];
const parentStack = [];
const knownIds = Object.create(context.identifiers);
context.filters.forEach((name) => {
knownIds[name] = 1;
});
(0, compiler_core_1.walkIdentifiers)(ast, (node, parent, _, isReferenced, isLocal) => {
if ((0, compiler_core_1.isStaticPropertyKey)(node, parent)) {
return;
}
const needPrefix = isReferenced && canPrefix(node);
if (needPrefix && !isLocal) {
if ((0, compiler_core_1.isStaticProperty)(parent) && parent.shorthand) {
// property shorthand like { foo }, we need to add the key since
// we rewrite the value
;
node.prefix = `${node.name}: `;
}
node.name = rewriteIdentifier(node.name, parent, node);
ids.push(node);
}
else {
// The identifier is considered constant unless it's pointing to a
// local scope variable (a v-for alias, or a v-slot prop)
if (!(needPrefix && isLocal) && !bailConstant) {
;
node.isConstant = true;
}
// also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static)
ids.push(node);
}
}, true, // invoke on ALL identifiers
parentStack, knownIds);
// We break up the compound expression into an array of strings and sub
// expressions (for identifiers that have been prefixed). In codegen, if
// an ExpressionNode has the `.children` property, it will be used instead of
// `.content`.
const children = [];
ids.sort((a, b) => a.start - b.start);
ids.forEach((id, i) => {
// range is offset by -1 due to the wrapping parens when parsed
const start = id.start - 1;
const end = id.end - 1;
const last = ids[i - 1];
const leadingText = rawExp.slice(last ? last.end - 1 : 0, start);
if (leadingText.length || id.prefix) {
children.push(leadingText + (id.prefix || ``));
}
const source = rawExp.slice(start, end);
children.push((0, compiler_core_1.createSimpleExpression)(id.name, false, {
source,
start: (0, compiler_core_1.advancePositionWithClone)(node.loc.start, source, start),
end: (0, compiler_core_1.advancePositionWithClone)(node.loc.start, source, end),
}, id.isConstant ? 3 /* ConstantTypes.CAN_STRINGIFY */ : 0 /* ConstantTypes.NOT_CONSTANT */));
if (i === ids.length - 1 && end < rawExp.length) {
children.push(rawExp.slice(end));
}
});
let ret;
if (children.length) {
ret = (0, compiler_core_1.createCompoundExpression)(children, node.loc);
}
else {
ret = node;
ret.constType = bailConstant
? 0 /* ConstantTypes.NOT_CONSTANT */
: 3 /* ConstantTypes.CAN_STRINGIFY */;
}
ret.identifiers = Object.keys(knownIds);
return ret;
}
exports.processExpression = processExpression;
function canPrefix(id) {
// skip whitelisted globals
if ((0, shared_1.isGloballyWhitelisted)(id.name)) {
return false;
}
// special case for webpack compilation
if (id.name === 'require') {
return false;
}
return true;
}
function stringifyExpression(exp) {
if ((0, shared_1.isString)(exp)) {
return exp;
}
else if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
return exp.content;
}
else {
return exp.children
.map(stringifyExpression)
.join('');
}
}
const builtInIdentifiers = ['__l'];
function isBuiltInIdentifier(id) {
if (!(0, shared_1.isString)(id)) {
if (id.type !== 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
return false;
}
id = id.content;
}
return builtInIdentifiers.includes(id);
}
exports.isBuiltInIdentifier = isBuiltInIdentifier;