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

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.
@@ -281,6 +281,50 @@ function addThisPrefix(expr, extraReserved) {
281
281
  }).join('');
282
282
  }
283
283
  }
284
+ function emitTextValueAttr(rawText) {
285
+ const text = rawText.trim();
286
+ if (!/\{\{[\s\S]*?\}\}/.test(text)) {
287
+ return {
288
+ attr: {
289
+ value: text.replace(/\s*\n\s*/g, ' ')
290
+ },
291
+ needsBind: false
292
+ };
293
+ }
294
+ const parts = text.split(/(\{\{[\s\S]*?\}\})/);
295
+ const exprs = parts.filter(Boolean).map(p => {
296
+ const m = p.match(/^\{\{([\s\S]*?)\}\}$/);
297
+ if (m) return addThisPrefix(m[1].trim());
298
+ return `'${p.replace(/'/g, "\\'")}'`;
299
+ });
300
+ if (exprs.length === 1 && pathTestRE.test(exprs[0].replace(/^this\./, ''))) {
301
+ return {
302
+ attr: {
303
+ $value: exprs[0].replace(/^this\./, '')
304
+ },
305
+ needsBind: true
306
+ };
307
+ }
308
+ if (exprs.length === 1) {
309
+ return {
310
+ attr: {
311
+ $value: `function () { return ${exprs[0]}; }`
312
+ },
313
+ needsBind: true
314
+ };
315
+ }
316
+ const wrapped = exprs.map(e => {
317
+ if (e.startsWith("'") || e.startsWith('"')) return e;
318
+ if (/(\|\||&&|\?\s|\+)/.test(e) && !e.startsWith('(')) return `(${e})`;
319
+ return e;
320
+ });
321
+ return {
322
+ attr: {
323
+ $value: `function () { return '' + ${wrapped.join(' + ')}; }`
324
+ },
325
+ needsBind: true
326
+ };
327
+ }
284
328
  function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
285
329
  const tagName = el.tagName === 'img' ? 'image' : el.tagName;
