@zakijs/plugin-compiler-alipay
Version:
mor complier plugin for alipay mini program
300 lines • 11.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.templateProcessorToAlipay = void 0;
const constants_1 = require("./constants");
const templateTags_1 = require("./templateTags");
// 标签属性映射
const TAG_SPECIFIC_PROP_NAME_MAPPINGS = {
slider: {
backgroundColor: 'background-color',
activeColor: 'active-color',
'block-size': 'handle-size',
'block-color': 'handle-color'
}
};
// 通过 支付宝小程序的 ref 来实现
// selectComponent/selectAllComponents/selectOwnComponent 方法
const MOR_REF_METHOD_NAME = '$morSaveRef';
// 双向绑定dataset
const TWO_WAY_BINDING_DATASET = {
morTwoWayBindingMethod: 'data-mortwbmethod',
morTwoWayBindingEventKey: 'data-mortwbkey',
morTwoWayBindingValue: 'data-mortwbvalue'
};
/**
* 自定义 template 处理
*/
exports.templateProcessorToAlipay = {
onNode(node) {
processComponentCompatible(node);
},
onNodeExit(node, options, context) {
if (!node)
return;
if (node.attrs) {
const usingComponentNames = context.usingComponentNames || [];
// 检查当前节点是否为自定义组件标签, 如果是则需要通过 ref 注入组件实例到页面或组件
if (usingComponentNames.includes(node.tag)) {
node.attrs.ref = MOR_REF_METHOD_NAME;
// 支付宝小程序自定义组件设置 id 无效
// 在这里单独保存下
if (node.attrs.id)
node.attrs.morTagId = node.attrs.id;
// 保存 tag 标签名称
node.attrs.morTagName = node.tag;
}
// 检查 block 的 hidden 属性
// 并替换为 a:if
// 原因为: 支付宝不支持, 小程序IDE编译过程中会输出警告
if (node.tag === 'block' && node.attrs['hidden']) {
const blockIf = {
...node,
...{
attrs: {
'a:if': node.attrs['hidden'] === true
? '{{true}}'
: node.attrs['hidden']
},
content: []
}
};
const blockElse = { ...node, ...{ attrs: {} } };
blockElse.attrs['a:else'] = true;
node.content = [blockIf, blockElse];
delete node.attrs['hidden'];
}
// 检查 同一个 node 中是否同时存在 a:if 和 a:key
// 如果同时存在 则 将 a:key 替换为 key
if (node.attrs['a:if'] && node.attrs['a:key']) {
if (!node.attrs['key']) {
node.attrs['key'] = node.attrs['a:key'];
delete node.attrs['a:key'];
}
}
}
// 支付宝不支持 大写的标签名, 需要全部转换为小写
if (node.tag)
node.tag = node.tag.toLowerCase();
// 处理双向绑定支持
processTwoWayBinding(node, context);
},
onNodeAttr(attrName, node) {
if (!node.tag)
return;
// polyfills
// 针对 native tags
if (node.attrs[attrName] === '' && (0, templateTags_1.isNativeTag)(node.tag)) {
;
node.attrs[attrName] = true;
}
// 修复所有 a:else
if (attrName === 'a:else' && node.attrs[attrName] === '') {
;
node.attrs[attrName] = true;
}
// 事件处理
processEventsAttributes(attrName, node);
// 处理标签属性替换
if (node.tag &&
node.tag in TAG_SPECIFIC_PROP_NAME_MAPPINGS &&
TAG_SPECIFIC_PROP_NAME_MAPPINGS[node.tag]) {
// 属性映射及替换
const replaceAttrName = TAG_SPECIFIC_PROP_NAME_MAPPINGS[node.tag][attrName];
if (replaceAttrName && replaceAttrName !== attrName) {
node.attrs[replaceAttrName] = node.attrs[attrName];
delete node.attrs[attrName];
}
}
},
onNodeAttrExit(attrName, node, options, context) {
// 支付宝不支持将 sjs 模块的名称命名为 this
// 这里需要将其转换为 thisSjs
if (node.tag === constants_1.sjsTagName &&
attrName === constants_1.sjsModuleAttrName &&
node.attrs[attrName] === 'this') {
node.attrs[attrName] = 'thisSjs';
// 标记当前页面模版中存在 sjs 模块名称为 this
context.hasSjsModuleAttrNameAsThis = true;
}
// 如果当前页面有 this 作为 sjs 模块名称
// 则
if (context.hasSjsModuleAttrNameAsThis &&
node.tag !== constants_1.sjsTagName &&
typeof node.attrs[attrName] === 'string' &&
node.attrs[attrName].includes('this.')) {
node.attrs[attrName] = node.attrs[attrName].replace(/this\./g, 'thisSjs.');
}
}
};
/**
* 原生小程序组件事件在当前小程序环境中对应的事件
*/
const TAG_SPECIFIC_EVENT_MAPPINGS = {
'scroll-view': {
scrolltoupper: 'scrollToUpper',
scrolltolower: 'scrollToLower',
dragstart: 'touchStart',
dragging: 'touchMove',
dragend: 'touchEnd'
},
checkbox: {
tap: 'change'
},
map: {
regionchange: 'regionChange',
markertap: 'markerTap',
callouttap: 'calloutTap',
controltap: 'controlTap'
},
swiper: {
animationfinish: 'animationEnd'
}
};
// 事件映射
const COMMON_EVENT_MAPPINGS = {
touchstart: 'touchStart',
touchmove: 'touchMove',
touchend: 'touchEnd',
touchcancel: 'touchCancel',
tap: 'tap',
longtap: 'longTap',
longpress: 'longTap',
load: 'load',
change: 'change',
transition: 'transition',
transitionend: 'transitionEnd',
animationstart: 'animationStart',
animationiteration: 'animationIteration',
animationend: 'animationEnd'
};
// 支付宝中 组件 attr 匹配命中前缀,需要更换写法 onXxxx catchXxxx
const EVENT_BIND_REGEXP = /^bind:|bind/;
const EVENT_CATCH_REGEXP = /^catch:|catch/;
/**
* 处理事件转换
* bind:abc => onAbc
* catch:abc => catchAbc
*/
function processEventsAttributes(attrName, node) {
// 如果 tag 不存在, 则跳过
if (!node.tag)
return;
let eventPrefix;
let eventAttrName;
if (EVENT_BIND_REGEXP.test(attrName)) {
eventAttrName = attrName.replace(EVENT_BIND_REGEXP, '');
eventPrefix = 'on';
}
else if (EVENT_CATCH_REGEXP.test(attrName)) {
eventAttrName = attrName.replace(EVENT_CATCH_REGEXP, '');
eventPrefix = 'catch';
}
// 其他情况不处理
else {
return;
}
// 替换 native event 映射
if (COMMON_EVENT_MAPPINGS[eventAttrName]) {
eventAttrName = COMMON_EVENT_MAPPINGS[eventAttrName];
}
// 标签专属事件替换
else if (node.tag in TAG_SPECIFIC_EVENT_MAPPINGS &&
TAG_SPECIFIC_EVENT_MAPPINGS[node.tag] &&
eventAttrName in TAG_SPECIFIC_EVENT_MAPPINGS[node.tag]) {
eventAttrName = TAG_SPECIFIC_EVENT_MAPPINGS[node.tag][eventAttrName];
}
// 否则该事件判定为用户自定义事件
// 支付宝事件传递函数只支持 on 开头
// 这里强制 eventPrefix 为 on
else {
eventPrefix = 'on';
}
if (eventPrefix && eventAttrName) {
const eventName = eventPrefix +
eventAttrName
.replace(/^[a-zA-Z]{1}/, (s) => s.toUpperCase())
.replace(/-./g, (s) => s[1].toUpperCase());
node.attrs[eventName] = node.attrs[attrName];
delete node.attrs[attrName];
}
}
/**
* 处理组件兼容性
*/
function processComponentCompatible(node) {
// button 标签兼容
if (node.tag === 'button' && node.attrs) {
// 手机号码授权
if (node.attrs['open-type'] === 'getPhoneNumber') {
node.attrs['open-type'] = 'getAuthorize';
node.attrs.scope = 'phoneNumber';
node.attrs['onGetAuthorize'] = node.attrs['bind:getphonenumber'];
delete node.attrs['bind:getphonenumber'];
}
// 用户信息授权
if (node.attrs['open-type'] === 'getUserInfo') {
node.attrs['open-type'] = 'getAuthorize';
node.attrs.scope = 'userInfo';
node.attrs['onGetAuthorize'] = node.attrs['bind:getuserinfo'];
delete node.attrs['bind:getuserinfo'];
}
}
}
/**
* 处理微信转支付宝的双向绑定支持
*/
function processTwoWayBinding(node, context) {
const attrs = node.attrs;
if (!attrs)
return;
Object.keys(attrs).forEach(function (attrName) {
var _a;
// 双向绑定语法符合model:xx,如model:value={{bindingValue}}
const leftKeyMatchedResult = attrName === null || attrName === void 0 ? void 0 : attrName.match(/model:(.*)/);
if (!(leftKeyMatchedResult === null || leftKeyMatchedResult === void 0 ? void 0 : leftKeyMatchedResult[1]))
return;
// model:value -> value
const twoWayBindingLeftKey = leftKeyMatchedResult[1];
const attrValue = attrs[attrName];
attrs[twoWayBindingLeftKey] = attrValue;
delete attrs[attrName];
// {{bindingValue}} -> bindingValue
const rightKeyMatchedResult = typeof attrValue === 'string' ? attrValue.match(/{{(.+?)}}/) : [];
const twoWayBindingRightKey = (_a = rightKeyMatchedResult[1]) === null || _a === void 0 ? void 0 : _a.trim();
// 自定义组件
const usingComponentNames = context.usingComponentNames || [];
if (usingComponentNames.includes(node.tag)) {
attrs.onMorChildTWBProxy = '$morParentTWBProxy';
// custom-property -> customProperty
const processedLeftKey = twoWayBindingLeftKey.replace(/-./g, (s) => s[1].toUpperCase());
// 同一个 tag,多个双向绑定时,存储键值对,供运行时消费
// 格式为 a:b,c:d
const bindMap = `${processedLeftKey}:${twoWayBindingRightKey}`;
if (attrs.morChildTwbMap) {
if (!attrs.morChildTwbMap.includes(bindMap)) {
attrs.morChildTwbMap = `${attrs.morChildTwbMap},${bindMap}`;
}
}
else {
attrs.morChildTwbMap = bindMap;
}
}
else {
// 已支持双向绑定的tag组件
const tagComponent = constants_1.twoWayBindingComponents[node.tag];
if (!tagComponent) {
return;
}
// 双向绑定信息,存在 dataset 上,命名使用小写,兼容web端的dataset小写
attrs[TWO_WAY_BINDING_DATASET.morTwoWayBindingMethod] =
attrs[tagComponent.bindEventName];
attrs[TWO_WAY_BINDING_DATASET.morTwoWayBindingEventKey] =
tagComponent.bindEventKey;
attrs[TWO_WAY_BINDING_DATASET.morTwoWayBindingValue] =
twoWayBindingRightKey;
// 自定义事件,劫持tag组件事件
attrs[tagComponent.bindEventName] = '$morTWBProxy';
}
});
}
//# sourceMappingURL=templateProcessorToAlipay.js.map