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

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
  };
@@ -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]));
@@ -205,10 +213,17 @@ class ViteCompiler {
205
213
  commonjsOptions: {
206
214
  transformMixedEsModules: true
207
215
  },
208
- minify: param.mode === 'production' ? 'terser' : false,
216
+ minify: param.mode === 'production' ? userVite.build?.minify ?? 'terser' : false,
209
217
  terserOptions: {
210
218
  format: {
211
- comments: false
219
+ comments: false,
220
+ ...(userVite.build?.terserOptions?.format ?? {})
221
+ },
222
+ compress: {
223
+ ...(userVite.build?.terserOptions?.compress ?? {})
224
+ },
225
+ mangle: {
226
+ ...(userVite.build?.terserOptions?.mangle ?? {})
212
227
  }
213
228
  }
214
229
  },
@@ -244,9 +259,11 @@ class ViteCompiler {
244
259
  async load(id) {
245
260
  if (!id.endsWith('.ux')) return null;
246
261
  const content = _fsExtra.default.readFileSync(id, 'utf-8');
247
- const isApp = id.endsWith('/app.ux') && _path.default.basename(_path.default.dirname(id)) === 'src';
262
+ const idPosix = id.replace(/\\/g, '/');
263
+ const resolvedEntryPosix = resolvedEntry.replace(/\\/g, '/');
264
+ const isApp = idPosix.endsWith('/app.ux') && _path.default.basename(_path.default.dirname(id)) === 'src';
248
265
  // Generate template.json and css.json
249
- if (id === resolvedEntry) {
266
+ if (idPosix === resolvedEntryPosix) {
250
267
  const parse5 = require("aiot-parse5");
251
268
  const doc = parse5.parseFragment(content, {
252
269
  scriptingEnabled: false
@@ -407,9 +424,19 @@ class ViteCompiler {
407
424
  }
408
425
  }
409
426
  } catch (err) {
410
- console.warn(`vite: failed to build ${entryName}:`, err.message);
427
+ const msg = err.message;
428
+ console.warn(`vite: failed to build ${entryName}:`, msg);
429
+ entryFailures.push({
430
+ entry: entryName,
431
+ error: msg
432
+ });
411
433
  }
412
434
  }
435
+ if (entryAttempted > 0 && entryFailures.length === entryAttempted) {
436
+ const sample = entryFailures.slice(0, 3).map(f => ` ${f.entry}: ${f.error}`).join('\n');
437
+ 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}`);
439
+ }
413
440
  // Second pass: compile ALL .ux files that weren't entry pages
414
441
  // Each .ux file gets its own template.json + css.json + js
415
442
  const allUxFiles = [];
@@ -425,7 +452,7 @@ class ViteCompiler {
425
452
  const compiledEntries = new Set(Object.values(entries).map(v => _path.default.resolve(originalProjectPath, v.split('?')[0])));
426
453
  for (const uxFile of allUxFiles) {
427
454
  if (compiledEntries.has(uxFile)) continue; // already compiled as entry
428
- if (uxFile.endsWith('/app.ux')) continue; // app.ux handled separately
455
+ if (uxFile.replace(/\\/g, '/').endsWith('/app.ux')) continue; // app.ux handled separately
429
456
  const relPath = _path.default.relative(srcPath, uxFile).replace(/\.ux$/, '');
430
457
  const outputJs = _path.default.join(buildPath, `${relPath}.js`);
431
458
  const outputTpl = _path.default.join(buildPath, `${relPath}.template.json`);
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.6",
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.6",
23
+ "@aiot-toolkit/parser": "2.1.0-prender.6",
24
+ "@aiot-toolkit/shared-utils": "2.1.0-prender.6",
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.6",
32
32
  "file-loader": "^6.2.0",
33
33
  "fs-extra": "^11.2.0",
34
34
  "jsrsasign": "^11.1.0",