wcc.js
Version:
Compiler for wxml and wxss files.
616 lines (589 loc) • 18.7 kB
JavaScript
/**
* optimize the ml ast tree
*/
const zParser = require('./zParser.js');
const xsParser = require('./xsParser.js');
const util = require('./util.js');
const error = require('./error.js');
function dfsWalk(root, parentNode, node, wcc) {
let res;
if (node.optimized) {
return;
}
node.optimized = true;
node.children = node.children || [];
if (node.type === 'root') {
//do nothing
} else if (node.type === 'tag') {
res = processTag(root, parentNode, node, wcc);
} else if (node.type === 'text') {
res = processText(root, parentNode, node, wcc);
}
if (res instanceof error.WccError) {
return res;
}
while (true) {
let childIdx = -1;
for (let i = 0; i < node.children.length; ++i) {
if (!node.children[i].optimized) {
childIdx = i;
break;
}
}
if (childIdx >= 0) {
res = dfsWalk(root, node, node.children[childIdx], wcc);
if (res instanceof error.WccError) {
return res;
}
} else {
break;
}
}
if(wcc.cmd.msn){
if (node.type === 'tag') {
let isStatic = true;
let attributes = node.attributes || [];
let children = node.children || [];
for(let i = 0; i < attributes.length; ++i){
isStatic = attributes[i].isStatic;
if(!isStatic){
break;
}
}
if(isStatic){
for(let i = 0; i < children.length; ++i){
isStatic = children[i].isStatic;
if(!isStatic){
break;
}
}
}
node.isStatic = isStatic;
if(node.isStatic){
//非特殊的标签,才把子节点的isStatic属性去掉
if(! (isTag(node, 'include') || isTag(node, 'import') || isTag(node, 'template') || isTag(node, 'block') ) ){
for(let i = 0; i < children.length; ++i){
children[i].isStatic = false;
}
}
// try{
// node.rawHash = hash.sha256(JSON.stringify(node));
// for(let i = 0; i < attributes.length; ++i){
// delete attributes[i].isStatic;
// }
// }catch(err){
// let path = root.path;
// let code = -1;
// let message = `${path}:${node.openTag.start.loc.line}:${node.openTag.start.loc.col}: rawHash calculate fail, ${err.message}\n`;
// return (new error.WccError(code, message));
// }
}
}else if(node.type === 'text'){
// if(node.isStatic){
// try{
// node.rawHash = hash.sha256(JSON.stringify(node));
// }catch(err){
// let path = root.path;
// let code = -1;
// let message = `${path}:${node.openTag.start.loc.line}:${node.openTag.start.loc.col}: rawHash calculate fail, ${err.message}\n`;
// return (new error.WccError(code, message));
// }
// }
}
}
return res;
};
exports.optimize = function (templatesObjs = [], wcc) {
let wccError;
for (let idx = 0; idx < templatesObjs.length; ++idx) {
let templatesObj = templatesObjs[idx];
if (templatesObj.type === 'xml') {
let ast = templatesObj.ast;
ast.info.z = []; // z数组
ast.info.ti = []; //import 数组
ast.info.ic = []; //include 数组
ast.template = []; // template 定义
ast.xsD = []; //xs define
ast.xsR = []; //xs require
if (ast.optimized) {
return;
}
let res = dfsWalk(ast, null, ast, wcc);
if (res instanceof error.WccError) {
wccError = res;
break;
}
} else if (templatesObj.type === 'xs') {
let parseRes = xsParser.parse(templatesObj.template, templatesObj.path || '', 1, wcc);
if (parseRes instanceof error.WccError) {
wccError = parseRes;
break;
}
templatesObj.template = parseRes;
}
}
if (wccError) {
return wccError;
} else {
return templatesObjs;
}
};
function processTag(root, parentNode, node, wcc) {
let res;
uniqueAttributes(node, wcc);
if (isTag(node, 'include')) {
res = processIncludeTag(root, parentNode, node, wcc);
} else if (isTag(node, 'import')) {
res = processImportTag(root, parentNode, node, wcc);
} else if (isTag(node, 'template')) {
res = processTemplateTag(root, parentNode, node, wcc);
} else if (isTag(node, 'block')) {
res = processBlockTag(root, parentNode, node, wcc);
} else if (isTag(node, 'wxs')) {
res = processWxsTag(root, parentNode, node, wcc);
}
if(res instanceof error.WccError){
return res;
}
res = processGenericAttribute(root, parentNode, node, wcc); //抽象节点处理
res = processForAttribute(root, parentNode, node, wcc);
res = processIfAttribute(root, parentNode, node, wcc);
res = processAttributes(root, parentNode, node, wcc);
return res;
}
function isTag(node, tag) {
let tagName = node.openTag.str;
if (tagName === tag) {
return true;
} else {
return false;
}
}
function processGenericAttribute(root, parentNode, node, wcc) {
let genericAttributes = getAndRemoveAttribute(node, {
prefix: 'generic:'
});
node.generics = [];
for (let i = 0; i < genericAttributes.length; ++i) {
genericAttributes[i].name.str = genericAttributes[i].name.str.substring(8);
let res = processAttribute(root, node, genericAttributes[i], wcc);
if (res instanceof error.WccError) {
return res;
}
node.generics.push(genericAttributes[i]);
}
}
function processForAttribute(root, parentNode, node, wcc) {
let forAttribute = getAndRemoveSpecialAttribute(node, 'for');
if (!forAttribute) {
forAttribute = getAndRemoveSpecialAttribute(node, 'for-items');
} else {
getAndRemoveSpecialAttribute(node, 'for-items');
}
if (!forAttribute || !forAttribute.value || !forAttribute.value.str) {
getAndRemoveSpecialAttribute(node, 'for-index');
getAndRemoveSpecialAttribute(node, 'for-item');
getAndRemoveSpecialAttribute(node, 'key');
return;
}
let forIndexAttribute = getAndRemoveSpecialAttribute(node, 'for-index');
let forItemAttribute = getAndRemoveSpecialAttribute(node, 'for-item');
let keyAttribute = getAndRemoveSpecialAttribute(node, 'key');
let res = processAttribute(root, node, forAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.for = forAttribute;
if (forIndexAttribute && forIndexAttribute.value && forIndexAttribute.value.str) {
let res = processAttribute(root, node, forIndexAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.forIndex = forIndexAttribute;
}
if (forItemAttribute && forItemAttribute.value && forItemAttribute.value.str) {
let res = processAttribute(root, node, forItemAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.forItem = forItemAttribute;
}
if (keyAttribute && keyAttribute.value && typeof keyAttribute.value.str === 'string' ) {
let res = processAttribute(root, node, keyAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.key = keyAttribute;
}
}
function processIfAttribute(root, parentNode, node, wcc) {
let ifAttribute = getAndRemoveSpecialAttribute(node, 'if');
if (!ifAttribute) {
ifAttribute = getAndRemoveAttribute(node, {
name: 'wxIf'
}); // wx-if 也支持,无语了
}
if (ifAttribute && ifAttribute.value && ifAttribute.value.str) {
let res = processAttribute(root, node, ifAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.if = ifAttribute;
node.ifSiblings = [];
let startIdx = parentNode.children.indexOf(node);
let endIdx = startIdx;
for (let i = startIdx + 1; i < parentNode.children.length; ++i) {
let curNode = parentNode.children[i];
uniqueAttributes(curNode, wcc); //这里提前处理了兄弟节点,需要预处理下兄弟节点的属性
let elifAttribute = getAndRemoveSpecialAttribute(curNode, 'elif');
let elseAttribute = getAndRemoveSpecialAttribute(curNode, 'else');
if (elseAttribute) {
let res = processAttribute(root, node, elseAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
curNode.else = elseAttribute;
curNode.ifSibling = true;
endIdx = i;
break;
} else if (elifAttribute && elifAttribute.value && elifAttribute.value.str) {
let res = processAttribute(root, node, elifAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
curNode.elif = elifAttribute;
curNode.ifSibling = true;
endIdx = i;
} else {
break;
}
}
startIdx++;
for (let i = startIdx; i <= endIdx; ++i) {
node.ifSiblings.push(parentNode.children[i]);
}
getAndRemoveSpecialAttribute(node, 'elif');
getAndRemoveSpecialAttribute(node, 'else');
}
}
function processIncludeTag(root, parentNode, node, wcc) {
let srcAttribute = getAndRemoveAttribute(node, {
name: 'src'
});
if (srcAttribute && srcAttribute.value.str) {
node.include = srcAttribute;
}else{
removeChild(parentNode, node);
}
}
function processImportTag(root, parentNode, node, wcc) {
let srcAttribute = getAndRemoveAttribute(node, {
name: 'src'
});
if (srcAttribute && srcAttribute.value.str) {
node.import = srcAttribute;
}else{
removeChild(parentNode, node);
}
}
function processTemplateTag(root, parentNode, node, wcc) {
let nameAttribute = getAndRemoveAttribute(node, {
name: 'name'
});
let isAttribute = getAndRemoveAttribute(node, {
name: 'is'
});
let dataAttribute = getAndRemoveAttribute(node, {
name: 'data'
});
if (nameAttribute && nameAttribute.value && nameAttribute.value.str) {
//template定义
let res = processAttribute(root, node, nameAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.name = nameAttribute;
removeChild(parentNode, node);
root.template.push(node);
} else if (isAttribute && isAttribute.value && isAttribute.value.str) {
//template引用
let res = processAttribute(root, node, isAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.is = isAttribute;
if (dataAttribute && dataAttribute.value && dataAttribute.value.str) {
let res = processAttribute(root, node, dataAttribute, wcc);
if (res instanceof error.WccError) {
return res;
}
node.data = dataAttribute;
}
}else{
removeChild(parentNode, node);
}
}
function processBlockTag(root, parentNode, node, wcc) {
node.block = true;
}
function processWxsTag(root, parentNode, node, wcc) {
let srcAttribute = getAndRemoveAttribute(node, {
name: 'src'
});
let moduleAttribute = getAndRemoveAttribute(node, {
name: 'module'
});
if (moduleAttribute && moduleAttribute.value) {
if (srcAttribute && srcAttribute.value && srcAttribute.value.str) {
//wxs外部依赖
let src = srcAttribute.value.str;
let path = root.path;
let handledSrc = util.getNormalizePath(path, src);
if(wcc.existTemplateObj(handledSrc)){
root.xsR.push({
src: srcAttribute,
module: moduleAttribute,
type: 'p_'
});
}else{
let code = -1;
let message = `${path}:${node.openTag.start.loc.line}:${node.openTag.start.loc.col}: ${src} not found from ${path}\n`;
return (new error.WccError(code, message));
}
} else {
//wxs内部依赖
root.xsR.push({
module: moduleAttribute,
type: 'm_'
});
//wxs定义
let children = node.children || [];
if (children.length && children[0] && children[0].type === 'text' && children[0].value.str) {
let parseRes = xsParser.parse(children[0].value.str, root.path || '', children[0].value.start.loc.line, wcc);
if (parseRes instanceof error.WccError) {
return parseRes;
}
children[0].value.str = parseRes;
root.xsD.push({
module: moduleAttribute,
source: children[0]
});
}
}
}else{
let path = root.path;
let code = -1;
let message = `${path}:${node.openTag.start.loc.line}:${node.openTag.start.loc.col}: module expected in wxs tag\n`;
return (new error.WccError(code, message));
}
removeChild(parentNode, node);
node.children = [];
};
function processAttributes(root, parentNode, node, wcc) {
let attribute = getAndRemoveSpecialAttribute(node, 'else');
if(attribute){
let path = root.path;
let code = -1;
let message = `${path}:${attribute.name.start.loc.line}:${attribute.name.start.loc.col}: Bad attr \`wx:else\` with message: \`wx:if not found, then something must be wrong\`.\n`;
return (new error.WccError(code, message));
}
attribute = getAndRemoveSpecialAttribute(node, 'elif');
if(getAndRemoveSpecialAttribute(node, 'elif')){
let path = root.path;
let code = -1;
let message = `${path}:${attribute.name.start.loc.line}:${attribute.name.start.loc.col}: Bad attr \`wx:elif\` with message: \`wx:if not found, then something must be wrong\`.\n`;
}
node.attributes = node.attributes || [];
for (let i = 0; i < node.attributes.length; ++i) {
let res = processAttribute(root, node, node.attributes[i], wcc);
if (res instanceof error.WccError) {
return res;
}
}
}
function camelizeAttributeKey(key) {
if (key.substring(0, 5) === 'data-') {
return key;
} else if (key.substring(0, 3) === 'wx:') {
return key;
} else if (key.substring(0, 8) === 'generic:') {
return key;
} else if (key.substring(0, 13) === 'capture-bind:') {
return key;
} else {
return util.camelize(key);
}
};
/**
* @description make a default attribute value
* @param {*} attribute
* @returns
*/
function makeDefaultAttributeValue(attribute) {
if (attribute.value) {
return;
}
attribute.value = {
str: '',
start: {
c: '',
loc: {
line: -1,
col: -1
}
},
end: {
c: '',
loc: {
line: -1,
col: -1
}
}
}
}
/**
* @description 去掉重复的属性
* @param {*} node
*/
function uniqueAttributes(node, wcc) {
node.attributes = node.attributes || [];
let newAttributes = [];
let map = {};
//相同属性名,取最后一个
for (let i = node.attributes.length - 1; i >= 0; --i) {
let attribute = node.attributes[i];
let attributeName = attribute.name.str;
attribute.name.str = camelizeAttributeKey(attributeName);
let key = attribute.name.str;
if (!map[key]) {
newAttributes.push(attribute);
map[key] = true;
}
}
newAttributes.sort(function (attributeA, attributeB) {
if (attributeA.name.str < attributeB.name.str) {
return -1;
} else if (attributeA.name.str > attributeB.name.str) {
return 1;
} else {
return 0;
}
});
node.attributes = newAttributes;
}
/**
* @description 解析数据绑定
* @param {*} root ats根节点
* @param {*} node 节点,为attribute节点或者text节点
* @param {*} type 节点类型,'attribute'或者'text'
* @param {*} txt 需要解析数据绑定的字符串
* @param {*} sholdWrapAsObject 是否需要用对象包裹数据绑定字符串,用在自定义模板的数据
* @param {*} loc 位置信息
* @param {*} isStatic 是否强制解析为静态的
* @param {*} wcc wcc实例
* @returns 解析结果或者错误对象
*/
function zParseTxt(root, node, type, txt, sholdWrapAsObject, loc, isStatic, wcc) {
let path = root.path;
let zParseRes = zParser.parse(txt, {
sholdWrapAsObject,
loc,
path,
isStatic,
node,
type,
wcc
});
if (zParseRes instanceof error.WccError) {
return zParseRes;
}
let zIdx = root.info.z.indexOf(zParseRes);
if (zIdx === -1) {
root.info.z.push(zParseRes);
zIdx = root.info.z.length - 1;
}
return zIdx;
}
function processAttribute(root, ownerNode, attributeNode, wcc) {
// !attributeNode.vale && makeDefaultAttributeValue(attributeNode);
let attributeName = attributeNode.name.str;
if (!attributeNode.value) {
return;
}
let attributeValue = attributeNode.value.str;
let sholdWrapAsObject = false;
let isStatic = false;
if (isTag(ownerNode, 'template') && attributeName === 'data') {
sholdWrapAsObject = true;
} else if (attributeName.indexOf('generic:') === 0) {
//节点的 generic 引用 generic:xxx="yyy" 中,值 yyy 只能是静态值,不能包含数据绑定。
isStatic = true;
}
let zIdx = zParseTxt(root, attributeNode, 'attribute', attributeValue, sholdWrapAsObject, attributeNode.value.start.loc, isStatic, wcc);
if (zIdx instanceof error.WccError) {
return zIdx;
}
attributeNode.value.zIdx = zIdx;
}
function processText(root, parentNode, textNode, wcc) {
let textValue = textNode.value && textNode.value.str || '';
if (!textValue) {
return;
}
let zIdx = zParseTxt(root, textNode, 'text', textValue, false, textNode.value.start.loc, false, wcc);
if (zIdx instanceof error.WccError) {
return zIdx;
}
textNode.value.zIdx = zIdx;
}
function getAndRemoveAttribute(node, options = {}) {
node.attributes = node.attributes || [];
if (options.name) {
let removeIdx = -1;
for (let i = 0; i < node.attributes.length; ++i) {
let attribute = node.attributes[i];
if (options.name && attribute.name.str === options.name) {
removeIdx = i;
break;
}
}
let removedAttribute = null;
if (removeIdx >= 0) {
removedAttribute = node.attributes[removeIdx];
node.attributes.splice(removeIdx, 1);
}
return removedAttribute;
} else if (options.prefix) {
let newAttributes = [];
let removedAttributes = [];
for (let i = 0; i < node.attributes.length; ++i) {
let attribute = node.attributes[i];
if (options.prefix && attribute.name.str.substring(0, options.prefix.length) === options.prefix) {
removedAttributes.push(attribute);
} else {
newAttributes.push(attribute);
}
}
node.attributes = newAttributes;
return removedAttributes;
}
}
function getAndRemoveSpecialAttribute(node, name) {
let wxName = 'wx:' + name;
let wxAttribute = getAndRemoveAttribute(node, {
name: wxName
});
if (wxAttribute) {
return wxAttribute;
} else {
return null;
}
}
function removeChild(parent, child) {
let children = parent.children;
let removeIdx = children.indexOf(child);
if (removeIdx > -1) {
children.splice(removeIdx, 1);
}
return removeIdx;
}