@aiot-toolkit/aiotpack 2.1.0-prender.5 → 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.
@@ -44,11 +44,16 @@ class UxAfterCompile {
44
44
  level: _sharedUtils.Loglevel.SUCCESS,
45
45
  message: ['javascript compile success']
46
46
  }]);
47
- }).catch(_ref => {
48
- let {
49
- errors,
50
- warnings
51
- } = _ref;
47
+ }).catch(err => {
48
+ const errors = err?.errors;
49
+ const warnings = err?.warnings;
50
+ if (!errors?.length && !warnings?.length) {
51
+ onLog?.([{
52
+ level: _sharedUtils.Loglevel.THROW,
53
+ message: [`javascript compile failed: ${err?.message ?? err}`]
54
+ }]);
55
+ throw err instanceof Error ? err : new Error(String(err));
56
+ }
52
57
  const errorLength = errors?.length || 0;
53
58
  const messages = [`webpack error:\r\n`];
54
59
  if (errors?.length) {
@@ -12,16 +12,6 @@ declare function uxToTemplateJson(templateNode: any, styleObjectId?: number, sty
12
12
  * `screen and ` is preserved; `not` / `only` are preserved as-is.
13
13
  */
14
14
  declare function normalizeMediaQuery(condition: string): string;
15
- /**
16
- * Parse compiled $app_style$ array. Each rule is either:
17
- * [selectors, props] — base rule
18
- * [{condition: "<media-query>"}, selectors, props] — conditional rule (@media or @import with query)
19
- *
20
- * Returns a flat map: selectors → props for base rules, plus
21
- * `"@media <normalizedConditionText>"` keys whose values are the per-condition
22
- * `{selector: props}` map. The condition text is normalized to Level 3 so
23
- * runtime only needs a Level-3 matcher.
24
- */
25
15
  declare function parseStyleArray(code: string): Record<string, any>;
26
16
  /** Convert CSS text to css.json format */
27
17
  export { uxToTemplateJson, parseStyleArray, normalizeMediaQuery };
@@ -100,10 +100,74 @@ function normalizeMediaQuery(condition) {
100
100
  * `{selector: props}` map. The condition text is normalized to Level 3 so
101
101
  * runtime only needs a Level-3 matcher.
102
102
  */
103
+ const NAMED_COLORS = new Set(['transparent', 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']);
104
+ const COLOR_PROP_RE = /(?:^|[a-z])[Cc]olor$/;
105
+ const NON_NAMED_COLOR_VALUE_RE = /^(?:#[0-9a-fA-F]{3,8}|rgba?\(|hsla?\(|currentColor)/;
103
106
  function parseStyleArray(code) {
104
107
  const styleSheet = {};
108
+ const expandShortHex = value => {
109
+ const short = /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])?$/.exec(value);
110
+ if (!short) return value;
111
+ const r = short[1] + short[1];
112
+ const g = short[2] + short[2];
113
+ const b = short[3] + short[3];
114
+ const a = short[4] ? short[4] + short[4] : '';
115
+ return ('#' + r + g + b + a).toLowerCase();
116
+ };
117
+ const normalizeTransform = obj => {
118
+ if (!obj || typeof obj !== 'object') return typeof obj === 'string' ? obj : JSON.stringify(obj);
119
+ const out = {};
120
+ for (const [k, v] of Object.entries(obj)) {
121
+ out[k] = typeof v === 'number' && /^translate[XYZ]?$/.test(k) ? `${v}px` : v;
122
+ }
123
+ return JSON.stringify(out);
124
+ };
125
+ const expandKeyframeSteps = value => {
126
+ let arr = [];
127
+ if (typeof value === 'string') {
128
+ try {
129
+ arr = JSON.parse(value);
130
+ } catch {
131
+ return [];
132
+ }
133
+ } else if (Array.isArray(value)) {
134
+ arr = value;
135
+ } else {
136
+ return [];
137
+ }
138
+ return arr.map(step => {
139
+ const out = {};
140
+ const entries = Object.entries(step ?? {});
141
+ for (const [k, v] of entries) {
142
+ if (k === 'time') continue;
143
+ out[k] = k === 'transform' && v && typeof v === 'object' ? normalizeTransform(v) : v;
144
+ }
145
+ if ('time' in (step ?? {})) out.time = step.time;
146
+ return out;
147
+ });
148
+ };
149
+ const normalizeStyleProps = props => {
150
+ if (!props || typeof props !== 'object') return props;
151
+ const out = {};
152
+ for (const [k, v] of Object.entries(props)) {
153
+ if ((k === 'animationIterationCount' || k === 'iterationCount') && v === 'infinite') {
154
+ out[k] = -1;
155
+ } else if (typeof v === 'string' && COLOR_PROP_RE.test(k)) {
156
+ if (NAMED_COLORS.has(v.toLowerCase())) {
157
+ out[k] = v;
158
+ } else if (NON_NAMED_COLOR_VALUE_RE.test(v)) {
159
+ out[k] = expandShortHex(v);
160
+ }
161
+ } else {
162
+ out[k] = v;
163
+ }
164
+ }
165
+ return out;
166
+ };
105
167
  const ingest = parsed => {
106
168
  if (!Array.isArray(parsed)) return;
169
+ let currentMediaCondition = null;
170
+ let currentMediaEntry = null;
107
171
  for (const rule of parsed) {
108
172
  if (!Array.isArray(rule) || rule.length < 2) continue;
109
173
  let selectors, props, conditionText;
@@ -115,16 +179,47 @@ function parseStyleArray(code) {
115
179
  selectors = rule[0];
116
180
  props = rule[1];
117
181
  }
118
- if (!Array.isArray(selectors)) continue;
119
- let target = styleSheet;
182
+ if (!Array.isArray(selectors) || selectors.length === 0) continue;
183
+ let target;
120
184
  if (conditionText) {
121
- const key = `@media ${normalizeMediaQuery(conditionText)}`;
122
- target = styleSheet[key] = styleSheet[key] || {};
185
+ const normalized = normalizeMediaQuery(conditionText);
186
+ if (currentMediaCondition !== normalized) {
187
+ const list = styleSheet['@MEDIA'] = styleSheet['@MEDIA'] || [];
188
+ currentMediaEntry = {
189
+ condition: normalized
190
+ };
191
+ list.push(currentMediaEntry);
192
+ currentMediaCondition = normalized;
193
+ }
194
+ target = currentMediaEntry;
195
+ } else {
196
+ target = styleSheet;
197
+ currentMediaCondition = null;
198
+ currentMediaEntry = null;
123
199
  }
124
- for (const sel of selectors) {
125
- if (Array.isArray(sel) && sel.length >= 2) {
126
- target[sel[0] === 0 ? `.${sel[1]}` : sel[1]] = props;
200
+ const normalizedProps = normalizeStyleProps(props);
201
+ const firstSel = selectors[0];
202
+ if (Array.isArray(firstSel) && firstSel.length >= 2 && firstSel[0] === 3) {
203
+ const bucket = target['@KEYFRAMES'] = target['@KEYFRAMES'] || {};
204
+ bucket[firstSel[1]] = expandKeyframeSteps(props?.keyframes ?? '[]');
205
+ } else if (Array.isArray(firstSel) && firstSel.length >= 2 && firstSel[0] === 4) {
206
+ const bucket = target['@FONT-FACE'] = target['@FONT-FACE'] || {};
207
+ bucket[firstSel[1]] = props?.fontface ?? normalizedProps;
208
+ } else {
209
+ const pieces = [];
210
+ for (let i = 0; i < selectors.length; i++) {
211
+ const sel = selectors[i];
212
+ if (!Array.isArray(sel) || sel.length < 2) continue;
213
+ if (sel[0] === 5) {
214
+ pieces.push(String(sel[1]));
215
+ continue;
216
+ }
217
+ if (pieces.length && pieces[pieces.length - 1] !== '>' && pieces[pieces.length - 1] !== '~' && pieces[pieces.length - 1] !== '+') {
218
+ pieces.push(' ');
219
+ }
220
+ if (sel[0] === 0) pieces.push(`.${sel[1]}`);else if (sel[0] === 1) pieces.push(`#${sel[1]}`);else pieces.push(String(sel[1]));
127
221
  }
222
+ if (pieces.length) target[pieces.join('')] = normalizedProps;
128
223
  }
129
224
  }
130
225
  };
@@ -186,6 +281,50 @@ function addThisPrefix(expr, extraReserved) {
186
281
  }).join('');
187
282
  }
188
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
+ }
189
328
  function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
190
329
  const tagName = el.tagName === 'img' ? 'image' : el.tagName;
191
330
  const node = {
@@ -396,8 +535,11 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
396
535
  hasBind = true;
397
536
  } else {
398
537
  let staticVal = value ? value.replace(/ {2,}/g, ' ') : '';
399
- // Normalize relative path attrs (src, href) to absolute paths from src root
400
- 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) {
401
543
  // Resolve relative to pageDir, then make absolute from src root
402
544
  const segments = pageDir.split('/').filter(Boolean).concat(staticVal.split('/'));
403
545
  const stack = [];
@@ -414,34 +556,9 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
414
556
  for (const child of el.childNodes || []) {
415
557
  const textElements = new Set(['text', 'span', 'a', 'input', 'qrcode', 'barcode', 'marquee', 'arc-text', 'option', 'button', 'label', 'textarea', 'richtext']);
416
558
  if (child.value?.trim() && !child.tagName && !(el.childNodes || []).some(c => c.tagName) && textElements.has(tagName)) {
417
- // Collapse whitespace for static text; dynamic text is trimmed separately
418
- const text = child.value;
419
- if (/\{\{.*\}\}/.test(text)) {
420
- // Build function expression from template literal
421
- // Trim surrounding whitespace before splitting (vivo ignores indentation whitespace)
422
- const trimmedText = text.trim();
423
- const parts = trimmedText.split(/(\{\{[\s\S]*?\}\})/);
424
- const exprs = parts.filter(Boolean).map(p => {
425
- const m = p.match(/^\{\{([\s\S]*?)\}\}$/);
426
- if (m) return addThisPrefix(m[1].trim());
427
- return `'${p.replace(/'/g, "\\'")}'`;
428
- });
429
- if (exprs.length === 1 && pathTestRE.test(exprs[0].replace(/^this\./, ''))) {
430
- attrObj['$value'] = exprs[0].replace(/^this\./, '');
431
- } else if (exprs.length === 1) {
432
- attrObj['$value'] = `function () { return ${exprs[0]}; }`;
433
- } else {
434
- const wrappedExprs = exprs.map(e => {
435
- if (e.startsWith("'") || e.startsWith('"')) return e;
436
- if (/(\|\||&&|\?\s|\+)/.test(e) && !e.startsWith('(')) return `(${e})`;
437
- return e;
438
- });
439
- attrObj['$value'] = `function () { return '' + ${wrappedExprs.join(' + ')}; }`;
440
- }
441
- hasBind = true;
442
- } else {
443
- attrObj['value'] = text.replace(/\s*\n\s*/g, ' ');
444
- }
559
+ const emitted = emitTextValueAttr(child.value);
560
+ Object.assign(attrObj, emitted.attr);
561
+ if (emitted.needsBind) hasBind = true;
445
562
  }
446
563
  }
447
564
  // Output in vivo order: attr → bind → class → events → import → style → children
@@ -477,13 +594,19 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
477
594
  }
478
595
  if (Object.keys(events).length) node.events = events;
479
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="...".
480
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.
481
601
  if (styleSheet) {
482
602
  const merged = {};
483
603
  if (styleSheet[el.tagName]) Object.assign(merged, styleSheet[el.tagName]);
484
604
  if (className && !hasDynClass) {
485
- for (const cls of className.split(/\s+/)) {
486
- 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
+ }
487
610
  }
488
611
  }
489
612
  Object.assign(merged, staticInlineStyle);
@@ -502,14 +625,12 @@ function elementToJson(el, styleSheet, importMap, scopeVars, pageDir) {
502
625
  if (child.tagName) {
503
626
  childNodes.push(elementToJson(child, styleSheet, importMap, scopeVars, pageDir));
504
627
  } else if (isMixedContent && child.value?.trim()) {
505
- // Wrap text nodes in <span> for mixed content
506
- const text = child.value.replace(/[ \t]*\n[ \t]*/g, ' ').replace(/ {2,}/g, ' ');
628
+ const emitted = emitTextValueAttr(child.value);
507
629
  const spanNode = {
508
630
  type: 'span',
509
- attr: {
510
- value: text
511
- }
631
+ attr: emitted.attr
512
632
  };
633
+ if (emitted.needsBind) spanNode.bind = 1;
513
634
  childNodes.push(spanNode);
514
635
  }
515
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"));
@@ -147,6 +147,11 @@ class ViteCompiler {
147
147
  }
148
148
  async compile(param) {
149
149
  await this.clean(param);
150
+ const nodeMajor = parseInt((process.versions.node ?? '0').split('.')[0], 10);
151
+ const nodeMinor = parseInt((process.versions.node ?? '0').split('.')[1] ?? '0', 10);
152
+ if (nodeMajor < 20 || nodeMajor === 20 && nodeMinor < 19) {
153
+ throw new Error(`vite ^8 requires Node ^20.19.0 || >=22.12.0; current Node is v${process.versions.node}. ` + `Upgrade Node (recommend 22 LTS) and re-run the build.`);
154
+ }
150
155
  const {
151
156
  projectPath,
152
157
  sourceRoot,
@@ -167,10 +172,13 @@ class ViteCompiler {
167
172
  message: [`Failed to load quickapp.config.js: ${e.message}`]
168
173
  }]);
169
174
  }
175
+ const entryFailures = [];
176
+ let entryAttempted = 0;
170
177
  for (const [entryName, entryValue] of Object.entries(entries)) {
171
178
  const entryPath = entryValue.split('?')[0];
172
179
  const resolvedEntry = _path.default.resolve(originalProjectPath, entryPath);
173
180
  if (!_fsExtra.default.existsSync(resolvedEntry)) continue;
181
+ entryAttempted++;
174
182
  const outputFile = _path.default.join(buildPath, `${entryName}.js`);
175
183
  _fsExtra.default.ensureDirSync(_path.default.dirname(outputFile));
176
184
  const allEntryPaths = Object.values(entries).map(v => _path.default.resolve(originalProjectPath, v.split('?')[0]));
@@ -200,15 +208,32 @@ class ViteCompiler {
200
208
  external: id => id.startsWith('@app-module/') || /^@(system|service|android|hap)\./.test(id),
201
209
  output: {
202
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);
203
221
  }
204
222
  },
205
223
  commonjsOptions: {
206
224
  transformMixedEsModules: true
207
225
  },
208
- minify: param.mode === 'production' ? 'terser' : false,
226
+ minify: param.mode === 'production' ? userVite.build?.minify ?? 'terser' : false,
209
227
  terserOptions: {
210
228
  format: {
211
- comments: false
229
+ comments: false,
230
+ ...(userVite.build?.terserOptions?.format ?? {})
231
+ },
232
+ compress: {
233
+ ...(userVite.build?.terserOptions?.compress ?? {})
234
+ },
235
+ mangle: {
236
+ ...(userVite.build?.terserOptions?.mangle ?? {})
212
237
  }
213
238
  }
214
239
  },
@@ -234,6 +259,26 @@ class ViteCompiler {
234
259
  map: null
235
260
  } : null;
236
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
+ }
237
282
  }, {
238
283
  name: 'ux-loader',
239
284
  resolveId(source, importer) {
@@ -244,9 +289,11 @@ class ViteCompiler {
244
289
  async load(id) {
245
290
  if (!id.endsWith('.ux')) return null;
246
291
  const content = _fsExtra.default.readFileSync(id, 'utf-8');
247
- const isApp = id.endsWith('/app.ux') && _path.default.basename(_path.default.dirname(id)) === 'src';
292
+ const idPosix = id.replace(/\\/g, '/');
293
+ const resolvedEntryPosix = resolvedEntry.replace(/\\/g, '/');
294
+ const isApp = idPosix.endsWith('/app.ux') && _path.default.basename(_path.default.dirname(id)) === 'src';
248
295
  // Generate template.json and css.json
249
- if (id === resolvedEntry) {
296
+ if (idPosix === resolvedEntryPosix) {
250
297
  const parse5 = require("aiot-parse5");
251
298
  const doc = parse5.parseFragment(content, {
252
299
  scriptingEnabled: false
@@ -407,9 +454,22 @@ class ViteCompiler {
407
454
  }
408
455
  }
409
456
  } catch (err) {
410
- console.warn(`vite: failed to build ${entryName}:`, err.message);
457
+ const msg = err.message;
458
+ console.warn(`vite: failed to build ${entryName}:`, msg);
459
+ entryFailures.push({
460
+ entry: entryName,
461
+ error: msg
462
+ });
411
463
  }
412
464
  }
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.
469
+ const sample = entryFailures.slice(0, 3).map(f => ` ${f.entry}: ${f.error}`).join('\n');
470
+ const more = entryFailures.length > 3 ? `\n ...and ${entryFailures.length - 3} more` : '';
471
+ throw new Error(`vite build failed for ${entryFailures.length}/${entryAttempted} entry page(s).\n` + `Failures:\n${sample}${more}`);
472
+ }
413
473
  // Second pass: compile ALL .ux files that weren't entry pages
414
474
  // Each .ux file gets its own template.json + css.json + js
415
475
  const allUxFiles = [];
@@ -425,7 +485,7 @@ class ViteCompiler {
425
485
  const compiledEntries = new Set(Object.values(entries).map(v => _path.default.resolve(originalProjectPath, v.split('?')[0])));
426
486
  for (const uxFile of allUxFiles) {
427
487
  if (compiledEntries.has(uxFile)) continue; // already compiled as entry
428
- if (uxFile.endsWith('/app.ux')) continue; // app.ux handled separately
488
+ if (uxFile.replace(/\\/g, '/').endsWith('/app.ux')) continue; // app.ux handled separately
429
489
  const relPath = _path.default.relative(srcPath, uxFile).replace(/\.ux$/, '');
430
490
  const outputJs = _path.default.join(buildPath, `${relPath}.js`);
431
491
  const outputTpl = _path.default.join(buildPath, `${relPath}.template.json`);
@@ -504,6 +564,10 @@ class ViteCompiler {
504
564
  _UxCompileUtil.default.clean([outputPath, releasePath].map(item => _path.default.resolve(projectPath, item)));
505
565
  }
506
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
+ }
507
571
  function wrapOutput(code, entryName, isApp) {
508
572
  const componentName = _path.default.parse(entryName).name;
509
573
  const defineId = isApp ? '@app-component/app' : `@app-component/${componentName}`;
@@ -534,8 +598,10 @@ function wrapOutput(code, entryName, isApp) {
534
598
  // Remove rolldown region comments
535
599
  code = code.replace(/\/\/#region[^\n]*\n/g, '');
536
600
  code = code.replace(/\/\/#endregion[^\n]*/g, '');
537
- // Remove rolldown runtime helpers (not needed in final output)
538
- 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);
539
605
  code = code.replace(/var __exportAll = [\s\S]*?return target;\s*\};\n?/g, '');
540
606
  // Inline __exportAll calls: __exportAll({key: () => value, ...}) -> {key: value, ...}
541
607
  // Match __exportAll(...) or /* @__PURE__ */ __exportAll(...)
@@ -576,4 +642,7 @@ $app_define$("${defineId}", [], function($app_require$, $app_exports$, $app_modu
576
642
  $app_bootstrap$("${bootstrapId}");
577
643
  `;
578
644
  }
645
+ const __test = exports.__test = {
646
+ stripDeadDefProp
647
+ };
579
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.5",
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.5",
23
- "@aiot-toolkit/parser": "2.1.0-prender.5",
24
- "@aiot-toolkit/shared-utils": "2.1.0-prender.5",
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.5",
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",