@aiot-toolkit/aiotpack 2.1.0-prender.7 → 2.1.0-prender.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,126 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
13
13
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
14
  // Template.json compilation from parse5 AST
15
15
 
16
+ // vivo blueos-pack's aggressive camelCase applied to transition-property values.
17
+ // Every letter that follows a non-letter character (separator or digit) is upper-cased,
18
+ // then all non-alphanumeric characters are dropped. This intentionally covers both the
19
+ // simple case (`background-position` → `backgroundPosition`) and the odd shorthand-inside-
20
+ // transition-property form we see in inline styles (`transform-origin 0.2s ease-in-out .5s`
21
+ // → `transformOrigin02SEaseInOut5S`).
22
+ function normalizeTransitionPropertyValue(s) {
23
+ let out = '';
24
+ let capNext = false;
25
+ for (const ch of s) {
26
+ if (/[A-Za-z]/.test(ch)) {
27
+ out += capNext ? ch.toUpperCase() : ch;
28
+ capNext = false;
29
+ } else if (/[0-9]/.test(ch)) {
30
+ out += ch;
31
+ capNext = true;
32
+ } else {
33
+ capNext = true;
34
+ }
35
+ }
36
+ return out;
37
+ }
38
+
39
+ // vivo blueos-pack's inline `animation-timing-function` normalization.
40
+ // Returns:
41
+ // - string → the normalized value to emit
42
+ // - null (drop) → invalid; caller should skip this inline decl so the class-level
43
+ // value (or default) applies
44
+ // Rules (traced empirically from build-vivo/watch-round baseline):
45
+ // - Predefined keywords (`linear`, `ease`, `ease-in`, `ease-out`, `ease-in-out`,
46
+ // `step-start`, `step-end`) → keep as-is
47
+ // - `steps(N)` → normalize to `steps(N, end)` (fill default dir)
48
+ // - `steps(N, start|end)` → keep as-is
49
+ // - `cubic-bezier(a, b, c, d)`
50
+ // * all 4 numeric and x1,x2 ∈ [0,1] → keep (spec-compliant)
51
+ // * otherwise → drop (invalid per vivo validator)
52
+ // - Any other unrecognized function → drop
53
+ function normalizeAnimationTimingFunction(s) {
54
+ const trimmed = s.trim();
55
+ if (['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end'].includes(trimmed)) return trimmed;
56
+ const stepsMatch = /^steps\s*\(\s*([^,)]+?)\s*(?:,\s*(start|end)\s*)?\)$/.exec(trimmed);
57
+ if (stepsMatch) {
58
+ const n = stepsMatch[1];
59
+ const dir = stepsMatch[2] || 'end';
60
+ return `steps(${n}, ${dir})`;
61
+ }
62
+ const cubicMatch = /^cubic-bezier\s*\(\s*([^)]+)\)$/.exec(trimmed);
63
+ if (cubicMatch) {
64
+ const args = cubicMatch[1].split(',').map(a => a.trim());
65
+ if (args.length !== 4) return null;
66
+ const nums = args.map(a => Number(a));
67
+ if (nums.some(n => !isFinite(n))) return null;
68
+ // spec: x1 and x2 must be in [0, 1]; y1 and y2 unbounded. Reproduce vivo behavior:
69
+ // any component outside [0, 1] is treated as invalid (vivo is stricter than spec).
70
+ if (nums.some(n => n < 0 || n > 1)) return null;
71
+ return `cubic-bezier(${args.join(', ')})`;
72
+ }
73
+ return null;
74
+ }
75
+
76
+ // `transform: translate(40px) scale(0.5) ...` (CSS function-list from inline style="")
77
+ // → `{"translateX":"40px","translateY":"0px","scaleX":0.5,"scaleY":0.5}`.
78
+ // Returns null when input isn't a plain CSS function list (already JSON, dynamic
79
+ // binding, or unknown function), so callers can fall back to the raw string.
80
+ function normalizeCssTransform(input) {
81
+ if (typeof input !== 'string') return null;
82
+ const s = input.trim();
83
+ if (!s || s.startsWith('{') || /\{\{/.test(s)) return null;
84
+ const out = {};
85
+ const funcRe = /(\w+)\s*\(([^)]*)\)/g;
86
+ let matched = false;
87
+ let m;
88
+ const toNum = x => /^-?\d+(?:\.\d+)?$/.test(x) ? Number(x) : x;
89
+ // Add a CSS unit to bare numeric arguments; keep existing unit / percent forms.
90
+ const withUnit = (x, defaultUnit) => {
91
+ if (x === undefined) return '0' + defaultUnit;
92
+ return /^-?\d+(?:\.\d+)?$/.test(x) ? x + defaultUnit : x;
93
+ };
94
+ while ((m = funcRe.exec(s)) !== null) {
95
+ matched = true;
96
+ const fn = m[1];
97
+ const args = m[2].split(',').map(a => a.trim()).filter(Boolean);
98
+ switch (fn) {
99
+ case 'translate':
100
+ out.translateX = withUnit(args[0], 'px');
101
+ out.translateY = withUnit(args[1], 'px');
102
+ break;
103
+ case 'scale':
104
+ {
105
+ const x = args[0] ?? '1';
106
+ out.scaleX = toNum(x);
107
+ out.scaleY = args[1] !== undefined ? toNum(args[1]) : toNum(x);
108
+ break;
109
+ }
110
+ case 'translateX':
111
+ case 'translateY':
112
+ case 'translateZ':
113
+ out[fn] = withUnit(args[0], 'px');
114
+ break;
115
+ case 'scaleX':
116
+ case 'scaleY':
117
+ out[fn] = toNum(args[0] ?? '1');
118
+ break;
119
+ case 'rotate':
120
+ case 'rotateX':
121
+ case 'rotateY':
122
+ case 'rotateZ':
123
+ case 'skew':
124
+ case 'skewX':
125
+ case 'skewY':
126
+ // Only the first arg is meaningful for single-axis rotate/skew; vivo silently
127
+ // discards extras (e.g. `rotate(90deg,120deg)` → `rotate: "90deg"`). We follow suit.
128
+ out[fn] = withUnit(args[0], 'deg');
129
+ break;
130
+ default:
131
+ return null;
132
+ }
133
+ }
134
+ return matched ? JSON.stringify(out) : null;
135
+ }
16
136
  function uxToTemplateJson(templateNode, styleObjectId, styleSheet, importMap, pageDir) {
17
137
  const children = templateNode.content?.childNodes || templateNode.childNodes || [];
18
138
  const rootEl = children.find(n => n.tagName && n.nodeName !== '#text');
@@ -485,7 +605,17 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
485
605
  hasDynamicInlineStyle = true;
486
606
  } else {
487
607
  // Static value — accumulate to staticInlineStyle for merging into node.style later
488
- staticInlineStyle[key] = propVal;
608
+ if (key === 'transform') {
609
+ staticInlineStyle[key] = normalizeCssTransform(propVal) ?? propVal;
610
+ } else if (key === 'transitionProperty') {
611
+ staticInlineStyle[key] = normalizeTransitionPropertyValue(propVal);
612
+ } else if (key === 'animationTimingFunction' || key === 'transitionTimingFunction') {
613
+ const normalized = normalizeAnimationTimingFunction(propVal);
614
+ if (normalized !== null) staticInlineStyle[key] = normalized;
615
+ // else drop — invalid inline value; class-level rule (or default) applies
616
+ } else {
617
+ staticInlineStyle[key] = propVal;
618
+ }
489
619
  }
490
620
  }
