@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.
- package/lib/afterCompile/ux/UxAfterCompile.js +10 -5
- package/lib/compiler/javascript/TemplateCompiler.d.ts +0 -10
- package/lib/compiler/javascript/TemplateCompiler.js +165 -44
- package/lib/compiler/javascript/ViteCompiler.d.ts +4 -0
- package/lib/compiler/javascript/ViteCompiler.js +78 -9
- package/lib/compiler/javascript/vela/utils/Jsc.js +10 -0
- package/package.json +5 -5
|
@@ -44,11 +44,16 @@ class UxAfterCompile {
|
|
|
44
44
|
level: _sharedUtils.Loglevel.SUCCESS,
|
|
45
45
|
message: ['javascript compile success']
|
|
46
46
|
}]);
|
|
47
|
-
}).catch(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
182
|
+
if (!Array.isArray(selectors) || selectors.length === 0) continue;
|
|
183
|
+
let target;
|
|
120
184
|
if (conditionText) {
|
|
121
|
-
const
|
|
122
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
if (
|
|
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
|
-
|
|
486
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
538
|
-
|
|
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.
|
|
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.
|
|
23
|
-
"@aiot-toolkit/parser": "2.1.0-prender.
|
|
24
|
-
"@aiot-toolkit/shared-utils": "2.1.0-prender.
|
|
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.
|
|
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",
|