okam-build
Version:
The build tool for Okam develop framework
155 lines (132 loc) • 5.52 kB
JavaScript
/**
* @file transform event attr
* @author sharonzd
*/
;
const {
EVENT_HANDLE_REGEXP,
VARIABLE_EVENT,
NOT_SUPPORT_MODIFIERS
} = require('./constant');
const {toHyphen} = require('../../../../util').string;
/**
* show warning log when parsing event name
*
* @param {string} name attribute name
* @param {string} newName new attribute name
* @param {Array} eventModifiers array of event modifier
* @param {Object} attrs all attributes
* @param {Object} tplOpts template options
*/
function showEventNameLog(name, newName, eventModifiers, attrs, tplOpts) {
let {logger, file, appType} = tplOpts;
NOT_SUPPORT_MODIFIERS.forEach(item => {
if (eventModifiers.includes(item)) {
logger.warn(
`${file.path} template event attribute ${name}`,
`is not support with ${item} modifier in ${appType} env`
);
}
});
if (attrs.hasOwnProperty(newName)) {
logger.warn(`${file.path} template attribute ${name} is conflicted with ${newName}`);
}
}
/**
* parse handlerString to function name and function arguments,replace $event
* 解析函数名及参数
*
* eg:
* <view @click="handleClick($event, '$event')"></view>
* 会被解析为
* <view bindtap="__handlerProxy"
* data-tap-event-proxy="handleClick"
* data-tap-arguments-proxy="{{['u2mirq0709_$event', '$event'}}"
* data-tap-event-object-alias="u2mirq0709_$event"></view>
*
* 其中$event加上随机数前缀的原因是:
* 如果未对$event做转换,即<view data-tap-arguments-proxy="{{[$event, '$event'}}"></view>,那么模板被渲染时,会因为找不到对应的变量$event而报错。
* 因此需要将$event转换为字符串,但是为了避免用户也传递了字符串'$event'导致__handleProxy误解析,所以加上前缀以区分。
* 同时,由于加任何前缀,都有可能碰上用户巧合传递了同样的字符串,避免传参重复。所以用随机数作前缀(取到随机数后还会判断是否在参数列表中,在的话重取)。
*
* @param {string} handlerString the whole string
* @return {Object}
* handlerName: function name,
* handlerArgs: function arguments
* eventObjectAlias: the event object alias
*/
function parseHandlerByREGEXP(handlerString) {
// match handlerName and handle arguments
const matchArray = handlerString.match(EVENT_HANDLE_REGEXP);
const handlerName = matchArray && matchArray[1] || '';
let handlerArgs = matchArray && matchArray[2] || '';
let eventObjectAlias = '';
// when there are arguments in function, use __handlerProxy(okam-core/base/component) to handle arguments
if (handlerArgs && handlerArgs.length > 0) {
// 避免相邻$event不能被正确的替换e.g. "$event,$event"
if (handlerArgs.includes(',$event')) {
handlerArgs = handlerArgs.replace(/,\$event/g, ', $event');
}
const pseudoArgsArray = handlerArgs.replace(/\s*/g, '').split(',');
// transform $event to 'eventObjectAlias', for handleProxy passing on the event Object
if (pseudoArgsArray.includes('$event')) {
eventObjectAlias = `${getRandomStringNotIn(handlerArgs)}_$event`;
handlerArgs = ` ${handlerArgs} `.replace(VARIABLE_EVENT, `$1'${eventObjectAlias}'$2`).trim();
}
}
return {
handlerName,
handlerArgs,
eventObjectAlias
};
}
/**
* get a random string which is not in the string
*
* @param {string} string the parameter string
* @return {string} the random string
*/
function getRandomStringNotIn(string) {
const randomString = Math.random().toString(36).substr(2);
if (string.indexOf(randomString) === -1) {
return randomString;
}
return getRandomStringNotIn(string);
}
// 1. 使用正则将函数名和函数参数拆分开
// 2. 给事件绑定事件代理函数__handlerProxy
// 3. 将原函数名handleClick保存在data-tap-event-proxy中
// 4. 使用正则解析函数参数
// 5. 将函数参数中的event对象($event)转化为字符串标识,并且在代理函数中提取出event对象
module.exports = function (attrs, name, tplOpts, parseEventName) {
// eventType, like 'tap'
// eventAttrName, like 'bindtap'
// eventModifier, like '[once,self]'
let {eventType, eventAttrName, eventModifiers} = parseEventName(name);
let handlerString = attrs[name].trim();
// match handleName and handle arguments
let {
handlerName = handlerString,
handlerArgs,
eventObjectAlias
} = parseHandlerByREGEXP(handlerString);
showEventNameLog(name, eventAttrName, eventModifiers, attrs, tplOpts);
// use __handlerProxy(in okam-core/base/component) to agent the event handler
attrs[eventAttrName] = '__handlerProxy';
eventType = toHyphen(eventType); // covert the camel case to kebab-case
// save the real event handler
attrs[`data-${eventType}-proxy`] = handlerName;
// save all arguments in dataSet
if (handlerArgs && handlerArgs.length > 0) {
attrs[`data-${eventType}-args`] = `{{[${handlerArgs}]}}`;
if (eventObjectAlias) {
attrs[`data-${eventType}-event`] = eventObjectAlias;
}
}
if (eventModifiers.includes('self')) {
// true => data-x-self
// 'true' => data-x-self = "true"
attrs[`data-${eventType}-self`] = 'true';
}
delete attrs[name];
};