From 2c2ab41a95fb7772f11a17a3356655936d12e88c Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Mon, 28 Oct 2024 16:21:43 +0800 Subject: [PATCH 1/9] fix: add judgement for whether the attr is an expression Signed-off-by: lileirjyb --- .../hap-compiler/src/template/validator.js | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/hap-compiler/src/template/validator.js b/packages/hap-compiler/src/template/validator.js index 1b755584..108b2152 100644 --- a/packages/hap-compiler/src/template/validator.js +++ b/packages/hap-compiler/src/template/validator.js @@ -1827,15 +1827,17 @@ function checkCustomDirective(name, value, output, node) { function checkAttr(name, value, output, tagName, locationInfo, options) { if (name && isValidValue(value)) { if (shouldConvertPath(name, value, tagName)) { - // 判断路径下资源是否存在 - const hasFile = fileExists(value, options.filePath) - if (!hasFile) { - output.log.push({ - line: locationInfo.line, - column: locationInfo.column, - reason: - 'WARN: ' + tagName + ' 属性 ' + name + ' 的值 ' + value + ' 下不存在对应的文件资源' - }) + if (!exp.isExpr(value)) { + // 若路径不包含表达式,判断路径下资源是否存在 + const hasFile = fileExists(value, options.filePath) + if (!hasFile) { + output.log.push({ + line: locationInfo.line, + column: locationInfo.column, + reason: + 'WARN: ' + tagName + ' 属性 ' + name + ' 的值 ' + value + ' 下不存在对应的文件资源' + }) + } } // 转换为以项目源码为根的绝对路径 value = resolvePath(value, options.filePath) From fa344e1a1aa4112673d4aa5146290e5669e473a3 Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Mon, 28 Oct 2024 19:44:01 +0800 Subject: [PATCH 2/9] fix: modify entry and pages fields required validation for card project Signed-off-by: lileirjyb --- packages/hap-packager/src/common/utils.js | 2 - .../src/gen-webpack-conf/manifest-schema.js | 2 +- .../src/gen-webpack-conf/validate.js | 12 +++ yarn.lock | 82 +++++++++++++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/packages/hap-packager/src/common/utils.js b/packages/hap-packager/src/common/utils.js index 76f507c1..fcfe49c2 100644 --- a/packages/hap-packager/src/common/utils.js +++ b/packages/hap-packager/src/common/utils.js @@ -204,8 +204,6 @@ export function genPriorities(manifest, skeletonConf) { const entry = manifest.router.entry const entrySkFiles = getEntrySkeleton(skeletonConf, entry) priorities.splice(5, 0, ...entrySkFiles, new RegExp(`^${entry}/$`), new RegExp(`^${entry}/.+`)) - } else { - colorconsole.error(`manifest.json 中未配置入口页面 router.entry`) } return priorities } diff --git a/packages/hap-toolkit/src/gen-webpack-conf/manifest-schema.js b/packages/hap-toolkit/src/gen-webpack-conf/manifest-schema.js index a7e476ae..64871f4e 100644 --- a/packages/hap-toolkit/src/gen-webpack-conf/manifest-schema.js +++ b/packages/hap-toolkit/src/gen-webpack-conf/manifest-schema.js @@ -86,7 +86,7 @@ export default { router: { // TODO 更详细的后台运行配置信息, type: 'object', - required: ['entry', 'pages'], + requireError: ['entry', 'pages'], properties: { entry: { type: 'string', diff --git a/packages/hap-toolkit/src/gen-webpack-conf/validate.js b/packages/hap-toolkit/src/gen-webpack-conf/validate.js index af8b2494..dcefb609 100644 --- a/packages/hap-toolkit/src/gen-webpack-conf/validate.js +++ b/packages/hap-toolkit/src/gen-webpack-conf/validate.js @@ -39,6 +39,18 @@ const docSrc = 'https://doc.quickapp.cn/framework/manifest.html' export function validateJson(jsonInfo, filePath) { const ajv = new Ajv({ allErrors: true, jsonPointers: true }) AjvErrors(ajv) + ajv.addKeyword('requireError', { + validate: function (schema, data) { + if (!data || !data.widgets) { + const message = `快应用项目 manifest.json 中,router 字段下 ${JSON.stringify( + schema + )} 字段为必填` + colorconsole.error(message) + } + // 项目工程有卡片时无需校验 entry 和 pages 字段的必填性 + return true + } + }) ajv.validate(manifestSchema, jsonInfo) if (!ajv.errors) { return [] diff --git a/yarn.lock b/yarn.lock index 46b4e9db..441cfd38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,15 @@ # yarn lockfile v1 +"@aiot-toolkit/card-expression@^1.0.7": + version "1.0.7" + resolved "https://npm.vmic.xyz/@aiot-toolkit%2fcard-expression/-/card-expression-1.0.7.tgz#7f892bfcf588f3e813ffd6a2c6019d38f36328e6" + integrity sha512-QwcTI2zfQTxyKQd1wFRhHm99iamxDFM83zXIdTmkCwjLafjSSxXdVQ5po0MEGBfgbS7Ox8xNWPFfs5REhUJIMA== + dependencies: + "@babel/generator" "^7.25.6" + "@babel/parser" "^7.25.6" + "@babel/types" "^7.25.6" + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -97,6 +106,17 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.25.6": + version "7.26.0" + resolved "https://npm.vmic.xyz/@babel%2fgenerator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" + integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== + dependencies: + "@babel/parser" "^7.26.0" + "@babel/types" "^7.26.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -331,11 +351,21 @@ resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://npm.vmic.xyz/@babel%2fhelper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://npm.vmic.xyz/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" @@ -388,6 +418,13 @@ resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== +"@babel/parser@^7.25.6", "@babel/parser@^7.26.0": + version "7.26.1" + resolved "https://npm.vmic.xyz/@babel%2fparser/-/parser-7.26.1.tgz#44e02499960df2cdce2c456372a3e8e0c3c5c975" + integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw== + dependencies: + "@babel/types" "^7.26.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1123,6 +1160,14 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.25.6", "@babel/types@^7.26.0": + version "7.26.0" + resolved "https://npm.vmic.xyz/@babel%2ftypes/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.npmmirror.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -1410,16 +1455,35 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://npm.vmic.xyz/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://npm.vmic.xyz/@jridgewell%2fresolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://npm.vmic.xyz/@jridgewell%2fset-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.2": version "0.3.3" resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" @@ -1446,6 +1510,11 @@ resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://npm.vmic.xyz/@jridgewell%2fsourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" @@ -1454,6 +1523,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://npm.vmic.xyz/@jridgewell%2ftrace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.npmmirror.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -8185,6 +8262,11 @@ jsesc@^2.5.1: resolved "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://npm.vmic.xyz/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha1-u4sJpll7pCZCXy5KByRcPQC5ND4= + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" From 7835c9b5485bcea4e9dce06ccafaa76ddf3b3f12 Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Wed, 30 Oct 2024 19:23:17 +0800 Subject: [PATCH 3/9] feat: optimize expression result in prefix way and mark keys with $ sign in template and actions Signed-off-by: lileirjyb --- .eslintrc | 3 +- .../hap-compiler/src/template/validator.js | 3 +- .../src/plugins/lite-card-plugin.js | 4 +- .../hap-packager/src/post-handler/index.js | 306 ++++++++++++++++++ 4 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 packages/hap-packager/src/post-handler/index.js diff --git a/.eslintrc b/.eslintrc index 61a6d96e..0fd3515d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -9,6 +9,7 @@ "rules": { "camelcase": 0, "no-debugger": 1, - "no-unused-vars": 1 + "no-unused-vars": 1, + "no-control-regex": 1 } } diff --git a/packages/hap-compiler/src/template/validator.js b/packages/hap-compiler/src/template/validator.js index 108b2152..c56b9f89 100644 --- a/packages/hap-compiler/src/template/validator.js +++ b/packages/hap-compiler/src/template/validator.js @@ -2019,5 +2019,6 @@ export default { getTagChildren, isSupportedSelfClosing, isEmptyElement, - isNotTextContentAtomic + isNotTextContentAtomic, + isExpr: exp.isExpr } diff --git a/packages/hap-packager/src/plugins/lite-card-plugin.js b/packages/hap-packager/src/plugins/lite-card-plugin.js index 32f9cca2..70817659 100644 --- a/packages/hap-packager/src/plugins/lite-card-plugin.js +++ b/packages/hap-packager/src/plugins/lite-card-plugin.js @@ -7,6 +7,7 @@ import Compilation from 'webpack/lib/Compilation' import path from 'path' import { getLastLoaderPath, calcDataDigest } from '../common/utils' import { LOADER_INFO_LIST, LOADER_PATH_UX, LOADER_PATH_STYLE } from '../common/constant' +import { postHandleLiteCardRes } from '../post-handler' const SUFFIX_UX = '.ux' const CARD_ENTRY = '#entry' @@ -59,7 +60,8 @@ class LiteCardPlugin { pathSrc, bundleFilePath ) - compilation.assets[templateFileName] = new ConcatSource(JSON.stringify(liteCardRes)) + const templateJsonStr = postHandleLiteCardRes(liteCardRes) + compilation.assets[templateFileName] = new ConcatSource(templateJsonStr) compilation.assets[cssFileName] = new ConcatSource(JSON.stringify(styleRes)) } } diff --git a/packages/hap-packager/src/post-handler/index.js b/packages/hap-packager/src/post-handler/index.js new file mode 100644 index 00000000..54a701f6 --- /dev/null +++ b/packages/hap-packager/src/post-handler/index.js @@ -0,0 +1,306 @@ +import { templateValueToCardCode } from '@aiot-toolkit/card-expression' +import { templater } from '@hap-toolkit/compiler' +const { validator } = templater + +const literalValueRE = /^(?:true|false|null|undefined|Infinity|NaN|\d+)$/ +const CARD_ENTRY = '#entry' +const TYPE_IMPORT = 'import' +// 需要进行后处理的模块key +const TEMPLATE_KEY = 'template' +const ACTIONS_KEY = 'actions' + +// 节点标记,标记优先级依次提升 +const ENUM_KIND_TYPE = { + ELEMENT: 1, // 普通动态节点、事件节点、带id属性的节点 + FRAGMENT: 2, // if/for 动态节点 + COMPONENT: 3 // 自定义组件节点 +} + +function postHandleActions(actions) { + if (!isObject(actions)) return + + markType(actions) + markUrl(actions) + markMethod(actions) + markParams(actions) +} + +function markType(actions) { + // 如果有表达式,则用前缀方式优化 + if (isExpr(actions.type)) { + const prefixExpr = getPrefixExpr(actions.type) + delete actions.type + actions[getExprKey('type', prefixExpr)] = prefixExpr + } +} + +function markUrl(actions) { + // 如果有表达式,则用前缀方式优化 + if (isExpr(actions.url)) { + const prefixExpr = getPrefixExpr(actions.url) + delete actions.url + actions[getExprKey('url', prefixExpr)] = prefixExpr + } +} +function markMethod(actions) { + // 如果有表达式,则用前缀方式优化 + if (isExpr(actions.method)) { + const prefixExpr = getPrefixExpr(actions.method) + delete actions.method + actions[getExprKey('method', prefixExpr)] = prefixExpr + } +} + +function markParams(actions) { + if (!isObject(actions.params)) return + + // 标准只支持一级结构中绑定变量,做一级结构的key遍历即可 + Object.keys(actions.params).forEach((key) => { + const value = actions.params[key] + if (isExpr(value)) { + const prefixExpr = getPrefixExpr(value) + delete actions.params[key] + actions.params[getExprKey(key, prefixExpr)] = prefixExpr + } + }) +} + +function postHandleTemplate(template, liteCardRes) { + if (!isObject(template)) return + + markStyle(template) + markClassList(template) + markAttrs(template) + markEvents(template) + markId(template) + markIs(template) + markIf(template) + markFor(template) + markCustomComp(template, liteCardRes) + + const children = template.children + if (children && Array.isArray(children)) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + postHandleTemplate(child, liteCardRes) + } + } +} + +function markKind(oldKind, newKind) { + if (!oldKind || oldKind < newKind) return newKind + return oldKind +} + +function markCustomComp(template, liteCardRes) { + const importList = Object.keys(liteCardRes[CARD_ENTRY][TYPE_IMPORT]) + + if (importList.includes(template.type)) { + template.kind = markKind(template.kind, ENUM_KIND_TYPE.COMPONENT) + } +} + +function markIf(template) { + // 如果有表达式,则用前缀方式优化 + if (isExpr(template.shown)) { + const prefixExpr = getPrefixExpr(template.shown) + delete template.shown + template[getExprKey('shown', prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) + } +} + +function markIs(template) { + // 如果有表达式,则用前缀方式优化 + if (isExpr(template.is)) { + const prefixExpr = getPrefixExpr(template.is) + delete template.is + template[getExprKey('is', prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } +} + +function markId(template) { + if (!template.id) return + + // 如果有表达式,则用前缀方式优化 + if (isExpr(template.id)) { + const prefixExpr = getPrefixExpr(template.id) + delete template.id + template[getExprKey('id', prefixExpr)] = prefixExpr + } + // 节点有id属性,标记为kind=1 + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) +} + +function markFor(template) { + if (isObject(template.repeat) && isExpr(template.repeat.exp)) { + /** +
-> + "repeat": { + "exp": "{{ItemList}}", + "key": "index", + "value": "item" + }, + +
-> + "repeat": { + "exp": "[1,2,3]", + "key": "index", + "value": "item" + }, + */ + const prefixExpr = getPrefixExpr(template.repeat.exp) + delete template.repeat.exp + template.repeat[getExprKey('exp', prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) + } else if (isExpr(template.repeat)) { + /** +
-> + "repeat": "{{ItemList}}", + */ + const prefixExpr = getPrefixExpr(template.repeat) + delete template.repeat + template[getExprKey('repeat', prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) + } +} + +function markStyle(template) { + if (!template.style) return + + const style = template.style + if (typeof style === 'object') { + Object.keys(style).forEach((key) => { + const value = style[key] + // 如果有表达式,则用前缀方式优化 + if (isExpr(value)) { + const prefixExpr = getPrefixExpr(value) + delete template.style[key] + template.style[getExprKey(key, prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } + }) + } else { + // 如果有表达式,则用前缀方式优化 + if (isExpr(style)) { + const prefixExpr = getPrefixExpr(style) + delete template.style + template[getExprKey('style', prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } + } +} + +function markClassList(template) { + if (!template.classList || template.classList.length === 0) return + + let hasBinding = false + const cList = template.classList.map((classEle) => { + // 如果有表达式,则用前缀方式优化 + if (isExpr(classEle)) { + hasBinding = true + return getPrefixExpr(classEle) + } + return classEle + }) + if (hasBinding) { + // 如果 classList 元素有表达式 + delete template.classList + template[getExprKey('classList', cList, template)] = cList + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } +} + +function markEvents(template) { + if (template.events) { + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } +} + +function markAttrs(template) { + const attrs = template.attr + if (isObject(attrs)) { + Object.keys(attrs).forEach((attrKey) => { + const attrValue = attrs[attrKey] + // 如果有表达式,则用前缀方式优化 + if (isExpr(attrValue)) { + const prefixExpr = getPrefixExpr(attrValue) + delete attrs[attrKey] + attrs[getExprKey(attrKey, prefixExpr)] = prefixExpr + template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) + } + }) + } +} + +function isExpr(val) { + if (!val) return false + return validator.isExpr(val) +} + +function isObject(obj) { + return obj && Object.prototype.toString.call(obj) === '[object Object]' && obj !== null +} + +function getPrefixExpr(expr) { + const res = templateValueToCardCode(expr) + // 表达式是保留字面量,返回 parse 后的结果。如: {{true}} -> true + if (literalValueRE.test(res)) { + return JSON.parse(res) + } + // 表达式非保留字面量,返回字符串。如:-> 如: {{ myVal }} -> "['$', 'myVal']" + return res +} + +function getExprKey(key, expr) { + // 表达式是保留字面量或常数,key无需加$ -> 如: "value": true 或 "value": 10 + if (literalValueRE.test(expr)) { + return key + } + // 表达式非保留字面量,key加$表示 -> 如: "$value": "['$', 'myVal']" + return `$${key}` +} + +function recordKeys(liteCardRes, templateKeys) { + const helper = function (obj) { + if (!obj || typeof obj !== 'object') return + + const keys = Object.keys(obj) + templateKeys.push(...keys) + keys.forEach((key) => { + return helper(obj[key], templateKeys) + }) + } + + helper(liteCardRes, templateKeys) +} + +export function postHandleLiteCardRes(liteCardRes) { + const uxList = Object.keys(liteCardRes) + + // template + for (let i = 0; i < uxList.length; i++) { + const compName = uxList[i] + postHandleTemplate(liteCardRes[compName][TEMPLATE_KEY], liteCardRes) + } + + // actions + for (let i = 0; i < uxList.length; i++) { + const compName = uxList[i] + const actionEvents = liteCardRes[compName][ACTIONS_KEY] + Object.keys(actionEvents).forEach((eventName) => { + postHandleActions(actionEvents[eventName]) + }) + } + + // 用于修改 template 的 key 的 stringify 的顺序 + let templateKeys = [] + recordKeys(liteCardRes, templateKeys) + + templateKeys = [...new Set(templateKeys.sort())] + .filter((key) => key !== 'children') + .concat('children') + + return JSON.stringify(liteCardRes, templateKeys) +} From 7d885d5529698bcd38b6172aa6c78f76b83a3bd8 Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Fri, 1 Nov 2024 10:07:09 +0800 Subject: [PATCH 4/9] fix: move styleObjectId into template node Signed-off-by: lileirjyb --- packages/hap-packager/src/common/constant.js | 11 ++-- .../src/plugins/lite-card-plugin.js | 63 +++++++++++++------ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/hap-packager/src/common/constant.js b/packages/hap-packager/src/common/constant.js index 304feea8..cde8c688 100644 --- a/packages/hap-packager/src/common/constant.js +++ b/packages/hap-packager/src/common/constant.js @@ -37,16 +37,18 @@ const LOADER_PATH_STYLE = { type: 'styles' } +const LOADER_PATH_TEMPLATE = { + path: '/template-loader.js', + type: 'template' +} + /** * loader path */ const LOADER_INFO_LIST = [ LOADER_PATH_UX, - { - path: '/template-loader.js', - type: 'template' - }, LOADER_PATH_STYLE, + LOADER_PATH_TEMPLATE, { path: '/script-loader.js', type: 'script' @@ -75,5 +77,6 @@ export { BUILD_INFO_FILE, LOADER_PATH_UX, LOADER_PATH_STYLE, + LOADER_PATH_TEMPLATE, LOADER_INFO_LIST } diff --git a/packages/hap-packager/src/plugins/lite-card-plugin.js b/packages/hap-packager/src/plugins/lite-card-plugin.js index 70817659..8ffe11f0 100644 --- a/packages/hap-packager/src/plugins/lite-card-plugin.js +++ b/packages/hap-packager/src/plugins/lite-card-plugin.js @@ -6,7 +6,12 @@ import Compilation from 'webpack/lib/Compilation' import path from 'path' import { getLastLoaderPath, calcDataDigest } from '../common/utils' -import { LOADER_INFO_LIST, LOADER_PATH_UX, LOADER_PATH_STYLE } from '../common/constant' +import { + LOADER_INFO_LIST, + LOADER_PATH_UX, + LOADER_PATH_STYLE, + LOADER_PATH_TEMPLATE +} from '../common/constant' import { postHandleLiteCardRes } from '../post-handler' const SUFFIX_UX = '.ux' @@ -158,11 +163,18 @@ class LiteCardPlugin { } else if (typeArr.includes(type)) { if (type === LOADER_PATH_STYLE.type) { // styles - const styleObjId = this.genStyleObjectId(compPath, styleRes) + const styleObjId = getStyleObjectId(compPath) styleRes[styleObjId] = obj - isCardRes - ? (currCompRes[CARD_ENTRY][STYLE_OBJECT_ID] = styleObjId) - : (currCompRes[STYLE_OBJECT_ID] = styleObjId) + } else if (type === LOADER_PATH_TEMPLATE.type) { + // template + const styleObjId = getStyleObjectId(compPath) + if (isCardRes) { + currCompRes[CARD_ENTRY][type] = obj + currCompRes[CARD_ENTRY][type][STYLE_OBJECT_ID] = styleObjId + } else { + currCompRes[type] = obj + currCompRes[type][STYLE_OBJECT_ID] = styleObjId + } } else { isCardRes ? (currCompRes[CARD_ENTRY][type] = obj) : (currCompRes[type] = obj) } @@ -173,21 +185,6 @@ class LiteCardPlugin { } } - genStyleObjectId(compPath, styleRes) { - const digest = calcDataDigest(Buffer.from(compPath, 'utf-8')) - const digestStr = digest.toString('hex') - const len = Math.min(6, digestStr.length) - let res = compPath - for (let i = len; i < digestStr.length; i++) { - res = digestStr.substring(0, i) - if (styleRes[res]) { - continue - } - break - } - return res - } - getComponentName(reqPath, pathSrc) { if (!reqPath) { return {} @@ -264,4 +261,30 @@ class LiteCardPlugin { } } +const componentIdMap = new Map() +const componentPathMap = new Map() +function getStyleObjectId(compPath) { + if (!componentPathMap.get(compPath)) { + const compId = getHash(compPath) + componentIdMap.set(compId, compPath) + componentPathMap.set(compPath, compId) + } + return componentPathMap.get(compPath) +} + +function getHash(compPath) { + const digest = calcDataDigest(Buffer.from(compPath, 'utf-8')) + const digestStr = digest.toString('hex') + const len = Math.min(6, digestStr.length) + let res = compPath + for (let i = len; i < digestStr.length; i++) { + res = digestStr.substring(0, i) + if (componentIdMap.has(res)) { + continue + } + break + } + return res +} + export { LiteCardPlugin } From 5b0d5f88defac5a9eabdd7765b513ccbe5b03c04 Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Fri, 1 Nov 2024 11:31:55 +0800 Subject: [PATCH 5/9] feat: flattten i18n json for lite card Signed-off-by: lileirjyb --- packages/hap-packager/package.json | 2 + .../src/plugins/resource-plugin.js | 11 +++-- yarn.lock | 43 +++++++++++-------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/packages/hap-packager/package.json b/packages/hap-packager/package.json index b8169e43..6396fab4 100644 --- a/packages/hap-packager/package.json +++ b/packages/hap-packager/package.json @@ -29,6 +29,7 @@ "babel.tree.config.js" ], "dependencies": { + "@aiot-toolkit/card-expression": "^1.0.7", "@babel/core": "^7.9.6", "@babel/generator": "^7.9.6", "@babel/parser": "^7.9.6", @@ -41,6 +42,7 @@ "@hap-toolkit/shared-utils": "1.9.14", "@jayfate/path": "^0.0.13", "babel-loader": "^8.1.0", + "flat": "^5.0.2", "fs-extra": "^10.0.0", "glob": "^7.1.6", "hash-sum": "^1.0.2", diff --git a/packages/hap-packager/src/plugins/resource-plugin.js b/packages/hap-packager/src/plugins/resource-plugin.js index 1df54541..3eb123db 100644 --- a/packages/hap-packager/src/plugins/resource-plugin.js +++ b/packages/hap-packager/src/plugins/resource-plugin.js @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import flatten from 'flat' import path from 'path' import fs from 'fs-extra' import aaptjs from '@hap-toolkit/aaptjs' @@ -122,21 +123,25 @@ function minifyWidgetI18nJSONFiles(targetDir) { let arr = [] for (let key in widgetsOption) { const widgetPath = widgetsOption[key].path + const isLite = widgetsOption[key].type === 'lite' const dir = path.join(targetDir, widgetPath, 'i18n') if (fs.existsSync(dir)) { const jsonFiles = getFiles('*.json', dir) jsonFiles.forEach((filePath) => { arr.push(filePath) - minifyJson(filePath, filePath) + minifyJson(filePath, filePath, isLite) }) } } } -function minifyJson(source, target) { +function minifyJson(source, target, isLite) { try { const contentStr = fs.readFileSync(source, 'utf8') - const content = JSON.parse(contentStr) + let content = JSON.parse(contentStr) + if (isLite) { + content = flatten.flatten(content) + } const minifiedContent = JSON.stringify(content) fs.writeFileSync(target, minifiedContent) } catch (err) { diff --git a/yarn.lock b/yarn.lock index 441cfd38..5bf1b3a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,7 +4,7 @@ "@aiot-toolkit/card-expression@^1.0.7": version "1.0.7" - resolved "https://npm.vmic.xyz/@aiot-toolkit%2fcard-expression/-/card-expression-1.0.7.tgz#7f892bfcf588f3e813ffd6a2c6019d38f36328e6" + resolved "https://registry.npmjs.org/@aiot-toolkit/card-expression/-/card-expression-1.0.7.tgz#7f892bfcf588f3e813ffd6a2c6019d38f36328e6" integrity sha512-QwcTI2zfQTxyKQd1wFRhHm99iamxDFM83zXIdTmkCwjLafjSSxXdVQ5po0MEGBfgbS7Ox8xNWPFfs5REhUJIMA== dependencies: "@babel/generator" "^7.25.6" @@ -107,11 +107,11 @@ jsesc "^2.5.1" "@babel/generator@^7.25.6": - version "7.26.0" - resolved "https://npm.vmic.xyz/@babel%2fgenerator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" - integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f" + integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw== dependencies: - "@babel/parser" "^7.26.0" + "@babel/parser" "^7.26.2" "@babel/types" "^7.26.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" @@ -353,7 +353,7 @@ "@babel/helper-string-parser@^7.25.9": version "7.25.9" - resolved "https://npm.vmic.xyz/@babel%2fhelper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": @@ -363,7 +363,7 @@ "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" - resolved "https://npm.vmic.xyz/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== "@babel/helper-validator-option@^7.21.0": @@ -418,10 +418,10 @@ resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== -"@babel/parser@^7.25.6", "@babel/parser@^7.26.0": - version "7.26.1" - resolved "https://npm.vmic.xyz/@babel%2fparser/-/parser-7.26.1.tgz#44e02499960df2cdce2c456372a3e8e0c3c5c975" - integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw== +"@babel/parser@^7.25.6", "@babel/parser@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11" + integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== dependencies: "@babel/types" "^7.26.0" @@ -1162,7 +1162,7 @@ "@babel/types@^7.25.6", "@babel/types@^7.26.0": version "7.26.0" - resolved "https://npm.vmic.xyz/@babel%2ftypes/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: "@babel/helper-string-parser" "^7.25.9" @@ -1457,7 +1457,7 @@ "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" - resolved "https://npm.vmic.xyz/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: "@jridgewell/set-array" "^1.2.1" @@ -1471,7 +1471,7 @@ "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://npm.vmic.xyz/@jridgewell%2fresolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.0.1": @@ -1481,7 +1481,7 @@ "@jridgewell/set-array@^1.2.1": version "1.2.1" - resolved "https://npm.vmic.xyz/@jridgewell%2fset-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/source-map@^0.3.2": @@ -1512,7 +1512,7 @@ "@jridgewell/sourcemap-codec@^1.4.14": version "1.5.0" - resolved "https://npm.vmic.xyz/@jridgewell%2fsourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": @@ -1525,7 +1525,7 @@ "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" - resolved "https://npm.vmic.xyz/@jridgewell%2ftrace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -6069,6 +6069,11 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^2.0.0: version "2.0.2" resolved "https://registry.npmmirror.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" @@ -8264,8 +8269,8 @@ jsesc@^2.5.1: jsesc@^3.0.2: version "3.0.2" - resolved "https://npm.vmic.xyz/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha1-u4sJpll7pCZCXy5KByRcPQC5ND4= + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== jsesc@~0.5.0: version "0.5.0" From 9f1e939dc2a891c3171aec7682afda2fb57d7d5a Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Fri, 1 Nov 2024 16:24:29 +0800 Subject: [PATCH 6/9] feat: add actions validator Signed-off-by: lileirjyb --- packages/hap-compiler/src/actions/index.js | 36 +++++++++++++++++++ .../hap-compiler/src/actions/validator.js | 30 ++++++++++++++++ packages/hap-compiler/src/index.js | 16 +++++++-- .../hap-dsl-xvm/src/loaders/action-loader.js | 11 +++--- .../hap-packager/src/post-handler/index.js | 8 +++-- 5 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 packages/hap-compiler/src/actions/index.js create mode 100644 packages/hap-compiler/src/actions/validator.js diff --git a/packages/hap-compiler/src/actions/index.js b/packages/hap-compiler/src/actions/index.js new file mode 100644 index 00000000..502c5cc3 --- /dev/null +++ b/packages/hap-compiler/src/actions/index.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021-present, the hapjs-platform Project Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { checkParams } from './validator' + +function parse(actionObj) { + /** + * "actionObj": { + "jumpWithParams": { + "type": "router", + "url": "{{jumpParamsUrl}}", + "params": { + "type": "{{jumpParam}}" + } + }, + } + */ + if (actionObj && Object.prototype.toString.call(actionObj) !== '[object Object]') { + throw new Error(` 事件 actions 必须为 Object 对象`) + } + + // 检查params参数合法性 + Object.keys(actionObj).forEach((key) => { + checkParams(actionObj[key].params, 1) + }) + + return { + jsonAction: actionObj + } +} + +export default { + parse +} diff --git a/packages/hap-compiler/src/actions/validator.js b/packages/hap-compiler/src/actions/validator.js new file mode 100644 index 00000000..ea69a8e3 --- /dev/null +++ b/packages/hap-compiler/src/actions/validator.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-present, the hapjs-platform Project Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import exp from '../template/exp' + +function isReserved(str) { + // $开头 + const c = (str + '').charCodeAt(0) + return c === 0x24 +} + +function checkParams(paramsObj, dep) { + if (!paramsObj || Object.prototype.toString.call(paramsObj) !== '[object Object]') return + + Object.keys(paramsObj).forEach((key) => { + if (isReserved(key)) { + throw new Error(` 事件 actions 中 params 参数名不能以 “$” 开头`) + } + + if (exp.isExpr(paramsObj[key]) && dep > 1) { + throw new Error(` 事件 actions 中 params 参数值只支持一级结构中绑定变量`) + } + + checkParams(paramsObj[key], dep + 1) + }) +} + +export { checkParams } diff --git a/packages/hap-compiler/src/index.js b/packages/hap-compiler/src/index.js index fb37116c..4e6ab2dc 100644 --- a/packages/hap-compiler/src/index.js +++ b/packages/hap-compiler/src/index.js @@ -6,11 +6,12 @@ 'use strict' import parse5 from 'parse5' import templater from './template' +import actioner from './actions' import styler from './style' import scripter from './script' import { serialize } from './utils' -export { scripter, styler, templater } +export { scripter, styler, templater, actioner } export * from './style' /** @@ -156,5 +157,16 @@ function parseScript(source) { return { parsed: parsed } } -export { parseFragmentsWithCache, parseTemplate, parseStyle, parseScript, serialize } +/** + * 解析actions + * @param {String} jsonObj - actions对象 + * @returns {Object} + */ +function parseActions(jsonObj) { + const { jsonAction } = actioner.parse(jsonObj) + const parsed = JSON.stringify(jsonAction) + return { parsed } +} + +export { parseFragmentsWithCache, parseTemplate, parseStyle, parseScript, parseActions, serialize } export { ENTRY_TYPE, FRAG_TYPE, isEmptyObject } from './utils' diff --git a/packages/hap-dsl-xvm/src/loaders/action-loader.js b/packages/hap-dsl-xvm/src/loaders/action-loader.js index 05177b8b..8932a7cd 100644 --- a/packages/hap-dsl-xvm/src/loaders/action-loader.js +++ b/packages/hap-dsl-xvm/src/loaders/action-loader.js @@ -2,14 +2,17 @@ * Copyright (c) 2024-present, the hapjs-platform Project Contributors * SPDX-License-Identifier: Apache-2.0 */ +import { parseActions } from '@hap-toolkit/compiler' export default function actionLoader(source) { - let jsonObj = {} + let actionStr = '' try { const obj = JSON.parse(source) - jsonObj = obj.actions || {} + const jsonObj = obj.actions || {} + const { parsed } = parseActions(jsonObj) + actionStr = parsed } catch (e) { - throw new Error(`Invalid in ${this.resourcePath}:: ${e}`) + throw new Error(`Invalid in ${this.resourcePath}\n${e}`) } - return `module.exports = ${JSON.stringify(jsonObj)}` + return `module.exports = ${actionStr}` } diff --git a/packages/hap-packager/src/post-handler/index.js b/packages/hap-packager/src/post-handler/index.js index 54a701f6..c0223775 100644 --- a/packages/hap-packager/src/post-handler/index.js +++ b/packages/hap-packager/src/post-handler/index.js @@ -289,9 +289,11 @@ export function postHandleLiteCardRes(liteCardRes) { for (let i = 0; i < uxList.length; i++) { const compName = uxList[i] const actionEvents = liteCardRes[compName][ACTIONS_KEY] - Object.keys(actionEvents).forEach((eventName) => { - postHandleActions(actionEvents[eventName]) - }) + if (actionEvents) { + Object.keys(actionEvents).forEach((eventName) => { + postHandleActions(actionEvents[eventName]) + }) + } } // 用于修改 template 的 key 的 stringify 的顺序 From 42baadc4a8bd0d70c29d914667965400c7fa228d Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Fri, 1 Nov 2024 16:35:15 +0800 Subject: [PATCH 7/9] fix: add dynamic component validation Signed-off-by: lileirjyb --- packages/hap-compiler/src/template/validator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/hap-compiler/src/template/validator.js b/packages/hap-compiler/src/template/validator.js index c56b9f89..c0e073c9 100644 --- a/packages/hap-compiler/src/template/validator.js +++ b/packages/hap-compiler/src/template/validator.js @@ -261,6 +261,7 @@ const tagNatives = { } }, component: { + supportCard: true, excludeRoot: true, attrs: { extendCommon: false, // 不支持通用属性 From a9e3abd09a8d1f29968f3bfb954b01919868410e Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Tue, 5 Nov 2024 10:22:44 +0800 Subject: [PATCH 8/9] fix: modify simple expression result Signed-off-by: lileirjyb --- packages/hap-compiler/src/template/index.js | 2 +- .../hap-packager/src/post-handler/index.js | 59 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/hap-compiler/src/template/index.js b/packages/hap-compiler/src/template/index.js index d8c81401..329db3e8 100644 --- a/packages/hap-compiler/src/template/index.js +++ b/packages/hap-compiler/src/template/index.js @@ -174,7 +174,7 @@ function traverse(node, output, previousNode, conditionList, options) { output.log.push({ line: node.__location.line, column: node.__location.col, - reason: `Warn: 组件 ${node.tagName} 不支持文本内容作为字节点` + reason: `Warn: 组件 ${node.tagName} 不支持文本内容作为子节点` }) } } diff --git a/packages/hap-packager/src/post-handler/index.js b/packages/hap-packager/src/post-handler/index.js index c0223775..8393635b 100644 --- a/packages/hap-packager/src/post-handler/index.js +++ b/packages/hap-packager/src/post-handler/index.js @@ -2,7 +2,6 @@ import { templateValueToCardCode } from '@aiot-toolkit/card-expression' import { templater } from '@hap-toolkit/compiler' const { validator } = templater -const literalValueRE = /^(?:true|false|null|undefined|Infinity|NaN|\d+)$/ const CARD_ENTRY = '#entry' const TYPE_IMPORT = 'import' // 需要进行后处理的模块key @@ -30,7 +29,7 @@ function markType(actions) { if (isExpr(actions.type)) { const prefixExpr = getPrefixExpr(actions.type) delete actions.type - actions[getExprKey('type', prefixExpr)] = prefixExpr + actions['$type'] = prefixExpr } } @@ -39,7 +38,7 @@ function markUrl(actions) { if (isExpr(actions.url)) { const prefixExpr = getPrefixExpr(actions.url) delete actions.url - actions[getExprKey('url', prefixExpr)] = prefixExpr + actions['$url'] = prefixExpr } } function markMethod(actions) { @@ -47,7 +46,7 @@ function markMethod(actions) { if (isExpr(actions.method)) { const prefixExpr = getPrefixExpr(actions.method) delete actions.method - actions[getExprKey('method', prefixExpr)] = prefixExpr + actions['$method'] = prefixExpr } } @@ -60,7 +59,7 @@ function markParams(actions) { if (isExpr(value)) { const prefixExpr = getPrefixExpr(value) delete actions.params[key] - actions.params[getExprKey(key, prefixExpr)] = prefixExpr + actions.params['$' + key] = prefixExpr } }) } @@ -105,7 +104,7 @@ function markIf(template) { if (isExpr(template.shown)) { const prefixExpr = getPrefixExpr(template.shown) delete template.shown - template[getExprKey('shown', prefixExpr)] = prefixExpr + template['$shown'] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) } } @@ -115,7 +114,7 @@ function markIs(template) { if (isExpr(template.is)) { const prefixExpr = getPrefixExpr(template.is) delete template.is - template[getExprKey('is', prefixExpr)] = prefixExpr + template['$is'] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) } } @@ -127,7 +126,7 @@ function markId(template) { if (isExpr(template.id)) { const prefixExpr = getPrefixExpr(template.id) delete template.id - template[getExprKey('id', prefixExpr)] = prefixExpr + template['$id'] = prefixExpr } // 节点有id属性,标记为kind=1 template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) @@ -138,30 +137,30 @@ function markFor(template) { /**
-> "repeat": { - "exp": "{{ItemList}}", + "$exp": "{{ItemList}}", "key": "index", "value": "item" },
-> "repeat": { - "exp": "[1,2,3]", + "$exp": "[\"~\",1,2,3]", "key": "index", "value": "item" }, */ const prefixExpr = getPrefixExpr(template.repeat.exp) delete template.repeat.exp - template.repeat[getExprKey('exp', prefixExpr)] = prefixExpr + template.repeat['$exp'] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) } else if (isExpr(template.repeat)) { /**
-> - "repeat": "{{ItemList}}", + "$repeat": "{{ItemList}}", */ const prefixExpr = getPrefixExpr(template.repeat) delete template.repeat - template[getExprKey('repeat', prefixExpr)] = prefixExpr + template['$repeat'] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.FRAGMENT) } } @@ -177,7 +176,7 @@ function markStyle(template) { if (isExpr(value)) { const prefixExpr = getPrefixExpr(value) delete template.style[key] - template.style[getExprKey(key, prefixExpr)] = prefixExpr + template.style['$' + key] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) } }) @@ -186,7 +185,7 @@ function markStyle(template) { if (isExpr(style)) { const prefixExpr = getPrefixExpr(style) delete template.style - template[getExprKey('style', prefixExpr)] = prefixExpr + template['$style'] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) } } @@ -207,7 +206,7 @@ function markClassList(template) { if (hasBinding) { // 如果 classList 元素有表达式 delete template.classList - template[getExprKey('classList', cList, template)] = cList + template['$classList'] = cList template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) } } @@ -227,7 +226,7 @@ function markAttrs(template) { if (isExpr(attrValue)) { const prefixExpr = getPrefixExpr(attrValue) delete attrs[attrKey] - attrs[getExprKey(attrKey, prefixExpr)] = prefixExpr + attrs['$' + attrKey] = prefixExpr template.kind = markKind(template.kind, ENUM_KIND_TYPE.ELEMENT) } }) @@ -245,21 +244,18 @@ function isObject(obj) { function getPrefixExpr(expr) { const res = templateValueToCardCode(expr) - // 表达式是保留字面量,返回 parse 后的结果。如: {{true}} -> true - if (literalValueRE.test(res)) { - return JSON.parse(res) + const parsed = JSON.parse(res) + if (isSimpleExpr(parsed)) { + // 表达式是简单标识符,直接返回模板字符串。如: {{ name }} -> {{ name }} + return expr + } else { + // 表达式为复杂表达式,返回前缀表达式字符串。如: {{ $item.name }} -> "[\".\",[\"$\",\"$item\"],\"name\"]" + return res } - // 表达式非保留字面量,返回字符串。如:-> 如: {{ myVal }} -> "['$', 'myVal']" - return res } -function getExprKey(key, expr) { - // 表达式是保留字面量或常数,key无需加$ -> 如: "value": true 或 "value": 10 - if (literalValueRE.test(expr)) { - return key - } - // 表达式非保留字面量,key加$表示 -> 如: "$value": "['$', 'myVal']" - return `$${key}` +function isSimpleExpr(expr) { + return Array.isArray(expr) && expr.length === 2 && expr[0] === '$' } function recordKeys(liteCardRes, templateKeys) { @@ -296,13 +292,14 @@ export function postHandleLiteCardRes(liteCardRes) { } } - // 用于修改 template 的 key 的 stringify 的顺序 + // 用于修改 template 的 key 的 stringify 的顺序,type放第一个,children放最后一个 let templateKeys = [] recordKeys(liteCardRes, templateKeys) templateKeys = [...new Set(templateKeys.sort())] - .filter((key) => key !== 'children') + .filter((key) => key !== 'children' && key !== 'type') .concat('children') + .unshift('type') return JSON.stringify(liteCardRes, templateKeys) } From ff4c8d4dd9dc68d56628061ccb631e65a62c24ee Mon Sep 17 00:00:00 2001 From: lileirjyb Date: Tue, 5 Nov 2024 14:46:25 +0800 Subject: [PATCH 9/9] fix: update expresssion npm lib version Signed-off-by: lileirjyb --- packages/hap-packager/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/hap-packager/package.json b/packages/hap-packager/package.json index 6396fab4..aa486db3 100644 --- a/packages/hap-packager/package.json +++ b/packages/hap-packager/package.json @@ -29,7 +29,7 @@ "babel.tree.config.js" ], "dependencies": { - "@aiot-toolkit/card-expression": "^1.0.7", + "@aiot-toolkit/card-expression": "^1.0.8", "@babel/core": "^7.9.6", "@babel/generator": "^7.9.6", "@babel/parser": "^7.9.6", diff --git a/yarn.lock b/yarn.lock index 5bf1b3a5..64502613 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@aiot-toolkit/card-expression@^1.0.7": - version "1.0.7" - resolved "https://registry.npmjs.org/@aiot-toolkit/card-expression/-/card-expression-1.0.7.tgz#7f892bfcf588f3e813ffd6a2c6019d38f36328e6" - integrity sha512-QwcTI2zfQTxyKQd1wFRhHm99iamxDFM83zXIdTmkCwjLafjSSxXdVQ5po0MEGBfgbS7Ox8xNWPFfs5REhUJIMA== +"@aiot-toolkit/card-expression@^1.0.8": + version "1.0.8" + resolved "https://registry.npmmirror.com/@aiot-toolkit/card-expression/-/card-expression-1.0.8.tgz#a1e8722caaff73691ba17c40bfa5bd622de8e520" + integrity sha512-AaVVN4EabxLOds/DpFdGzmYbs0VatzE6FADBmsEQRp+eo+pXpWuiVltqEd5dydOuidvZPQ4aG1cW+G0Z8Cuvog== dependencies: "@babel/generator" "^7.25.6" "@babel/parser" "^7.25.6"