You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
7.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapperVOn = exports.transformOn = void 0;
const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
const compiler_core_1 = require("@vue/compiler-core");
const shared_1 = require("@vue/shared");
const __1 = require("..");
const runtimeHelpers_1 = require("../runtimeHelpers");
const transformExpression_1 = require("./transformExpression");
const vFor_1 = require("./vFor");
const utils_1 = require("./utils");
const fnExpRE = /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/;
const transformOn = (dir, node, _context, augmentor) => {
const context = _context;
const { loc, modifiers, arg } = dir;
if (!dir.exp && !modifiers.length) {
context.onError((0, compiler_core_1.createCompilerError)(compiler_core_1.ErrorCodes.X_V_ON_NO_EXPRESSION, loc));
}
let eventName;
if (arg.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) {
const rawName = arg.content;
// for all event listeners, auto convert it to camelCase. See issue #2249
eventName = (0, compiler_core_1.createSimpleExpression)((0, shared_1.toHandlerKey)((0, shared_1.camelize)(rawName)), true, arg.loc);
}
else {
// #2388
eventName = (0, compiler_core_1.createCompoundExpression)([
// `${context.helperString(TO_HANDLER_KEY)}(`,
arg,
// `)`,
]);
}
}
else {
// already a compound expression.
eventName = arg;
eventName.children.unshift(`${context.helperString(compiler_core_1.TO_HANDLER_KEY)}(`);
eventName.children.push(`)`);
}
// handler processing
let exp = dir.exp;
if (exp && !exp.content.trim()) {
exp = undefined;
}
let shouldCache = context.cacheHandlers && !exp && !context.inVOnce;
if (exp) {
const isMemberExp = (0, compiler_core_1.isMemberExpression)(exp.content, context);
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));
const hasMultipleStatements = exp.content.includes(`;`);
// process the expression since it's been skipped
if (context.prefixIdentifiers) {
isInlineStatement && context.addIdentifiers(`$event`);
exp = dir.exp = (0, transformExpression_1.processExpression)(exp, context, false, hasMultipleStatements);
isInlineStatement && context.removeIdentifiers(`$event`);
// with scope analysis, the function is hoistable if it has no reference
// to scope variables.
shouldCache =
context.cacheHandlers &&
// unnecessary to cache inside v-once
!context.inVOnce &&
// runtime constants don't need to be cached
// (this is analyzed by compileScript in SFC <script setup>)
!(exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) &&
// #1541 bail if this is a member exp handler passed to a component -
// we need to use the original function to preserve arity,
// e.g. <transition> relies on checking cb.length to determine
// transition end handling. Inline function is ok since its arity
// is preserved even when cached.
!(isMemberExp && node.tagType === compiler_core_1.ElementTypes.COMPONENT) &&
// bail if the function references closure variables (v-for, v-slot)
// it must be passed fresh to avoid stale values.
!(0, compiler_core_1.hasScopeRef)(exp, context.identifiers) &&
// wxs event
!(0, utils_1.isFilterExpr)(exp, context);
// If the expression is optimizable and is a member expression pointing
// to a function, turn it into invocation (and wrap in an arrow function
// below) so that it always accesses the latest value when called - thus
// avoiding the need to be patched.
if (shouldCache && isMemberExp) {
if (exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
exp.content = `${exp.content} && ${exp.content}(...args)`;
}
else {
exp.children = [...exp.children, ` && `, ...exp.children, `(...args)`];
}
}
}
if (isInlineStatement || (shouldCache && isMemberExp)) {
// wrap inline statement in a function expression
exp = (0, compiler_core_1.createCompoundExpression)([
`${isInlineStatement
? context.isTS
? `($event: any)`
: `$event`
: `${context.isTS ? `\n//@ts-ignore\n` : ``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,
exp,
hasMultipleStatements ? `}` : `)`,
]);
}
}
let ret = {
props: [
(0, compiler_core_1.createObjectProperty)(eventName, exp || (0, compiler_core_1.createSimpleExpression)(`() => {}`, false, loc)),
],
};
// apply extended compiler augmentor
if (augmentor) {
ret = augmentor(ret);
}
// TODO
if (shouldCache) {
// cache handlers so that it's always the same handler being passed down.
// this avoids unnecessary re-renders when users use inline handlers on
// components.
// ret.props[0].value = wrapper(
// context.cache(ret.props[0].value) as ExpressionNode,
// context
// )
ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
}
else {
ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
}
// mark the key as handler for props normalization check
ret.props.forEach((p) => (p.key.isHandlerKey = true));
return ret;
};
exports.transformOn = transformOn;
function wrapperVOn(value, node, context) {
if ((0, transformExpression_1.isBuiltInIdentifier)(value)) {
return value;
}
// wxs event
if ((0, utils_1.isFilterExpr)(value, context)) {
return value;
}
const keys = [];
if (context.miniProgram.event?.key && context.inVFor) {
let keyProp = (0, compiler_core_1.findProp)(node, 'key');
if (!keyProp) {
const vForScope = (0, vFor_1.parseVForScope)(context.currentScope);
if (vForScope) {
keyProp = (0, compiler_core_1.findProp)(vForScope.node, 'key');
}
}
// 对 for 中的所有事件增加 key 标记,避免微信小程序不更新事件对象
if (keyProp && (0, uni_cli_shared_1.isDirectiveNode)(keyProp) && keyProp.exp) {
const keyCode = (0, __1.genExpr)(keyProp.exp);
if (keyCode) {
keys.push(',');
keys.push((0, __1.genExpr)(keyProp.exp));
}
}
}
const newExpr = (0, compiler_core_1.createCompoundExpression)([
`${context.helperString(runtimeHelpers_1.V_ON)}(`,
value,
...keys,
`)`,
]);
// 严格限制生效范围仅当有keys时才保存原始事件表达式
if (keys.length) {
// @ts-expect-error 内部 parseVOn 时使用
newExpr.__withoutKeysVOnExpr = (0, compiler_core_1.createCompoundExpression)([
`${context.helperString(runtimeHelpers_1.V_ON)}(`,
value,
`)`,
]);
}
return newExpr;
}
exports.wrapperVOn = wrapperVOn;