286
330
  const node = {
@@ -491,8 +535,11 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
491
535
  hasBind = true;
492
536
  } else {
493
537
  let staticVal = value ? value.replace(/ {2,}/g, ' ') : '';
494
- // Normalize relative path attrs (src, href) to absolute paths from src root
495
- if (staticVal && (name === 'src' || name === 'href') && !staticVal.startsWith('/') && !/^[a-z]+:/.test(staticVal) && pageDir) {
538
+ // Normalize relative path attrs (src, href, alt) to absolute paths from src root.
539
+ // alt can also carry prose (e.g. "blank") only rewrite when the value starts with
540
+ // ./ or ../ so image alt-text prose passes through untouched.
541
+ const isRelPath = staticVal.startsWith('./') || staticVal.startsWith('../');
542
+ if (staticVal && isRelPath && (name === 'src' || name === 'href' || name === 'alt') && pageDir) {
496
543
  // Resolve relative to pageDir, then make absolute from src root
497
544
  const segments = pageDir.split('/').filter(Boolean).concat(staticVal.split('/'));
498
545
  const stack = [];
@@ -509,34 +556,9 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
509
556
  for (const child of el.childNodes || []) {
510
557
  const textElements = new Set(['text', 'span', 'a', 'input', 'qrcode', 'barcode', 'marquee', 'arc-text', 'option', 'button', 'label', 'textarea', 'richtext']);
511
558
  if (child.value?.trim() && !child.tagName && !(el.childNodes || []).some(c => c.tagName) && textElements.has(tagName)) {
512
- // Collapse whitespace for static text; dynamic text is trimmed separately
513
- const text = child.value;
514
- if (/\{\{.*\}\}/.test(text)) {
515
- // Build function expression from template literal
516
- // Trim surrounding whitespace before splitting (vivo ignores indentation whitespace)
517
- const trimmedText = text.trim();
518
- const parts = trimmedText.split(/(\{\{[\s\S]*?\}\})/);
519
- const exprs = parts.filter(Boolean).map(p => {
520
- const m = p.match(/^\{\{([\s\S]*?)\}\}$/);
521
- if (m) return addThisPrefix(m[1].trim());
522
- return `'${p.replace(/'/g, "\\'")}'`;
523
- });
524
- if (exprs.length === 1 && pathTestRE.test(exprs[0].replace(/^this\./, ''))) {
525
- attrObj['$value'] = exprs[0].replace(/^this\./, '');
526
- } else if (exprs.length === 1) {
527
- attrObj['$value'] = `function () { return ${exprs[0]}; }`;
528
- } else {
529
- const wrappedExprs = exprs.map(e => {
530
- if (e.startsWith("'") || e.startsWith('"')) return e;
531
- if (/(\|\||&&|\?\s|\+)/.test(e) && !e.startsWith('(')) return `(${e})`;
532
- return e;
533
- });
534
- attrObj['$value'] = `function () { return '' + ${wrappedExprs.join(' + ')}; }`;
535
- }
536
- hasBind = true;
537
- } else {
538
- attrObj['value'] = text.replace(/\s*\n\s*/g, ' ');
539
- }
559
+ const emitted = emitTextValueAttr(child.value);
560
+ Object.assign(attrObj, emitted.attr);
561
+ if (emitted.needsBind) hasBind = true;
540
562
  }
541
563
  }
542
564
  // Output in vivo order: attr → bind → class → events → import → style → children
@@ -572,13 +594,19 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
572
594
  }
573
595
  if (Object.keys(events).length) node.events = events;
574
596
  // Inline style from styleSheet (class selector + tag selector) + static portion of style="..."
597
+ // Inline style from styleSheet (class selector + tag selector) + static portion of style="...".
575
598
  // Only base (non-@media) selectors are inlined; @media rules are applied at runtime by SDK.
599
+ // Class rules apply in stylesheet declaration order (later declarations override earlier),
600
+ // NOT in class-attribute order.
576
601
  if (styleSheet) {
577
602
  const merged = {};
578
603
  if (styleSheet[el.tagName]) Object.assign(merged, styleSheet[el.tagName]);
579
604
  if (className && !hasDynClass) {
580
- for (const cls of className.split(/\s+/)) {
581
- if (cls && styleSheet[`.${cls}`]) Object.assign(merged, styleSheet[`.${cls}`]);
605
+ const classSet = new Set(className.split(/\s+/).filter(Boolean));
606
+ for (const key of Object.keys(styleSheet)) {
607
+ if (key.length > 1 && key.startsWith('.') && classSet.has(key.slice(1))) {
608
+ Object.assign(merged, styleSheet[key]);
609
+ }
582
610
  }
583
611
  }
584
612
  Object.assign(merged, staticInlineStyle);
@@ -597,14 +625,12 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
597
625
  if (child.tagName) {
598
626
  childNodes.push(elementToJson(child, styleSheet, importMap, scopeVars, pageDir));
599
627
  } else if (isMixedContent && child.value?.trim()) {
600
- // Wrap text nodes in <span> for mixed content
601
- const text = child.value.replace(/[ \t]*\n[ \t]*/g, ' ').replace(/ {2,}/g, ' ');
628
+ const emitted = emitTextValueAttr(child.value);
602
629
  const spanNode = {
603
630
  type: 'span',
604
- attr: {
605
- value: text
606
- }
631
+ attr: emitted.attr
607
632
  };
633
+ if (emitted.needsBind) spanNode.bind = 1;
608
634
  childNodes.push(spanNode);
609
635
  }
610
636
  }
@@ -10,4 +10,8 @@ declare class ViteCompiler implements ICompiler {
10
10
  compile(param: IJavascriptCompileOption): Promise<void>;
11
11
  clean(param: ICompileParam & IJavascriptCompileOption): Promise<void>;
12
12
  }
13
+ declare function stripDeadDefProp(code: string): string;
14
+ export declare const __test: {
15
+ stripDeadDefProp: typeof stripDeadDefProp;
16
+ };
13
17
  export default ViteCompiler;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = void 0;
6
+ exports.default = exports.__test = void 0;
7
7
  var _TemplateCompiler = require("./TemplateCompiler");
8
8
  var _path = _interopRequireDefault(require("path"));
9
9
  var _fsExtra = _interopRequireDefault(require("fs-extra"));
@@ -208,6 +208,16 @@ class ViteCompiler {
208
208
  external: id => id.startsWith('@app-module/') || /^@(system|service|android|hap)\./.test(id),
209
209
  output: {
210
210
  globals: id => `$app_require$("@app-module/${id.replace(/^@(system|service|android|hap|app-module)\./, '')}")`
211
+ },
212
+ onwarn(warning, defaultHandler) {
213
+ // Rolldown silently emits `(void 0)()` for namespace-member accesses whose
214
+ // name is not exported by the target module — leaves runtime with an opaque
215
+ // TypeError instead of the source identifier. Escalate to a build error so
216
+ // the source location surfaces before ship.
217
+ if (warning.code === 'IMPORT_IS_UNDEFINED') {
218
+ throw new Error(`[toolkit] ${warning.message ?? 'IMPORT_IS_UNDEFINED'} — this would silently emit (void 0)() at the call site.`);
219
+ }
220
+ defaultHandler(warning);
211
221
  }
212
222
  },
213
223
  commonjsOptions: {
@@ -249,6 +259,26 @@ class ViteCompiler {
249
259
  map: null
250
260
  } : null;
251
261
  }
262
+ }, {
263
+ name: 'namespace-preserve',
264
+ enforce: 'pre',
265
+ transform(code, id) {
266
+ if (!/import\s*\*\s*as\s+\w+\s+from/.test(code)) return null;
267
+ // Rolldown resolves `import * as L from './x'; L.foo()` at compile time —
268
+ // if `foo` isn't exported by `./x` the call collapses to `(void 0)()` and
269
+ // the source identifier is lost. Rewriting the alias to a runtime variable
270
+ // (with webpack-style default-interop) preserves the identifier so runtime
271
+ // errors point at the actual source name.
272
+ const out = code.replace(/import\s*\*\s*as\s+(\w+)\s+from\s+(['"][^'"]+['"])/g, (_m, local, src) => {
273
+ const ns = `__${local}_ns`,
274
+ key = `__${local}_key`;
275
+ return `import * as ${ns} from ${src}; const ${key} = 'default'; const ${local} = ${ns}[${key}] != null ? ${ns}[${key}] : ${ns};`;
276
+ });
277
+ return out !== code ? {
278
+ code: out,
279
+ map: null
280
+ } : null;
281
+ }
252
282
  }, {
253
283
  name: 'ux-loader',
254
284
  resolveId(source, importer) {
@@ -432,10 +462,13 @@ class ViteCompiler {
432
462
  });
433
463
  }
434
464
  }
435
- if (entryAttempted > 0 && entryFailures.length === entryAttempted) {
465
+ if (entryFailures.length > 0) {
466
+ // Any per-entry failure fails the whole build — silently shipping an rpk with
467
+ // one page missing is exactly the class of bug that lets `(void 0)()` /
468
+ // empty jsc / dropped app.js leak to users.
436
469
  const sample = entryFailures.slice(0, 3).map(f => ` ${f.entry}: ${f.error}`).join('\n');
437
470
  const more = entryFailures.length > 3 ? `\n ...and ${entryFailures.length - 3} more` : '';
438
- throw new Error(`vite build failed for all ${entryAttempted} entry page(s); no JS was emitted.\n` + `First failures:\n${sample}${more}`);
471
+ throw new Error(`vite build failed for ${entryFailures.length}/${entryAttempted} entry page(s).\n` + `Failures:\n${sample}${more}`);
439
472
  }
440
473
  // Second pass: compile ALL .ux files that weren't entry pages
441
474
  // Each .ux file gets its own template.json + css.json + js
@@ -531,6 +564,10 @@ class ViteCompiler {
531
564
  _UxCompileUtil.default.clean([outputPath, releasePath].map(item => _path.default.resolve(projectPath, item)));
532
565
  }
533
566
  }
567
+ function stripDeadDefProp(code) {
568
+ const withoutDefine = code.replace(/var __defProp = Object\.defineProperty;\n?/g, '');
569
+ return /\b__defProp\s*\(/.test(withoutDefine) ? code : withoutDefine;
570
+ }
534
571
  function wrapOutput(code, entryName, isApp) {
535
572
  const componentName = _path.default.parse(entryName).name;
536
573
  const defineId = isApp ? '@app-component/app' : `@app-component/${componentName}`;
@@ -561,8 +598,10 @@ function wrapOutput(code, entryName, isApp) {
561
598
  // Remove rolldown region comments
562
599
  code = code.replace(/\/\/#region[^\n]*\n/g, '');
563
600
  code = code.replace(/\/\/#endregion[^\n]*/g, '');
564
- // Remove rolldown runtime helpers (not needed in final output)
565
- code = code.replace(/var __defProp = Object\.defineProperty;\n?/g, '');
601
+ // Remove rolldown runtime helpers. __defProp is only dead when nothing else calls it;
602
+ // when the CJS→ESM interop chain (__toCommonJS / __copyProps / __publicField) survives
603
+ // tree-shaking, __defProp is referenced by those helpers and must stay.
604
+ code = stripDeadDefProp(code);
566
605
  code = code.replace(/var __exportAll = [\s\S]*?return target;\s*\};\n?/g, '');
567
606
  // Inline __exportAll calls: __exportAll({key: () => value, ...}) -> {key: value, ...}
568
607
  // Match __exportAll(...) or /* @__PURE__ */ __exportAll(...)
@@ -603,4 +642,7 @@ $app_define$("${defineId}", [], function($app_require$, $app_exports$, $app_modu
603
642
  $app_bootstrap$("${bootstrapId}");
604
643
  `;
605
644
  }
645
+ const __test = exports.__test = {
646
+ stripDeadDefProp
647
+ };
606
648
  var _default = exports.default = ViteCompiler;
@@ -20,6 +20,7 @@ class Jsc {
20
20
  projectPath,
21
21
  buildPath
22
22
  } = this;
23
+ const beforeJsCount = Jsc.listFiles(buildPath, '.js').length;
23
24
  return _sharedUtils.CommonUtil.requireNodeModule(projectPath, `@aiot-toolkit/jsc`).then(async module => {
24
25
  this.logBeforeInvoke();
25
26
  const start = Date.now();
@@ -32,6 +33,15 @@ class Jsc {
32
33
  }
33
34
  this.logAfterInvoke(elapsed);
34
35
  }).then(() => {
36
+ // aiotjsc silently exits 0 on Windows when the buildPath contains CJK chars
37
+ // (win32_aiotjsc.exe uses ANSI POSIX APIs) — leaves 0 .jsc while the delete step
38
+ // below removes all .js, shipping an empty VRU. Fail loud before the deletion
39
+ // runs so the source path issue surfaces at build time.
40
+ const afterJscCount = Jsc.listFiles(buildPath, '.jsc').length;
41
+ if (beforeJsCount > 0 && afterJscCount < beforeJsCount) {
42
+ _sharedUtils.ColorConsole.throw(`[jsc] silent failure: expected at least ${beforeJsCount} .jsc file(s) but got ${afterJscCount}. ` + `Most common cause on Windows: buildPath contains non-ASCII characters ` + `(rename the project directory to ASCII-only and rebuild).`);
43
+ return Promise.reject(new Error(`[jsc] produced ${afterJscCount} of ${beforeJsCount} expected .jsc files`));
44
+ }
35
45
  if (deleteJs) {
36
46
  return _sharedUtils.FileUtil.del(`${buildPath}/**/*.js`);
37
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/aiotpack",
3
- "version": "2.1.0-prender.6",
3
+ "version": "2.1.0-prender.7",
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.6",
23
- "@aiot-toolkit/parser": "2.1.0-prender.6",
24
- "@aiot-toolkit/shared-utils": "2.1.0-prender.6",
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",
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.6",
31
+ "file-lane": "2.1.0-prender.7",
32
32
  "file-loader": "^6.2.0",
33
33
  "fs-extra": "^11.2.0",
34
34
  "jsrsasign": "^11.1.0",