Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@hap-toolkit/compiler

Package Overview
Dependencies
Maintainers
4
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hap-toolkit/compiler - npm Package Compare versions

Comparing version 1.9.3-beta.1 to 1.9.3-beta.2

198

lib/index.js

@@ -1,2 +0,198 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseFragmentsWithCache=parseFragmentsWithCache,exports.parseTemplate=parseTemplate,exports.parseStyle=parseStyle,exports.parseScript=parseScript;var _parse=_interopRequireDefault(require("parse5")),_template=_interopRequireDefault(require("./template")),_style=_interopRequireDefault(require("./style")),_script=_interopRequireDefault(require("./script")),_utils=require("./utils");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _formatFragment(e,t){let r,a,s,n;const o={};if(t.__location){const e=t.__location;e.startTag&&e.endTag?(r=e.startTag.endOffset||0,a=e.endTag.startOffset||0):(r=e.startOffset||0,a=e.endOffset||0),s=e.line,n=e.col}else r=a=s=n=0;return t.attrs&&t.attrs.length&&t.attrs.forEach((function(e){o[e.name]=e.value})),{type:t.nodeName,attrs:o,content:e.substring(r,a),location:{start:r,end:a,line:s,column:n}}}function parseFragments(e,t){const r={import:[],template:[],style:[],script:[]};return _parse.default.parseFragment(e,{treeAdapter:_parse.default.treeAdapters.default,locationInfo:!0}).childNodes.forEach(a=>{const s=_formatFragment(e,a);if(r[a.nodeName]&&r[a.nodeName].push(s),"import"===a.nodeName&&a.__location&&!a.__location.endTag)throw new Error(`${t} : <import> 组件缺少闭合标签,请检查 @${s.location.line} : ${s.location.column}`);if("plaintext"===a.nodeName.toLowerCase())throw new Error(`${t} : 禁止使用plaintext @${s.location.line} : ${s.location.column}`)}),r}const fragsCache=new Map;function parseFragmentsWithCache(e,t){return fragsCache.has(t)&&fragsCache.get(t).source===e||fragsCache.set(t,{source:e,frags:parseFragments(e,t)}),fragsCache.get(t).frags}function parseTemplate(e,t){const r=_template.default.parse(e,t),{jsonTemplate:a,log:s,depFiles:n}=r;return{parsed:(0,_utils.serialize)(a,2),log:s,depFiles:n}}function parseStyle(e){const t=_style.default.parse(e),{jsonStyle:r,depList:a,log:s,depFiles:n}=t;return{parsed:JSON.stringify(r,null,2),depList:a,log:s,depFiles:n,jsonStyle:r}}function parseScript(e){return{parsed:_script.default.parse(e)}}
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parseFragmentsWithCache = parseFragmentsWithCache;
exports.parseTemplate = parseTemplate;
exports.parseStyle = parseStyle;
exports.parseScript = parseScript;
var _parse = _interopRequireDefault(require("parse5"));
var _template = _interopRequireDefault(require("./template"));
var _style = _interopRequireDefault(require("./style"));
var _script = _interopRequireDefault(require("./script"));
var _utils = require("./utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 格式化节点
* @param source
* @param node
*/
function _formatFragment(source, node) {
let start, end, line, column;
const attrs = {}; // 获取当前节点在文档中的位置信息
if (node.__location) {
const __location = node.__location;
if (__location.startTag && __location.endTag) {
start = __location.startTag.endOffset || 0;
end = __location.endTag.startOffset || 0;
} else {
start = __location.startOffset || 0;
end = __location.endOffset || 0;
}
line = __location.line;
column = __location.col;
/* istanbul ignore else */
} else {
start = end = line = column = 0;
}
if (node.attrs && node.attrs.length) {
node.attrs.forEach(function (item) {
attrs[item.name] = item.value;
});
}
return {
type: node.nodeName,
attrs: attrs,
// 节点属性值
content: source.substring(start, end),
// 节点的文本内容
location: {
start: start,
end: end,
line: line,
column: column
}
};
}
/**
* 解析片段
* @param {string} source - 源码
* @param {string} filePath - 文件绝对路径
*/
function parseFragments(source, filePath) {
const frags = {
import: [],
// 导入组件
template: [],
// 模板
style: [],
// 样式
script: [] // 脚本
};
const fragment = _parse.default.parseFragment(source, {
treeAdapter: _parse.default.treeAdapters.default,
locationInfo: true
}); // 存储片段解析结果
fragment.childNodes.forEach(node => {
const fragmentInfo = _formatFragment(source, node);
frags[node.nodeName] && frags[node.nodeName].push(fragmentInfo); // 判断<import>组件标签是否闭合
if (node.nodeName === 'import' && node.__location && !node.__location.endTag) {
throw new Error(`${filePath} : <import> 组件缺少闭合标签,请检查 @${fragmentInfo.location.line} : ${fragmentInfo.location.column}`);
}
if (node.nodeName.toLowerCase() === 'plaintext') {
throw new Error(`${filePath} : 禁止使用${`plaintext`} @${fragmentInfo.location.line} : ${fragmentInfo.location.column}`);
}
});
return frags;
} // 片段缓存
const fragsCache = new Map();
/**
* 解析片段,优先从缓存中获取
* @param {string} source - 源码
* @param {string} filePath - 文件绝对路径
* @returns {Object}
*/
function parseFragmentsWithCache(source, filePath) {
if (!fragsCache.has(filePath) || fragsCache.get(filePath).source !== source) {
// 解析并缓存片段
fragsCache.set(filePath, {
source: source,
frags: parseFragments(source, filePath)
});
}
return fragsCache.get(filePath).frags;
}
/**
* 解析模板
* @param {String} source - 源码
* @param {Object} options
* @param {String} options.uxType - 文件类型
* @param {String} options.filePath - 当前执行文件的绝对路径
* @returns {Object}
*/
function parseTemplate(source, options) {
const templateObj = _template.default.parse(source, options);
const {
jsonTemplate,
log,
depFiles
} = templateObj;
const parsed = (0, _utils.serialize)(jsonTemplate, 2);
return {
parsed,
log,
depFiles
};
}
/**
* 解析CSS
* @param {String} source
* @returns {Object}
*/
function parseStyle(source) {
const styleObj = _style.default.parse(source);
const {
jsonStyle,
depList,
log,
depFiles
} = styleObj;
const parsed = JSON.stringify(jsonStyle, null, 2);
return {
parsed,
depList,
log,
depFiles,
jsonStyle
};
}
/**
* 解析脚本
* @param {String} source - 源码
* @returns {Object}
*/
function parseScript(source) {
const parsed = _script.default.parse(source);
return {
parsed: parsed
};
}
//# sourceMappingURL=index.js.map

@@ -1,2 +0,50 @@

"use strict";function parse(e){return e}function replaceModuleImport(e){let r=e.replace(/require\s*\(\s*(['"])@([\w$_][\w$-.]*?)\1\)/gm,(e,r,p)=>`$app_require$(${r}@app-module/${p}${r})`);return r=r.replace(/import\s+([\w${}]+?)\s+from\s+(['"])@([\w$_][\w$-.]*?)\2/gm,(e,r,p,t)=>`var ${r} = $app_require$(${p}@app-module/${t}${p})`),r}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _default={parse:parse,replaceModuleImport:replaceModuleImport};exports.default=_default;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
/**
* 解析源码
* @param source
*/
function parse(source) {
return source;
}
/**
* 替换脚本中对模块的引用
*
* NOTE: 正则表达式 ([\w$_][\w$-.]) 部分对模块的名称给予了大于快应用实际的范围
* @param {String} source - 代码
* @return {String}
*/
function replaceModuleImport(source) {
// require('@mod') => $app_require$('@app-module/mod')
let result = source.replace(/require\s*\(\s*(['"])@([\w$_][\w$-.]*?)\1\)/gm, (_, quote, mod) => {
return `$app_require$(${quote}@app-module/${mod}${quote})`;
});
/*
* import mod from '@mod' => var mod = require('@app-module/mod')
* import {prop} from '@mod' ?? => var {prop} = require('@app-module/mod')
* TODO source 已被转换成 require,下面的代码其实不再需要
*/
result = result.replace(/import\s+([\w${}]+?)\s+from\s+(['"])@([\w$_][\w$-.]*?)\2/gm, (_, ref, quote, mod) => {
return `var ${ref} = $app_require$(${quote}@app-module/${mod}${quote})`;
});
return result;
}
var _default = {
parse,
replaceModuleImport
};
exports.default = _default;
//# sourceMappingURL=index.js.map

@@ -1,2 +0,173 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.compressDescSelector=compressDescSelector,exports.compressCssAttr=compressCssAttr;const cssDescSelectorList=[{newN:"t",oldN:"type",newV:["d","a","t","u","p","pe"],oldV:["descendant","attribute","tag","universal","pseudo","pseudo-element"]},{newN:"n",oldN:"name"},{newN:"i",oldN:"ignoreCase"},{newN:"a",oldN:"action"},{newN:"v",oldN:"value"}],cssAttrMap={width:"w",height:"h",paddingLeft:"pL",paddingRight:"pR",paddingTop:"pT",paddingBottom:"pB",marginLeft:"mL",marginRight:"mR",marginTop:"mT",marginBottom:"mB",borderLeftWidth:"bLW",borderTopWidth:"bTW",borderRightWidth:"bRW",borderBottomWidth:"bBW",borderLeftColor:"bLC",borderTopColor:"bTC",borderRightColor:"bRC",borderBottomColor:"bBC",borderStyle:"bS",borderRadius:"bR",borderBottomLeftRadius:"bBLR",borderBottomRightRadius:"bBRR",borderTopLeftRadius:"bTLR",borderTopRightRadius:"bTRR",indicatorSize:"iS",flex:"f",flexGrow:"fG",flexShrink:"fS",flexBasis:"fB",flexDirection:"fD",flexWrap:"fW",justifyContent:"jC",alignItems:"aI",alignContent:"aC",alignSelf:"aS",position:"p",top:"t",bottom:"b",left:"l",right:"r",zIndex:"zI",opacity:"o",background:"bg",backgroundColor:"bgC",backgroundImage:"bgI",backgroundSize:"bgS",backgroundRepeat:"bgR",backgroundPosition:"bgP",display:"d",visibility:"v",lines:"ls",color:"c",fontSize:"foS",fontStyle:"fSt",fontWeight:"foW",textDecoration:"tD",textAlign:"tA",lineHeight:"lH",textOverflow:"tO",transform:"ts",transformOrigin:"tsO",animationName:"aN",animationDuration:"aD",animationTimingFunction:"aTF",animationDelay:"aDe",animationIterationCount:"aIC",animationFillMode:"aFM",placeholderColor:"pC",selectedColor:"sC",textColor:"tC",timeColor:"tiC",textHighlightColor:"tHC",strokeWidth:"sW",progressColor:"prC",indicatorColor:"iC",indicatorSelectedColor:"iSC",slideWidth:"slW",slideMargin:"sM",resizeMode:"rM",columns:"col",columnSpan:"cS",maskColor:"mC",starBackground:"sB",starForeground:"sF",starSecondary:"sS"};function compressDescSelector(o){o=o[0]||[];for(let t=0;t<o.length;t++){const e=o[t]||{};cssDescSelectorList.forEach((function(o){e.hasOwnProperty(o.oldN)&&(e[o.newN]=e[o.oldN],delete e[o.oldN],o.oldV&&o.oldV.indexOf(e[o.newN])>-1&&(e[o.newN]=o.newV[o.oldV.indexOf(e[o.newN])]))}))}return o}function compressCssAttr(o){for(const t in o){const e=o[t];if("@KEYFRAMES"!==t&&"object"==typeof e)for(const o in e)if(cssAttrMap[o]){e[cssAttrMap[o]]=e[o],delete e[o]}}}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.compressDescSelector = compressDescSelector;
exports.compressCssAttr = compressCssAttr;
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
// CSS后代选择器全写-缩写对照表
const cssDescSelectorList = [{
newN: 't',
oldN: 'type',
newV: ['d', 'a', 't', 'u', 'p', 'pe'],
oldV: ['descendant', 'attribute', 'tag', 'universal', 'pseudo', 'pseudo-element']
}, {
newN: 'n',
oldN: 'name'
}, {
newN: 'i',
oldN: 'ignoreCase'
}, {
newN: 'a',
oldN: 'action'
}, {
newN: 'v',
oldN: 'value'
}]; // CSS属性名全写-缩写对照表
const cssAttrMap = {
// boxModel
width: 'w',
height: 'h',
paddingLeft: 'pL',
paddingRight: 'pR',
paddingTop: 'pT',
paddingBottom: 'pB',
marginLeft: 'mL',
marginRight: 'mR',
marginTop: 'mT',
marginBottom: 'mB',
borderLeftWidth: 'bLW',
borderTopWidth: 'bTW',
borderRightWidth: 'bRW',
borderBottomWidth: 'bBW',
borderLeftColor: 'bLC',
borderTopColor: 'bTC',
borderRightColor: 'bRC',
borderBottomColor: 'bBC',
borderStyle: 'bS',
borderRadius: 'bR',
borderBottomLeftRadius: 'bBLR',
borderBottomRightRadius: 'bBRR',
borderTopLeftRadius: 'bTLR',
borderTopRightRadius: 'bTRR',
indicatorSize: 'iS',
// flexbox
flex: 'f',
flexGrow: 'fG',
flexShrink: 'fS',
flexBasis: 'fB',
flexDirection: 'fD',
flexWrap: 'fW',
justifyContent: 'jC',
alignItems: 'aI',
alignContent: 'aC',
alignSelf: 'aS',
// position
position: 'p',
top: 't',
bottom: 'b',
left: 'l',
right: 'r',
zIndex: 'zI',
// common
opacity: 'o',
background: 'bg',
backgroundColor: 'bgC',
backgroundImage: 'bgI',
backgroundSize: 'bgS',
backgroundRepeat: 'bgR',
backgroundPosition: 'bgP',
display: 'd',
visibility: 'v',
// text
lines: 'ls',
color: 'c',
fontSize: 'foS',
fontStyle: 'fSt',
fontWeight: 'foW',
textDecoration: 'tD',
textAlign: 'tA',
lineHeight: 'lH',
textOverflow: 'tO',
// animation
transform: 'ts',
transformOrigin: 'tsO',
animationName: 'aN',
animationDuration: 'aD',
animationTimingFunction: 'aTF',
animationDelay: 'aDe',
animationIterationCount: 'aIC',
animationFillMode: 'aFM',
// custom
placeholderColor: 'pC',
selectedColor: 'sC',
textColor: 'tC',
timeColor: 'tiC',
textHighlightColor: 'tHC',
strokeWidth: 'sW',
progressColor: 'prC',
indicatorColor: 'iC',
indicatorSelectedColor: 'iSC',
slideWidth: 'slW',
slideMargin: 'sM',
resizeMode: 'rM',
columns: 'col',
columnSpan: 'cS',
maskColor: 'mC',
// custom style
starBackground: 'sB',
starForeground: 'sF',
starSecondary: 'sS'
};
/**
* 压缩后代选择器
* @param itemList
* @returns {*|Array}
*/
function compressDescSelector(itemList) {
itemList = itemList[0] || [];
for (let i = 0; i < itemList.length; i++) {
const item = itemList[i] || {};
cssDescSelectorList.forEach(function (ccl) {
if (item.hasOwnProperty(ccl.oldN)) {
item[ccl.newN] = item[ccl.oldN];
delete item[ccl.oldN];
if (ccl.oldV && ccl.oldV.indexOf(item[ccl.newN]) > -1) {
item[ccl.newN] = ccl.newV[ccl.oldV.indexOf(item[ccl.newN])];
}
}
});
}
return itemList;
}
/**
* 压缩CSS属性名
* @param jsonStyle
*/
function compressCssAttr(jsonStyle) {
for (const selector in jsonStyle) {
const cssAttrObj = jsonStyle[selector];
if (selector !== '@KEYFRAMES' && typeof cssAttrObj === 'object') {
for (const cssAttrName in cssAttrObj) {
if (cssAttrMap[cssAttrName]) {
const cssAttrAbbrName = cssAttrMap[cssAttrName];
cssAttrObj[cssAttrAbbrName] = cssAttrObj[cssAttrName];
delete cssAttrObj[cssAttrName];
}
}
}
}
}
//# sourceMappingURL=compress.js.map

@@ -1,2 +0,236 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _path=_interopRequireDefault(require("path")),_css=_interopRequireDefault(require("css")),_compilationConfig=require("@hap-toolkit/shared-utils/compilation-config"),_validator=require("./validator"),_compress=require("./compress"),_process=require("./process"),_utils=require("../utils");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function parse(e){let t;const s={},o=[];let r=[],a=e.code||"";const l=e.filePath,i=_path.default.dirname(l),n=[];a=(0,_process.processImport)(a,i,o,n);const c=_css.default.parse(a,{silent:!0});return c.stylesheet.parsingErrors&&c.stylesheet.parsingErrors.length&&(t=c.stylesheet.parsingErrors,t.forEach((function(e){o.push({line:e.line,column:e.column,reason:e.toString()})}))),c&&"stylesheet"===c.type&&c.stylesheet&&c.stylesheet.rules&&c.stylesheet.rules.length&&c.stylesheet.rules.forEach((function(e){const t=e.type,a={};if("rule"===t&&e.declarations&&e.declarations.length&&(0,_process.processSingleClass)(e,s,a,o,l,r),"media"===t)s["@MEDIA"]||(s["@MEDIA"]=[]),(0,_process.processMediaQueryCss)(e,s["@MEDIA"],o,l,"",r);else if("font-face"===t){if(e.declarations&&e.declarations.length){const t={};e.declarations.forEach((function(e){if("declaration"!==e.type)return;const s=(0,_utils.hyphenedToCamelCase)(e.property),a=e.value;if("fontFamily"===s)t.fontFamily=a.replace(/['"]+/g,"");else if("src"===s){const s=(0,_validator.validate)("fontSrc",a,{filePath:l});s.log&&o.push({line:e.position.start.line,column:e.position.start.column,reason:s.log.reason}),t.src=s.value;const i=s.value||[];r=r.concat(i)}})),s["@FONT-FACE"]||(s["@FONT-FACE"]={}),t.fontName=t.fontFamily,t.fontSrc=t.src,s["@FONT-FACE"][t.fontFamily]=t}}else if("keyframes"===t&&e.keyframes&&e.keyframes.length){const t=e.name,a=[];e.keyframes.forEach((function(s){let i;if("keyframe"===s.type&&s.declarations&&s.declarations.length)if(i={},s.declarations.forEach((function(e){if("declaration"!==e.type)return;const t=e.property,s=e.value,a=(0,_utils.hyphenedToCamelCase)(t),n=(0,_validator.validate)(a,s,{filePath:l});n.value.forEach(e=>{(0,_utils.isValidValue)(e.v)&&(i[e.n]=e.v,(0,_process.shouldAddToDependency)(e.n,e.v)&&r.push(e.v))}),n.log&&o.push({line:e.position.start.line,column:e.position.start.column,reason:n.log.reason})})),(0,_utils.isEmptyObject)(i))o.push({line:e.position.start.line,column:e.position.start.column,reason:"ERROR: 动画 `"+t+"` 的关键帧 `"+JSON.stringify(s.values)+"` 没有有效的属性"});else{let e;s.values.forEach(t=>{e="from"===t?0:"to"===t?100:parseFloat(t.replace("%","")),i.time=e,a.push(i)})}})),a.sort((function(e,t){return e.time-t.time})),s["@KEYFRAMES"]||(s["@KEYFRAMES"]={}),s["@KEYFRAMES"][t]=a}})),_compilationConfig.compileOptionsObject.optimizeCssAttr&&(0,_compress.compressCssAttr)(s),{jsonStyle:s,depList:n,log:o,depFiles:r}}var _default={parse:parse,validateDelaration:_validator.validate,mightReferlocalResource:_validator.mightReferlocalResource,shouldAddToDependency:_process.shouldAddToDependency};exports.default=_default;
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _path = _interopRequireDefault(require("path"));
var _css = _interopRequireDefault(require("css"));
var _compilationConfig = require("@hap-toolkit/shared-utils/compilation-config");
var _validator = require("./validator");
var _compress = require("./compress");
var _process = require("./process");
var _utils = require("../utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 解析<style>为JSON对象
* @param {String} source - 源码
* @returns {Object}
*/
function parse(source) {
let err;
const jsonStyle = {};
const log = [];
let depFiles = [];
let code = source.code || '';
const filePath = source.filePath;
const curDir = _path.default.dirname(filePath); // 引入的CSS文件列表
const depList = []; // 合并css
code = (0, _process.processImport)(code, curDir, log, depList); // css解析
const ast = _css.default.parse(code, {
silent: true
}); // 异常处理,打印错误
if (ast.stylesheet.parsingErrors && ast.stylesheet.parsingErrors.length) {
err = ast.stylesheet.parsingErrors;
err.forEach(function (err) {
log.push({
line: err.line,
column: err.column,
reason: err.toString()
});
});
} // 遍历
if (ast && ast.type === 'stylesheet' && ast.stylesheet && ast.stylesheet.rules && ast.stylesheet.rules.length) {
// 遍历样式规则
ast.stylesheet.rules.forEach(function (rule) {
const type = rule.type;
const ruleResult = {}; // 只考虑rule和fontface,其余暂时不支持
if (type === 'rule') {
if (rule.declarations && rule.declarations.length) {
(0, _process.processSingleClass)(rule, jsonStyle, ruleResult, log, filePath, depFiles);
}
}
if (type === 'media') {
if (!jsonStyle['@MEDIA']) {
jsonStyle['@MEDIA'] = [];
}
(0, _process.processMediaQueryCss)(rule, jsonStyle['@MEDIA'], log, filePath, '', depFiles);
} else if (type === 'font-face') {
if (rule.declarations && rule.declarations.length) {
const fontFaceObj = {};
rule.declarations.forEach(function (declaration) {
/* istanbul ignore if */
if (declaration.type !== 'declaration') {
return;
}
const name = (0, _utils.hyphenedToCamelCase)(declaration.property);
const value = declaration.value;
if (name === 'fontFamily') {
// 记录字体名.剔除字体名的外层引号
fontFaceObj.fontFamily = value.replace(/['"]+/g, '');
} else if (name === 'src') {
// 校验属性值
const subResult = (0, _validator.validate)('fontSrc', value, {
filePath
});
if (subResult.log) {
log.push({
line: declaration.position.start.line,
column: declaration.position.start.column,
reason: subResult.log.reason
});
}
fontFaceObj.src = subResult.value;
const srcFiles = subResult.value || [];
depFiles = depFiles.concat(srcFiles);
}
}); // 所有的fontface放入一个对象中
if (!jsonStyle['@FONT-FACE']) {
jsonStyle['@FONT-FACE'] = {};
} // 兼容之前平台版本
fontFaceObj.fontName = fontFaceObj.fontFamily;
fontFaceObj.fontSrc = fontFaceObj.src;
jsonStyle['@FONT-FACE'][fontFaceObj.fontFamily] = fontFaceObj;
}
} else if (type === 'keyframes') {
if (rule.keyframes && rule.keyframes.length) {
const name = rule.name;
const frameResult = [];
rule.keyframes.forEach(function (keyframe) {
let keyResult;
/* istanbul ignore if */
if (keyframe.type !== 'keyframe') {
return;
} // 处理关键帧内部样式
if (keyframe.declarations && keyframe.declarations.length) {
keyResult = {};
keyframe.declarations.forEach(function (declaration) {
const subType = declaration.type;
/* istanbul ignore if */
if (subType !== 'declaration') {
return;
} // 样式的属性和值
const subname = declaration.property;
const subvalue = declaration.value; // 校验属性值
const subcamelCasedName = (0, _utils.hyphenedToCamelCase)(subname);
const subResult = (0, _validator.validate)(subcamelCasedName, subvalue, {
filePath
});
subResult.value.forEach(item => {
// 如果校验成功,则保存转换后的属性值
if ((0, _utils.isValidValue)(item.v)) {
keyResult[item.n] = item.v;
if ((0, _process.shouldAddToDependency)(item.n, item.v)) {
depFiles.push(item.v);
}
}
});
if (subResult.log) {
log.push({
line: declaration.position.start.line,
column: declaration.position.start.column,
reason: subResult.log.reason
});
}
}); // 检查对象是否为空
if ((0, _utils.isEmptyObject)(keyResult)) {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason: 'ERROR: 动画 `' + name + '` 的关键帧 `' + JSON.stringify(keyframe.values) + '` 没有有效的属性'
});
} else {
// 可能包含多个
let percentValue;
keyframe.values.forEach(v => {
if (v === 'from') {
percentValue = 0;
} else if (v === 'to') {
percentValue = 100;
} else {
percentValue = parseFloat(v.replace('%', ''));
}
keyResult['time'] = percentValue;
frameResult.push(keyResult);
});
}
}
}); // 排序
frameResult.sort(function (a, b) {
return a.time - b.time;
}); // 所有的keyframes放入一个数组中
if (!jsonStyle['@KEYFRAMES']) {
jsonStyle['@KEYFRAMES'] = {};
}
jsonStyle['@KEYFRAMES'][name] = frameResult;
}
}
});
} // 是否压缩CSS属性名
if (_compilationConfig.compileOptionsObject.optimizeCssAttr) {
(0, _compress.compressCssAttr)(jsonStyle);
}
return {
jsonStyle,
depList,
log,
depFiles
};
}
var _default = {
parse,
validateDelaration: _validator.validate,
mightReferlocalResource: _validator.mightReferlocalResource,
shouldAddToDependency: _process.shouldAddToDependency
};
exports.default = _default;
//# sourceMappingURL=index.js.map

@@ -1,2 +0,424 @@

"use strict";const RE_MEDIA_QUERY=/(?:(only|not)?\s*)?(\s*[^\s()]+)?(?:(?:\s*(and)\s*)?(.+))?/i,RE_MEDIA_FEATURE=/^(\(\s*[^()]+\)\s+[a-zA-Z]+\s+)+\(\s*[^()]+\)\s*$|^\(\s*[^()]+\)$/,RE_MQ_DISCRETE_EXPRESSION=/\(\s*([^\s:)]+)\s*(?::\s*([^\s)]+))?\s*\)/,RE_MQ_RANGE_EXPRESSION=/^\((?:([-+]?\d*\.?(?:\d+[a-zA-Z]*|\d+\s*\/\s*\d+))\s*(<|>|<=|>=)?\s*)?(aspect-ratio|resolution|width|height|device-width|device-height)(?:\s*(<|>|<=|>=)?\s*([-+]?\d*\.?(?:\d+[a-zA-Z]*|\d+\s*\/\s*\d+)))?\)$/,mediaQueryTypes=["screen"],featureValidatorMap={height:"number","min-height":"number","max-height":"number",width:"number","min-width":"number","max-width":"number",resolution:"resolution","min-resolution":"resolution","max-resolution":"resolution",orientation:"orientation","aspect-ratio":"ratio","min-aspect-ratio":"ratio","max-aspect-ratio":"ratio","device-height":"number","min-device-height":"number","max-device-height":"number","device-width":"number","min-device-width":"number","max-device-width":"number","prefers-color-scheme":"preferColorScheme"},featureValidator={number:e=>/^(\d+)(px|dp)?$/.test(e)?{value:e}:{reason:function(r){return"ERROR: 媒体特征 `"+r+"` 的值 `"+e+"` 不正确, 必须为 `数值`"}},resolution(e){if(/^\d+(dpi|dppx)$/.test(e))return{value:e};if(/^\d+$/.test(e)){return{value:e+"dpi",reason:function(e){return"WARN: 媒体特征 `"+e+"` 的单位为 `dpi | dppx` 自动补齐为 `dpi`"}}}return{reason:function(r){return"ERROR: 媒体特征 `"+r+"` 的值 `"+e+"` 不正确, 必须为 `数值 + dpi | dppx`"}}},orientation:e=>/^(portrait|landscape)$/.test(e)?{value:e}:{reason:function(r){return"WARN: 媒体特征 `"+r+"` 的值 `"+e+"` 不正确, 必须为 `portrait | landscape`"}},ratio:e=>/^(\d+\s*\/\s*\d+|\d+\.\d+|\d+)$/.test(e)?{value:e}:{reason:function(r){return"WARN: 媒体特征 `"+r+"` 的值 `"+e+"` 不正确, 必须为 `数值 | 数值/数值`"}},preferColorScheme:e=>/^(light|dark|no-preference)$/.test(e)?{value:e}:{reason:function(r){return"WARN: 媒体特征 `"+r+"` 的值 `"+e+"` 不正确, 必须为 `light | dark | no-preference`"}}};function parseQuery(e){const r=[];return{result:e.split(",").map((function(e){const t=(e=e.trim()).match(RE_MEDIA_QUERY),n=t[1],i=t[2],a=t[3],o=t[4]||"";if(o&&!o.match(RE_MEDIA_FEATURE))return void r.push("WARN: 媒体特征格式错误");const s={};s.modifier=n,s.type=i?i.toLowerCase():"screen";let u=o.match(/\([^)]+\)/g)||[];return s.operator=a,s.expressions=u.map((function(t){const n=new RegExp("\\)\\s+([a-zA-Z]+)?\\s+"+t.replace(/\(/,"\\(").replace(/\)/,"\\)")),i=e.match(n);let a="";if(i&&(a=i[1],"and"!==a&&"or"!==a))return void r.push("WARN: 媒体特征连接符必须为 and 或者 or");let o=t.match(RE_MQ_DISCRETE_EXPRESSION);return o?{type:"discrete",combineSymbol:a,feature:o[1],value:o[2]}:(o=t.match(RE_MQ_RANGE_EXPRESSION),o?{type:"range",combineSymbol:a,beforeValue:o[1],beforeSymbol:o[2],feature:o[3],afterSymbol:o[4],afterValue:o[5]}:void r.push('WARN: 无效的媒体特征表达式: "'+t+'"'))})),s})),error:r}}function validateDiscreteValue(e,r,t,n){const i=featureValidator[t](e.value),{value:a,reason:o}=i;let s="";return o&&o&&n.push(o(r)),a&&(s+=`${r}: ${a}`),s}function validateRangeValue(e,r,t,n){function i(e){let i=featureValidator[t](e);const{reason:a,value:o}=i;return a&&n.push(a(r)),o}let{beforeValue:a,beforeSymbol:o,afterSymbol:s,afterValue:u}=e,d=r;if(a){if(a=i(a),!a)return"";d=`${a} ${o} ${d}`}if(u){if(u=i(u),!u)return"";d=`${d} ${s} ${u}`}return d}function validateMediaCondition(e){if(e){const r=[],t=parseQuery(e);if(t.error.length>0)return t.error.forEach(t=>{t+=", 表达式: `"+e+"` 有错误,请检查",r.push(t)}),{reason:r};const n=[];return t.result.forEach(e=>{const{modifier:t,type:i,operator:a,expressions:o}=e;if(-1===mediaQueryTypes.indexOf(i))return void r.push("WARN: 媒体类型 `"+i+"` 不支持");let s=a||"";o.length>0&&!a&&(s="and");let u=t?`${t} ${i} ${s}`:`${i} ${s}`;o.forEach(e=>{const{feature:t,combineSymbol:n,type:i}=e,a=featureValidatorMap[t];if(!a)return u="",void r.push("WARN: 媒体特征 `"+t+"` 不支持");let o="";"discrete"===i?o=validateDiscreteValue(e,t,a,r):"range"===i&&(o=validateRangeValue(e,t,a,r)),o?u+=n?` ${n} (${o})`:` (${o})`:u=""}),n.push(u)}),{value:n.join(" or "),reason:r}}return{value:""}}function findMediaClassByCondition(e,r){if(r){return e.find(e=>e.condition===r)}return null}function wrapMediaCode(e,r){return`@media ${r} {\n${e}\n}`}module.exports={validateMediaCondition:validateMediaCondition,findMediaClassByCondition:findMediaClassByCondition,wrapMediaCode:wrapMediaCode};
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict'; // 完整的媒体查询
const RE_MEDIA_QUERY = /(?:(only|not)?\s*)?(\s*[^\s()]+)?(?:(?:\s*(and)\s*)?(.+))?/i; // feture集合表达式
// (foo) and (bar) | (baz)
const RE_MEDIA_FEATURE = /^(\(\s*[^()]+\)\s+[a-zA-Z]+\s+)+\(\s*[^()]+\)\s*$|^\(\s*[^()]+\)$/; // (min-width: 100) 此类表达式
const RE_MQ_DISCRETE_EXPRESSION = /\(\s*([^\s:)]+)\s*(?::\s*([^\s)]+))?\s*\)/; // (width > 100) 此类表达式
// ([-+]?\d*\.?(?:\d+[a-zA-Z]*|\d+\/\d+)) => 如,100、100px、1/1
// (<|>|<=|>=)?\s*) => 如,< 、< 、>= 、<= 、
const RE_MQ_RANGE_EXPRESSION = /^\((?:([-+]?\d*\.?(?:\d+[a-zA-Z]*|\d+\s*\/\s*\d+))\s*(<|>|<=|>=)?\s*)?(aspect-ratio|resolution|width|height|device-width|device-height)(?:\s*(<|>|<=|>=)?\s*([-+]?\d*\.?(?:\d+[a-zA-Z]*|\d+\s*\/\s*\d+)))?\)$/; // 媒体查询类型
const mediaQueryTypes = ['screen'];
const featureValidatorMap = {
height: 'number',
'min-height': 'number',
'max-height': 'number',
width: 'number',
'min-width': 'number',
'max-width': 'number',
resolution: 'resolution',
'min-resolution': 'resolution',
'max-resolution': 'resolution',
orientation: 'orientation',
'aspect-ratio': 'ratio',
'min-aspect-ratio': 'ratio',
'max-aspect-ratio': 'ratio',
'device-height': 'number',
'min-device-height': 'number',
'max-device-height': 'number',
'device-width': 'number',
'min-device-width': 'number',
'max-device-width': 'number',
'prefers-color-scheme': 'preferColorScheme'
};
/**
* 媒体特征各类型的值校验
*/
const featureValidator = {
number(value) {
const reg = /^(\d+)(px|dp)?$/;
if (reg.test(value)) {
return {
value
};
}
return {
reason: function (feature) {
return 'ERROR: 媒体特征 `' + feature + '` 的值 `' + value + '` 不正确, 必须为 `数值`';
}
};
},
resolution(value) {
const reg = /^\d+(dpi|dppx)$/;
if (reg.test(value)) {
return {
value
};
}
if (/^\d+$/.test(value)) {
const newVal = value + 'dpi';
return {
value: newVal,
reason: function (feature) {
return 'WARN: 媒体特征 `' + feature + '` 的单位为 `dpi | dppx` ' + '自动补齐为 `' + 'dpi`';
}
};
}
return {
reason: function (feature) {
return 'ERROR: 媒体特征 `' + feature + '` 的值 `' + value + '` 不正确, 必须为 `数值 + dpi | dppx`';
}
};
},
orientation(value) {
const reg = /^(portrait|landscape)$/;
if (reg.test(value)) {
return {
value
};
}
return {
reason: function (feature) {
return 'WARN: 媒体特征 `' + feature + '` 的值 `' + value + '` 不正确, 必须为 `portrait | landscape`';
}
};
},
ratio(value) {
const reg = /^(\d+\s*\/\s*\d+|\d+\.\d+|\d+)$/;
if (reg.test(value)) {
return {
value
};
}
return {
reason: function (feature) {
return 'WARN: 媒体特征 `' + feature + '` 的值 `' + value + '` 不正确, 必须为 `数值 | 数值/数值`';
}
};
},
preferColorScheme(value) {
const reg = /^(light|dark|no-preference)$/;
if (reg.test(value)) {
return {
value
};
}
return {
reason: function (feature) {
return 'WARN: 媒体特征 `' + feature + '` 的值 `' + value + '` 不正确, 必须为 `light | dark | no-preference`';
}
};
}
};
/**
* 解析返回媒体查询表达式
* @param {String} mediaQuery - 媒体查询表达式
* @returns {Object} object 解析后的结果
* https://github.com/ericf/css-mediaquery
*/
function parseQuery(mediaQuery) {
const error = [];
const result = mediaQuery.split(',').map(function (query) {
query = query.trim();
const captures = query.match(RE_MEDIA_QUERY);
const modifier = captures[1];
const type = captures[2];
const operator = captures[3]; // 分割第一个操作符
const features = captures[4] || ''; // @media screen {} 无匹配条件 features 为undefined
if (features && !features.match(RE_MEDIA_FEATURE)) {
error.push('WARN: 媒体特征格式错误');
return;
}
const parsed = {}; // 媒体特征前面的标识符
parsed.modifier = modifier; // 媒体类型
parsed.type = type ? type.toLowerCase() : 'screen'; // Split expressions into a list.
let expressions = features.match(/\([^)]+\)/g) || [];
parsed.operator = operator;
parsed.expressions = expressions.map(function (expression) {
const combineReg = new RegExp(`\\)\\s+([a-zA-Z]+)?\\s+${expression.replace(/\(/, '\\(').replace(/\)/, '\\)')}`);
const combineMatch = query.match(combineReg); // 单条媒体特征前面跟着的连接符:"or"、"and"
let combineSymbol = '';
if (combineMatch) {
combineSymbol = combineMatch[1];
if (combineSymbol !== 'and' && combineSymbol !== 'or') {
error.push('WARN: 媒体特征连接符必须为 and 或者 or');
return;
}
} // 是否为: 形式
let captures = expression.match(RE_MQ_DISCRETE_EXPRESSION);
if (captures) {
return {
type: 'discrete',
combineSymbol,
feature: captures[1],
value: captures[2]
};
} // 是否为 < > <= >= 形式
captures = expression.match(RE_MQ_RANGE_EXPRESSION);
if (captures) {
return {
type: 'range',
combineSymbol,
beforeValue: captures[1],
// 媒体特征左边的值
beforeSymbol: captures[2],
// 媒体特征左边的运算符
feature: captures[3],
// 媒体特征
afterSymbol: captures[4],
// 媒体特征右边的值
afterValue: captures[5] // 媒体特征右边的运算符
};
}
error.push('WARN: 无效的媒体特征表达式: "' + expression + '"');
});
return parsed;
});
return {
result,
error
};
}
/**
* 检验(min-width: 100px)此类表达式的值
* @param {String} exp - 媒体特征表达式
* @param {String} feature - 媒体特征
* @param {String} featureType - 媒体特征的类型
* @param {Array} logReason - log
* @returns {String} 返回解析后的表达式
*/
function validateDiscreteValue(exp, feature, featureType, logReason) {
const valResult = featureValidator[featureType](exp.value);
const {
value,
reason
} = valResult;
let expStr = '';
if (reason) {
reason && logReason.push(reason(feature));
}
if (value) {
expStr += `${feature}: ${value}`;
}
return expStr;
}
/**
* 检验(100 < width < 200)此类表达式的值
* @param {String} exp - 媒体特征表达式
* @param {String} feature - 媒体特征
* @param {String} featureType - 媒体特征的类型
* @param {Array} logReason - log
* @returns {String} 返回解析后的表达式
*/
function validateRangeValue(exp, feature, featureType, logReason) {
function getValue(v) {
let valResult = featureValidator[featureType](v);
const {
reason,
value
} = valResult;
reason && logReason.push(reason(feature));
return value;
} // 左边值,左边运算符,右边运算符,右边值
let {
beforeValue,
beforeSymbol,
afterSymbol,
afterValue
} = exp;
let expStr = feature;
if (beforeValue) {
beforeValue = getValue(beforeValue);
if (beforeValue) {
expStr = `${beforeValue} ${beforeSymbol} ${expStr}`;
} else {
return '';
}
}
if (afterValue) {
afterValue = getValue(afterValue);
if (afterValue) {
expStr = `${expStr} ${afterSymbol} ${afterValue}`;
} else {
return '';
}
}
return expStr;
}
/**
* 检验及解析media query的触发条件, 表达式一处错误,整条失效
* @param {String} condition - 查询条件
* @returns {Object} 返回解析后的查询条件
*/
function validateMediaCondition(condition) {
if (condition) {
const logReason = [];
const ast = parseQuery(condition);
if (ast.error.length > 0) {
ast.error.forEach(err => {
err += ', 表达式: `' + condition + '`' + ' 有错误,请检查';
logReason.push(err);
});
return {
reason: logReason
};
}
const conditionArr = []; // 当用","表示或的时候解析为数组
ast.result.forEach(_ast => {
const {
modifier,
type,
operator,
expressions
} = _ast; // type 需符合指定类型
if (mediaQueryTypes.indexOf(type) === -1) {
logReason.push('WARN: 媒体类型 `' + type + '` 不支持');
return;
}
let connector = operator || '';
if (expressions.length > 0 && !operator) {
connector = 'and';
}
let conditionStr = modifier ? `${modifier} ${type} ${connector}` : `${type} ${connector}`;
expressions.forEach(exp => {
const {
feature,
combineSymbol,
type: expType
} = exp; // feature 需符合指定类型. 同时寻找对应的校验模式
const featureType = featureValidatorMap[feature];
if (!featureType) {
conditionStr = '';
logReason.push('WARN: 媒体特征 `' + feature + '` 不支持');
return;
}
let expStr = '';
if (expType === 'discrete') {
expStr = validateDiscreteValue(exp, feature, featureType, logReason);
} else if (expType === 'range') {
expStr = validateRangeValue(exp, feature, featureType, logReason);
}
if (expStr) {
conditionStr += combineSymbol ? ` ${combineSymbol} (${expStr})` : ` (${expStr})`;
} else {
conditionStr = '';
}
}); // conditionStr形如: "all and (min-height: 400px)"
conditionArr.push(conditionStr);
});
return {
value: conditionArr.join(' or '),
reason: logReason
};
}
return {
value: ''
};
}
/**
* 通过传入的条件查找数组里匹配的类
* @param {Object[]} mediaClasses - 媒体查询的类集合数组
* @param {String} condition - 查询条件
* @returns {Object|null} 返回匹配的类或者null
*/
function findMediaClassByCondition(mediaClasses, condition) {
if (condition) {
let mediaCls = mediaClasses.find(cls => {
return cls.condition === condition;
});
return mediaCls;
}
return null;
}
/**
* 生成带有媒体查询的代码
* @param {String} code - 代码
* @param {String} mediaquery - 媒体查询条件
* @returns {String} 返回已加上媒体查询的代码
*/
function wrapMediaCode(code, mediaquery) {
return `@media ${mediaquery} {\n${code}\n}`;
}
module.exports = {
validateMediaCondition,
findMediaClassByCondition,
wrapMediaCode
};
//# sourceMappingURL=mediaquery.js.map

@@ -1,2 +0,329 @@

"use strict";var _fs=_interopRequireDefault(require("fs")),_path=_interopRequireDefault(require("path")),_css=_interopRequireDefault(require("css")),_cssWhat=_interopRequireDefault(require("css-what")),_compilationConfig=require("@hap-toolkit/shared-utils/compilation-config"),_validator=require("./validator"),_compress=require("./compress"),_mediaquery=require("./mediaquery"),_utils=require("../utils");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const VALID_IMPORT_FLAG="__VALID_IMPORT__",IMPORT_REG=new RegExp("@import\\s+__VALID_IMPORT__((?:['\"]([^()]+?)['\"])|(?:(?:url\\(([^()]+?)\\))))((?:\\s*)|(?:\\s+[^;]+))__VALID_IMPORT__;","g"),IMPORT_URL_REG=new RegExp("__VALID_IMPORT__(?:(?:['\"]([^()]+?)['\"])|(?:(?:url\\(([^()]+?)\\))))(\\s+[^;]+)?__VALID_IMPORT__;");function shouldAddToDependency(e,s){return(0,_validator.mightReferlocalResource)(e)&&!/^(data:|http)/.test(s)&&"string"==typeof s}function signValidCssImport(e){let s=!0;const t=_css.default.parse(e);if(t&&"stylesheet"===t.type&&t.stylesheet&&t.stylesheet.rules&&t.stylesheet.rules.length){t.stylesheet.rules.forEach(e=>{const t=e.type;"import"!==t&&"comment"!==t&&(s=!1),"import"===t&&s&&(e.import=VALID_IMPORT_FLAG+e.import+VALID_IMPORT_FLAG)}),e=_css.default.stringify(t)}return e}function processImport(e,s,t,o){let i=signValidCssImport(e);const r=i.match(IMPORT_REG);return r&&r.length>0&&(s?r.forEach(e=>{const r=e.match(IMPORT_URL_REG);if(r.length>1){const n=r[3],a=_path.default.resolve(s,r[1]||r[2]),l=_fs.default.readFileSync(a);if(l){const s=_path.default.dirname(a);let r=processImport(l.toString(),s,t,o);n&&(r=(0,_mediaquery.wrapMediaCode)(r,n)),i=i.replace(e,"\n"+r+"\n"),o.push(a)}else t.push({line:1,column:1,reason:"ERROR: 找不到文件 `"+e+"` , 导入失败"})}}):t.push({line:1,column:1,reason:"ERROR: 找不到资源路径, 无法处理@import"})),i}function processSingleClass(e,s,t,o,i,r){e.declarations.forEach((function(e){if("declaration"!==e.type)return;const s=e.property,n=e.value,a=(0,_utils.hyphenedToCamelCase)(s),l=(0,_validator.validate)(a,n,{filePath:i});l.value.forEach(e=>{(0,_utils.isValidValue)(e.v)&&(t[e.n]=e.v,shouldAddToDependency(e.n,e.v)&&r.push(e.v))}),l.log&&o.push({line:e.position.start.line,column:e.position.start.column,reason:l.log.reason})}));const n=/^[.#]?[A-Za-z0-9_\-:]+$/,a=/^([.#]?[A-Za-z0-9_-]+(\s+|\s*>\s*))+([.#]?[A-Za-z0-9_\-:]+)$/;e.selectors.forEach((function(i){const r={key:i,val:t};if(i.match(n)||i.match(a)){if(!processPseudoClass(r,o,e))return;if(!_compilationConfig.compileOptionsObject.optimizeDescMeta&&i.match(a))try{r.val=Object.assign({},r.val),r.val._meta={},r.val._meta.ruleDef=(0,_compress.compressDescSelector)((0,_cssWhat.default)(r.key))}catch(s){return void o.push({line:e.position.start.line,column:e.position.start.column,reason:"ERROR: 选择器 `"+r.key+"` 不支持"})}s[r.key]=(0,_utils.extend)({},s[r.key]||{},r.val)}else o.push({line:e.position.start.line,column:e.position.start.column,reason:"ERROR: 选择器 `"+i+"` 非法"})}))}function processMediaQueryCss(e,s,t,o,i,r){const n=(0,_mediaquery.validateMediaCondition)(e.media),a=n.value,l=n.reason;if(l&&l.length>0&&n.reason.forEach(s=>{t.push({line:e.position.start.line,column:e.position.start.column,reason:s})}),!a)return;const c=i?`${i} and ${a}`:a;e.rules.forEach(e=>{if("rule"===e.type){if(e.declarations&&e.declarations.length){let i=(0,_mediaquery.findMediaClassByCondition)(s,c);const n=!i;n&&(i={condition:c}),processSingleClass(e,i,{},t,o,r),n&&s.push(i)}}else"media"===e.type&&processMediaQueryCss(e,s,t,o,c,r)})}function processPseudoClass(e,s,t){const o=e.key.indexOf(":");if(o>-1){const i=e.key.slice(o);if(!(0,_validator.validatePseudoClass)(i))return s.push({line:t.position.start.line,column:t.position.start.column,reason:"ERROR: 不支持伪类选择器`"+i+"`"}),!1;e.key=e.key.slice(0,o);const r={};Object.keys(e.val).forEach((function(s){r[s+i]=e.val[s]})),e.val=r}return!0}module.exports={processImport:processImport,processSingleClass:processSingleClass,processMediaQueryCss:processMediaQueryCss,shouldAddToDependency:shouldAddToDependency};
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict';
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _css = _interopRequireDefault(require("css"));
var _cssWhat = _interopRequireDefault(require("css-what"));
var _compilationConfig = require("@hap-toolkit/shared-utils/compilation-config");
var _validator = require("./validator");
var _compress = require("./compress");
var _mediaquery = require("./mediaquery");
var _utils = require("../utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 有效@import的标识前后缀
const VALID_IMPORT_FLAG = '__VALID_IMPORT__'; // 匹配@import的正则
// (?:['"]([^()]+?)['"]) => "foo.css"
// (?:(?:url\\(([^()]+?)\\))) => url(bar.css)
// ((?:\\s*)|(?:\\s+[^;]+)) => 空格 或 除`;`之外字符
const IMPORT_REG = new RegExp(`@import\\s+${VALID_IMPORT_FLAG}((?:['"]([^()]+?)['"])|(?:(?:url\\(([^()]+?)\\))))((?:\\s*)|(?:\\s+[^;]+))${VALID_IMPORT_FLAG};`, 'g'); // 匹配@import url的正则
const IMPORT_URL_REG = new RegExp(`${VALID_IMPORT_FLAG}(?:(?:['"]([^()]+?)['"])|(?:(?:url\\(([^()]+?)\\))))(\\s+[^;]+)?${VALID_IMPORT_FLAG};`);
/**
* 是否将资源添加到依赖
* @param {string} name - 属性名
* @param {string} value - 属性值
* @returns {boolean}
*/
function shouldAddToDependency(name, value) {
return (0, _validator.mightReferlocalResource)(name) && !/^(data:|http)/.test(value) && typeof value === 'string';
}
/**
* 解析css中的有效的@import(只在最顶级最上面有效)
* 此情况存在于未使用任何css预处理,使用less,sass等会依据自己语法解析css
* @desc 给有效的@import打上标识前后缀,便于正则查找,返回新代码
* @param {String} csscode - css代码
* @returns {String} 新的css代码
*/
function signValidCssImport(csscode) {
let isOnTop = true;
const ast = _css.default.parse(csscode);
if (ast && ast.type === 'stylesheet' && ast.stylesheet && ast.stylesheet.rules && ast.stylesheet.rules.length) {
// 只需做最顶层的获取
const rules = ast.stylesheet.rules;
rules.forEach(rule => {
const type = rule.type; // 在import前面的只能是注释或者import
if (type !== 'import' && type !== 'comment') {
isOnTop = false;
}
if (type === 'import' && isOnTop) {
// 打上标识符
rule.import = VALID_IMPORT_FLAG + rule.import + VALID_IMPORT_FLAG;
}
});
csscode = _css.default.stringify(ast);
}
return csscode;
}
/**
* 处理@import
* @desc 支持 @import '.file.css';或者 @import url(./file.css);
* @param {String} csscode - css代码
* @param {String} dir - css文件所在的目录
* @param {String} log - log对象
* @param {Array} depList - 引入的CSS文件列表
* @returns {*}
*/
function processImport(csscode, dir, log, depList) {
// 处理@import
let mergeCode = signValidCssImport(csscode);
const importList = mergeCode.match(IMPORT_REG);
if (importList && importList.length > 0) {
if (dir) {
// 读取css
importList.forEach(res => {
const inMatch = res.match(IMPORT_URL_REG);
if (inMatch.length > 1) {
// 媒体查询条件,这里不做检验
const importMediaQuery = inMatch[3];
const importPath = _path.default.resolve(dir, inMatch[1] || inMatch[2]);
const importCode = _fs.default.readFileSync(importPath);
if (importCode) {
const importDir = _path.default.dirname(importPath); // 获取import文件里面的内容
let importContent = processImport(importCode.toString(), importDir, log, depList);
if (importMediaQuery) {
importContent = (0, _mediaquery.wrapMediaCode)(importContent, importMediaQuery);
}
mergeCode = mergeCode.replace(res, '\n' + importContent + '\n');
depList.push(importPath);
} else {
log.push({
line: 1,
column: 1,
reason: 'ERROR: 找不到文件 `' + res + '` , 导入失败'
});
}
}
});
} else {
log.push({
line: 1,
column: 1,
reason: 'ERROR: 找不到资源路径, 无法处理@import'
});
}
}
return mergeCode;
}
/**
* 解析单个class为JSON对象
* @param {Object} rule - ast的rule字段
* @param {Object} jsonStyle - 当前页面所有css解析结果
* @param {Object} ruleResult - 当前类的解析结果
* @param {Array} log - log对象
* @param {String} filePath - 文件路径
* @param {Array} depFiles - 使用到的资源集合
*/
function processSingleClass(rule, jsonStyle, ruleResult, log, filePath, depFiles) {
rule.declarations.forEach(function (declaration) {
const subType = declaration.type; // 只考虑声明类型
if (subType !== 'declaration') {
return;
} // 样式的属性和值
const name = declaration.property;
const value = declaration.value; // 校验属性值
const camelCasedName = (0, _utils.hyphenedToCamelCase)(name);
const subResult = (0, _validator.validate)(camelCasedName, value, {
filePath
});
subResult.value.forEach(item => {
// 如果校验成功,则保存转换后的属性值
if ((0, _utils.isValidValue)(item.v)) {
ruleResult[item.n] = item.v;
if (shouldAddToDependency(item.n, item.v)) {
depFiles.push(item.v);
}
}
});
if (subResult.log) {
log.push({
line: declaration.position.start.line,
column: declaration.position.start.column,
reason: subResult.log.reason
});
}
}); // 单个选择器:tag, class, id
const REGEXP_SEL = /^[.#]?[A-Za-z0-9_\-:]+$/; // 复合选择器:tag, class, id后代选择
const REGEXP_SEL_COMPLEX = /^([.#]?[A-Za-z0-9_-]+(\s+|\s*>\s*))+([.#]?[A-Za-z0-9_\-:]+)$/;
rule.selectors.forEach(function (selector) {
// 定义
const hash = {
key: selector,
val: ruleResult
};
if (selector.match(REGEXP_SEL) || selector.match(REGEXP_SEL_COMPLEX)) {
// 处理伪类
const isValid = processPseudoClass(hash, log, rule);
if (!isValid) {
return;
} // 是否编译复合选择器,生成_meta信息
if (!_compilationConfig.compileOptionsObject.optimizeDescMeta && selector.match(REGEXP_SEL_COMPLEX)) {
try {
hash.val = Object.assign({}, hash.val);
hash.val._meta = {};
hash.val._meta.ruleDef = (0, _compress.compressDescSelector)((0, _cssWhat.default)(hash.key));
} catch (err) {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason: 'ERROR: 选择器 `' + hash.key + '` 不支持'
});
return;
}
} // 如果样式已经存在,则叠加,覆盖同名属性
jsonStyle[hash.key] = (0, _utils.extend)({}, jsonStyle[hash.key] || {}, hash.val);
} else {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason: 'ERROR: 选择器 `' + selector + '` 非法'
});
}
});
}
/**
* 解析media query为JSON对象
* @param {Object} rule - ast的rule字段
* @param {Object} jsonStyleMedia - media部分所有css解析结果
* @param {Array} log - log对象
* @param {String} filePath - 文件路径
* @param {String} upperCondition - 上级的查询条件,若本身为顶级则无此值
* @param {Array} depFiles - 使用到的资源集合
*/
function processMediaQueryCss(rule, jsonStyleMedia, log, filePath, upperCondition, depFiles) {
const validateResult = (0, _mediaquery.validateMediaCondition)(rule.media);
const currentCondition = validateResult.value;
const errorReason = validateResult.reason;
if (errorReason && errorReason.length > 0) {
validateResult.reason.forEach(reason => {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason
});
});
} // 若无查询条件,判断为不合法,数据无效
if (!currentCondition) {
return;
} // 嵌套层级的条件用and连接起来
const condition = upperCondition ? `${upperCondition} and ${currentCondition}` : currentCondition;
rule.rules.forEach(_rule => {
if (_rule.type === 'rule') {
if (_rule.declarations && _rule.declarations.length) {
let jsonClassMedia = (0, _mediaquery.findMediaClassByCondition)(jsonStyleMedia, condition);
const isNewCondition = !jsonClassMedia;
const ruleResult = {};
if (isNewCondition) {
jsonClassMedia = {
condition
};
}
processSingleClass(_rule, jsonClassMedia, ruleResult, log, filePath, depFiles); // 新的查询条件class需要push
isNewCondition && jsonStyleMedia.push(jsonClassMedia);
}
} else if (_rule.type === 'media') {
processMediaQueryCss(_rule, jsonStyleMedia, log, filePath, condition, depFiles);
}
});
}
/**
* 处理伪类,将伪类写到Style的每个值上
* @param hash
* @return {boolean}
*/
function processPseudoClass(hash, log, rule) {
// 处理伪选择器
const pseudoIndex = hash.key.indexOf(':');
if (pseudoIndex > -1) {
const pseudoCls = hash.key.slice(pseudoIndex);
if (!(0, _validator.validatePseudoClass)(pseudoCls)) {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason: 'ERROR: 不支持伪类选择器`' + pseudoCls + '`'
});
return false;
}
hash.key = hash.key.slice(0, pseudoIndex);
const pseudoRuleResult = {}; // 将伪选择器text:active中的样式color属性名转换为color:active
Object.keys(hash.val).forEach(function (prop) {
pseudoRuleResult[prop + pseudoCls] = hash.val[prop];
});
hash.val = pseudoRuleResult;
}
return true;
}
module.exports = {
processImport,
processSingleClass,
processMediaQueryCss,
shouldAddToDependency
};
//# sourceMappingURL=process.js.map

@@ -1,2 +0,45 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.compressTemplateAttr=compressTemplateAttr;const templateAttrMap={type:"t",attr:"a",classList:"cL",style:"s",events:"e",children:"c"};function compressTemplateAttr(t){if(t){if(t.children)for(let e=0,r=t.children.length;e<r;e++){compressTemplateAttr(t.children[e])}for(const e in t)if(templateAttrMap[e]){t[templateAttrMap[e]]=t[e],delete t[e]}}}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.compressTemplateAttr = compressTemplateAttr;
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
// 模板属性名全写-缩写对照表
const templateAttrMap = {
type: 't',
attr: 'a',
classList: 'cL',
style: 's',
events: 'e',
children: 'c'
};
/**
* 压缩模板属性名
* @param jsonObject
*/
function compressTemplateAttr(jsonObject) {
if (!jsonObject) {
return;
}
if (jsonObject.children) {
for (let i = 0, len = jsonObject.children.length; i < len; i++) {
const child = jsonObject.children[i];
compressTemplateAttr(child);
}
}
for (const k in jsonObject) {
if (templateAttrMap[k]) {
const kAbbr = templateAttrMap[k];
jsonObject[kAbbr] = jsonObject[k];
delete jsonObject[k];
}
}
}
//# sourceMappingURL=compress.js.map

@@ -1,2 +0,94 @@

"use strict";var _expression=_interopRequireDefault(require("./lib/expression")),_text=_interopRequireDefault(require("./lib/text")),_filterParser=_interopRequireDefault(require("./lib/filter-parser"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function trimhtml(e){if((e=e.replace(/^\s\s+/," ")).length<=1)return e;const t=" "===e.charAt(0)?1:0;return e.length-(e=e.trim()).length-t>=1&&(e+=" "),(t?" ":"")+e}function transExpr(expContent,toFunc){let ret;const trimExpContent=expContent.trim();if(_text.default.isExpr(trimExpContent)){ret=[];const tokens=_text.default.parseText(trimExpContent),isSingle=1===tokens.length;if(tokens.forEach((function(e){if(e.tag){let t=_expression.default.parseExpression((0,_filterParser.default)(e.value));isSingle||(t="("+t+")"),ret.push(t)}else ret.push("'"+e.value+"'")})),isSingle||ret.unshift("''"),ret=ret.join(" + "),!1!==toFunc)try{ret=eval("(function () {return "+ret+"})")}catch(e){throw e.isExpressionError=!0,e.expression=trimExpContent,e}}else ret=trimhtml(expContent);return ret}transExpr.isExpr=_text.default.isExpr,transExpr.singleExpr=_text.default.singleExpr,transExpr.removeExprffix=_text.default.removeExprffix,transExpr.addExprffix=_text.default.addExprffix,module.exports=transExpr;
"use strict";
var _expression = _interopRequireDefault(require("./lib/expression"));
var _text = _interopRequireDefault(require("./lib/text"));
var _filterParser = _interopRequireDefault(require("./lib/filter-parser"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
// 去除字符串头部空格或指定字符
function trimhtml(str) {
// 2个空格以上, 仅保留一个空格
str = str.replace(/^\s\s+/, ' ');
if (str.length <= 1) {
return str;
}
const startSpace = str.charAt(0) === ' ' ? 1 : 0;
const oldLength = str.length;
str = str.trim(); // 尾部多余1个空格
if (oldLength - str.length - startSpace >= 1) {
str = str + ' ';
}
return (startSpace ? ' ' : '') + str;
}
/**
* 表达式转换
* @param expContent
* @param toFunc
* @returns {*}
*/
function transExpr(expContent, toFunc) {
let ret;
const trimExpContent = expContent.trim();
if (!_text.default.isExpr(trimExpContent)) {
ret = trimhtml(expContent);
} else {
ret = [];
const tokens = _text.default.parseText(trimExpContent);
const isSingle = tokens.length === 1;
tokens.forEach(function (token) {
if (token.tag) {
let res = _expression.default.parseExpression((0, _filterParser.default)(token.value));
if (!isSingle) {
res = '(' + res + ')';
}
ret.push(res);
} else {
ret.push("'" + token.value + "'");
}
}); // 确保多个插值表达式相邻时,是字符串拼接,而不是数值相加,例如:{{number1}}{{number2}}
if (!isSingle) {
ret.unshift("''");
}
ret = ret.join(' + ');
if (toFunc !== false) {
try {
/* eslint-disable no-eval */
ret = eval('(function () {return ' + ret + '})');
/* eslint-enable no-eval */
} catch (err) {
err.isExpressionError = true;
err.expression = trimExpContent;
throw err;
}
}
}
return ret;
}
transExpr.isExpr = _text.default.isExpr;
transExpr.singleExpr = _text.default.singleExpr;
transExpr.removeExprffix = _text.default.removeExprffix;
transExpr.addExprffix = _text.default.addExprffix;
module.exports = transExpr;
//# sourceMappingURL=exp.js.map

@@ -1,2 +0,491 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _parse=_interopRequireDefault(require("parse5")),_parser=_interopRequireDefault(require("parse5/lib/parser")),_tokenizer=_interopRequireDefault(require("parse5/lib/tokenizer")),_sharedUtils=require("@hap-toolkit/shared-utils"),_compilationConfig=require("@hap-toolkit/shared-utils/compilation-config"),_validator=_interopRequireDefault(require("./validator")),_compress=require("./compress");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function calcSubTextNodesNum(e,t){let a=0;if(_validator.default.isSupportSpan(e)){const l=_validator.default.getTagChildren(e);t.forEach((function(e){("#text"===e.nodeName&&e.value.trim()||l.indexOf(e.nodeName)>-1)&&++a}))}return a}function traverse(e,t,a,l,o){_validator.default.checkTagName(e,t,o),(e.attrs||[]).forEach((function(r){let i=r.name;const n=i.match(/^:+/);n&&(i=i.slice(n.length));const s=r.value;let c={line:1,column:1};switch(e.__location&&(c={line:e.__location.line,column:e.__location.col}),i){case"id":_validator.default.checkId(s,t),_validator.default.checkAttr(i,s,t,e.tagName,c);break;case"class":_validator.default.checkClass(s,t);break;case"style":_validator.default.checkStyle(s,t,c,o);break;case"if":e._isroot||_validator.default.checkIf(s,t,!1,c,l);break;case"is":_validator.default.checkIs(s,t,c);break;case"else":e._isroot||a&&a.__cond__&&_validator.default.checkElse(a.__cond__,t,c,l);break;case"elif":e._isroot||a&&a.__cond__&&(e.__cond__=_validator.default.checkElif(s,a.__cond__,t,c,l));break;case"for":e._isroot||_validator.default.checkFor(s,t,c);break;case"tree":_validator.default.checkBuild("tree",t);break;case"model":_validator.default.checkModel(s,t,e,o);break;default:i.match(/^(on|@)/)?_validator.default.checkEvent(i,s,t):_validator.default.checkAttr(i,s,t,e.tagName,c,o)}}));const r=t.result,i=e.childNodes;if(i&&i.length){let a,l;const n=[],s=calcSubTextNodesNum(r.type,i);i.forEach((function(c,u){u>0&&(l=i[u-1],l.nodeName.match(/^#/)||(a=l,a.__cond__||a.attrs&&a.attrs.forEach((function(e){"if"!==e.name&&"elif"!==e.name||(a.__cond__=e.value)}))));const _={};if(c.nodeName.match(/^#/)){if("#text"===c.nodeName&&c.value.trim()){l&&_validator.default.isSupportedSelfClosing(l.nodeName)||_validator.default.isNotTextContentAtomic(e.tagName)&&t.log.push({line:e.__location.line,column:e.__location.col,reason:`Warn: 组件 ${e.tagName} 不支持文本内容作为字节点`});const a=_validator.default.isSupportSpan(e.tagName)&&s>=2,i=o.importNames&&o.importNames.indexOf(e.tagName)>-1;if((a||i)&&(_.type="span",t.result=_,r.children=r.children||[],r.children.push(_),t.log.push({line:e.__location.line,column:e.__location.col,reason:`WARNING: 文本和span标签并行存在,编译时将文本节点:"${c.value}" 用span包裹(关于span嵌套的使用,请参考官方文档"span嵌套")`}),_validator.default.checkAttr("value",c.value,t)),"option"===e.tagName){const e=t.result;return t.result=r,r.attr.hasOwnProperty("value")||_validator.default.checkAttr("value",c.value,t),_validator.default.checkAttr("content",c.value,t),void(t.result=e)}if(_validator.default.isSupportSpan(e.tagName)&&1===s||_validator.default.isTextContentAtomic(e.tagName)){const e=t.result;t.result=r,_validator.default.checkAttr("value",c.value,t),t.result=e}}}else t.result=_,r.children=r.children||[],r.children.push(_),traverse(c,t,a,n,o)})),r.children&&0===r.children.length&&(r.children=void 0)}t.result=r}function initParser(e,t,a){const l=new _parser.default(t),o=l._appendElement,r=l._insertElement;function i(e){if(!e.tagName)return;const t=e.tagName.toLowerCase(),o=e.selfClosing,r=_validator.default.isSupportedSelfClosing(t),i=_validator.default.isEmptyElement(t);if(l.__m.tagName&&!l.__m.selfClosing&&!i){const t=String(e.location.line)+":"+String(e.location.col);(!r||t!==l.__m.pos&&e.type===_tokenizer.default.START_TAG_TOKEN)&&(_sharedUtils.colorconsole.warn(`${l.__m.tagName}标签要闭合,请遵循XML规范 ${a}@${l.__m.pos}`),l.__m={})}r&&(e.type!==_tokenizer.default.START_TAG_TOKEN||o||(l.__m.tagName=t,l.__m.selfClosing=!1,l.__m.pos=String(e.location.line)+":"+String(e.location.col)),e.type===_tokenizer.default.END_TAG_TOKEN&&t===l.__m.tagName&&(l.__m.selfClosing=!0))}function n(e){e.tagName&&"plaintext"===e.tagName.toLowerCase()&&_sharedUtils.colorconsole.error(`${a} : 禁止使用 plaintext 标签@${e.location.line}:${e.location.col}`)}return l._insertElement=function(e){const t=(e.tagName||"").toLowerCase(),l=e.selfClosing,i=_validator.default.isSupportedSelfClosing(t);l&&!i&&_sharedUtils.colorconsole.error(`${t}标签,禁止使用自闭合 ${a}@${e.location.line}:${e.location.col}`),i||l&&t?o.apply(this,arguments):r.apply(this,arguments)},l.__m={},l._runParsingLoop=function(e){for(;!this.stopped;){this._setupTokenizerCDATAMode();const t=this.tokenizer.getNextToken();if(i(t),n(t),t.type===_tokenizer.default.HIBERNATION_TOKEN)break;if(this.skipNextNewLine&&(this.skipNextNewLine=!1,t.type===_tokenizer.default.WHITESPACE_CHARACTER_TOKEN&&"\n"===t.chars[0])){if(1===t.chars.length)continue;t.chars=t.chars.substr(1)}if(this._processInputToken(t),e&&this.pendingScript)break}},l.parseFragment(e)}function parse(e,t){const a=initParser(e,{treeAdapter:_parse.default.treeAdapters.default,locationInfo:!0},t.filePath),l={result:{},log:[],depFiles:[]};if(!a||!a.childNodes)return l.log.push({reason:"ERROR: <template>解析失败",line:1,column:1}),{jsonTemplate:l.result,log:l.log,depFiles:l.depFiles};const o=a.childNodes.filter((function(e){return"#"!==e.nodeName.charAt(0)}));if(0===o.length)return l.log.push({reason:"ERROR: 没有合法的根节点",line:1,column:1}),{jsonTemplate:l.result,log:l.log,depFiles:l.depFiles};if(o.length>1)return l.log.push({reason:"ERROR: <template>节点里只能有一个根节点",line:1,column:1}),{jsonTemplate:l.result,log:l.log,depFiles:l.depFiles};const r=o[0];r._isroot=!0;try{traverse(r,l,null,null,t)}catch(e){if(!e.isExpressionError)throw e;l.log.push({reason:`ERROR: 表达式解析失败 ${e.message}\n\n> ${e.expression}\n\nat ${t.filePath}`})}return _compilationConfig.compileOptionsObject.optimizeTemplateAttr&&(0,_compress.compressTemplateAttr)(l.result),{jsonTemplate:l.result,log:l.log,depFiles:l.depFiles}}var _default={parse:parse};exports.default=_default;
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _parse = _interopRequireDefault(require("parse5"));
var _parser = _interopRequireDefault(require("parse5/lib/parser"));
var _tokenizer = _interopRequireDefault(require("parse5/lib/tokenizer"));
var _sharedUtils = require("@hap-toolkit/shared-utils");
var _compilationConfig = require("@hap-toolkit/shared-utils/compilation-config");
var _validator = _interopRequireDefault(require("./validator"));
var _compress = require("./compress");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 计算支持span的节点的有效子节点数
* @param tagName - 标签名
* @param childNodes - 标签的子节点
* @returns {number}
*/
function calcSubTextNodesNum(tagName, childNodes) {
let subTextNodesNum = 0;
if (_validator.default.isSupportSpan(tagName)) {
const tagChildren = _validator.default.getTagChildren(tagName);
childNodes.forEach(function (child) {
if (child.nodeName === '#text' && child.value.trim() || tagChildren.indexOf(child.nodeName) > -1) {
++subTextNodesNum;
}
});
}
return subTextNodesNum;
}
/**
* 遍历模板,检查标签、属性和子节点是否合法
* @param {Object} node - 页面template模块的编译后的树对象
* @param {Object} output
* @param {Object} output.result - 结果收集
* @param {Array} output.log - 日志收集
* @param {Object} previousNode - 前一个节点
* @param {Object} conditionList - 条件列表
* @param {Object} options
* @param {String} options.uxType - 文件类型
* @param {String} options.filePath - 当前执行文件的绝对路径
* @param {Array} options.importNames - 页面引入的自定义组件的name
*/
function traverse(node, output, previousNode, conditionList, options) {
// 检查标签名
_validator.default.checkTagName(node, output, options); // 处理标签属性
// attrs: id/class/style/if/for/event/attr/show/model
const attrs = node.attrs || [];
attrs.forEach(function switchAttr(attr) {
let name = attr.name;
const inMatch = name.match(/^:+/);
if (inMatch) {
name = name.slice(inMatch.length);
}
const value = attr.value; // 获取位置信息
let locationInfo = {
line: 1,
column: 1
};
if (node.__location) {
locationInfo = {
line: node.__location.line,
column: node.__location.col
};
}
switch (name) {
case 'id':
// 保留checkId为兼容原有:新打的RPK包兼容原来的APK平台
_validator.default.checkId(value, output);
_validator.default.checkAttr(name, value, output, node.tagName, locationInfo);
break;
case 'class':
_validator.default.checkClass(value, output);
break;
case 'style':
_validator.default.checkStyle(value, output, locationInfo, options);
break;
case 'if':
if (!node._isroot) {
_validator.default.checkIf(value, output, false, locationInfo, conditionList);
}
break;
case 'is':
_validator.default.checkIs(value, output, locationInfo);
break;
case 'else':
if (!node._isroot) {
if (previousNode && previousNode.__cond__) {
_validator.default.checkElse(previousNode.__cond__, output, locationInfo, conditionList);
}
}
break;
case 'elif':
if (!node._isroot) {
if (previousNode && previousNode.__cond__) {
node.__cond__ = _validator.default.checkElif(value, previousNode.__cond__, output, locationInfo, conditionList);
}
}
break;
case 'for':
if (!node._isroot) {
_validator.default.checkFor(value, output, locationInfo);
}
break;
case 'tree':
_validator.default.checkBuild('tree', output);
break;
case 'model':
_validator.default.checkModel(value, output, node, options);
break;
default:
if (name.match(/^(on|@)/)) {
// 事件以on或@开头
_validator.default.checkEvent(name, value, output);
} else {
// 其余为普通属性
_validator.default.checkAttr(name, value, output, node.tagName, locationInfo, options);
}
}
}); // 处理子节点
const originResult = output.result;
const childNodes = node.childNodes;
if (childNodes && childNodes.length) {
let previous;
let preNode;
const curNodeCondList = []; // 支持span的节点的有效子节点数
const subTextNodesNum = calcSubTextNodesNum(originResult.type, childNodes);
childNodes.forEach(function (child, i) {
if (i > 0) {
preNode = childNodes[i - 1];
if (!preNode.nodeName.match(/^#/)) {
previous = preNode;
if (!previous.__cond__) {
previous.attrs && previous.attrs.forEach(function (attr) {
if (attr.name === 'if' || attr.name === 'elif') {
previous.__cond__ = attr.value;
}
});
}
}
}
const childResult = {};
if (child.nodeName.match(/^#/)) {
// 处理#text节点内容
if (child.nodeName === '#text' && child.value.trim()) {
// 兄弟节点不为自闭合标签且非文本标签非原子组件
if (!preNode || !_validator.default.isSupportedSelfClosing(preNode.nodeName)) {
if (_validator.default.isNotTextContentAtomic(node.tagName)) {
output.log.push({
line: node.__location.line,
column: node.__location.col,
reason: `Warn: 组件 ${node.tagName} 不支持文本内容作为字节点`
});
}
} // 文本节点使用span包裹:
// 1. 父节点支持span, 且有效子节点数不少于2
const useSpanForSupportNode = _validator.default.isSupportSpan(node.tagName) && subTextNodesNum >= 2; // 2. 自定义组件中嵌入文本,替换内部slot节点
const useSpanForCustomSlot = options.importNames && options.importNames.indexOf(node.tagName) > -1;
if (useSpanForSupportNode || useSpanForCustomSlot) {
childResult.type = 'span';
output.result = childResult;
originResult.children = originResult.children || [];
originResult.children.push(childResult);
output.log.push({
line: node.__location.line,
column: node.__location.col,
reason: `WARNING: 文本和span标签并行存在,编译时将文本节点:"${child.value}" 用span包裹(关于span嵌套的使用,请参考官方文档"span嵌套")`
});
_validator.default.checkAttr('value', child.value, output);
} // 如果父节点是option, 处理value和content属性
if (node.tagName === 'option') {
const tempResult = output.result;
output.result = originResult;
if (!originResult.attr.hasOwnProperty('value')) {
_validator.default.checkAttr('value', child.value, output);
}
_validator.default.checkAttr('content', child.value, output);
output.result = tempResult;
return;
} // 父节点支持span,且有且仅有一个有效子节点,或父节点为允许文本内容的原子节点,直接设置value值
if (_validator.default.isSupportSpan(node.tagName) && subTextNodesNum === 1 || _validator.default.isTextContentAtomic(node.tagName)) {
const tempResult = output.result; // 备份当前result
output.result = originResult;
_validator.default.checkAttr('value', child.value, output);
output.result = tempResult;
}
}
return;
}
output.result = childResult;
originResult.children = originResult.children || [];
originResult.children.push(childResult);
traverse(child, output, previous, curNodeCondList, options);
}); // 无孩子
if (originResult.children && originResult.children.length === 0) {
originResult.children = undefined;
}
}
output.result = originResult;
}
/**
* 对模板解析器的配置初始化
* @param {String} code - 代码内容
* @param {Object} options - 解析选项
* @param {String} filePath - code 文件路径
*/
function initParser(code, options, filePath) {
const parser = new _parser.default(options);
const oldAppendElement = parser._appendElement;
const oldInsertElement = parser._insertElement; // 支持自闭合标签
parser._insertElement = function (token) {
const tagName = (token.tagName || '').toLowerCase();
const selfClosing = token.selfClosing; // Fixed 对不允许自闭合的标签进行提示
const selfClosable = _validator.default.isSupportedSelfClosing(tagName);
if (selfClosing && !selfClosable) {
_sharedUtils.colorconsole.error(`${tagName}标签,禁止使用自闭合 ${filePath}@${token.location.line}:${token.location.col}`);
}
if (selfClosable || selfClosing && tagName) {
oldAppendElement.apply(this, arguments);
} else {
oldInsertElement.apply(this, arguments);
}
};
parser.__m = {}; // 对标签进行xml规范检查
function checkToken(token) {
if (!token.tagName) {
return;
}
const tagName = token.tagName.toLowerCase();
const selfClosing = token.selfClosing;
const selfClosable = _validator.default.isSupportedSelfClosing(tagName);
const empty = _validator.default.isEmptyElement(tagName);
if (parser.__m['tagName'] && !parser.__m['selfClosing'] && !empty) {
const pos = String(token.location.line) + ':' + String(token.location.col);
if (!selfClosable || pos !== parser.__m['pos'] && token.type === _tokenizer.default.START_TAG_TOKEN) {
_sharedUtils.colorconsole.warn(`${parser.__m['tagName']}标签要闭合,请遵循XML规范 ${filePath}@${parser.__m['pos']}`);
parser.__m = {};
}
}
if (selfClosable) {
if (token.type === _tokenizer.default.START_TAG_TOKEN && !selfClosing) {
parser.__m['tagName'] = tagName;
parser.__m['selfClosing'] = false;
parser.__m['pos'] = String(token.location.line) + ':' + String(token.location.col);
}
if (token.type === _tokenizer.default.END_TAG_TOKEN && tagName === parser.__m['tagName']) {
parser.__m['selfClosing'] = true;
}
}
}
function checkPlainText(token) {
if (!token.tagName) {
return;
}
if (token.tagName.toLowerCase() === 'plaintext') {
_sharedUtils.colorconsole.error(`${filePath} : 禁止使用 plaintext 标签@${token.location.line}:${token.location.col}`);
}
}
parser._runParsingLoop = function (scriptHandler) {
while (!this.stopped) {
this._setupTokenizerCDATAMode();
const token = this.tokenizer.getNextToken();
checkToken(token);
checkPlainText(token);
if (token.type === _tokenizer.default.HIBERNATION_TOKEN) {
break;
}
if (this.skipNextNewLine) {
this.skipNextNewLine = false;
if (token.type === _tokenizer.default.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') {
if (token.chars.length === 1) {
continue;
}
token.chars = token.chars.substr(1);
}
}
this._processInputToken(token);
if (scriptHandler && this.pendingScript) {
break;
}
}
};
return parser.parseFragment(code);
}
/**
* 解析<template>
* @param {String} source - 源码
* @param {Object} options
* @param {String} options.filePath - 文件绝对路径
* @returns {{jsonTemplate: ({}|result), log: Array}}
*/
function parse(source, options) {
const doc = initParser(source, {
treeAdapter: _parse.default.treeAdapters.default,
locationInfo: true
}, options.filePath);
const output = {
result: {},
log: [],
depFiles: []
}; // 模板为空或解析失败
/* istanbul ignore if */
if (!doc || !doc.childNodes) {
output.log.push({
reason: 'ERROR: <template>解析失败',
line: 1,
column: 1
});
return {
jsonTemplate: output.result,
log: output.log,
depFiles: output.depFiles
};
} // 过滤合法标签名,如果标签名以#开头,则代表节点被注释或者#text, #comment
const rootElements = doc.childNodes.filter(function (child) {
return child.nodeName.charAt(0) !== '#';
}); // 合法节点数目只能等于1,0表示没有根容器
/* istanbul ignore if */
if (rootElements.length === 0) {
output.log.push({
reason: 'ERROR: 没有合法的根节点',
line: 1,
column: 1
});
return {
jsonTemplate: output.result,
log: output.log,
depFiles: output.depFiles
};
} // 合法节点数目只能等于1,否则模板存在多个根容器
if (rootElements.length > 1) {
output.log.push({
reason: 'ERROR: <template>节点里只能有一个根节点',
line: 1,
column: 1
});
return {
jsonTemplate: output.result,
log: output.log,
depFiles: output.depFiles
};
} // 从根目录开始, 遍历树
const current = rootElements[0];
current._isroot = true;
try {
traverse(current, output, null, null, options);
} catch (err) {
if (err.isExpressionError) {
output.log.push({
reason: `ERROR: 表达式解析失败 ${err.message}\n\n> ${err.expression}\n\nat ${options.filePath}`
});
} else {
throw err;
}
} // 检查是否包含ERROR记录
// if (output.log.length > 0) {
// }
// 是否压缩模板属性名
if (_compilationConfig.compileOptionsObject.optimizeTemplateAttr) {
(0, _compress.compressTemplateAttr)(output.result);
} // 返回结果
return {
jsonTemplate: output.result,
log: output.log,
depFiles: output.depFiles
};
}
var _default = {
parse
};
exports.default = _default;
//# sourceMappingURL=index.js.map

@@ -1,2 +0,149 @@

"use strict";const allowedKeywords="Infinity,undefined,NaN,null,isFinite,isNaN,true,false,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,this,require",allowedKeywordsRE=new RegExp("^("+allowedKeywords.replace(/,/g,"\\b|")+"\\b)"),improperKeywords="break,case,class,catch,const,continue,debugger,default,delete,do,else,export,extends,finally,for,function,if,import,in,instanceof,let,return,super,switch,throw,try,var,while,with,yield,enum,await,implements,package,protected,static,interface,private,public",improperKeywordsRE=new RegExp("^("+improperKeywords.replace(/,/g,"\\b|")+"\\b)"),wsRE=/\s/g,newlineRE=/\n/g,saveRE=/[{,]\s*[\w$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g,restoreRE=/"(\d+)"/g,pathTestRE=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/,identRE=/[^\w$.](?:[A-Za-z_$][\w$]*)/g,literalValueRE=/^(?:true|false|null|undefined|Infinity|NaN)$/,saveRegRe=/(\/.+\/[gimy]*)/g,restoreRegRe=/"&&&(\d+)"/g,saved=[],savedReg=[];function save(e,t){const r=saved.length;return saved[r]=t?e.replace(newlineRE,"\\n"):e,'"'+r+'"'}function saveReg(e){const t=savedReg.length;return savedReg[t]=e,'"&&&'+t+'"'}function rewrite(e){const t=e.charAt(0);let r=e.slice(1);return allowedKeywordsRE.test(r)?e:(r=r.indexOf('"')>-1?r.replace(restoreRE,restore):r,t+"this."+r)}function restore(e,t){return saved[t]}function restoreReg(e,t){return savedReg[t]}function compileGetter(e){improperKeywordsRE.test(e)&&console.warn("### App Toolkit ### 不要在表达式中使用保留关键字: "+e),saved.length=0;let t=e.replace(saveRE,save).replace(saveRegRe,saveReg).replace(wsRE,"");return t=(" "+t).replace(identRE,rewrite).replace(restoreRegRe,restoreReg).replace(restoreRE,restore),t.trim()}function parseExpression(e){e=e.trim();return/^\/.+\/[gimy]*$/.test(e)?e:isSimplePath(e)&&e.indexOf("[")<0?"this."+e:compileGetter(e)}function isSimplePath(e){return pathTestRE.test(e)&&!literalValueRE.test(e)&&"Math."!==e.slice(0,5)}module.exports={parseExpression:parseExpression};
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict'; // 表达式中允许的关键字
const allowedKeywords = 'Infinity,undefined,NaN,null,isFinite,isNaN,true,false,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,' + 'this,' + // this
'require'; // 适配 Webpack/Browserify
const allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)'); // 表达式中不能包含的关键字
const improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public';
const improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)');
const wsRE = /\s/g;
const newlineRE = /\n/g;
const saveRE = /[{,]\s*[\w$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g;
const restoreRE = /"(\d+)"/g;
const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/;
const identRE = /[^\w$.](?:[A-Za-z_$][\w$]*)/g;
const literalValueRE = /^(?:true|false|null|undefined|Infinity|NaN)$/;
const saveRegRe = /(\/.+\/[gimy]*)/g;
const restoreRegRe = /"&&&(\d+)"/g;
const saved = [];
const savedReg = [];
/**
* 将目标字符串替换为索引
* @param str
* @param isString
* @returns {string}
*/
function save(str, isString) {
const i = saved.length;
saved[i] = isString ? str.replace(newlineRE, '\\n') // 回车转变为'\\n'
: str;
return '"' + i + '"';
}
/**
* 将正则表达式替换为索引 &&& + i
* @param str
* @returns {string}
*/
function saveReg(str) {
const i = savedReg.length;
savedReg[i] = str; // 使用&&&防止被\w匹配到
return '"' + '&&&' + i + '"';
}
/**
* 将之前save的数字转换为字符串
* @param {String} raw
* @return {String}
*/
function rewrite(raw) {
const c = raw.charAt(0);
let path = raw.slice(1);
if (allowedKeywordsRE.test(path)) {
return raw;
} else {
path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path;
return c + 'this.' + path;
}
}
/**
* 获取save字符串
* @param str
* @param i
* @returns {*}
*/
function restore(str, i) {
return saved[i];
}
/**
* 获取saveReg正则表达式字符串
* @param str
* @param i
* @returns {*}
*/
function restoreReg(str, i) {
return savedReg[i];
}
/**
* 编译表达式, 添加this前缀
* @param {String} exp
* @return {Function}
*/
function compileGetter(exp) {
/* istanbul ignore if */
if (improperKeywordsRE.test(exp)) {
console.warn('### App Toolkit ### 不要在表达式中使用保留关键字: ' + exp);
} // 重置状态
saved.length = 0; // 处理表达式
let body = exp.replace(saveRE, save).replace(saveRegRe, saveReg).replace(wsRE, ''); // 剔除空格/分隔符
// 生成新表达式
body = (' ' + body).replace(identRE, rewrite).replace(restoreRegRe, restoreReg).replace(restoreRE, restore);
return body.trim();
}
/**
* 解析表达式
* @param {String} exp
* @return {String}
*/
function parseExpression(exp) {
exp = exp.trim(); // 处理正则表达式字面量
const regReg = /^\/.+\/[gimy]*$/;
if (regReg.test(exp)) {
return exp;
}
const res = isSimplePath(exp) && exp.indexOf('[') < 0 ? 'this.' + exp // 简单表达式, 直接添加this即可
: compileGetter(exp); // 复杂表达式, 需要遍历处理
return res;
}
/**
* 检查表达式简单路径
* @param {String} exp
* @return {Boolean}
*/
function isSimplePath(exp) {
return pathTestRE.test(exp) && // true/false/null/undefined/Infinity/NaN
!literalValueRE.test(exp) && // Math常量
exp.slice(0, 5) !== 'Math.';
}
module.exports = {
parseExpression: parseExpression
};
//# sourceMappingURL=expression.js.map

@@ -1,2 +0,134 @@

"use strict";const validDivisionCharRE=/[\w).+\-_$\]]/;function parseFilters(e){let i,r,s,t,a,c,l=!1,o=!1,n=!1,f=!1,h=0,d=0,u=0,b=0;for(s=0;s<e.length;s++)if(r=i,i=e.charCodeAt(s),l)39===i&&92!==r&&(l=!1);else if(o)34===i&&92!==r&&(o=!1);else if(n)96===i&&92!==r&&(n=!1);else if(f)47===i&&92!==r&&(f=!1);else if(124!==i||124===e.charCodeAt(s+1)||124===e.charCodeAt(s-1)||h||d||u){switch(i){case 34:o=!0;break;case 39:l=!0;break;case 96:n=!0;break;case 40:u++;break;case 41:u--;break;case 91:d++;break;case 93:d--;break;case 123:h++;break;case 125:h--}if(47===i){let i=s-1;for(;i>=0&&(c=e.charAt(i)," "===c);i--);c&&validDivisionCharRE.test(c)||(f=!0)}}else void 0===t?(b=s+1,t=e.slice(0,s).trim()):k();function k(){(a||(a=[])).push(e.slice(b,s).trim()),b=s+1}if(void 0===t?t=e.slice(0,s).trim():0!==b&&k(),a)for(s=0;s<a.length;s++)t=wrapFilter(t,a[s]);return t}function wrapFilter(e,i){const r=i.indexOf("(");if(r<0)return`this.${i}(${e})`;{const s=i.slice(0,r),t=i.slice(r+1);return`this.${s}(${e}${")"!==t?","+t:t}`}}module.exports=parseFilters;
"use strict";
const validDivisionCharRE = /[\w).+\-_$\]]/;
function parseFilters(exp) {
let inSingle = false;
let inDouble = false;
let inTemplateString = false;
let inRegex = false;
let curly = 0;
let square = 0;
let paren = 0;
let lastFilterIndex = 0;
let c, prev, i, expression, filters, p;
for (i = 0; i < exp.length; i++) {
prev = c;
c = exp.charCodeAt(i);
if (inSingle) {
if (c === 0x27 && prev !== 0x5c) inSingle = false;
} else if (inDouble) {
if (c === 0x22 && prev !== 0x5c) inDouble = false;
} else if (inTemplateString) {
if (c === 0x60 && prev !== 0x5c) inTemplateString = false;
} else if (inRegex) {
if (c === 0x2f && prev !== 0x5c) inRegex = false;
} else if (c === 0x7c && // pipe
exp.charCodeAt(i + 1) !== 0x7c && exp.charCodeAt(i - 1) !== 0x7c && !curly && !square && !paren) {
if (expression === undefined) {
lastFilterIndex = i + 1;
expression = exp.slice(0, i).trim();
} else {
pushFilter();
}
} else {
switch (c) {
case 0x22:
inDouble = true;
break;
// "
case 0x27:
inSingle = true;
break;
// '
case 0x60:
inTemplateString = true;
break;
// `
case 0x28:
paren++;
break;
// (
case 0x29:
paren--;
break;
// )
case 0x5b:
square++;
break;
// [
case 0x5d:
square--;
break;
// ]
case 0x7b:
curly++;
break;
// {
case 0x7d:
curly--;
break;
// }
}
if (c === 0x2f) {
// /
let j = i - 1;
for (; j >= 0; j--) {
p = exp.charAt(j);
if (p !== ' ') break;
}
if (!p || !validDivisionCharRE.test(p)) {
inRegex = true;
}
}
}
}
if (expression === undefined) {
expression = exp.slice(0, i).trim();
} else if (lastFilterIndex !== 0) {
pushFilter();
}
function pushFilter() {
;
(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim());
lastFilterIndex = i + 1;
}
if (filters) {
for (i = 0; i < filters.length; i++) {
expression = wrapFilter(expression, filters[i]);
}
}
return expression;
}
function wrapFilter(exp, filter) {
const i = filter.indexOf('(');
if (i < 0) {
return `this.${filter}(${exp})`;
} else {
const name = filter.slice(0, i);
const args = filter.slice(i + 1);
return `this.${name}(${exp}${args !== ')' ? ',' + args : args}`;
}
}
module.exports = parseFilters;
//# sourceMappingURL=filter-parser.js.map

@@ -1,2 +0,123 @@

"use strict";const tagSource="{{{([\\s\\S]+?)}}}|{{([\\s\\S]+?)}}",tagRE=new RegExp(tagSource,"g"),expRE=new RegExp(tagSource),htmlRE=new RegExp("^{{{[\\s\\S]*}}}$"),sexpRE=new RegExp("^{{{([\\s\\S]+?)}}}$|^{{([\\s\\S]+?)}}$");function parseText(e){if(e=e.replace(/\n/g,""),!tagRE.test(e))return null;const t=[];let r,s,x,n,p,i,E=tagRE.lastIndex=0;for(;r=tagRE.exec(e);)s=r.index,s>E&&t.push({value:e.slice(E,s)}),x=htmlRE.test(r[0]),n=x?r[1]:r[2],p=n.charCodeAt(0),i=42===p,n=i?n.slice(1):n,t.push({tag:!0,value:n.trim(),html:x,oneTime:i}),E=s+r[0].length;return E<e.length&&t.push({value:e.slice(E)}),t}function isExpr(e){return expRE.test(e)}function singleExpr(e){return sexpRE.test(e.trim())}function removeExprffix(e){return singleExpr(e)?e.replace(/^\s*{{/,"").replace(/}}\s*$/,""):e}function addExprffix(e){return singleExpr(e)||(e="{{"+e+"}}"),e}module.exports={parseText:parseText,isExpr:isExpr,singleExpr:singleExpr,addExprffix:addExprffix,removeExprffix:removeExprffix};
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
'use strict';
const tagSource = '{{{([\\s\\S]+?)}}}|{{([\\s\\S]+?)}}'; // {{ }} 格式
const tagRE = new RegExp(tagSource, 'g');
const expRE = new RegExp(tagSource);
const htmlRE = new RegExp('^{{{[\\s\\S]*}}}$');
const sexpRE = new RegExp('^{{{([\\s\\S]+?)}}}$|^{{([\\s\\S]+?)}}$');
/**
* 将模板字符串解析为token数组
*
* @param {String} text
* @return {Array<Object> | null}
* - {String} type
* - {String} value
* - {Boolean} [html]
* - {Boolean} [oneTime]
*/
function parseText(text) {
// 剔除回车
text = text.replace(/\n/g, '');
/* istanbul ignore if */
if (!tagRE.test(text)) {
return null;
}
const tokens = [];
let lastIndex = tagRE.lastIndex = 0;
let match, index, html, value, first, oneTime;
/* eslint-disable no-cond-assign */
while (match = tagRE.exec(text)) {
/* eslint-enable no-cond-assign */
index = match.index; // push text token
if (index > lastIndex) {
tokens.push({
value: text.slice(lastIndex, index)
});
} // tag token
html = htmlRE.test(match[0]);
value = html ? match[1] : match[2];
first = value.charCodeAt(0);
oneTime = first === 42; // *
value = oneTime ? value.slice(1) : value;
tokens.push({
tag: true,
value: value.trim(),
html: html,
oneTime: oneTime
});
lastIndex = index + match[0].length;
}
if (lastIndex < text.length) {
tokens.push({
value: text.slice(lastIndex)
});
}
return tokens;
}
/**
* 检测是否包含表达式
* @param {String} text
* @return {Boolean}
*/
function isExpr(text) {
return expRE.test(text);
}
/**
* 检测是否为单个表达式
* @param text
* @returns {boolean}
*/
function singleExpr(text) {
return sexpRE.test(text.trim());
}
/**
* 移除头尾'{{}}'
*/
function removeExprffix(text) {
if (!singleExpr(text)) {
return text;
}
return text.replace(/^\s*{{/, '').replace(/}}\s*$/, '');
}
/**
* 添加头尾'{{}}'
*/
function addExprffix(text) {
if (!singleExpr(text)) {
text = '{{' + text + '}}';
}
return text;
}
module.exports = {
parseText: parseText,
isExpr: isExpr,
singleExpr: singleExpr,
addExprffix: addExprffix,
removeExprffix: removeExprffix
};
//# sourceMappingURL=text.js.map

@@ -1,2 +0,261 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=model;var _exp=_interopRequireDefault(require("./exp")),_utils=require("../utils"),_validator=_interopRequireDefault(require("./validator.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function model(e,t,n,u){if(!(0,_utils.isValidValue)(e))return!1;e=_exp.default.addExprffix(e);const a=t.result.type,d=getBindingAttr(n,"type")||"";let l=!1;"input"===a&&d.match(/^this\.+/)&&(l=!0),l?genDynamicModel(n,e,t,d):u.importNames&&u.importNames.indexOf(a)>-1?genComponentModel(e,t):"select"===a?genSelectModel(e,t):"input"===a&&"checkbox"===d?genCheckboxModel(n,e,t):"input"===a&&"radio"===d?genRadioModel(n,e,t):"input"===a||"textarea"===a?genDefaultModel(e,t):_validator.default.isReservedTag(a)||genComponentModel(e,t)}function genCheckboxModel(node,value,output){const expValue=(0,_exp.default)(value,!1),valueBinding=getBindingAttr(node,"value",!0)||"null",trueValueBinding=getBindingAttr(node,"true-value",!0)||"true",falseValueBinding=getBindingAttr(node,"false-value",!0)||"false",attrCheckedCode=`\n if (Array.isArray(${expValue})) {\n return ${expValue}.indexOf(${valueBinding}) > -1\n } else {\n return ${expValue}${"true"===trueValueBinding?"":"=== "+trueValueBinding}\n }`,eventChangeCode=`\n const checked = evt.checked;\n if (Array.isArray(${expValue})) {\n const index = ${expValue}.indexOf(${valueBinding})\n if (checked) {\n index < 0 && (${expValue} = ${expValue}.concat([${valueBinding}]))\n } else {\n index > -1 && (${expValue} = ${expValue}.slice(0, index).concat(${expValue}.slice(index + 1)))\n }\n } else {\n ${expValue} = checked ? ${trueValueBinding} : ${falseValueBinding}\n }`;return addAttr(output.result,"checked",eval(`(function() {${attrCheckedCode}})`)),addHandler(output.result,"change",eval(`(function(evt) {${eventChangeCode}})`)),{attr:{checked:attrCheckedCode},events:{change:eventChangeCode}}}function genRadioModel(node,value,output){const valueBinding=getBindingAttr(node,"value",!0)||"null",attrCheckedCode=`return ${(0,_exp.default)(value,!1)} === ${valueBinding}`,eventChangeCode=`${(0,_exp.default)(value,!1)} = ${valueBinding}`;return addAttr(output.result,"checked",eval(`(function() {${attrCheckedCode}})`)),addHandler(output.result,"change",eval(`(function(evt) {${eventChangeCode}})`)),{attr:{checked:attrCheckedCode},events:{change:eventChangeCode}}}function genSelectModel(value,output){addHandler(output.result,"change",eval(`(function(evt) { ${(0,_exp.default)(value,!1)} = evt.newValue})`))}function genDefaultModel(value,output){const eventChangeCode=(0,_exp.default)(value,!1)+" = evt.target.value";return addAttr(output.result,"value",(0,_exp.default)(value)),addHandler(output.result,"change",eval(`(function(evt) {${eventChangeCode}})`)),{events:{change:eventChangeCode}}}function genComponentModel(value,output){const expValue=(0,_exp.default)(value,!1);output.result.model={value:(0,_exp.default)(value),callback:eval(`(function (evt) { ${expValue} = evt.detail})`)}}function genDynamicModel(node,value,output,expType){const checkboxCode=genCheckboxModel(node,value,output),radioCode=genRadioModel(node,value,output),textCode=genDefaultModel(value,output);addAttr(output.result,"checked",eval(`\n (function() { \n if (${expType} === 'checkbox') {\n ${checkboxCode.attr.checked}\n } else if (${expType} === 'radio') {\n ${radioCode.attr.checked}\n }\n })\n `)),addHandler(output.result,"change",eval(`\n (function(evt) {\n if (${expType} === 'checkbox') {\n ${checkboxCode.events.change}\n } else if (${expType} === 'radio') {\n ${radioCode.events.change}\n } else {\n ${textCode.events.change}\n }\n })\n `))}function getBindingAttr(e,t,n=!1){const u=e.attrs||[];for(let e=0;e<u.length;e++){let a=u[e].name;const d=a.match(/^:+/);if(d&&(a=a.slice(d.length)),a===t){const t=(0,_exp.default)(u[e].value,!1);return n&&!t.match(/^this\.+/)?`"${t}"`:t}}}function addAttr(e,t,n){(e.attr||(e.attr={}))[t]=n}function addHandler(e,t,n){(e.events||(e.events={}))[t]=n}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = model;
var _exp = _interopRequireDefault(require("./exp"));
var _utils = require("../utils");
var _validator = _interopRequireDefault(require("./validator.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 构建model属性
* @param {string} value model 绑定的数据名
* @param {object} output 构建的输出结果
* @param {object} node parse5 生成的 node 节点
* @param {object} options 配置信息
*/
function model(value, output, node, options) {
if (!(0, _utils.isValidValue)(value)) {
return false;
}
value = _exp.default.addExprffix(value);
const tag = output.result.type; // 标签名
const type = getBindingAttr(node, 'type') || ''; // type 属性的值
let isDynamicType = false; // input type 为动态时绑定的属性和事件需要额外处理
if (tag === 'input' && type.match(/^this\.+/)) {
isDynamicType = true;
}
/* eslint-disable no-eval */
if (!isDynamicType) {
if (options.importNames && options.importNames.indexOf(tag) > -1) {
genComponentModel(value, output);
} else if (tag === 'select') {
genSelectModel(value, output);
} else if (tag === 'input' && type === 'checkbox') {
genCheckboxModel(node, value, output);
} else if (tag === 'input' && type === 'radio') {
genRadioModel(node, value, output);
} else if (tag === 'input' || tag === 'textarea') {
genDefaultModel(value, output);
} else if (!_validator.default.isReservedTag(tag)) {
genComponentModel(value, output);
}
} else {
genDynamicModel(node, value, output, type);
}
}
/**
* 构建 type 为 checkbox 的 input 组件的 model 属性
* @param {object} node parse5 生成的 node 节点
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
* @returns {object} 构建过程中生成的代码字符串组成的对象,供 genDynamicModel 使用
*/
function genCheckboxModel(node, value, output) {
const expValue = (0, _exp.default)(value, false);
const valueBinding = getBindingAttr(node, 'value', true) || 'null';
const trueValueBinding = getBindingAttr(node, 'true-value', true) || 'true';
const falseValueBinding = getBindingAttr(node, 'false-value', true) || 'false';
const attrCheckedCode = `
if (Array.isArray(${expValue})) {
return ${expValue}.indexOf(${valueBinding}) > -1
} else {
return ${expValue}${trueValueBinding === 'true' ? '' : `=== ${trueValueBinding}`}
}`;
const eventChangeCode = `
const checked = evt.target.checked;
if (Array.isArray(${expValue})) {
const index = ${expValue}.indexOf(${valueBinding})
if (checked) {
index < 0 && (${expValue} = ${expValue}.concat([${valueBinding}]))
} else {
index > -1 && (${expValue} = ${expValue}.slice(0, index).concat(${expValue}.slice(index + 1)))
}
} else {
${expValue} = checked ? ${trueValueBinding} : ${falseValueBinding}
}`;
addAttr(output.result, 'checked', eval(`(function() {${attrCheckedCode}})`));
addHandler(output.result, 'change', eval(`(function(evt) {${eventChangeCode}})`));
return {
attr: {
checked: attrCheckedCode
},
events: {
change: eventChangeCode
}
};
}
/**
* 构建 type 为 radio 时的 input 组件的 model 属性
* @param {object} node parse5 生成的 node 节点
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
* @returns {object} 构建过程中生成的代码字符串组成的对象,供 genDynamicModel 使用
*/
function genRadioModel(node, value, output) {
const valueBinding = getBindingAttr(node, 'value', true) || 'null';
const attrCheckedCode = `return ${(0, _exp.default)(value, false)} === ${valueBinding}`;
const eventChangeCode = `${(0, _exp.default)(value, false)} = ${valueBinding}`;
addAttr(output.result, 'checked', eval(`(function() {${attrCheckedCode}})`));
addHandler(output.result, 'change', eval(`(function(evt) {${eventChangeCode}})`));
return {
attr: {
checked: attrCheckedCode
},
events: {
change: eventChangeCode
}
};
}
/**
* 构建 select 组件的 model
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
*/
function genSelectModel(value, output) {
addHandler(output.result, 'change', eval(`(function(evt) { ${(0, _exp.default)(value, false)} = evt.newValue})`));
}
/**
* 构建 type 为 text 的 input 组件和 textarea 组件的 model 属性
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
* @returns {object} 构建过程中生成的代码字符串组成的对象,供 genDynamicModel 使用
*/
function genDefaultModel(value, output) {
const eventChangeCode = `console.log('888'); ${(0, _exp.default)(value, false)} = evt.target.value`;
addAttr(output.result, 'value', (0, _exp.default)(value));
addHandler(output.result, 'change', eval(`(function(evt) {${eventChangeCode}})`));
return {
events: {
change: eventChangeCode
}
};
}
/**
* 构建自定义组件的 model 属性
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
*/
function genComponentModel(value, output) {
const expValue = (0, _exp.default)(value, false); // 使用一个属性保存配置信息。运行时读取用户的model配置再动态绑定属性和事件
output.result.model = {
value: (0, _exp.default)(value),
callback: eval(`(function (evt) { ${expValue} = evt.detail})`)
};
}
/**
* 构建 type 为动态值时的 input 组件的 model 属性
* @param {object} node parse5 生成的 node 节点
* @param {string} value model 绑定的值
* @param {object} output 构建的输出结果
* @param {string} expType type 属性绑定的值
*/
function genDynamicModel(node, value, output, expType) {
const checkboxCode = genCheckboxModel(node, value, output);
const radioCode = genRadioModel(node, value, output);
const textCode = genDefaultModel(value, output);
addAttr(output.result, 'value', (0, _exp.default)(value));
addAttr(output.result, 'checked', eval(`
(function() {
if (${expType} === 'checkbox') {
${checkboxCode.attr.checked}
} else if (${expType} === 'radio') {
${radioCode.attr.checked}
} else {
return false
}
})
`));
addHandler(output.result, 'change', eval(`
(function(evt) {
if (${expType} === 'checkbox') {
${checkboxCode.events.change}
} else if (${expType} === 'radio') {
${radioCode.events.change}
} else {
${textCode.events.change}
}
})
`));
}
/**
* 获取节点上绑定的属性的值
* @param {object} node parse5 生成的 node 节点
* @param {string} attrName 想要获取值的属性名
* @param {boolean} addQuotation 是否对不是插值的结果(非this.xxx)添加双引号包裹。如name -> "name"
* @returns {string} 属性对应的值,当为插值时返回 this.xxx
*/
function getBindingAttr(node, attrName, addQuotation = false) {
const attrs = node.attrs || [];
for (let i = 0; i < attrs.length; i++) {
let name = attrs[i].name;
const inMatch = name.match(/^:+/);
if (inMatch) {
name = name.slice(inMatch.length);
}
if (name === attrName) {
const expValue = (0, _exp.default)(attrs[i].value, false);
if (addQuotation && !expValue.match(/^this\.+/)) {
return `"${expValue}"`;
}
return expValue;
}
}
}
/**
* 往结果上添加属性
* @param {object} result 输出的结果
* @param {string} name 需要添加的属性名
* @param {function} value 获取值时需要执行的回调
*/
function addAttr(result, name, value) {
;
(result.attr || (result.attr = {}))[name] = value;
}
/**
* 往结果上添加事件
* @param {object} result 输出的结果
* @param {string} name 需要添加的事件名
* @param {function} value 事件触发时需要执行的回调
*/
function addHandler(result, name, value) {
;
(result.events || (result.events = {}))[name] = value;
}
//# sourceMappingURL=model.js.map

@@ -1,2 +0,1899 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _path=_interopRequireDefault(require("path")),_sharedUtils=require("@hap-toolkit/shared-utils"),_exp=_interopRequireDefault(require("./exp")),_style=_interopRequireDefault(require("../style")),_model=_interopRequireDefault(require("./model")),_utils=require("../utils");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const extList=[".mix",".ux",".vue"],richtextType=["mix","ux"],REG_TAG_DATA_ATTR=/^data-\w+/,REG_CLASS_VALUE=/[^{}\s]+((?=\s)|$)|[^{}\s]*\{\{[^]*?\}\}[^\s]*/g,RESERVED_TAGS=Object.keys(_utils.FRAG_TYPE).map(e=>_utils.FRAG_TYPE[e]),tagCommon={events:["click","focus","blur","key","longpress","appear","disappear","swipe","touchstart","touchmove","touchend","touchcancel","resize","animationstart","animationiteration","animationend"],attrs:{id:{},style:{},class:{},disabled:{enum:["false","true"]},if:{def:"true"},elif:{def:"true"},else:{},for:{},tid:{},show:{def:"true"},"aria-label":{},"aria-unfocusable":{enum:["false","true"]},forcedark:{enum:["true","false"]},focusable:{enum:["false","true"]},vtags:{},vindex:{},autofocus:{enum:["false","true"]},descendantfocusability:{enum:["before","after","block"]}},children:["block","slot","component"],parents:["block"]},tagNatives={div:{supportCard:!0,attrs:{enablevideofullscreencontainer:{enum:["false","true"]}}},a:{supportCard:!0,textContent:!0,children:["span"],attrs:{visited:{enum:["false","true"]},href:{}}},text:{supportCard:!0,textContent:!0,children:["a","span"],attrs:{type:{enum:["text","html"]}}},span:{supportCard:!0,textContent:!0,excludeRoot:!0,children:["span"],parents:["text","a","span"],attrs:{extendCommon:!1,id:{},style:{},class:{},for:{},tid:{},if:{def:"true"},elif:{def:"true"},else:{}}},label:{supportCard:!0,textContent:!0,atomic:!0,attrs:{target:{}}},image:{events:["complete","error"],empty:!0,supportCard:!0,selfClosing:!0,alias:["img"],atomic:!0,attrs:{src:{},alt:{},enablenightmode:{enum:["true","false"]},autoplay:{enum:["true","false"]}}},slider:{selfClosing:!0,atomic:!0,attrs:{enabled:{enum:["true","false"]},min:{def:0},max:{def:100},step:{def:1},value:{def:0}},events:["change"]},web:{atomic:!0,events:["pagestart","pagefinish","titlereceive","error","message","progress"],attrs:{src:{},trustedurl:{},allowthirdpartycookies:{enum:["false","true"]},enablenightmode:{enum:["true","false"]},showloadingdialog:{enum:["false","true"]},supportzoom:{enum:["true","false"]}}},list:{children:["list-item"],attrs:{scrollpage:{enum:["false","true"]},focusbehavior:{enum:["aligned","edged"]}},events:["scroll","scrollbottom","scrolltop","scrollend","scrolltouchup","selected"]},"list-item":{excludeRoot:!0,parents:["list"],attrs:{type:{required:!0}}},block:{excludeRoot:!0,supportCard:!0,attrs:{extendCommon:!1,for:{},tid:{},if:{def:"true"},elif:{def:"true"},else:{}}},component:{excludeRoot:!0,attrs:{extendCommon:!1,is:{required:!0},for:{},tid:{},if:{def:"true"},elif:{def:"true"},else:{}}},slot:{excludeRoot:!0,attrs:{name:{def:"default"},extendCommon:!1,content:{}}},input:{supportCard:!0,selfClosing:!0,atomic:!0,empty:!0,attrs:{type:{enum:["text","button","checkbox","radio","email","date","time","number","password","tel","eventbutton"]},autocomplete:{enum:["on","off"]},enterkeytype:{enum:["default","next","go","done","send","search"]},eventtype:{enum:["shortcut"]},model:{},maxlength:{},checked:{enum:["false","true"]},name:{},value:{},placeholder:{}},events:["change","enterkeyclick","selectionchange"]},button:{supportCard:!0,textContent:!0,atomic:!0},refresh:{attrs:{offset:{def:"132px"},refreshing:{enum:["false","true"]},type:{enum:["auto","pulldown"]},"enable-refresh":{enum:["true","false"]}},events:["refresh"]},refresh2:{attrs:{pulldownrefreshing:{enum:["false","true"]},pulluprefreshing:{enum:["false","true"]},animationduration:{def:300},enablepulldown:{enum:["true","false"]},enablepullup:{enum:["false","true"]},reboundable:{enum:["false","true"]},gesture:{enum:["true","false"]},offset:{def:"132px"},refreshing:{enum:["false","true"]},type:{enum:["auto","pulldown"]}},events:["pulldownrefresh","pulluprefresh","refresh"],onceChildren:["refresh-header","refresh-footer"]},"refresh-header":{attrs:{dragrate:{def:300},triggerratio:{def:300},tiggersize:{def:300},maxdragratio:{def:300},maxdragsize:{def:300},refreshdisplayratio:{def:300},refreshdisplaysize:{def:300},spinnerstyle:{enum:["translation","front","behind"]},autorefresh:{enum:["false","true"]},translationwithcontent:{enum:["false","true"]}},events:["move"],parents:["refresh2"]},"refresh-footer":{attrs:{dragrate:{def:300},triggerratio:{def:300},tiggersize:{def:300},maxdragratio:{def:300},maxdragdize:{def:300},refreshdisplayratio:{def:300},refreshdisplaysize:{def:300},spinnerstyle:{enum:["translation","front","behind"]},autorefresh:{enum:["false","true"]},translationwithcontent:{enum:["true","false"]}},events:["move"],parents:["refresh2"]},ad:{attrs:{unitid:{def:""},type:{def:"native"}},events:["load","error"]},swiper:{attrs:{index:{def:0},autoplay:{enum:["false","true"]},interval:{def:3e3},indicator:{enum:["true","false"]},loop:{enum:["true","false"]},duration:{},vertical:{enum:["false","true"]},previousmargin:{def:"0px"},nextmargin:{def:"0px"},enableswipe:{enum:["true","false"]}},events:["change"]},progress:{supportCard:!0,selfClosing:!0,atomic:!0,attrs:{percent:{def:0},type:{enum:["horizontal","circular"]}}},picker:{supportCard:!0,selfClosing:!0,atomic:!0,attrs:{type:{required:!0,enum:["text","date","time","multi-text"]},start:{def:"1970-1-1"},end:{def:"2100-12-31"},range:{},selected:{},value:{}},events:["change","columnchange","cancel"]},switch:{supportCard:!0,selfClosing:!0,atomic:!0,attrs:{checked:{enum:["false","true"]}},events:["change"]},textarea:{supportCard:!0,atomic:!0,textContent:!0,attrs:{placeholder:{},maxlength:{},model:{}},events:["change","selectionchange","linechange"]},video:{empty:!0,attrs:{src:{},muted:{enum:["false","true"]},autoplay:{enum:["false","true"]},controls:{enum:["true","false"]},poster:{},orientation:{enum:["landscape","portrait"]},titlebar:{enum:["true","false"]},title:{},playcount:{}},events:["prepared","start","pause","finish","error","seeking","seeked","timeupdate","fullscreenchange"]},camera:{atomic:!0,selfClosing:!0,attrs:{deviceposition:{enum:["back","front"]},flash:{enum:["auto","on","off","torch"]},framesize:{enum:["low","normal","high"]},autoexposurelock:{enum:["false","true"]},autowhitebalancelock:{enum:["false","true"]}},events:["error","camerainitdone","cameraframe"]},map:{children:["custommarker"],attrs:{latitude:{},longitude:{},coordtype:{},scale:{def:0},rotate:{def:0},markers:{},showmylocation:{enum:["true","false"]},polylines:{},polygons:{},circles:{},controls:{},groundoverlays:{},includepoints:{},heatmaplayer:{},showcompass:{enum:["true","false"]},enableoverlooking:{enum:["false","true"]},enablezoom:{enum:["true","false"]},enablescroll:{enum:["true","false"]},enablerotate:{enum:["true","false"]},showscale:{enum:["false","true"]},showzoom:{enum:["false","true"]}},events:["loaded","regionchange","tap","markertap","callouttap","controltap","poitap"]},custommarker:{parents:["map"],attrs:{custommarkerattr:{}}},canvas:{atomic:!0},stack:{supportCard:!0,events:["fullscreenchange"]},richtext:{textContent:!0,atomic:!0,attrs:{type:{required:!0,enum:["html"].concat(richtextType)}},events:["start","complete"]},tabs:{children:["tab-bar","tab-content"],attrs:{index:{def:0}},events:["change"]},"tab-content":{parents:["tabs"],attrs:{scrollable:{enum:["true","false"]}}},"tab-bar":{parents:["tabs"],attrs:{mode:{enum:["fixed","scrollable"]}}},popup:{supportCard:!0,children:["text"],attrs:{target:{required:!0},placement:{enum:["left","top","right","bottom","topLeft","topRight","bottomLeft","bottomRight"],def:"bottom"}},events:["visibilitychange"]},rating:{supportCard:!0,atomic:!0,attrs:{numstars:{def:"5"},rating:{def:"0"},stepsize:{def:"0.5"},indicator:{enum:["false","true"]}},events:["change"]},select:{supportCard:!0,children:["option"],events:["change"],excludeRoot:!0,attrs:{model:{}}},option:{supportCard:!0,parents:["select"],atomic:!0,textContent:!0,attrs:{selected:{def:!1},value:{}},excludeRoot:!0},marquee:{supportCard:!0,textContent:!0,atomic:!0,attrs:{scrollamount:{def:6},loop:{def:-1},direction:{enum:["left","right"]},value:{def:"6px"}},events:["bounce","finish","start"]},scrollview:{attrs:{"scroll-direction":{enum:["vertical","horizontal","vertical_horizontal"]},"show-scrollbar":{enum:["true","false"]}},events:["scroll","scrollbegindrag","scrollenddrag","scrollstart","scrollstop"]},drawer:{attrs:{enableswipe:{enum:["true","false"]}},events:["change","scroll"]},lottie:{empty:!0,attrs:{source:{required:!0},progress:{},speed:{},loop:{enum:[!0,!1]},autoplay:{enum:[!1,!0]},rendermode:{enum:["AUTOMATIC","HARDWARE","SOFTWARE"]}},events:["complete","error","change"]},"drawer-navigation":{parents:["drawer"],attrs:{direction:{enum:["start","end"]}}},"slide-view":{attrs:{edge:{enum:["right","left"]},enableslide:{enum:["true","false"]},isopen:{enum:["false","true"]},layer:{enum:["above","same"]},buttons:{}},events:["open","close","slide","buttonclick"]},"section-list":{children:["section-group","section-item"],events:["scroll","scrollend","scrolltouchup","scrolltop","scrollbottom"]},"section-group":{children:["section-group","section-item","section-header"],attrs:{expand:{enum:["false","true"]}},events:["change"]},"section-header":{parents:["section-group"]},"section-item":{}},tagReserved={};let tagComponents=[];const tagNativeKeys=Object.keys(tagNatives),tagAliasMap={},tagAttrMap={},tagEnumAttrMap={},tagDefaultAttrMap={},tagRequireAttrMap={},tagAtomics={},tagEmpty={},tagTextContent={},tagChildrenMap={},tagOnceChildrenMap={},tagParentsMap={},tagEventsMap={},tagNotRoot=[];function checkTagName(e,t,a={}){const n=t.result,s=t.log,r=_path.default.extname(a.filePath);let l=e.tagName;const o=e.childNodes||[],i=e.__location||{};tagComponents=a.importNames||[],tagAliasMap[l]&&("img"!==l&&s.push({line:i.line||1,column:i.col||1,reason:"NOTE: 组件名 `"+l+"` 自动转换为 `"+tagAliasMap[l]+"`"}),l=tagAliasMap[l]),n.type=l,RESERVED_TAGS.indexOf(l)>=0?s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件名 `"+l+"` 是系统保留的组件名, 请修改"}):hasTagDefined(l)||s.push({line:i.line||1,column:i.col||1,reason:`WARN: 未识别的标签名 '${l}',如果是自定义组件,请确认已经引入`}),a.uxType===_utils.ENTRY_TYPE.CARD&&tagNatives[l]&&!tagNatives[l].supportCard&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 卡片不支持组件名 `"+l+"`, 请修改"}),e._isroot&&tagNotRoot.hasOwnProperty(l)&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 不能作为根组件"}),tagEmpty.hasOwnProperty(l)&&o.length>0&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 内不能有子节点与文字,请修改"}),tagAtomics.hasOwnProperty(l)&&(tagTextContent.hasOwnProperty(l)?o.length>0&&o.every(e=>"#text"===e.nodeName||(s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 是原子类型,不应该有子节点"}),!1)):(o.length>1||o[0]&&"#text"!==o[0].nodeName)&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 只能有一个文字子节点"})),n.attr=n.attr||{};const u=e.attrs||[],c=[];if(u.forEach((function(e){c.push(e.name.toLowerCase())})),tagDefaultAttrMap[l]&&Object.keys(tagDefaultAttrMap[l]).forEach(t=>{const n=c.indexOf(t);n>=0&&""===u[n].value?(u[n].value=tagDefaultAttrMap[l][t],s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 属性 `"+t+"` 值为空, 默认设置为缺省值 `"+tagDefaultAttrMap[l][t]+"`"})):"slot"===l&&n<0&&(e.attrs.push({name:t,value:tagDefaultAttrMap[l][t]}),_sharedUtils.colorconsole.warn(`标签${l}的属性 \`${t}\` 值为空, 默认设置为缺省值 \`${tagDefaultAttrMap[l][t]}\` ${a.filePath}@${i.line||1},${i.col||1}`))}),e._isroot){const e=["for","if","elif","else","show"];c.forEach(t=>{e.indexOf(t)>=0&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 根节点 `"+l+"` 不能使用属性 `"+t+"`"})})}if(tagRequireAttrMap[l]&&tagRequireAttrMap[l].forEach(e=>{c.indexOf(e)<0&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 没有定义属性 `"+e+"`"})}),tagEnumAttrMap[l]&&Object.keys(tagEnumAttrMap[l]).forEach(e=>{const t=c.indexOf(e);if(t>=0){const a=u[t].value;if(!_exp.default.isExpr(a)){const n=tagEnumAttrMap[l][e];n.indexOf(a)<0&&(u[t].value=n[0],s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 属性 `"+e+"` 的值 `"+a+"`非法, 默认设置为缺省值 `"+n[0]+"`"}))}}}),tagAttrMap[l]&&"component"!==l&&c.forEach(e=>{if(!e.match(/^(on|@)/)){if(r.indexOf(extList[2])>=-1&&/^(:|v-|key|ref|is|slot|slot-scope)/.test(e))return;const t=checkDataAttr(e),a=Object.prototype.hasOwnProperty.call(tagAttrMap[l],e);t||a||s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 不支持属性 `"+e+"`,支持的属性有 ["+Object.keys(tagAttrMap[l]).join(", ")+"]"})}}),tagEventsMap[l]&&"component"!==l){const e=tagEventsMap[l];c.forEach(t=>{if(t.match(/^(on|@)/)){const n=t.replace(/^(on|@)/,""),r=["appear","disappear","swipe"];a.uxType===_utils.ENTRY_TYPE.CARD&&r.indexOf(n.toLowerCase())>-1&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 卡片组件 `"+l+"` 不支持事件 `"+n+"`"}),e.indexOf(n.toLowerCase())<0&&s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 不支持事件 `"+n+"`"})}})}if(o.length>0){const t=new Set,n={};o.forEach((e,r)=>{if(isReservedTag(l)&&isReservedTag(e.nodeName)){const o=tagParentsMap[e.nodeName],u=tagChildrenMap[l],c=tagOnceChildrenMap[l];if("slot"===e.nodeName){let n=e.parentNode;for(;n;){if("slot"===n.tagName){_sharedUtils.colorconsole.warn(`slot标签内不应该嵌套slot ${a.filePath}@${i.line||1},${i.col||1}`);break}n=n.parentNode}const s={};e.attrs.map(e=>{s[e.name]=e.value}),s.hasOwnProperty("name")?/\{\{\s*[\w$]+\s*\}\}/.test(s.name)&&_sharedUtils.colorconsole.warn(`标签${e.nodeName}的name属性暂时不支持动态绑定 ${a.filePath}@${i.line||1},${i.col||1}`):s.name="default",t.has(s.name)?_sharedUtils.colorconsole.warn(`标签${l}内存在name为 \`${s.name}\` 重复slot ${a.filePath}@${i.line||1},${i.col||1}`):t.add(s.name)}if(o&&o.indexOf(l)<0||u&&u.indexOf(e.nodeName)<0)s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 不支持子组件 `"+e.nodeName+"`"});else if(c&&c.indexOf(e.nodeName)>-1){let t;(n[e.nodeName]||0===n[e.nodeName])&&(t=!0),n[e.nodeName]={index:r,duplicated:t}}}});const r=Object.keys(n);r.length&&(r.filter(e=>n[e].duplicated).forEach(e=>{s.push({line:i.line||1,column:i.col||1,reason:`'WARNING: 组件 \`${e}\` 只允许在 \`${l}\` 组件中出现一次`})}),e.childNodes=o.filter((e,t)=>!(n[e.nodeName]&&n[e.nodeName].duplicated&&t!==n[e.nodeName].index)))}isSupportedSelfClosing(l)||!i.startTag||i.endTag||s.push({line:i.line||1,column:i.col||1,reason:"ERROR: 组件 `"+l+"` 缺少闭合标签,请检查"}),"list-item"===l&&hasIfOrFor(o)&&s.push({line:i.line||1,column:i.col||1,reason:"WARN: `list-item` 内部需谨慎使用指令 if 或 for,相同 type 属性的 list-item, DOM 结构必须完全相同`"})}function checkId(e,t){e&&(t.result.id=_exp.default.isExpr(e)?(0,_exp.default)(e):e)}function checkDataAttr(e){return REG_TAG_DATA_ATTR.test(e)}function checkBuild(e,t){e&&(t.result.append="tree"===e?"tree":"single")}function checkClass(className,output){let hasBinding,classList=[];if(className=className.trim(),className){let start=0,end=0;const segs=[],matched=className.match(REG_CLASS_VALUE);if(matched){let e;matched.forEach(t=>{end=className.indexOf(t,start),e=className.slice(start,end),e.length&&segs.push(e),segs.push(t),start+=e.length+t.length})}if(segs.push(className.slice(start)),classList=segs.reduce((e,t)=>_exp.default.isExpr(t)?(hasBinding=!0,e.push((0,_exp.default)(t,!1)),e):e.concat(t.split(/\s+/g).filter(e=>e).map(e=>`'${e}'`)),[]),classList=classList.filter(e=>e.trim()),hasBinding){const code="(function () {return ["+classList.join(", ")+"]})";try{output.result.classList=eval(code)}catch(e){throw e.isExpressionError=!0,e.expression=className,e}}else output.result.classList=classList.map(e=>e.slice(1,-1))}}function checkStyle(e,t,a,n){let s={};const r=t.log;if(e){if(_exp.default.singleExpr(e)){const n=_exp.default.removeExprffix(e);return _exp.default.isExpr(n)?r.push({line:a.line||1,column:a.col||1,reason:"ERROR: style 属性不能嵌套多层{{}}"}):s=(0,_exp.default)(e),void(t.result.style=s)}if(e.split(";").forEach((function(e){let t,l,o,i=e.trim().split(":");i.length>2&&(i[1]=i.slice(1).join(":"),i=i.slice(0,2)),2===i.length&&(t=i[0].trim(),t=(0,_utils.hyphenedToCamelCase)(t),l=i[1].trim(),l=(0,_exp.default)(l),o=_style.default.validateDelaration(t,l,n),l=o.value,l.forEach(e=>{((0,_utils.isValidValue)(e.v)||"function"==typeof e.v)&&(s[e.n]=e.v)}),o.log&&r.push({line:a.line||1,column:a.col||1,reason:o.log.reason}))})),"object"==typeof s)for(let e in s)_style.default.shouldAddToDependency(e,s[e])&&t.depFiles.push(s[e]);t.result.style=s}}function checkIs(e,t,a){const n=t.log;e?(e=_exp.default.addExprffix(e),t.result.is=(0,_exp.default)(e)):n.push({line:a.line||1,column:a.col||1,reason:"WARNING: is 属性为空"})}function checkIf(e,t,a,n,s){const r=t.log;e?(e=_exp.default.addExprffix(e),a?e="{{"+buildConditionExp(s)+"}}":(s.length>0&&(s.length=0),s.push(""+e.substr(2,e.length-4))),t.result.shown=(0,_exp.default)(e)):a||r.push({line:n.line||1,column:n.col||1,reason:"WARNING: if 属性为空"})}function checkElse(e,t,a,n){checkIf(e,t,!0,a,n),n.length=0}function checkElif(e,t,a,n,s){const r=a.log;let l=t;return e?(e=_exp.default.addExprffix(e),t=_exp.default.addExprffix(t),l="{{("+e.substr(2,e.length-4)+") && "+buildConditionExp(s)+"}}",a.result.shown=(0,_exp.default)(l),s.push(""+e.substr(2,e.length-4))):r.push({line:n.line||1,column:n.col||1,reason:"WARNING: Elif 属性为空"}),l}function checkFor(e,t,a){const n=t.log;if(e){let a,n;const s=(e=_exp.default.removeExprffix(e)).match(/(.*) (?:in) (.*)/);if(s){const t=s[1].match(/\((.*),(.*)\)/);t?(a=t[1].trim(),n=t[2].trim()):n=s[1].trim(),e=s[2]}let r;e="{{"+e+"}}",a||n?(r={exp:(0,_exp.default)(e)},a&&(r.key=a),n&&(r.value=n)):r=(0,_exp.default)(e),t.result.repeat=r}else n.push({line:a.line||1,column:a.col||1,reason:"WARNING: for 属性为空"})}function checkEvent(name,value,output){const originValue=value,eventName=name.replace(/^(on|@)/,"");if(eventName&&value){value=_exp.default.removeExprffix(value);const paramsMatch=value.match(/(.*)\((.*)\)/);if(paramsMatch){const funcName=paramsMatch[1];let params=paramsMatch[2];params?(params=params.split(/\s*,\s*/),-1===params.indexOf("evt")&&(params[params.length]="evt")):params=["evt"],value="{{"+funcName+"("+params.join(",")+")}}";try{value=eval("(function (evt) { return "+(0,_exp.default)(value,!1).replace("this.evt","evt")+"})")}catch(e){throw e.isExpressionError=!0,e.expression=originValue,e}}output.result.events=output.result.events||{},output.result.events[eventName]=value}}function checkAttr(e,t,a,n,s,r){if(e&&(0,_utils.isValidValue)(t)){if(shouldConvertPath(e,t,n)){(0,_utils.fileExists)(t,r.filePath)||a.log.push({line:s.line,column:s.column,reason:"WARNING: "+n+" 属性 "+e+" 的值 "+t+" 下不存在对应的文件资源"}),t=(0,_utils.resolvePath)(t,r.filePath),a.depFiles.push(t)}a.result.attr=a.result.attr||{},a.result.attr[(0,_utils.hyphenedToCamelCase)(e)]=(0,_exp.default)(t),"value"===e&&"text"===n&&a.log.push({line:s.line,column:s.column,reason:"WARNING: `value` 应该写在<text>标签中"})}}function shouldConvertPath(e,t,a){return!("alt"===e&&"blank"===t)&&!(!(["src","alt"].includes(e)&&t&&["img","video"].indexOf(a)>-1)||/^(data:|http|{{)/.test(t))}function isReservedTag(e){return tagReserved.hasOwnProperty(e)}function isTextContentAtomic(e){return tagTextContent.hasOwnProperty(e)&&tagAtomics.hasOwnProperty(e)}function isNotTextContentAtomic(e){return!tagTextContent.hasOwnProperty(e)&&!tagAtomics.hasOwnProperty(e)}function isSupportSpan(e){if(e&&"string"==typeof e)return tagChildrenMap[e]&&tagChildrenMap[e].indexOf("span")>-1}function getTagChildren(e){if(e&&"string"==typeof e)return tagChildrenMap[e]||[]}function isSupportedSelfClosing(e){if(e&&"string"==typeof e)return e=tagAliasMap[e]||e,tagNatives[e]&&!!tagNatives[e].selfClosing}function hasTagDefined(e){return tagNativeKeys.indexOf(e)>-1||tagComponents.indexOf(e)>-1}function isEmptyElement(e){return e=tagAliasMap[e]||e,tagNatives[e]&&!0===tagNatives[e].empty}function buildConditionExp(e){return e.map(e=>`!(${e})`).join(" && ")}function hasIfOrFor(e){let t=!1;return e.find(e=>(e.attrs||[]).findIndex(e=>["for","if"].indexOf(e.name)>-1)>-1?(t=!0,t):Array.isArray(e.childNodes)?(t=hasIfOrFor(e.childNodes),t):void 0),t}tagNativeKeys.forEach((function(e){tagReserved[e]=!0;const t=tagNatives[e];t.atomic&&(tagAtomics[e]=!0),t.textContent&&(tagTextContent[e]=!0),t.empty&&(tagEmpty[e]=!0),t.alias&&t.alias.length&&t.alias.forEach((function(t){tagAliasMap[t]=e})),!0===t.excludeRoot&&(tagNotRoot[e]=!0);let a=(0,_utils.extend)({},t.attrs);const n={},s={},r=[];t.attrs&&!1===t.attrs.extendCommon||(a=(0,_utils.extend)(a,tagCommon.attrs)),"extendCommon"in a&&delete a.extendCommon,Object.keys(a).forEach((function(e){const t=a[e];t.enum&&t.enum.length>0&&(n[e]=t.enum,s[e]=t.enum[0]),t.def&&(s[e]=t.def),!0===t.required&&r.push(e)})),tagAttrMap[e]=a,tagEnumAttrMap[e]=n,tagDefaultAttrMap[e]=s,tagRequireAttrMap[e]=r,tagChildrenMap[e]=t.children?(0,_utils.merge)([],tagCommon.children,t.children):null,tagOnceChildrenMap[e]=t.onceChildren?(0,_utils.merge)([],t.onceChildren):null,tagParentsMap[e]=t.parents?(0,_utils.merge)([],tagCommon.parents,t.parents):null,tagEventsMap[e]=(0,_utils.merge)([],tagCommon.events,t.events)}));var _default={checkTagName:checkTagName,checkId:checkId,checkClass:checkClass,checkStyle:checkStyle,checkIs:checkIs,checkIf:checkIf,checkElse:checkElse,checkElif:checkElif,checkFor:checkFor,checkEvent:checkEvent,checkAttr:checkAttr,checkBuild:checkBuild,checkModel:_model.default,isReservedTag:isReservedTag,isTextContentAtomic:isTextContentAtomic,isSupportSpan:isSupportSpan,getTagChildren:getTagChildren,isSupportedSelfClosing:isSupportedSelfClosing,isEmptyElement:isEmptyElement,isNotTextContentAtomic:isNotTextContentAtomic};exports.default=_default;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _path = _interopRequireDefault(require("path"));
var _sharedUtils = require("@hap-toolkit/shared-utils");
var _exp = _interopRequireDefault(require("./exp"));
var _style = _interopRequireDefault(require("../style"));
var _model = _interopRequireDefault(require("./model"));
var _utils = require("../utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
// 支持的后缀名列表
const extList = ['.mix', '.ux', '.vue']; // 富文本支持的类型
const richtextType = ['mix', 'ux']; // 标签拥有data属性
const REG_TAG_DATA_ATTR = /^data-\w+/; // class属性
const REG_CLASS_VALUE = /[^{}\s]+((?=\s)|$)|[^{}\s]*\{\{[^]*?\}\}[^\s]*/g; // 保留标签名
const RESERVED_TAGS = Object.keys(_utils.FRAG_TYPE).map(k => _utils.FRAG_TYPE[k]); // 公共属性定义
const tagCommon = {
events: ['click', 'focus', 'blur', 'key', 'longpress', 'appear', 'disappear', 'swipe', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'resize', 'animationstart', 'animationiteration', 'animationend'],
attrs: {
id: {},
style: {},
class: {},
disabled: {
enum: ['false', 'true']
},
if: {
def: 'true' // 缺省值
},
elif: {
def: 'true'
},
else: {},
for: {},
tid: {},
show: {
def: 'true'
},
'aria-label': {},
'aria-unfocusable': {
enum: ['false', 'true']
},
forcedark: {
enum: ['true', 'false']
},
focusable: {
enum: ['false', 'true']
},
vtags: {},
vindex: {},
autofocus: {
enum: ['false', 'true']
},
descendantfocusability: {
enum: ['before', 'after', 'block']
}
},
children: ['block', 'slot', 'component'],
// 通用控制组件
parents: ['block'] // 通用父组件
}; // 原生标签定义
// atomic: 不允许有子节点,但允许#text内容
// empty: 是否为空元素——不可能存在子节点(内嵌的元素或文本)
// supportCard:是否支持卡片,默认不支持卡片
const tagNatives = {
div: {
supportCard: true,
attrs: {
enablevideofullscreencontainer: {
enum: ['false', 'true']
}
}
},
a: {
supportCard: true,
textContent: true,
children: ['span'],
attrs: {
visited: {
enum: ['false', 'true']
},
href: {}
}
},
text: {
supportCard: true,
textContent: true,
children: ['a', 'span'],
attrs: {
type: {
enum: ['text', 'html']
}
}
},
span: {
supportCard: true,
textContent: true,
excludeRoot: true,
children: ['span'],
parents: ['text', 'a', 'span'],
attrs: {
extendCommon: false,
// 不支持通用属性
id: {},
style: {},
class: {},
for: {},
tid: {},
if: {
def: 'true'
},
elif: {
def: 'true'
},
else: {}
}
},
label: {
supportCard: true,
textContent: true,
atomic: true,
attrs: {
target: {}
}
},
image: {
events: ['complete', 'error'],
empty: true,
supportCard: true,
selfClosing: true,
alias: ['img'],
atomic: true,
attrs: {
src: {},
alt: {},
enablenightmode: {
enum: ['true', 'false']
},
autoplay: {
enum: ['true', 'false']
}
}
},
slider: {
selfClosing: true,
atomic: true,
attrs: {
enabled: {
enum: ['true', 'false']
},
min: {
def: 0
},
max: {
def: 100
},
step: {
def: 1
},
value: {
def: 0
}
},
events: ['change']
},
web: {
atomic: true,
events: ['pagestart', 'pagefinish', 'titlereceive', 'error', 'message', 'progress'],
attrs: {
src: {},
trustedurl: {},
allowthirdpartycookies: {
enum: ['false', 'true']
},
enablenightmode: {
enum: ['true', 'false']
},
showloadingdialog: {
enum: ['false', 'true']
},
supportzoom: {
enum: ['true', 'false']
}
}
},
list: {
children: ['list-item'],
attrs: {
scrollpage: {
enum: ['false', 'true']
},
focusbehavior: {
enum: ['aligned', 'edged']
}
},
events: ['scroll', 'scrollbottom', 'scrolltop', 'scrollend', 'scrolltouchup', 'selected']
},
'list-item': {
excludeRoot: true,
parents: ['list'],
attrs: {
type: {
required: true
}
}
},
block: {
excludeRoot: true,
supportCard: true,
attrs: {
extendCommon: false,
// 不支持通用属性
for: {},
tid: {},
if: {
def: 'true'
},
elif: {
def: 'true'
},
else: {}
}
},
component: {
excludeRoot: true,
attrs: {
extendCommon: false,
// 不支持通用属性
is: {
required: true
},
for: {},
tid: {},
if: {
def: 'true'
},
elif: {
def: 'true'
},
else: {}
}
},
slot: {
excludeRoot: true,
attrs: {
name: {
def: 'default'
},
extendCommon: false,
// 不支持通用属性
content: {}
}
},
input: {
supportCard: true,
selfClosing: true,
atomic: true,
empty: true,
attrs: {
type: {
enum: ['text', 'button', 'checkbox', 'radio', 'email', 'date', 'time', 'number', 'password', 'tel', 'eventbutton']
},
autocomplete: {
enum: ['on', 'off']
},
enterkeytype: {
enum: ['default', 'next', 'go', 'done', 'send', 'search']
},
eventtype: {
enum: ['shortcut']
},
model: {},
'true-value': {},
'false-value': {},
maxlength: {},
checked: {
enum: ['false', 'true']
},
name: {},
value: {},
placeholder: {}
},
events: ['change', 'enterkeyclick', 'selectionchange']
},
button: {
supportCard: true,
textContent: true,
atomic: true
},
refresh: {
attrs: {
offset: {
def: '132px'
},
refreshing: {
enum: ['false', 'true']
},
type: {
enum: ['auto', 'pulldown']
},
'enable-refresh': {
enum: ['true', 'false']
}
},
events: ['refresh']
},
refresh2: {
attrs: {
pulldownrefreshing: {
enum: ['false', 'true']
},
pulluprefreshing: {
enum: ['false', 'true']
},
animationduration: {
def: 300
},
enablepulldown: {
enum: ['true', 'false']
},
enablepullup: {
enum: ['false', 'true']
},
reboundable: {
enum: ['false', 'true']
},
gesture: {
enum: ['true', 'false']
},
offset: {
def: '132px'
},
refreshing: {
enum: ['false', 'true']
},
type: {
enum: ['auto', 'pulldown']
}
},
events: ['pulldownrefresh', 'pulluprefresh', 'refresh'],
// 对于只允许出现一次的子节点,保留最后一个
onceChildren: ['refresh-header', 'refresh-footer']
},
'refresh-header': {
attrs: {
dragrate: {
def: 300
},
triggerratio: {
def: 300
},
tiggersize: {
def: 300
},
maxdragratio: {
def: 300
},
maxdragsize: {
def: 300
},
refreshdisplayratio: {
def: 300
},
refreshdisplaysize: {
def: 300
},
spinnerstyle: {
enum: ['translation', 'front', 'behind']
},
autorefresh: {
enum: ['false', 'true']
},
translationwithcontent: {
enum: ['false', 'true']
}
},
events: ['move'],
parents: ['refresh2']
},
'refresh-footer': {
attrs: {
dragrate: {
def: 300
},
triggerratio: {
def: 300
},
tiggersize: {
def: 300
},
maxdragratio: {
def: 300
},
maxdragdize: {
def: 300
},
refreshdisplayratio: {
def: 300
},
refreshdisplaysize: {
def: 300
},
spinnerstyle: {
enum: ['translation', 'front', 'behind']
},
autorefresh: {
enum: ['false', 'true']
},
translationwithcontent: {
enum: ['true', 'false']
}
},
events: ['move'],
parents: ['refresh2']
},
ad: {
attrs: {
unitid: {
def: ''
},
type: {
def: 'native'
}
},
events: ['load', 'error']
},
swiper: {
attrs: {
index: {
def: 0
},
autoplay: {
enum: ['false', 'true']
},
interval: {
def: 3000
},
indicator: {
enum: ['true', 'false']
},
loop: {
enum: ['true', 'false']
},
duration: {},
vertical: {
enum: ['false', 'true']
},
previousmargin: {
def: '0px'
},
nextmargin: {
def: '0px'
},
enableswipe: {
enum: ['true', 'false']
}
},
events: ['change']
},
progress: {
supportCard: true,
selfClosing: true,
atomic: true,
attrs: {
percent: {
def: 0
},
type: {
enum: ['horizontal', 'circular']
}
}
},
picker: {
supportCard: true,
selfClosing: true,
atomic: true,
attrs: {
type: {
required: true,
enum: ['text', 'date', 'time', 'multi-text']
},
start: {
def: '1970-1-1'
},
end: {
def: '2100-12-31'
},
range: {},
selected: {},
value: {}
},
events: ['change', 'columnchange', 'cancel']
},
switch: {
supportCard: true,
selfClosing: true,
atomic: true,
attrs: {
checked: {
enum: ['false', 'true']
}
},
events: ['change']
},
textarea: {
supportCard: true,
atomic: true,
textContent: true,
attrs: {
placeholder: {},
maxlength: {},
model: {}
},
events: ['change', 'selectionchange', 'linechange']
},
video: {
empty: true,
attrs: {
src: {},
muted: {
enum: ['false', 'true']
},
autoplay: {
enum: ['false', 'true']
},
controls: {
enum: ['true', 'false']
},
poster: {},
orientation: {
enum: ['landscape', 'portrait']
},
titlebar: {
enum: ['true', 'false']
},
title: {},
playcount: {}
},
events: ['prepared', 'start', 'pause', 'finish', 'error', 'seeking', 'seeked', 'timeupdate', 'fullscreenchange']
},
camera: {
atomic: true,
selfClosing: true,
attrs: {
deviceposition: {
enum: ['back', 'front']
},
flash: {
enum: ['auto', 'on', 'off', 'torch']
},
framesize: {
enum: ['low', 'normal', 'high']
},
autoexposurelock: {
enum: ['false', 'true']
},
autowhitebalancelock: {
enum: ['false', 'true']
}
},
events: ['error', 'camerainitdone', 'cameraframe']
},
map: {
children: ['custommarker'],
attrs: {
latitude: {},
longitude: {},
coordtype: {},
scale: {
def: 0
},
rotate: {
def: 0
},
markers: {},
showmylocation: {
enum: ['true', 'false']
},
polylines: {},
polygons: {},
circles: {},
controls: {},
groundoverlays: {},
includepoints: {},
heatmaplayer: {},
showcompass: {
enum: ['true', 'false']
},
enableoverlooking: {
enum: ['false', 'true']
},
enablezoom: {
enum: ['true', 'false']
},
enablescroll: {
enum: ['true', 'false']
},
enablerotate: {
enum: ['true', 'false']
},
showscale: {
enum: ['false', 'true']
},
showzoom: {
enum: ['false', 'true']
}
},
events: ['loaded', 'regionchange', 'tap', 'markertap', 'callouttap', 'controltap', 'poitap']
},
custommarker: {
parents: ['map'],
attrs: {
custommarkerattr: {}
}
},
canvas: {
atomic: true
},
stack: {
supportCard: true,
events: ['fullscreenchange']
},
richtext: {
textContent: true,
atomic: true,
attrs: {
type: {
required: true,
enum: ['html'].concat(richtextType)
}
},
events: ['start', 'complete']
},
tabs: {
children: ['tab-bar', 'tab-content'],
attrs: {
index: {
def: 0
}
},
events: ['change']
},
'tab-content': {
parents: ['tabs'],
attrs: {
scrollable: {
enum: ['true', 'false']
}
}
},
'tab-bar': {
parents: ['tabs'],
attrs: {
mode: {
enum: ['fixed', 'scrollable']
}
}
},
popup: {
supportCard: true,
children: ['text'],
attrs: {
target: {
required: true
},
placement: {
enum: ['left', 'top', 'right', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'],
def: 'bottom'
}
},
events: ['visibilitychange']
},
rating: {
supportCard: true,
atomic: true,
attrs: {
numstars: {
def: '5'
},
rating: {
def: '0'
},
stepsize: {
def: '0.5'
},
indicator: {
enum: ['false', 'true']
}
},
events: ['change']
},
select: {
supportCard: true,
children: ['option'],
events: ['change'],
excludeRoot: true,
attrs: {
model: {}
}
},
option: {
supportCard: true,
parents: ['select'],
atomic: true,
textContent: true,
attrs: {
selected: {
def: false
},
value: {}
},
excludeRoot: true
},
marquee: {
supportCard: true,
textContent: true,
atomic: true,
attrs: {
scrollamount: {
def: 6
},
loop: {
def: -1
},
direction: {
enum: ['left', 'right']
},
value: {
def: '6px'
}
},
events: ['bounce', 'finish', 'start']
},
scrollview: {
attrs: {
'scroll-direction': {
enum: ['vertical', 'horizontal', 'vertical_horizontal']
},
'show-scrollbar': {
enum: ['true', 'false']
}
},
events: ['scroll', 'scrollbegindrag', 'scrollenddrag', 'scrollstart', 'scrollstop']
},
drawer: {
attrs: {
enableswipe: {
enum: ['true', 'false']
}
},
events: ['change', 'scroll']
},
lottie: {
empty: true,
attrs: {
source: {
required: true
},
progress: {},
speed: {},
loop: {
enum: [true, false]
},
autoplay: {
enum: [false, true]
},
rendermode: {
enum: [`AUTOMATIC`, `HARDWARE`, `SOFTWARE`]
}
},
events: ['complete', 'error', 'change']
},
'drawer-navigation': {
parents: ['drawer'],
attrs: {
direction: {
enum: ['start', 'end']
}
}
},
'slide-view': {
attrs: {
edge: {
enum: ['right', 'left']
},
enableslide: {
enum: ['true', 'false']
},
isopen: {
enum: ['false', 'true']
},
layer: {
enum: ['above', 'same']
},
buttons: {}
},
events: ['open', 'close', 'slide', 'buttonclick']
},
'section-list': {
children: ['section-group', 'section-item'],
events: ['scroll', 'scrollend', 'scrolltouchup', 'scrolltop', 'scrollbottom']
},
'section-group': {
children: ['section-group', 'section-item', 'section-header'],
attrs: {
expand: {
enum: ['false', 'true']
}
},
events: ['change']
},
'section-header': {
parents: ['section-group']
},
'section-item': {}
}; // 保留标签
const tagReserved = {}; // 自定义组件标签名
let tagComponents = []; // 支持的组件列表
const tagNativeKeys = Object.keys(tagNatives); // 标签别名
const tagAliasMap = {}; // 标签属性
const tagAttrMap = {};
const tagEnumAttrMap = {};
const tagDefaultAttrMap = {};
const tagRequireAttrMap = {}; // 原子标签(允许子组件有text子组件)
const tagAtomics = {}; // 空元素标签
// 不允许有子组件,也不允许标签间嵌套文字
// 因为没有子组件了,所以他们也允许写成自闭合标签
const tagEmpty = {}; // 带文本内容的标签
const tagTextContent = {}; // 允许子节点
const tagChildrenMap = {}; // 只允许出现一次的子节点
const tagOnceChildrenMap = {}; // 允许父节点
const tagParentsMap = {}; // 允许事件
const tagEventsMap = {}; // 不能作为根节点
const tagNotRoot = [];
(function initRules() {
tagNativeKeys.forEach(function (tagName) {
tagReserved[tagName] = true;
const tagInfo = tagNatives[tagName];
if (tagInfo.atomic) {
tagAtomics[tagName] = true;
}
if (tagInfo.textContent) {
tagTextContent[tagName] = true;
}
if (tagInfo.empty) {
tagEmpty[tagName] = true;
}
if (tagInfo.alias && tagInfo.alias.length) {
tagInfo.alias.forEach(function (n) {
tagAliasMap[n] = tagName;
});
}
if (tagInfo.excludeRoot === true) {
tagNotRoot[tagName] = true;
} // 处理属性
let attrsMap = (0, _utils.extend)({}, tagInfo.attrs);
const enumAttr = {};
const defaultAttr = {};
const requireAttr = []; // 合并标签的通用属性
if (!(tagInfo.attrs && tagInfo.attrs.extendCommon === false)) {
attrsMap = (0, _utils.extend)(attrsMap, tagCommon.attrs);
} // 从属性中去除通用属性标志位
if ('extendCommon' in attrsMap) {
delete attrsMap.extendCommon;
}
Object.keys(attrsMap).forEach(function (name) {
const attr = attrsMap[name];
if (attr.enum && attr.enum.length > 0) {
enumAttr[name] = attr.enum;
defaultAttr[name] = attr.enum[0];
}
if (attr.def) {
defaultAttr[name] = attr.def;
}
if (attr.required === true) {
requireAttr.push(name);
}
});
tagAttrMap[tagName] = attrsMap;
tagEnumAttrMap[tagName] = enumAttr;
tagDefaultAttrMap[tagName] = defaultAttr;
tagRequireAttrMap[tagName] = requireAttr; // 处理子节点
tagChildrenMap[tagName] = tagInfo.children ? (0, _utils.merge)([], tagCommon.children, tagInfo.children) : null;
tagOnceChildrenMap[tagName] = tagInfo.onceChildren ? (0, _utils.merge)([], tagInfo.onceChildren) : null; // 处理父节点
tagParentsMap[tagName] = tagInfo.parents ? (0, _utils.merge)([], tagCommon.parents, tagInfo.parents) : null; // 处理事件
tagEventsMap[tagName] = (0, _utils.merge)([], tagCommon.events, tagInfo.events);
});
})();
/**
* 检查标签名
* @param {Object} node - 页面template模块的编译后的树对象
* @param {Object} output
* @param {Object} output.result - 结果收集
* @param {Array} output.log - 日志收集
* @param {Object} options
* @param {String} options.uxType - 文件类型
* @param {String} options.filePath - 当前执行文件的绝对路径
* @param {Array} options.importNames - 页面引入的自定义组件的name
*/
function checkTagName(node, output, options = {}) {
const result = output.result;
const log = output.log;
const type = _path.default.extname(options.filePath);
let tagName = node.tagName;
const childNodes = node.childNodes || [];
const location = node.__location || {}; // 获取自定义组件名
tagComponents = options.importNames || []; // 处理别名
if (tagAliasMap[tagName]) {
if (tagName !== 'img') {
// `parse5`自动将image转换为img
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'NOTE: 组件名 `' + tagName + '` 自动转换为 `' + tagAliasMap[tagName] + '`'
});
}
tagName = tagAliasMap[tagName];
} // 如果是组件标签
result.type = tagName;
if (RESERVED_TAGS.indexOf(tagName) >= 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件名 `' + tagName + '` 是系统保留的组件名, 请修改'
});
} else if (!hasTagDefined(tagName)) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: `WARN: 未识别的标签名 '${tagName}',如果是自定义组件,请确认已经引入`
});
}
if (options.uxType === _utils.ENTRY_TYPE.CARD && tagNatives[tagName] && !tagNatives[tagName].supportCard) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 卡片不支持组件名 `' + tagName + '`, 请修改'
});
} // 检测根组件合法性
if (node._isroot && tagNotRoot.hasOwnProperty(tagName)) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 不能作为根组件'
});
} // 空组件标签,不允许有子元素
if (tagEmpty.hasOwnProperty(tagName)) {
if (childNodes.length > 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 内不能有子节点与文字,请修改'
});
}
} // 如果是原子标签
if (tagAtomics.hasOwnProperty(tagName)) {
// 如果没有文本内容
if (tagTextContent.hasOwnProperty(tagName)) {
// 非文本节点
if (childNodes.length > 0) {
// 不处理#text子节点
childNodes.every(child => {
if (child.nodeName !== '#text') {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 是原子类型,不应该有子节点'
});
return false;
}
return true;
});
}
} else {
// 文本节点
if (childNodes.length > 1 || childNodes[0] && childNodes[0].nodeName !== '#text') {
// 只能是文本内容,否则报错
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 只能有一个文字子节点'
});
}
}
} // 检测非文本组件是否包含文本
// attr为空时,设置返回的attr的默认值
result.attr = result.attr || {}; // 处理必需属性
const attrs = node.attrs || []; // 标签的属性列表
const attrlist = []; // 转换为小写
attrs.forEach(function (item) {
attrlist.push(item.name.toLowerCase());
}); // 处理缺省属性(如果值为空, 则设置为缺省值)
if (tagDefaultAttrMap[tagName]) {
Object.keys(tagDefaultAttrMap[tagName]).forEach(name => {
const index = attrlist.indexOf(name);
if (index >= 0 && attrs[index].value === '') {
attrs[index].value = tagDefaultAttrMap[tagName][name];
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 属性 `' + name + '` 值为空, 默认设置为缺省值 `' + tagDefaultAttrMap[tagName][name] + '`'
});
} else if (tagName === 'slot' && index < 0) {
node.attrs.push({
name,
value: tagDefaultAttrMap[tagName][name]
});
_sharedUtils.colorconsole.warn(`标签${tagName}的属性 \`${name}\` 值为空, 默认设置为缺省值 \`${tagDefaultAttrMap[tagName][name]}\` ${options.filePath}@${location.line || 1},${location.col || 1}`);
}
});
} // 检查根节点属性合法性
if (node._isroot) {
const rootExcludeAttrList = ['for', 'if', 'elif', 'else', 'show'];
attrlist.forEach(name => {
if (rootExcludeAttrList.indexOf(name) >= 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 根节点 `' + tagName + '` 不能使用属性 `' + name + '`'
});
}
});
} // 检查必需的属性
if (tagRequireAttrMap[tagName]) {
tagRequireAttrMap[tagName].forEach(name => {
if (attrlist.indexOf(name) < 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 没有定义属性 `' + name + '`'
});
}
});
} // 检查属性枚举值的合法性
if (tagEnumAttrMap[tagName]) {
Object.keys(tagEnumAttrMap[tagName]).forEach(name => {
const index = attrlist.indexOf(name);
if (index >= 0) {
const value = attrs[index].value;
if (!_exp.default.isExpr(value)) {
// 如果是表达式,则跳过检测
const enums = tagEnumAttrMap[tagName][name];
if (enums.indexOf(value) < 0) {
attrs[index].value = enums[0];
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 属性 `' + name + '` 的值 `' + value + '`非法, 默认设置为缺省值 `' + enums[0] + '`'
});
}
}
}
});
} // 检查属性的合法性
if (tagAttrMap[tagName] && tagName !== 'component') {
attrlist.forEach(item => {
if (!item.match(/^(on|@)/)) {
// 对vue的属性检测适配
if (type.indexOf(extList[2]) >= -1) {
if (/^(:|v-|key|ref|is|slot|slot-scope)/.test(item)) return;
}
const isDataAttr = checkDataAttr(item);
const isTagAttr = Object.prototype.hasOwnProperty.call(tagAttrMap[tagName], item); // 避开事件和自定义属性(data-*),校验属性值
if (!(isDataAttr || isTagAttr)) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 不支持属性 `' + item + '`,支持的属性有 [' + Object.keys(tagAttrMap[tagName]).join(', ') + ']'
});
}
}
});
} // 检查事件合法性
if (tagEventsMap[tagName] && tagName !== 'component') {
const events = tagEventsMap[tagName];
attrlist.forEach(name => {
if (name.match(/^(on|@)/)) {
const eventName = name.replace(/^(on|@)/, ''); // 对卡片不支持的事件进行判断
const cardEventBlackList = ['appear', 'disappear', 'swipe'];
if (options.uxType === _utils.ENTRY_TYPE.CARD && cardEventBlackList.indexOf(eventName.toLowerCase()) > -1) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 卡片组件 `' + tagName + '` 不支持事件 `' + eventName + '`'
});
}
if (events.indexOf(eventName.toLowerCase()) < 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 不支持事件 `' + eventName + '`'
});
}
}
});
} // 检查子组件合法性
if (childNodes.length > 0) {
const slotSet = new Set();
const nodeNameMap = {};
childNodes.forEach((child, index) => {
// 只有父组件和子组件均为保留标签时,才需要检测合法性
if (isReservedTag(tagName) && isReservedTag(child.nodeName)) {
const tagParents = tagParentsMap[child.nodeName];
const tagChildren = tagChildrenMap[tagName];
const tagOnceChildren = tagOnceChildrenMap[tagName];
if (child.nodeName === 'slot') {
let currentNode = child.parentNode;
while (currentNode) {
if (currentNode.tagName === 'slot') {
_sharedUtils.colorconsole.warn(`slot标签内不应该嵌套slot ${options.filePath}@${location.line || 1},${location.col || 1}`);
break;
}
currentNode = currentNode.parentNode;
}
const attrsMap = {};
child.attrs.map(item => {
attrsMap[item.name] = item.value;
});
if (!attrsMap.hasOwnProperty('name')) {
attrsMap.name = 'default';
} else if (/\{\{\s*[\w$]+\s*\}\}/.test(attrsMap.name)) {
_sharedUtils.colorconsole.warn(`标签${child.nodeName}的name属性暂时不支持动态绑定 ${options.filePath}@${location.line || 1},${location.col || 1}`);
}
if (slotSet.has(attrsMap.name)) {
_sharedUtils.colorconsole.warn(`标签${tagName}内存在name为 \`${attrsMap.name}\` 重复slot ${options.filePath}@${location.line || 1},${location.col || 1}`);
} else {
slotSet.add(attrsMap.name);
}
} // 若子组件有parents属性,检查tagName是否为子组件合法的父组件;若父组件有children属性,检查child.nodeName是否为父组件合法的子组件
// 此处 if 条件不修改: 默认不包含在 tagOnceChildrenMap[child.nodeName] 中的组件也允许作为 tagName 组件的子组件
if (tagParents && tagParents.indexOf(tagName) < 0 || tagChildren && tagChildren.indexOf(child.nodeName) < 0) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 不支持子组件 `' + child.nodeName + '`'
});
} else if (tagOnceChildren && tagOnceChildren.indexOf(child.nodeName) > -1) {
let duplicated;
if (nodeNameMap[child.nodeName] || nodeNameMap[child.nodeName] === 0) {
duplicated = true;
} // 对于只允许出现一次的子节点,保留最后一个的索引
nodeNameMap[child.nodeName] = {
index,
duplicated
};
}
}
});
const keys = Object.keys(nodeNameMap);
if (keys.length) {
keys.filter(childName => nodeNameMap[childName].duplicated).forEach(childName => {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: `'WARNING: 组件 \`${childName}\` 只允许在 \`${tagName}\` 组件中出现一次`
});
});
node.childNodes = childNodes.filter((child, index) => !(nodeNameMap[child.nodeName] && nodeNameMap[child.nodeName].duplicated && index !== nodeNameMap[child.nodeName].index));
}
} // 检查是否漏写了结束标签(仅针对不支持自闭合的标签,如 <div>)
if (!isSupportedSelfClosing(tagName) && location.startTag && !location.endTag) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'ERROR: 组件 `' + tagName + '` 缺少闭合标签,请检查'
});
} // 检查 list-item 内是否使用了指令 if 或者 for
if (tagName === 'list-item' && hasIfOrFor(childNodes)) {
log.push({
line: location.line || 1,
column: location.col || 1,
reason: 'WARN: `list-item` 内部需谨慎使用指令 if 或 for,相同 type 属性的 list-item, DOM 结构必须完全相同`'
});
}
}
/**
* 检测Id属性
* @param id
* @param output
*/
function checkId(id, output) {
if (id) {
output.result.id = _exp.default.isExpr(id) ? (0, _exp.default)(id) : id;
}
}
/**
* 判断自定义(data-*)属性
* @param attrName
* @returns {boolean}
*/
function checkDataAttr(attrName) {
return REG_TAG_DATA_ATTR.test(attrName);
}
/**
* 检测构造模式
* @param id
* @param output
*/
function checkBuild(mode, output) {
if (mode) {
output.result.append = mode === 'tree' ? 'tree' : 'single';
}
}
/**
* 检测class属性
* 'a b c' -> ['a', 'b', 'c']
* 'a {{b}} c' -> function () {return ['a', this.b, 'c']}
* @param className
* @param output
*/
function checkClass(className, output) {
let hasBinding;
let classList = [];
className = className.trim();
if (className) {
let start = 0;
let end = 0;
const segs = [];
const matched = className.match(REG_CLASS_VALUE);
if (matched) {
let staticString; // 拆解 class 属性,将静态 class 和插值表达式分离
matched.forEach(exp => {
end = className.indexOf(exp, start);
staticString = className.slice(start, end);
if (staticString.length) {
segs.push(staticString);
}
segs.push(exp);
start += staticString.length + exp.length;
});
}
segs.push(className.slice(start)); // trailing static classes
classList = segs.reduce((list, seg) => {
if (_exp.default.isExpr(seg)) {
hasBinding = true;
list.push((0, _exp.default)(seg, false));
return list;
}
return list.concat(seg.split(/\s+/g).filter(s => s).map(s => `'${s}'`));
}, []);
classList = classList.filter(klass => klass.trim());
if (hasBinding) {
const code = '(function () {return [' + classList.join(', ') + ']})';
try {
/* eslint-disable no-eval */
output.result.classList = eval(code);
/* eslint-enable no-eval */
} catch (err) {
err.isExpressionError = true;
err.expression = className;
throw err;
}
} else {
output.result.classList = classList.map( // 去掉引号
klass => klass.slice(1, -1));
}
}
}
/**
* 检查样式
* '{{a}}' --> function () { return this.a; }
* 'b:{{v}};' --> {b: function() { return this.v; }}
* @param cssText
* @param output
* @param locationInfo
*/
function checkStyle(cssText, output, locationInfo, options) {
let style = {};
const log = output.log;
if (cssText) {
if (_exp.default.singleExpr(cssText)) {
// 检测是否嵌套{{}}
const incText = _exp.default.removeExprffix(cssText);
if (_exp.default.isExpr(incText)) {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: 'ERROR: style 属性不能嵌套多层{{}}'
});
} else {
style = (0, _exp.default)(cssText);
}
output.result.style = style;
return;
} // 如果是 a: {{}}; b: {{}};, 则分解处理
cssText.split(';').forEach(function (declarationText) {
let k, v, vResult;
let pair = declarationText.trim().split(':'); // 如果出现xxx:xxx:xxx的情况, 则将第一个:之后文本作为value
if (pair.length > 2) {
pair[1] = pair.slice(1).join(':');
pair = pair.slice(0, 2);
}
if (pair.length === 2) {
k = pair[0].trim();
k = (0, _utils.hyphenedToCamelCase)(k);
v = pair[1].trim();
v = (0, _exp.default)(v); // 处理值表达式
vResult = _style.default.validateDelaration(k, v, options);
v = vResult.value;
v.forEach(t => {
// 如果校验成功,则保存转换后的属性值
if ((0, _utils.isValidValue)(t.v) || typeof t.v === 'function') {
style[t.n] = t.v;
}
});
if (vResult.log) {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: vResult.log.reason
});
}
}
});
if (typeof style === 'object') {
for (let key in style) {
if (_style.default.shouldAddToDependency(key, style[key])) {
// 内联样式中的静态资源引用
output.depFiles.push(style[key]);
}
}
}
output.result.style = style;
}
}
/**
* 检查if语句
* @param value
* @param output
* @param not 是否取反
*/
function checkIs(value, output, locationInfo) {
const log = output.log;
if (value) {
// 如果没有,补充上{{}}
value = _exp.default.addExprffix(value); // 将表达式转换为function
output.result.is = (0, _exp.default)(value);
} else {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: 'WARNING: is 属性为空'
});
}
}
/**
* 检查if语句
* @param value
* @param output
* @param not 是否取反
*/
function checkIf(value, output, not, locationInfo, conditionList) {
const log = output.log;
if (value) {
// 如果没有,补充上{{}}
value = _exp.default.addExprffix(value);
if (not) {
value = '{{' + buildConditionExp(conditionList) + '}}';
} else {
// if动作前需要清除conditionList之前的结构
conditionList.length > 0 && (conditionList.length = 0);
conditionList.push(`${value.substr(2, value.length - 4)}`);
} // 将表达式转换为function
output.result.shown = (0, _exp.default)(value);
} else {
if (!not) {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: 'WARNING: if 属性为空'
});
}
}
}
/**
* 检查else语句
* @param value
* @param output
* @param not
*/
function checkElse(value, output, locationInfo, conditionList) {
checkIf(value, output, true, locationInfo, conditionList); // else动作之后需清除conditionList之前的结构
conditionList.length = 0;
}
/**
* 检查else if语句
* @param value
* @param output
* @param not
*/
function checkElif(value, cond, output, locationInfo, conditionList) {
const log = output.log;
let newcond = cond;
if (value) {
// 如果没有,补充上{{}}
value = _exp.default.addExprffix(value);
cond = _exp.default.addExprffix(cond);
newcond = '{{(' + value.substr(2, value.length - 4) + ') && ' + buildConditionExp(conditionList) + '}}'; // 将表达式转换为function
output.result.shown = (0, _exp.default)(newcond);
conditionList.push(`${value.substr(2, value.length - 4)}`);
} else {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: 'WARNING: Elif 属性为空'
});
}
return newcond;
}
/**
* 检查循环
* @param value
* @param output
*/
function checkFor(value, output, locationInfo) {
const log = output.log;
if (value) {
// 如果是单一表达式,去除{{}}
value = _exp.default.removeExprffix(value); // 如果是'key in values'的形式
let key;
let val;
const inMatch = value.match(/(.*) (?:in) (.*)/);
if (inMatch) {
// 如果key是以'(key,value)'格式
const itMatch = inMatch[1].match(/\((.*),(.*)\)/);
if (itMatch) {
key = itMatch[1].trim();
val = itMatch[2].trim();
} else {
val = inMatch[1].trim();
}
value = inMatch[2];
}
value = '{{' + value + '}}';
let repeat;
if (!key && !val) {
repeat = (0, _exp.default)(value);
} else {
// 如果指定key,value
repeat = {
exp: (0, _exp.default)(value)
};
if (key) {
repeat.key = key;
}
if (val) {
repeat.value = val;
}
}
output.result.repeat = repeat;
} else {
log.push({
line: locationInfo.line || 1,
column: locationInfo.col || 1,
reason: 'WARNING: for 属性为空'
});
}
}
/**
* 检查事件属性
* @param {string} name 事件名以on开头
* @param {string} value
* @param {object} output{result, deps[], log[]}
*/
function checkEvent(name, value, output) {
const originValue = value; // 去除开头的'on'
const eventName = name.replace(/^(on|@)/, '');
if (eventName && value) {
// 去除表达式的{{}}
value = _exp.default.removeExprffix(value); // 如果表达式形式为XXX(xxxx)
const paramsMatch = value.match(/(.*)\((.*)\)/);
if (paramsMatch) {
const funcName = paramsMatch[1];
let params = paramsMatch[2]; // 解析','分隔的参数
if (params) {
params = params.split(/\s*,\s*/); // 如果参数中没有'$evt',则自动在末尾添加'$evt'
if (params.indexOf('evt') === -1) {
params[params.length] = 'evt';
}
} else {
// 否则默认有一个参数'$evt'
params = ['evt'];
}
value = '{{' + funcName + '(' + params.join(',') + ')}}';
try {
// 将事件转换为函数对象
/* eslint-disable no-eval */
value = eval('(function (evt) { return ' + (0, _exp.default)(value, false).replace('this.evt', 'evt') + '})');
/* eslint-enable no-eval */
} catch (err) {
err.isExpressionError = true;
err.expression = originValue;
throw err;
}
}
output.result.events = output.result.events || {};
output.result.events[eventName] = value;
}
}
/**
* @param {string} name
* @param {string} value
* @param {object} output{result, deps[], log[]}
* @param {String} tagName
* @param {object} locationInfo{line, column}
* @param {object} options
*/
function checkAttr(name, value, output, tagName, locationInfo, options) {
if (name && (0, _utils.isValidValue)(value)) {
if (shouldConvertPath(name, value, tagName)) {
// 判断路径下资源是否存在
const hasFile = (0, _utils.fileExists)(value, options.filePath);
if (!hasFile) {
output.log.push({
line: locationInfo.line,
column: locationInfo.column,
reason: 'WARNING: ' + tagName + ' 属性 ' + name + ' 的值 ' + value + ' 下不存在对应的文件资源'
});
} // 转换为以项目源码为根的绝对路径
value = (0, _utils.resolvePath)(value, options.filePath);
output.depFiles.push(value);
}
output.result.attr = output.result.attr || {};
output.result.attr[(0, _utils.hyphenedToCamelCase)(name)] = (0, _exp.default)(value);
if (name === 'value' && tagName === 'text') {
output.log.push({
line: locationInfo.line,
column: locationInfo.column,
reason: 'WARNING: `value` 应该写在<text>标签中'
});
}
}
}
/**
* 是否转换资源引用路径
* @param name {string} - 属性名
* @param value {string} - 属性值
* @param tagName {string} - 标签名
* @returns {boolean}
*/
function shouldConvertPath(name, value, tagName) {
const skip = name === 'alt' && value === 'blank';
if (skip) {
return false;
}
if (['src', 'alt'].includes(name) && value && ['img', 'video'].indexOf(tagName) > -1) {
if (!/^(data:|http|{{)/.test(value)) {
return true;
}
}
return false;
}
/**
* 是否为保留标签
*/
function isReservedTag(tag) {
return tagReserved.hasOwnProperty(tag);
}
/**
* 是否为包含text内容的原子组件
*/
function isTextContentAtomic(tag) {
return tagTextContent.hasOwnProperty(tag) && tagAtomics.hasOwnProperty(tag);
}
/**
* 非文本标签且非原子组件
*/
function isNotTextContentAtomic(tag) {
return !tagTextContent.hasOwnProperty(tag) && !tagAtomics.hasOwnProperty(tag);
}
/**
* 判断标签是否支持span
* @param tag
*/
function isSupportSpan(tag) {
if (!tag || typeof tag !== 'string') {
return;
}
return tagChildrenMap[tag] && tagChildrenMap[tag].indexOf('span') > -1;
}
/**
* 获取标签支持的子节点
* @param tag
* @returns {*|Array}
*/
function getTagChildren(tag) {
if (!tag || typeof tag !== 'string') {
return;
}
return tagChildrenMap[tag] || [];
}
/**
*判断标签是否支持自闭合
* @param tag
*/
function isSupportedSelfClosing(tag) {
if (!tag || typeof tag !== 'string') {
return;
}
tag = tagAliasMap[tag] || tag;
return tagNatives[tag] && !!tagNatives[tag].selfClosing;
}
/**
* 判断标签名是否支持
* @param {string} tag - 标签的name
*/
function hasTagDefined(tag) {
return tagNativeKeys.indexOf(tag) > -1 || tagComponents.indexOf(tag) > -1;
}
/**
* 检查是否为空元素
* @param {String} tagName - 标签名
* @returns {Boolean}
*/
function isEmptyElement(tagName) {
tagName = tagAliasMap[tagName] || tagName;
return tagNatives[tagName] && tagNatives[tagName].empty === true;
}
/**
* 输出条件取反的字符串表示
* @param list
* @return {string}
*/
function buildConditionExp(list) {
return list.map(exp => {
return `!(${exp})`;
}).join(' && ');
}
/**
* 检查节点中是否含有指令 if 或者 for
* @param {Array} nodes
*/
function hasIfOrFor(nodes) {
let flag = false;
nodes.find(item => {
const index = (item.attrs || []).findIndex(attr => {
return ['for', 'if'].indexOf(attr.name) > -1;
});
if (index > -1) {
flag = true;
return flag;
}
if (Array.isArray(item.childNodes)) {
flag = hasIfOrFor(item.childNodes);
return flag;
}
});
return flag;
}
var _default = {
checkTagName,
checkId,
checkClass,
checkStyle,
checkIs,
checkIf,
checkElse,
checkElif,
checkFor,
checkEvent,
checkAttr,
checkBuild,
checkModel: _model.default,
isReservedTag,
isTextContentAtomic,
isSupportSpan,
getTagChildren,
isSupportedSelfClosing,
isEmptyElement,
isNotTextContentAtomic
};
exports.default = _default;
//# sourceMappingURL=validator.js.map

@@ -1,2 +0,321 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.extend=extend,exports.merge=merge,exports.hyphenedToCamelCase=hyphenedToCamelCase,exports.camelCaseToHyphened=camelCaseToHyphened,exports.isEmptyObject=isEmptyObject,exports.serialize=serialize,exports.splitAttr=splitAttr,exports.isValidValue=isValidValue,exports.resolvePath=resolvePath,exports.fileExists=fileExists,exports.FRAG_TYPE=exports.ENTRY_TYPE=void 0;var _path=_interopRequireDefault(require("path")),_fs=_interopRequireDefault(require("fs")),_config=_interopRequireDefault(require("@hap-toolkit/shared-utils/config"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function extend(e,...t){if("function"==typeof Object.assign)Object.assign(e,...t);else{const r=t.shift();for(const t in r)e[t]=r[t];t.length&&extend(e,...t)}return e}function merge(e,...t){return t.length&&t.forEach(t=>{e=e.concat(t)}),e}function hyphenedToCamelCase(e){return e.replace(/-([a-z])/g,(function(e,t){return t.toUpperCase()}))}function camelCaseToHyphened(e){return e.replace(/([A-Z])/g,(function(e,t){return"-"+t.toLowerCase()}))}function isEmptyObject(e){if(!e)return!0;for(const t in e)return!1;return!0}function serialize(e,t){const r=typeof e;if("undefined"===r)return e;if("function"===r)return e.toString();const n=[];let o=-1,i=`__FKS_${Math.random().toString(16).slice(2,10)}_FKE__`;const s=JSON.stringify(e,(e,t)=>"function"==typeof t?"":t);for(;s.indexOf(i)>-1;)i=`_${i}_`;let a=JSON.stringify(e,(function(e,t){return"function"==typeof t?(n.push(t),o++,i+o):t}),t);const f=new RegExp(`"${i}(\\d+)"`,"g");return a=a.replace(f,(e,t)=>n[t].toString()),a}function splitAttr(e,t){const r=[];if(t)switch(e.forEach((e,t)=>{r[t]={},r[t].n=e}),t.length){case 1:e.forEach((e,n)=>{r[n].v=t[0]});break;case 2:e.forEach((e,n)=>{r[n].v=n%2?t[1]:t[0]});break;case 3:e.forEach((e,n)=>{r[n].v=n%2?t[1]:t[n]});break;default:e.forEach((e,n)=>{r[n].v=t[n]})}return r}function isValidValue(e){return"number"==typeof e||"string"==typeof e}function resolvePath(e,t){if(t&&!_path.default.isAbsolute(e)){const r=_path.default.join(_path.default.dirname(t),e),n=_path.default.relative(_path.default.resolve(_config.default.projectPath,"./src"),r);e=_path.default.join("/",n).replace(/\\/g,"/")}return e}function fileExists(e,t){let r;if(_path.default.isAbsolute(e)){const t=_config.default.projectPath;r=_path.default.join(t,"./src",e)}else r=_path.default.resolve(t,"../",e);return _fs.default.existsSync(r)}const ENTRY_TYPE={APP:"app",PAGE:"page",COMP:"comp",CARD:"card",JS:"js"};exports.ENTRY_TYPE=ENTRY_TYPE;const FRAG_TYPE={IMPORT:"import",TEMPLATE:"template",STYLE:"style",SCRIPT:"script"};exports.FRAG_TYPE=FRAG_TYPE;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.extend = extend;
exports.merge = merge;
exports.hyphenedToCamelCase = hyphenedToCamelCase;
exports.camelCaseToHyphened = camelCaseToHyphened;
exports.isEmptyObject = isEmptyObject;
exports.serialize = serialize;
exports.splitAttr = splitAttr;
exports.isValidValue = isValidValue;
exports.resolvePath = resolvePath;
exports.fileExists = fileExists;
exports.FRAG_TYPE = exports.ENTRY_TYPE = void 0;
var _path = _interopRequireDefault(require("path"));
var _fs = _interopRequireDefault(require("fs"));
var _config = _interopRequireDefault(require("@hap-toolkit/shared-utils/config"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Copyright (C) 2017, hapjs.org. All rights reserved.
*/
// TODO duplicate of packager/src/common/utils.js
/**
* 扩展对象属性
* @param dest
* @param src
*/
function extend(target, ...src) {
if (typeof Object.assign === 'function') {
Object.assign(target, ...src);
} else {
const first = src.shift(); // 覆盖旧值
for (const key in first) {
target[key] = first[key];
}
if (src.length) {
extend(target, ...src);
}
}
return target;
}
/**
* 合并数组属性
* @param dest
* @param src
*/
function merge(target, ...src) {
if (src.length) {
src.forEach(item => {
target = target.concat(item);
});
}
return target;
}
/**
* -xxx-xxx转换为XxxXxx
* @param value
* @returns {void|string|XML|*}
*/
function hyphenedToCamelCase(value) {
return value.replace(/-([a-z])/g, function (s, m) {
return m.toUpperCase();
});
}
/**
* XxxXxx转换为-xxx-xxx
* @param value
* @returns {void|string|XML|*}
*/
function camelCaseToHyphened(value) {
return value.replace(/([A-Z])/g, function (s, m) {
return '-' + m.toLowerCase();
});
}
/**
* 判断对象是否为空(没有任何属性)
* @param e
* @returns {boolean}
*/
function isEmptyObject(obj) {
if (!obj) {
return !0;
}
/* eslint-disable no-unused-vars */
for (const t in obj) {
return !1;
}
/* eslint-enable no-unused-vars */
return !0;
}
/**
* 可序列化包含函数的的数据
* JSON.stringify 不能直接对函数进行序列化
* 序列化结果的函数两侧不会带有双引号
*
* 如:
* serialize({
* func: function foo () {
*
* }
* }, 2)
* =>
* // 字符串
* {
* "func": function foo () {
*
* }
* }
* 参考 [Yahoo][1] 的实现,但解决了“碰撞”问题
*
* [1]: https://github.com/yahoo/serialize-javascript/blob/master/index.js
*
* @param {*} target - 转换对象
* @param {Number | String} [space] - JSON.stringify 的第三个参数
* @returns {String}
*/
function serialize(target, space) {
const type = typeof target;
if (type === 'undefined') {
return target;
}
if (type === 'function') {
return target.toString();
} // 用于保存出现的函数
const functions = []; // 函数在上面数组中的序号(index)
let id = -1;
/*
* 1, 生成一个用户数据中不会出现的占位符,标记函数出现的位置
* 先将数据做简单序列化(函数的 key 也会计入检查),用于检查占位符,
* 确保不会出现“碰撞” (用户数据中正好包含占位符)
*/
let PLACEHOLDER = `__FKS_${Math.random().toString(16).slice(2, 10)}_FKE__`;
const origin = JSON.stringify(target, (key, value) => typeof value === 'function' ? '' : value);
while (origin.indexOf(PLACEHOLDER) > -1) {
PLACEHOLDER = `_${PLACEHOLDER}_`;
}
/*
* 2, 使用 JSON.stringify 做初步的序列化
* 保存出现的 function,并用占位符暂时替代
*/
let code = JSON.stringify(target, function (key, value) {
if (typeof value === 'function') {
functions.push(value);
id++;
return PLACEHOLDER + id; // 每个函数对应的占位符
}
return value;
}, space);
/*
* 3, 将 functions 中保存的函数转成字符串,替换对应占位符(包括双引号)
*/
const PLACEHOLDER_RE = new RegExp(`"${PLACEHOLDER}(\\d+)"`, 'g');
code = code.replace(PLACEHOLDER_RE, (_, id) => functions[id].toString());
return code;
}
/**
* 拆分上下左右类型简写属性
* @param names 属性名数组
* @param values 属性值数组
* @returns {array}
*/
function splitAttr(names, values) {
const resultArray = [];
if (values) {
names.forEach((n, idx) => {
resultArray[idx] = {};
resultArray[idx].n = n;
});
switch (values.length) {
case 1:
names.forEach((n, idx) => {
resultArray[idx].v = values[0];
});
break;
case 2:
names.forEach((n, idx) => {
if (idx % 2) {
resultArray[idx].v = values[1];
} else {
resultArray[idx].v = values[0];
}
});
break;
case 3:
names.forEach((n, idx) => {
if (idx % 2) {
resultArray[idx].v = values[1];
} else {
resultArray[idx].v = values[idx];
}
});
break;
default:
names.forEach((n, idx) => {
resultArray[idx].v = values[idx];
});
}
}
return resultArray;
}
/**
* 值的有效性检验
* @param value 值
*/
function isValidValue(value) {
return typeof value === 'number' || typeof value === 'string';
}
/**
* @param relativePath
* @param filePath
* @returns {*}
* @desc 将文件相对路径转为项目根目录('src/')下的绝对路径
*/
function resolvePath(relativePath, filePath) {
if (filePath && !_path.default.isAbsolute(relativePath)) {
const absolutePath = _path.default.join(_path.default.dirname(filePath), relativePath);
const relativeProjectPath = _path.default.relative(_path.default.resolve(_config.default.projectPath, './src'), absolutePath);
const newAbsolutePath = _path.default.join('/', relativeProjectPath); // 兼容windows
relativePath = newAbsolutePath.replace(/\\/g, '/');
}
return relativePath;
}
/**
* 判断文件是否存在
* @param {string} resourcePath
* @param {string} filePath
* @returns {boolean}
*/
function fileExists(resourcePath, filePath) {
let absolutePath;
if (_path.default.isAbsolute(resourcePath)) {
const projectPath = _config.default.projectPath;
absolutePath = _path.default.join(projectPath, './src', resourcePath);
} else {
absolutePath = _path.default.resolve(filePath, '../', resourcePath);
}
return _fs.default.existsSync(absolutePath);
}
/**
* ux文件的类型
*/
const ENTRY_TYPE = {
APP: 'app',
PAGE: 'page',
COMP: 'comp',
CARD: 'card',
JS: 'js'
};
/**
* 片段的类型
*/
exports.ENTRY_TYPE = ENTRY_TYPE;
const FRAG_TYPE = {
IMPORT: 'import',
TEMPLATE: 'template',
STYLE: 'style',
SCRIPT: 'script'
};
exports.FRAG_TYPE = FRAG_TYPE;
//# sourceMappingURL=utils.js.map

6

package.json
{
"name": "@hap-toolkit/compiler",
"version": "1.9.3-beta.1",
"version": "1.9.3-beta.2",
"description": "compiler of hap-toolkit",

@@ -21,3 +21,3 @@ "engines": {

"dependencies": {
"@hap-toolkit/shared-utils": "1.9.3-beta.1",
"@hap-toolkit/shared-utils": "1.9.3-beta.2",
"css": "^2.2.4",

@@ -27,3 +27,3 @@ "css-what": "^2.1.3",

},
"gitHead": "e6d13c5b1825854d462fff9646f14a76a48d1334"
"gitHead": "b8a1dcd1a9bb285635d09b8020460389da67ed6c"
}

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc