magix-composer
Version:
compile html, style and javascript files into javascript
174 lines (172 loc) • 7.43 kB
JavaScript
/*
mx事件处理
1. 检测是否按要求书写的事件
2. 检测单双引号的实体转义
3. 检测不支持的写法
*/
let chalk = require('chalk');
let slog = require('./util-log');
let utils = require('./util');
let jsGeneric = require('./js-generic');
let tmplCmd = require('./tmpl-cmd');
let acorn = require('./js-acorn');
let consts = require('./util-const');
let tmplChecker = require('./checker-tmpl');
let removeTempReg = /[\x02\x01\x03\x06]\.?/g;
let cmdReg = /\x07\d+\x07/g;
let onlyCmdReg = /^(?:\x07\d+\x07)+$/;
let dOutCmdReg = /<%([=@])([\s\S]+?)%>/g;
let unsupportOutCmdReg = /<%@[\s\S]+?%>/g;
let stringReg = /^['"]/;
let magixHolder = '\x1e';
let holder = '\x1f';
let processQuot = (str, refTmplCommands, mxEvent, e, toSrc) => {
str.replace(cmdReg, cm => {
let cmd = refTmplCommands[cm];
if (cmd) {
cmd = cmd.replace(dOutCmdReg, (m, o, c) => {
if (o == '@' && !onlyCmdReg.test(str)) {
let src = toSrc(str);
slog.ever(chalk.magenta('[MXC Tip(checker-tmpl)]'), chalk.red('unsupport ' + src), 'at', chalk.grey(e.shortHTMLFile), 'in', chalk.magenta(mxEvent));
}
if (o == '=') {
return '<%=$eq(' + c + ')%>';
}
return m;
});
refTmplCommands[cm] = cmd;
}
});
};
let htmlQEntityReg = /(\\*)("?|"?|'?|"?|'?)/g;
let encodeParams = (params, refTmplCommands, mxEvent, e, toSrc) => {
let index = 0;
let store = Object.create(null);
let cmdKey = utils.uId('\xaa', params);
let cmdPHReg = new RegExp(cmdKey + '\\d+' + cmdKey, 'g');
//console.log(JSON.stringify(params));
params = '(' + params.replace(cmdReg, m => {
let k = cmdKey + index++ + cmdKey;
store[k] = m;
return k;
}) + ')';
//console.log(JSON.stringify(params));
let ast;
try {
ast = acorn.parse(params);
} catch (ex) {
let origin = params.substring(1, params.length - 1).replace(cmdPHReg, m => store[m]).replace(cmdReg, m => refTmplCommands[m]);
let src = toSrc(origin);
slog.ever(chalk.red('[MXC Error(tmpl-attr-mxevent)] encode mx-event params error'), 'origin', chalk.magenta(src), (src != origin ? 'translate to ' + chalk.magenta(origin) : ''), chalk.red('mx-event params with template syntax must be a legal object literal'), 'e.g.', chalk.magenta('{id:{{=id}},name:\'{{if gender==\'male\'}}David{{else}}Lily{{/if}}\'}'));
throw ex;
}
let modifiers = [];
let processString = node => { //存储字符串,减少分析干扰
if (stringReg.test(node.raw)) {
let q = node.raw.charAt(0);
let raw = node.raw.slice(1, -1);
let replacement = raw.replace(htmlQEntityReg, (m, s, n) => {
return s && s.length % 2 ? m : s + '\\' + n;
});
if (raw != replacement) {
let tip = replacement
.replace(cmdPHReg, m => store[m])
.replace(cmdReg, m => refTmplCommands[m])
.replace(removeTempReg, '');
let tipRaw = raw
.replace(cmdPHReg, m => store[m])
.replace(cmdReg, m => refTmplCommands[m])
.replace(removeTempReg, '');
slog.ever(chalk.magenta(`[MXC Tip(tmpl-attr-mxevent)]`), chalk.red('beware!'), 'You should use', chalk.magenta(tip), 'instead of', chalk.magenta(tipRaw), 'at', chalk.grey(e.shortHTMLFile), 'in', chalk.magenta(mxEvent.replace(removeTempReg, '')));
}
let eq = jsGeneric.escapeQ(replacement, q);
replacement = replacement.replace(cmdPHReg, m => store[m]);
processQuot(replacement, refTmplCommands, mxEvent, e, toSrc);
modifiers.push({
start: node.start,
end: node.end,
content: q + eq + q
});
}
};
acorn.walk(ast, {
Property(node) {
let key = node.key;
if (key.type == 'Literal') {
processString(key);
}
let value = node.value;
if (value.type == 'Identifier') {
let cmd = value.name.replace(cmdPHReg, m => store[m]);
if (onlyCmdReg.test(cmd)) {
cmd = tmplCmd.recover(cmd, refTmplCommands);
let modify = false;
cmd.replace(dOutCmdReg, (m, o) => {
modify = o == '@';
});
if (modify) {
modifiers.push({
start: value.start,
end: value.end,
content: '\'' + value.name + '\''
});
}
} else {
cmd.replace(cmdReg, cm => {
let oCmd = refTmplCommands[cm];
if (oCmd) {
oCmd.replace(unsupportOutCmdReg, m => {
m = m.replace(removeTempReg, '');
slog.ever(chalk.red('[MXC Error(tmpl-attr-mxevent)] unsupport ' + m), 'at', chalk.grey(e.shortHTMLFile), 'in', chalk.magenta(mxEvent.replace(removeTempReg, '')));
});
}
});
}
}
},
Literal: processString
});
modifiers.sort((a, b) => { //根据start大小排序,这样修改后的fn才是正确的
return a.start - b.start;
});
for (let i = modifiers.length - 1, m; i >= 0; i--) {
m = modifiers[i];
params = params.substring(0, m.start) + m.content + params.substring(m.end);
}
params = params.replace(cmdPHReg, m => store[m]);
return params.slice(1, -1);
};
module.exports = (e, match, refTmplCommands, toSrc) => {
match = match.replace(consts.tmplMxEventReg, (m, name, double, single) => { //查找事件
tmplChecker.checkMxEventName(name, e);
if (double || single) {
let originalMatch = toSrc(m);
tmplChecker.checkMxEvengSingQuote(single, originalMatch, e);
let left = m.indexOf('(');
let right = m.lastIndexOf(')');
if (left > -1 && right > -1 && right > left) {
let params = m.substring(left + 1, right).trim();
left = m.substring(0, left + 1);
right = m.substring(right);
if (cmdReg.test(left) || cmdReg.test(right)) {
right = params + right;
} else if (params) {
tmplChecker.checkMxEventParams(name, params, originalMatch, e);
right = encodeParams(params, refTmplCommands, originalMatch, e, toSrc) + right;
}
let start = left.indexOf('=');
let c;
do {
c = left.charAt(start);
start++;
} while (c != '"' && c != '\'');
let rest = left.substring(start) + right;
return left.substring(0, start) + holder + magixHolder + rest;
} else {
return m;
}
}
return m;
});
return match;
};