491
621
  });
@@ -581,28 +711,45 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
581
711
  hasBind = true;
582
712
  hasDynClass = true;
583
713
  }
584
- if (isComponent) {
585
- node.bind = 3;
586
- node['import'] = importMap[el.tagName];
587
- } else if (node['$shown'] !== undefined || node['$repeat'] || node['repeat']?.['$exp']) {
714
+ if (node['$shown'] !== undefined || node['$repeat'] !== undefined || node['repeat']?.['$exp']) {
588
715
  node.bind = 2;
716
+ } else if (isComponent) {
717
+ node.bind = 3;
589
718
  } else if (hasBind) {
590
719
  node.bind = 1;
591
720
  }
721
+ if (isComponent) {
722
+ node['import'] = importMap[el.tagName];
723
+ }
592
724
  if (!hasDynClass && className) {
593
725
  node.class = className;
594
726
  }
595
727
  if (Object.keys(events).length) node.events = events;
596
- // Inline style from styleSheet (class selector + tag selector) + static portion of style="..."
597
728
  // Inline style from styleSheet (class selector + tag selector) + static portion of style="...".
598
- // Only base (non-@media) selectors are inlined; @media rules are applied at runtime by SDK.
729
+ // - Static class only: merge tag + class rules + staticInlineStyle → node.style
730
+ // - Dynamic class (partly {{}}): vivo emits merged tag + STATIC class rules into
731
+ // node.inlineStyle (alongside per-key $foo bindings),
732
+ // so the runtime still applies them even though the
733
+ // class list is only known at runtime.
734
+ // - Fully dynamic (only {{...}}): no static class tokens → tag rules only, still into
735
+ // inlineStyle.
736
+ // @media rules are applied at runtime by SDK, not inlined here.
599
737
  // Class rules apply in stylesheet declaration order (later declarations override earlier),
600
738
  // NOT in class-attribute order.
601
739
  if (styleSheet) {
602
740
  const merged = {};
603
741
  if (styleSheet[el.tagName]) Object.assign(merged, styleSheet[el.tagName]);
604
- if (className && !hasDynClass) {
605
- const classSet = new Set(className.split(/\s+/).filter(Boolean));
742
+ // Collect class tokens that are LITERAL strings (skip `{{ ... }}` binding parts).
743
+ const staticClassTokens = [];
744
+ if (className) {
745
+ const parts = className.split(/(\{\{[\s\S]*?\}\})/).filter(Boolean);
746
+ for (const p of parts) {
747
+ if (/^\{\{[\s\S]*?\}\}$/.test(p)) continue;
748
+ for (const tok of p.trim().split(/\s+/)) if (tok) staticClassTokens.push(tok);
749
+ }
750
+ }
751
+ if (staticClassTokens.length) {
752
+ const classSet = new Set(staticClassTokens);
606
753
  for (const key of Object.keys(styleSheet)) {
607
754
  if (key.length > 1 && key.startsWith('.') && classSet.has(key.slice(1))) {
608
755
  Object.assign(merged, styleSheet[key]);
@@ -610,7 +757,22 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
610
757
  }
611
758
  }
612
759
  Object.assign(merged, staticInlineStyle);
613
- if (Object.keys(merged).length) node.style = merged;
760
+ if (merged.animationName && styleSheet['@KEYFRAMES']?.[merged.animationName]) {
761
+ merged.animationKeyframes = {
762
+ [merged.animationName]: styleSheet['@KEYFRAMES'][merged.animationName]
763
+ };
764
+ }
765
+ if (Object.keys(merged).length) {
766
+ if (hasDynClass) {
767
+ const existing = node['inlineStyle'] || {};
768
+ node['inlineStyle'] = {
769
+ ...merged,
770
+ ...existing
771
+ };
772
+ } else {
773
+ node.style = merged;
774
+ }
775
+ }
614
776
  } else if (Object.keys(staticInlineStyle).length) {
615
777
  node.style = {
616
778
  ...staticInlineStyle
@@ -238,6 +238,30 @@ class ViteCompiler {
238
238
  }
239
239
  },
240
240
  plugins: [{
241
+ name: 'image-asset-string',
242
+ enforce: 'pre',
243
+ load(id) {
244
+ // require("./x.png") in .ux <script> must resolve to a path string,
245
+ // matching webpack file-loader / vivo blueos-pack semantics. Without
246
+ // this hook rolldown treats each image as an ES module and emits
247
+ // `(init_x(), __toCommonJS(x_exports))` — the site assigns
248
+ // `{ default: fn }` to src and runtime cannot render.
249
+ const match = id.match(/\.(png|jpe?g|gif|svg|webp|bmp|ico)(\?.*)?$/i);
250
+ if (!match) return null;
251
+ const filePath = id.replace(/\?.*$/, '');
252
+ if (!_fsExtra.default.existsSync(filePath)) return null;
253
+ const relFromSrc = _path.default.relative(srcPath, filePath).split(_path.default.sep).join('/');
254
+ // Assets outside src/ (typically node_modules) fall through to the
255
+ // default asset handler — the caller is responsible for their path
256
+ // resolution. Vivo blueos-pack + webpack file-loader use rpk-root
257
+ // absolute paths (`/common/...`, `/dynamicAssets/name-hash.png`);
258
+ // runtime <image src> resolves against the rpk root, so relative
259
+ // `./common/...` from a nested page directory would misdirect.
260
+ if (relFromSrc.startsWith('..')) return null;
261
+ const runtimePath = '/' + relFromSrc;
262
+ return `module.exports = ${JSON.stringify(runtimePath)};`;
263
+ }
264
+ }, {
241
265
  name: "cjs-shim",
242
266
  enforce: "pre",
243
267
  load(id) {
@@ -36,8 +36,11 @@ declare class ZipUtil {
36
36
  * Build the blueos-pack compatible "wrapped release" RPK.
37
37
  * Outer RPK (zip) contains:
38
38
  * - manifest.json (top-level metadata)
39
- * - logo.<ext> (icon - kept as png since VUG conversion is out of scope)
39
+ * - <icon-basename>.<ext> (icon - kept as png since VUG conversion is out of scope,
40
+ * basename taken from manifest.icon so runtime + IDE previews can locate it)
40
41
  * - META-INF/CERT (signature)
42
+ * - META-INF/build.txt (key=value pipeline info; includes `json=true`
43
+ * so downstream can distinguish this from legacy webpack output)
41
44
  * - <package>.vru (multi-file VRU containing all build artifacts)
42
45
  */
43
46
  private static createWrappedReleaseRpk;
@@ -88,8 +88,11 @@ class ZipUtil {
88
88
  * Build the blueos-pack compatible "wrapped release" RPK.
89
89
  * Outer RPK (zip) contains:
90
90
  * - manifest.json (top-level metadata)
91
- * - logo.<ext> (icon - kept as png since VUG conversion is out of scope)
91
+ * - <icon-basename>.<ext> (icon - kept as png since VUG conversion is out of scope,
92
+ * basename taken from manifest.icon so runtime + IDE previews can locate it)
92
93
  * - META-INF/CERT (signature)
94
+ * - META-INF/build.txt (key=value pipeline info; includes `json=true`
95
+ * so downstream can distinguish this from legacy webpack output)
93
96
  * - <package>.vru (multi-file VRU containing all build artifacts)
94
97
  */
95
98
  static async createWrappedReleaseRpk(param, config, files, signConfig) {
@@ -146,14 +149,16 @@ class ZipUtil {
146
149
  });
147
150
  }
148
151
 
149
- // Add icon (as-is, no VUG conversion)
152
+ // Add icon (as-is, no VUG conversion). Vivo blueos-pack preserves the icon
153
+ // basename from manifest.icon (e.g. `/resources/base/media/icon_small.png`
154
+ // → `icon_small.vug` at rpk root) so runtime + IDE previews can look it up
155
+ // by the manifest-declared name.
150
156
  if (config.icon) {
151
157
  const iconPath = _path.default.join(buildDir, config.icon.replace(/^\//, ''));
152
158
  if (_fsExtra.default.existsSync(iconPath)) {
153
159
  const iconBuf = _fsExtra.default.readFileSync(iconPath);
154
- const iconExt = _path.default.extname(iconPath) || '.png';
155
160
  outerPackage.addResource({
156
- fileBuildPath: `logo${iconExt}`,
161
+ fileBuildPath: _path.default.basename(iconPath),
157
162
  fileContentBuffer: iconBuf,
158
163
  fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(iconBuf)
159
164
  });
@@ -167,6 +172,18 @@ class ZipUtil {
167
172
  fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(vruBuffer)
168
173
  });
169
174
 
175
+ // Add META-INF/build.txt — key=value lines matching what UxAfterCompile
176
+ // writes for flat-mode rpks. Historical downstream (IDE preview, cli
177
+ // rebuild) reads this to identify the source toolkit / pipeline.
178
+ const buildInfo = this.createComment(param);
179
+ const buildTxt = Object.keys(buildInfo).map(k => `${k}=${buildInfo[k]}`).join('\n');
180
+ const buildTxtBuf = Buffer.from(buildTxt, 'utf-8');
181
+ outerPackage.addResource({
182
+ fileBuildPath: ZipUtil.BUILD_FILE_PATH,
183
+ fileContentBuffer: buildTxtBuf,
184
+ fileContentDigest: _sharedUtils.CommonUtil.calcDataDigest(buildTxtBuf)
185
+ });
186
+
170
187
  // Add CERT if present
171
188
  const certAbsPath = _path.default.join(buildDir, ZipUtil.CERT_PATH);
172
189
  if (_fsExtra.default.existsSync(certAbsPath)) {
@@ -367,7 +384,8 @@ class ZipUtil {
367
384
  node: process.version,
368
385
  platform: process.platform,
369
386
  arch: process.arch,
370
- component: true
387
+ component: true,
388
+ json: true
371
389
  };
372
390
  return obj;
373
391
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/aiotpack",
3
- "version": "2.1.0-prender.7",
3
+ "version": "2.1.0-prender.9",
4
4
  "description": "The process tool for packaging aiot projects.",
5
5
  "keywords": [
6
6
  "aiotpack"
@@ -19,16 +19,16 @@
19
19
  "test": "node ./__tests__/aiotpack.test.js"
20
20
  },
21
21
  "dependencies": {
22
- "@aiot-toolkit/generator": "2.1.0-prender.7",
23
- "@aiot-toolkit/parser": "2.1.0-prender.7",
24
- "@aiot-toolkit/shared-utils": "2.1.0-prender.7",
22
+ "@aiot-toolkit/generator": "2.1.0-prender.9",
23
+ "@aiot-toolkit/parser": "2.1.0-prender.9",
24
+ "@aiot-toolkit/shared-utils": "2.1.0-prender.9",
25
25
  "@hap-toolkit/aaptjs": "^2.0.0",
26
26
  "@rspack/core": "^1.3.9",
27
27
  "acorn": "^8.16.0",
28
28
  "aiot-parse5": "^1.0.2",
29
29
  "astring": "^1.9.0",
30
30
  "babel-loader": "^9.1.3",
31
- "file-lane": "2.1.0-prender.7",
31
+ "file-lane": "2.1.0-prender.9",
32
32
  "file-loader": "^6.2.0",
33
33
  "fs-extra": "^11.2.0",
34
34
  "jsrsasign": "^11.1.0",