@aiot-toolkit/aiotpack 2.1.0-prender.15 → 2.1.0-prender.16
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.
|
@@ -3,6 +3,48 @@ import ICompileParam from '../interface/ICompileParam';
|
|
|
3
3
|
import ICompiler from '../interface/ICompiler';
|
|
4
4
|
import { ILog } from '@aiot-toolkit/shared-utils';
|
|
5
5
|
import { IFileLaneContext } from 'file-lane';
|
|
6
|
+
/**
|
|
7
|
+
* Resolve an `<import src>` value to an absolute `.ux` path, accepting the
|
|
8
|
+
* extensionless and directory forms the loader also accepts.
|
|
9
|
+
*/
|
|
10
|
+
declare function resolveImportSrc(fromUxFile: string, src: string): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* `.ux` files reachable from the routed pages, following `<import src>` chains.
|
|
13
|
+
*
|
|
14
|
+
* aiot-toolkit 1.x only compiled routed pages (components were bundled into the
|
|
15
|
+
* host page), so a `.ux` that no route reaches never produced output. 2.x walks
|
|
16
|
+
* every `.ux` on disk, which ships dead files and surfaces their defects as
|
|
17
|
+
* build failures — prerender-apps/idiom has an unrouted `pages/index` whose
|
|
18
|
+
* leftover `import page from './index.js'` failed the build for a page no route
|
|
19
|
+
* can open.
|
|
20
|
+
*/
|
|
21
|
+
declare function collectReachableUx(entryUxFiles: string[]): Set<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Relative ESM imports a sub-component declares that nothing downstream can
|
|
24
|
+
* resolve: `.ux` imports are loader-generated and dropped later, and
|
|
25
|
+
* `@system.*`/`@app-module` imports become `$app_require$` calls.
|
|
26
|
+
*/
|
|
27
|
+
declare function findUnbundledRelativeImports(code: string): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Inline a sub-component's relative `.js`/`.ts` dependencies.
|
|
30
|
+
*
|
|
31
|
+
* An entry page is bundled by rolldown, so `import page from './index.js'`
|
|
32
|
+
* is inlined. A sub-component was instead compiled file-by-file by
|
|
33
|
+
* `UxLoaderUtils.compileUxToJavascript` with no bundler, leaving the statement
|
|
34
|
+
* in place — fatal, because the IIFE wrapper nests it in a function body. Run
|
|
35
|
+
* the same rolldown pipeline over the compiled component so such a dependency
|
|
36
|
+
* is resolved the way a page's is.
|
|
37
|
+
*
|
|
38
|
+
* The entry is a virtual module placed in the component's own directory, so
|
|
39
|
+
* relative specifiers resolve against the component without writing to disk.
|
|
40
|
+
* Components with no such import return unchanged, keeping the common case free.
|
|
41
|
+
*/
|
|
42
|
+
declare function bundleComponentCode(compCode: string, uxFile: string, srcPath: string, param: IJavascriptCompileOption, userVite: {
|
|
43
|
+
define?: Record<string, string>;
|
|
44
|
+
plugins?: any[];
|
|
45
|
+
resolve?: any;
|
|
46
|
+
build?: any;
|
|
47
|
+
}): Promise<string>;
|
|
6
48
|
declare class ViteCompiler implements ICompiler {
|
|
7
49
|
private readonly context;
|
|
8
50
|
private readonly onLog?;
|
|
@@ -22,11 +64,30 @@ declare function stripDeadDefProp(code: string): string;
|
|
|
22
64
|
* leaves `vibrator` as a free identifier, which is why the 咪咕/百度地图 keyboard
|
|
23
65
|
* could no longer vibrate.
|
|
24
66
|
*/
|
|
67
|
+
/**
|
|
68
|
+
* `<import name="comp-score" src="../Score/index.ux">` is compiled by the ux
|
|
69
|
+
* loader into `import comp_score_comp from '../Score/index.ux'`, but a
|
|
70
|
+
* sub-component is resolved purely through data: template.json carries
|
|
71
|
+
* `{type, import}` and the component's own `.js` registers
|
|
72
|
+
* `$app_define$("@app-component/<name>")`. The binding is never read, and
|
|
73
|
+
* leaving the statement in place is a hard error — the IIFE wrapper nests it
|
|
74
|
+
* inside a function body, where `import` is a syntax error.
|
|
75
|
+
*
|
|
76
|
+
* Only drop it when the binding is provably unused; a referenced binding means
|
|
77
|
+
* the loader contract changed and silently dropping it would reintroduce the
|
|
78
|
+
* free-identifier class of bug, so leave it for the caller's guard to report.
|
|
79
|
+
*/
|
|
80
|
+
declare function dropUnusedUxImports(code: string): string;
|
|
25
81
|
declare function buildComponentJs(compCode: string, compName: string, compSid: number, relPath: string): string;
|
|
26
82
|
declare function mergeStyleSheets(app: any, page: any): any;
|
|
27
83
|
export declare const __test: {
|
|
28
84
|
stripDeadDefProp: typeof stripDeadDefProp;
|
|
29
85
|
mergeStyleSheets: typeof mergeStyleSheets;
|
|
30
86
|
buildComponentJs: typeof buildComponentJs;
|
|
87
|
+
dropUnusedUxImports: typeof dropUnusedUxImports;
|
|
88
|
+
collectReachableUx: typeof collectReachableUx;
|
|
89
|
+
resolveImportSrc: typeof resolveImportSrc;
|
|
90
|
+
findUnbundledRelativeImports: typeof findUnbundledRelativeImports;
|
|
91
|
+
bundleComponentCode: typeof bundleComponentCode;
|
|
31
92
|
};
|
|
32
93
|
export default ViteCompiler;
|
|
@@ -216,6 +216,57 @@ function extractLegacyDefinePluginEntries(projectPath, configPath, mode) {
|
|
|
216
216
|
} catch {/* swallow config load errors */}
|
|
217
217
|
return captured;
|
|
218
218
|
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Resolve an `<import src>` value to an absolute `.ux` path, accepting the
|
|
222
|
+
* extensionless and directory forms the loader also accepts.
|
|
223
|
+
*/
|
|
224
|
+
function resolveImportSrc(fromUxFile, src) {
|
|
225
|
+
const base = _path.default.resolve(_path.default.dirname(fromUxFile), src);
|
|
226
|
+
for (const cand of [base, `${base}.ux`, _path.default.join(base, 'index.ux')]) {
|
|
227
|
+
if (cand.endsWith('.ux') && _fsExtra.default.existsSync(cand)) return cand;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* `.ux` files reachable from the routed pages, following `<import src>` chains.
|
|
234
|
+
*
|
|
235
|
+
* aiot-toolkit 1.x only compiled routed pages (components were bundled into the
|
|
236
|
+
* host page), so a `.ux` that no route reaches never produced output. 2.x walks
|
|
237
|
+
* every `.ux` on disk, which ships dead files and surfaces their defects as
|
|
238
|
+
* build failures — prerender-apps/idiom has an unrouted `pages/index` whose
|
|
239
|
+
* leftover `import page from './index.js'` failed the build for a page no route
|
|
240
|
+
* can open.
|
|
241
|
+
*/
|
|
242
|
+
function collectReachableUx(entryUxFiles) {
|
|
243
|
+
const parse5 = require('aiot-parse5');
|
|
244
|
+
const reachable = new Set();
|
|
245
|
+
const worklist = [...entryUxFiles];
|
|
246
|
+
while (worklist.length) {
|
|
247
|
+
const uxFile = worklist.pop();
|
|
248
|
+
const key = _path.default.resolve(uxFile);
|
|
249
|
+
if (reachable.has(key)) continue;
|
|
250
|
+
if (!_fsExtra.default.existsSync(key)) continue;
|
|
251
|
+
reachable.add(key);
|
|
252
|
+
let doc;
|
|
253
|
+
try {
|
|
254
|
+
doc = parse5.parseFragment(_fsExtra.default.readFileSync(key, 'utf-8'), {
|
|
255
|
+
scriptingEnabled: false
|
|
256
|
+
});
|
|
257
|
+
} catch {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
for (const node of doc.childNodes ?? []) {
|
|
261
|
+
if (node.nodeName !== 'import') continue;
|
|
262
|
+
const srcAttr = node.attrs?.find(a => a.name === 'src');
|
|
263
|
+
if (!srcAttr) continue;
|
|
264
|
+
const resolved = resolveImportSrc(key, srcAttr.value);
|
|
265
|
+
if (resolved) worklist.push(resolved);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return reachable;
|
|
269
|
+
}
|
|
219
270
|
function extractAppStyleLiteral(code) {
|
|
220
271
|
const m = code.match(/var\s+\$app_style\$\s*=\s*\[/);
|
|
221
272
|
if (!m) return null;
|
|
@@ -243,6 +294,161 @@ function extractAppStyleLiteral(code) {
|
|
|
243
294
|
}
|
|
244
295
|
return null;
|
|
245
296
|
}
|
|
297
|
+
/** Single dynamic-import site for vite, whose types this tsconfig cannot see. */
|
|
298
|
+
async function loadViteBuild() {
|
|
299
|
+
const {
|
|
300
|
+
build
|
|
301
|
+
} = await import('vite');
|
|
302
|
+
return build;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Plugins shared by the page-entry build and the sub-component bundler, so a
|
|
307
|
+
* component's dependencies are transformed exactly like a page's.
|
|
308
|
+
*/
|
|
309
|
+
function makeSharedPlugins(srcPath) {
|
|
310
|
+
return [{
|
|
311
|
+
name: 'image-asset-string',
|
|
312
|
+
enforce: 'pre',
|
|
313
|
+
load(id) {
|
|
314
|
+
// require("./x.png") in .ux <script> must resolve to a path string,
|
|
315
|
+
// matching webpack file-loader / vivo blueos-pack semantics. Without
|
|
316
|
+
// this hook rolldown treats each image as an ES module and emits
|
|
317
|
+
// `(init_x(), __toCommonJS(x_exports))` — the site assigns
|
|
318
|
+
// `{ default: fn }` to src and runtime cannot render.
|
|
319
|
+
const match = id.match(/\.(png|jpe?g|gif|svg|webp|bmp|ico)(\?.*)?$/i);
|
|
320
|
+
if (!match) return null;
|
|
321
|
+
const filePath = id.replace(/\?.*$/, '');
|
|
322
|
+
if (!_fsExtra.default.existsSync(filePath)) return null;
|
|
323
|
+
const relFromSrc = _path.default.relative(srcPath, filePath).split(_path.default.sep).join('/');
|
|
324
|
+
// Assets outside src/ (typically node_modules) fall through to the
|
|
325
|
+
// default asset handler — the caller is responsible for their path
|
|
326
|
+
// resolution. Vivo blueos-pack + webpack file-loader use rpk-root
|
|
327
|
+
// absolute paths (`/common/...`, `/dynamicAssets/name-hash.png`);
|
|
328
|
+
// runtime <image src> resolves against the rpk root, so relative
|
|
329
|
+
// `./common/...` from a nested page directory would misdirect.
|
|
330
|
+
if (relFromSrc.startsWith('..')) return null;
|
|
331
|
+
const runtimePath = '/' + relFromSrc;
|
|
332
|
+
return `module.exports = ${JSON.stringify(runtimePath)};`;
|
|
333
|
+
}
|
|
334
|
+
}, {
|
|
335
|
+
name: "cjs-shim",
|
|
336
|
+
enforce: "pre",
|
|
337
|
+
load(id) {
|
|
338
|
+
if (id.includes('node_modules') && !id.endsWith('.ux')) {
|
|
339
|
+
const content = _fsExtra.default.readFileSync(id, 'utf-8');
|
|
340
|
+
if (content.includes('module.exports') || content.includes('exports.')) {
|
|
341
|
+
return `var module = {exports:{}}; var exports = module.exports;\n${content}\nexport default module.exports;`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}, {
|
|
347
|
+
name: 'system-to-app-require',
|
|
348
|
+
enforce: 'pre',
|
|
349
|
+
transform(code, id) {
|
|
350
|
+
const out = rewriteSystemImports(code);
|
|
351
|
+
return out !== code ? {
|
|
352
|
+
code: out,
|
|
353
|
+
map: null
|
|
354
|
+
} : null;
|
|
355
|
+
}
|
|
356
|
+
}, {
|
|
357
|
+
name: 'namespace-preserve',
|
|
358
|
+
enforce: 'pre',
|
|
359
|
+
transform(code, id) {
|
|
360
|
+
if (!/import\s*\*\s*as\s+\w+\s+from/.test(code)) return null;
|
|
361
|
+
// Rolldown resolves `import * as L from './x'; L.foo()` at compile time —
|
|
362
|
+
// if `foo` isn't exported by `./x` the call collapses to `(void 0)()` and
|
|
363
|
+
// the source identifier is lost. Rewriting the alias to a runtime variable
|
|
364
|
+
// (with webpack-style default-interop) preserves the identifier so runtime
|
|
365
|
+
// errors point at the actual source name.
|
|
366
|
+
const out = code.replace(/import\s*\*\s*as\s+(\w+)\s+from\s+(['"][^'"]+['"])/g, (_m, local, src) => {
|
|
367
|
+
const ns = `__${local}_ns`,
|
|
368
|
+
key = `__${local}_key`;
|
|
369
|
+
return `import * as ${ns} from ${src}; const ${key} = 'default'; const ${local} = ${ns}[${key}] != null ? ${ns}[${key}] : ${ns};`;
|
|
370
|
+
});
|
|
371
|
+
return out !== code ? {
|
|
372
|
+
code: out,
|
|
373
|
+
map: null
|
|
374
|
+
} : null;
|
|
375
|
+
}
|
|
376
|
+
}];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Relative ESM imports a sub-component declares that nothing downstream can
|
|
381
|
+
* resolve: `.ux` imports are loader-generated and dropped later, and
|
|
382
|
+
* `@system.*`/`@app-module` imports become `$app_require$` calls.
|
|
383
|
+
*/
|
|
384
|
+
function findUnbundledRelativeImports(code) {
|
|
385
|
+
const out = [];
|
|
386
|
+
const re = /^[ \t]*import\s[^\n]*?\sfrom\s+(['"])(\.[^'"]*)\1/gm;
|
|
387
|
+
for (const m of code.matchAll(re)) {
|
|
388
|
+
if (!m[2].endsWith('.ux')) out.push(m[2]);
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Inline a sub-component's relative `.js`/`.ts` dependencies.
|
|
395
|
+
*
|
|
396
|
+
* An entry page is bundled by rolldown, so `import page from './index.js'`
|
|
397
|
+
* is inlined. A sub-component was instead compiled file-by-file by
|
|
398
|
+
* `UxLoaderUtils.compileUxToJavascript` with no bundler, leaving the statement
|
|
399
|
+
* in place — fatal, because the IIFE wrapper nests it in a function body. Run
|
|
400
|
+
* the same rolldown pipeline over the compiled component so such a dependency
|
|
401
|
+
* is resolved the way a page's is.
|
|
402
|
+
*
|
|
403
|
+
* The entry is a virtual module placed in the component's own directory, so
|
|
404
|
+
* relative specifiers resolve against the component without writing to disk.
|
|
405
|
+
* Components with no such import return unchanged, keeping the common case free.
|
|
406
|
+
*/
|
|
407
|
+
async function bundleComponentCode(compCode, uxFile, srcPath, param, userVite) {
|
|
408
|
+
if (findUnbundledRelativeImports(compCode).length === 0) return compCode;
|
|
409
|
+
const virtualId = _path.default.join(_path.default.dirname(uxFile), `__component_entry__${_path.default.basename(uxFile)}.js`);
|
|
410
|
+
const build = await loadViteBuild();
|
|
411
|
+
const result = await build({
|
|
412
|
+
root: _path.default.dirname(uxFile),
|
|
413
|
+
logLevel: 'silent',
|
|
414
|
+
define: userVite.define ?? {},
|
|
415
|
+
resolve: {
|
|
416
|
+
extensions: ['.js', '.ts', '.ux', '.json'],
|
|
417
|
+
...(userVite.resolve ?? {})
|
|
418
|
+
},
|
|
419
|
+
build: {
|
|
420
|
+
write: false,
|
|
421
|
+
lib: {
|
|
422
|
+
entry: virtualId,
|
|
423
|
+
formats: ['es'],
|
|
424
|
+
fileName: () => 'component.js'
|
|
425
|
+
},
|
|
426
|
+
rollupOptions: {
|
|
427
|
+
external: id => id.startsWith('@app-module/') || /^@(system|service|android|hap)\./.test(id)
|
|
428
|
+
},
|
|
429
|
+
commonjsOptions: {
|
|
430
|
+
transformMixedEsModules: true
|
|
431
|
+
},
|
|
432
|
+
minify: false
|
|
433
|
+
},
|
|
434
|
+
plugins: [{
|
|
435
|
+
name: 'component-virtual-entry',
|
|
436
|
+
enforce: 'pre',
|
|
437
|
+
resolveId(source) {
|
|
438
|
+
return source === virtualId ? virtualId : null;
|
|
439
|
+
},
|
|
440
|
+
load(id) {
|
|
441
|
+
return id === virtualId ? compCode : null;
|
|
442
|
+
}
|
|
443
|
+
}, ...makeSharedPlugins(srcPath), ...(userVite.plugins ?? [])]
|
|
444
|
+
});
|
|
445
|
+
const outputs = Array.isArray(result) ? result[0]?.output : result?.output;
|
|
446
|
+
const code = outputs?.[0]?.code;
|
|
447
|
+
if (!code) {
|
|
448
|
+
throw new Error(`[component] ${_path.default.relative(srcPath, uxFile)} bundling produced no output; ` + `its relative import(s) ${findUnbundledRelativeImports(compCode).join(', ')} could not be inlined.`);
|
|
449
|
+
}
|
|
450
|
+
return code;
|
|
451
|
+
}
|
|
246
452
|
class ViteCompiler {
|
|
247
453
|
constructor(context, onLog) {
|
|
248
454
|
this.context = context;
|
|
@@ -290,9 +496,7 @@ class ViteCompiler {
|
|
|
290
496
|
entries: allEntryPaths
|
|
291
497
|
};
|
|
292
498
|
try {
|
|
293
|
-
const
|
|
294
|
-
build
|
|
295
|
-
} = await import('vite');
|
|
499
|
+
const build = await loadViteBuild();
|
|
296
500
|
const result = await build({
|
|
297
501
|
root: originalProjectPath,
|
|
298
502
|
logLevel: 'silent',
|
|
@@ -341,73 +545,7 @@ class ViteCompiler {
|
|
|
341
545
|
}
|
|
342
546
|
}
|
|
343
547
|
},
|
|
344
|
-
plugins: [{
|
|
345
|
-
name: 'image-asset-string',
|
|
346
|
-
enforce: 'pre',
|
|
347
|
-
load(id) {
|
|
348
|
-
// require("./x.png") in .ux <script> must resolve to a path string,
|
|
349
|
-
// matching webpack file-loader / vivo blueos-pack semantics. Without
|
|
350
|
-
// this hook rolldown treats each image as an ES module and emits
|
|
351
|
-
// `(init_x(), __toCommonJS(x_exports))` — the site assigns
|
|
352
|
-
// `{ default: fn }` to src and runtime cannot render.
|
|
353
|
-
const match = id.match(/\.(png|jpe?g|gif|svg|webp|bmp|ico)(\?.*)?$/i);
|
|
354
|
-
if (!match) return null;
|
|
355
|
-
const filePath = id.replace(/\?.*$/, '');
|
|
356
|
-
if (!_fsExtra.default.existsSync(filePath)) return null;
|
|
357
|
-
const relFromSrc = _path.default.relative(srcPath, filePath).split(_path.default.sep).join('/');
|
|
358
|
-
// Assets outside src/ (typically node_modules) fall through to the
|
|
359
|
-
// default asset handler — the caller is responsible for their path
|
|
360
|
-
// resolution. Vivo blueos-pack + webpack file-loader use rpk-root
|
|
361
|
-
// absolute paths (`/common/...`, `/dynamicAssets/name-hash.png`);
|
|
362
|
-
// runtime <image src> resolves against the rpk root, so relative
|
|
363
|
-
// `./common/...` from a nested page directory would misdirect.
|
|
364
|
-
if (relFromSrc.startsWith('..')) return null;
|
|
365
|
-
const runtimePath = '/' + relFromSrc;
|
|
366
|
-
return `module.exports = ${JSON.stringify(runtimePath)};`;
|
|
367
|
-
}
|
|
368
|
-
}, {
|
|
369
|
-
name: "cjs-shim",
|
|
370
|
-
enforce: "pre",
|
|
371
|
-
load(id) {
|
|
372
|
-
if (id.includes('node_modules') && !id.endsWith('.ux')) {
|
|
373
|
-
const content = _fsExtra.default.readFileSync(id, 'utf-8');
|
|
374
|
-
if (content.includes('module.exports') || content.includes('exports.')) {
|
|
375
|
-
return `var module = {exports:{}}; var exports = module.exports;\n${content}\nexport default module.exports;`;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
}, {
|
|
381
|
-
name: 'system-to-app-require',
|
|
382
|
-
enforce: 'pre',
|
|
383
|
-
transform(code, id) {
|
|
384
|
-
const out = rewriteSystemImports(code);
|
|
385
|
-
return out !== code ? {
|
|
386
|
-
code: out,
|
|
387
|
-
map: null
|
|
388
|
-
} : null;
|
|
389
|
-
}
|
|
390
|
-
}, {
|
|
391
|
-
name: 'namespace-preserve',
|
|
392
|
-
enforce: 'pre',
|
|
393
|
-
transform(code, id) {
|
|
394
|
-
if (!/import\s*\*\s*as\s+\w+\s+from/.test(code)) return null;
|
|
395
|
-
// Rolldown resolves `import * as L from './x'; L.foo()` at compile time —
|
|
396
|
-
// if `foo` isn't exported by `./x` the call collapses to `(void 0)()` and
|
|
397
|
-
// the source identifier is lost. Rewriting the alias to a runtime variable
|
|
398
|
-
// (with webpack-style default-interop) preserves the identifier so runtime
|
|
399
|
-
// errors point at the actual source name.
|
|
400
|
-
const out = code.replace(/import\s*\*\s*as\s+(\w+)\s+from\s+(['"][^'"]+['"])/g, (_m, local, src) => {
|
|
401
|
-
const ns = `__${local}_ns`,
|
|
402
|
-
key = `__${local}_key`;
|
|
403
|
-
return `import * as ${ns} from ${src}; const ${key} = 'default'; const ${local} = ${ns}[${key}] != null ? ${ns}[${key}] : ${ns};`;
|
|
404
|
-
});
|
|
405
|
-
return out !== code ? {
|
|
406
|
-
code: out,
|
|
407
|
-
map: null
|
|
408
|
-
} : null;
|
|
409
|
-
}
|
|
410
|
-
}, {
|
|
548
|
+
plugins: [...makeSharedPlugins(srcPath), {
|
|
411
549
|
name: 'ux-loader',
|
|
412
550
|
resolveId(source, importer) {
|
|
413
551
|
if (source.endsWith('.ux')) {
|
|
@@ -554,7 +692,9 @@ class ViteCompiler {
|
|
|
554
692
|
spaces: 2
|
|
555
693
|
});
|
|
556
694
|
const compJsPath = _path.default.join(buildPath, `${compPath}.js`);
|
|
557
|
-
|
|
695
|
+
// Style data was already read off the unbundled code above.
|
|
696
|
+
const compBundled = await bundleComponentCode(compCode, compUxPath, srcPath, param, userVite);
|
|
697
|
+
_fsExtra.default.writeFileSync(compJsPath, buildComponentJs(compBundled, compName, compSid, compPath));
|
|
558
698
|
}
|
|
559
699
|
}
|
|
560
700
|
}
|
|
@@ -615,9 +755,15 @@ class ViteCompiler {
|
|
|
615
755
|
};
|
|
616
756
|
findUx(srcPath);
|
|
617
757
|
const compiledEntries = new Set(Object.values(entries).map(v => _path.default.resolve(originalProjectPath, v.split('?')[0])));
|
|
758
|
+
const reachableUx = collectReachableUx([...compiledEntries]);
|
|
759
|
+
const skippedUx = [];
|
|
618
760
|
for (const uxFile of allUxFiles) {
|
|
619
761
|
if (compiledEntries.has(uxFile)) continue; // already compiled as entry
|
|
620
762
|
if (uxFile.replace(/\\/g, '/').endsWith('/app.ux')) continue; // app.ux handled separately
|
|
763
|
+
if (!reachableUx.has(_path.default.resolve(uxFile))) {
|
|
764
|
+
skippedUx.push(_path.default.relative(srcPath, uxFile).replace(/\\/g, '/'));
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
621
767
|
const relPath = _path.default.relative(srcPath, uxFile).replace(/\.ux$/, '');
|
|
622
768
|
const outputJs = _path.default.join(buildPath, `${relPath}.js`);
|
|
623
769
|
const outputTpl = _path.default.join(buildPath, `${relPath}.template.json`);
|
|
@@ -683,7 +829,8 @@ class ViteCompiler {
|
|
|
683
829
|
// Generate js
|
|
684
830
|
const compName = _path.default.basename(relPath).toLowerCase();
|
|
685
831
|
_fsExtra.default.ensureDirSync(_path.default.dirname(outputJs));
|
|
686
|
-
|
|
832
|
+
const bundled = await bundleComponentCode(compCode, uxFile, srcPath, param, userVite);
|
|
833
|
+
_fsExtra.default.writeFileSync(outputJs, buildComponentJs(bundled, compName, sid, relPath));
|
|
687
834
|
} catch (e) {
|
|
688
835
|
// A component that fails to emit renders but has no behaviour, so don't
|
|
689
836
|
// swallow the guard errors that report exactly why.
|
|
@@ -691,6 +838,12 @@ class ViteCompiler {
|
|
|
691
838
|
console.warn(`vite: failed to emit component ${relPath}:`, e?.message ?? e);
|
|
692
839
|
}
|
|
693
840
|
}
|
|
841
|
+
if (skippedUx.length) {
|
|
842
|
+
this.onLog?.([{
|
|
843
|
+
level: 3,
|
|
844
|
+
message: [`Skipped ${skippedUx.length} .ux file(s) not reachable from any route or <import> chain ` + `(matches aiot-toolkit 1.x, which only compiled routed pages): ${skippedUx.join(', ')}. ` + `If one of these is opened by a dynamic router call, add it to manifest.json router.pages.`]
|
|
845
|
+
}]);
|
|
846
|
+
}
|
|
694
847
|
}
|
|
695
848
|
async clean(param) {
|
|
696
849
|
const {
|
|
@@ -717,6 +870,27 @@ function stripDeadDefProp(code) {
|
|
|
717
870
|
* leaves `vibrator` as a free identifier, which is why the 咪咕/百度地图 keyboard
|
|
718
871
|
* could no longer vibrate.
|
|
719
872
|
*/
|
|
873
|
+
/**
|
|
874
|
+
* `<import name="comp-score" src="../Score/index.ux">` is compiled by the ux
|
|
875
|
+
* loader into `import comp_score_comp from '../Score/index.ux'`, but a
|
|
876
|
+
* sub-component is resolved purely through data: template.json carries
|
|
877
|
+
* `{type, import}` and the component's own `.js` registers
|
|
878
|
+
* `$app_define$("@app-component/<name>")`. The binding is never read, and
|
|
879
|
+
* leaving the statement in place is a hard error — the IIFE wrapper nests it
|
|
880
|
+
* inside a function body, where `import` is a syntax error.
|
|
881
|
+
*
|
|
882
|
+
* Only drop it when the binding is provably unused; a referenced binding means
|
|
883
|
+
* the loader contract changed and silently dropping it would reintroduce the
|
|
884
|
+
* free-identifier class of bug, so leave it for the caller's guard to report.
|
|
885
|
+
*/
|
|
886
|
+
function dropUnusedUxImports(code) {
|
|
887
|
+
const uxImport = /^[ \t]*import\s+(\w+)\s+from\s+(['"])([^'"]*\.ux)\2[ \t]*;?[ \t]*$\n?/gm;
|
|
888
|
+
return code.replace(uxImport, (stmt, binding) => {
|
|
889
|
+
const rest = code.replace(stmt, '');
|
|
890
|
+
const used = new RegExp(`\\b${binding}\\b`).test(rest);
|
|
891
|
+
return used ? stmt : '';
|
|
892
|
+
});
|
|
893
|
+
}
|
|
720
894
|
function buildComponentJs(compCode, compName, compSid, relPath) {
|
|
721
895
|
let code = compCode;
|
|
722
896
|
// The raw `var $app_style$ = [...]` array is superseded by the $app_style$<sid>
|
|
@@ -733,7 +907,22 @@ function buildComponentJs(compCode, compName, compSid, relPath) {
|
|
|
733
907
|
}
|
|
734
908
|
// Turn @system.*/@app-module imports into $app_require$ calls, same as entries.
|
|
735
909
|
code = rewriteSystemImports(code);
|
|
736
|
-
|
|
910
|
+
const hadDefault = /export\s+default\s|\bas\s+(?:default\b|["']default["'])/.test(code);
|
|
911
|
+
// Bundled components come straight from rolldown, which leaves region markers
|
|
912
|
+
// and runtime helpers behind and re-exports the default as
|
|
913
|
+
// `export { X as default }` rather than `export default`.
|
|
914
|
+
code = stripRolldownArtifacts(code);
|
|
915
|
+
// Must run before the `export default` expression is sliced off, otherwise a
|
|
916
|
+
// binding referenced only inside that expression reads as unused.
|
|
917
|
+
code = dropUnusedUxImports(code);
|
|
918
|
+
let defaultExpr = null;
|
|
919
|
+
const asDefault = code.match(/export\s*\{\s*(\w+)\s+as\s+default\s*(?:,[^}]*)?\}\s*;?/);
|
|
920
|
+
if (asDefault) {
|
|
921
|
+
defaultExpr = asDefault[1];
|
|
922
|
+
code = code.replace(asDefault[0], '');
|
|
923
|
+
}
|
|
924
|
+
// Remaining named exports have no consumer in a $app_define$ module.
|
|
925
|
+
code = code.replace(/^[ \t]*export\s*\{[^}]*\}\s*;?[ \t]*$\n?/gm, '');
|
|
737
926
|
const edIdx = code.search(/export\s+default\s+/);
|
|
738
927
|
if (edIdx !== -1) {
|
|
739
928
|
const kw = code.slice(edIdx).match(/^export\s+default\s+/);
|
|
@@ -746,37 +935,22 @@ function buildComponentJs(compCode, compName, compSid, relPath) {
|
|
|
746
935
|
if (leftoverImport) {
|
|
747
936
|
throw new Error(`[component] ${relPath} has an unresolved ESM import that cannot be emitted into a ` + `standalone $app_define$ module: ${leftoverImport[0].trim()}\n` + `Only @system.*/@service.*/@android.*/@hap.*/@app-module imports are rewritten to ` + `$app_require$. Inline the dependency into the component or move it behind a feature module.`);
|
|
748
937
|
}
|
|
938
|
+
if (hadDefault && defaultExpr === null) {
|
|
939
|
+
// An unrecognized default-export shape would register an empty component:
|
|
940
|
+
// it renders but has no data, props or handlers.
|
|
941
|
+
throw new Error(`[component] ${relPath} declares a default export but none was extracted, ` + `which would register an empty component.`);
|
|
942
|
+
}
|
|
749
943
|
const varName = compName.replace(/-/g, '_') + '_default';
|
|
750
944
|
const moduleCode = code.trim();
|
|
751
|
-
|
|
945
|
+
const emittedDefault = defaultExpr ?? '{}';
|
|
946
|
+
return [`let $style$${compSid} = {"@info":{"styleObjectId":${compSid}}};`, `const $app_style$${compSid} = $style$${compSid};`, ...(moduleCode ? [moduleCode] : []), `var ${varName} = ${emittedDefault};`, `$app_define$("@app-component/${compName}", [], function($app_require$, $app_exports$, $app_module$) {`, ` $app_module$.exports = ${varName}.default || ${varName};`, ` $app_module$.exports.style = $app_style$${compSid};`, `});`, ''].join('\n');
|
|
752
947
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
if (code.startsWith('var _page_')) {
|
|
760
|
-
// Remove first line (the IIFE opening)
|
|
761
|
-
const firstNewline = code.indexOf('\n');
|
|
762
|
-
code = code.slice(firstNewline + 1);
|
|
763
|
-
// Remove the last return statement and closing })();
|
|
764
|
-
const lastReturn = code.lastIndexOf('\treturn ');
|
|
765
|
-
if (lastReturn !== -1) {
|
|
766
|
-
const afterReturn = code.indexOf('\n', lastReturn);
|
|
767
|
-
code = code.slice(0, lastReturn) + code.slice(afterReturn + 1);
|
|
768
|
-
}
|
|
769
|
-
// Remove the closing })(); or })(...)
|
|
770
|
-
const lastClose = code.lastIndexOf('})();');
|
|
771
|
-
if (lastClose !== -1) {
|
|
772
|
-
code = code.slice(0, lastClose);
|
|
773
|
-
} else {
|
|
774
|
-
const lastClose2 = code.lastIndexOf('})(');
|
|
775
|
-
if (lastClose2 !== -1) {
|
|
776
|
-
code = code.slice(0, lastClose2);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Normalize rolldown output: drop its region markers and runtime helpers, and
|
|
951
|
+
* turn surviving `require("@system.*")` calls into `$app_require$`.
|
|
952
|
+
*/
|
|
953
|
+
function stripRolldownArtifacts(code) {
|
|
780
954
|
// Remove rolldown region comments
|
|
781
955
|
code = code.replace(/\/\/#region[^\n]*\n/g, '');
|
|
782
956
|
code = code.replace(/\/\/#endregion[^\n]*/g, '');
|
|
@@ -817,6 +991,36 @@ function wrapOutput(code, entryName, isApp) {
|
|
|
817
991
|
if (!/\b__require\b/.test(code)) {
|
|
818
992
|
code = code.replace(/var __require\s*=\s*\/\*[^*]*\*\/\s*\(\(x\)\s*=>[\s\S]*?}\);\n?/g, '');
|
|
819
993
|
}
|
|
994
|
+
return code;
|
|
995
|
+
}
|
|
996
|
+
function wrapOutput(code, entryName, isApp) {
|
|
997
|
+
const componentName = _path.default.parse(entryName).name;
|
|
998
|
+
const defineId = isApp ? '@app-component/app' : `@app-component/${componentName}`;
|
|
999
|
+
const bootstrapId = isApp ? '@app-application/app' : `@app-component/${componentName}`;
|
|
1000
|
+
// Remove IIFE wrapper: var _page_ = (function(...) { ... return xxx; })(...);
|
|
1001
|
+
// Only remove the FIRST line and the LAST return+closing
|
|
1002
|
+
if (code.startsWith('var _page_')) {
|
|
1003
|
+
// Remove first line (the IIFE opening)
|
|
1004
|
+
const firstNewline = code.indexOf('\n');
|
|
1005
|
+
code = code.slice(firstNewline + 1);
|
|
1006
|
+
// Remove the last return statement and closing })();
|
|
1007
|
+
const lastReturn = code.lastIndexOf('\treturn ');
|
|
1008
|
+
if (lastReturn !== -1) {
|
|
1009
|
+
const afterReturn = code.indexOf('\n', lastReturn);
|
|
1010
|
+
code = code.slice(0, lastReturn) + code.slice(afterReturn + 1);
|
|
1011
|
+
}
|
|
1012
|
+
// Remove the closing })(); or })(...)
|
|
1013
|
+
const lastClose = code.lastIndexOf('})();');
|
|
1014
|
+
if (lastClose !== -1) {
|
|
1015
|
+
code = code.slice(0, lastClose);
|
|
1016
|
+
} else {
|
|
1017
|
+
const lastClose2 = code.lastIndexOf('})(');
|
|
1018
|
+
if (lastClose2 !== -1) {
|
|
1019
|
+
code = code.slice(0, lastClose2);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
code = stripRolldownArtifacts(code);
|
|
820
1024
|
// Extract the default export variable
|
|
821
1025
|
let exportVar = '_default';
|
|
822
1026
|
const m1 = code.match(/export\s*\{\s*(\w+)\s+as\s+default\s*\}/);
|
|
@@ -875,6 +1079,11 @@ function mergeStyleSheets(app, page) {
|
|
|
875
1079
|
const __test = exports.__test = {
|
|
876
1080
|
stripDeadDefProp,
|
|
877
1081
|
mergeStyleSheets,
|
|
878
|
-
buildComponentJs
|
|
1082
|
+
buildComponentJs,
|
|
1083
|
+
dropUnusedUxImports,
|
|
1084
|
+
collectReachableUx,
|
|
1085
|
+
resolveImportSrc,
|
|
1086
|
+
findUnbundledRelativeImports,
|
|
1087
|
+
bundleComponentCode
|
|
879
1088
|
};
|
|
880
1089
|
var _default = exports.default = ViteCompiler;
|
|
@@ -20,6 +20,12 @@ const REQUIRED_JSC_VERSION = '1.0.9';
|
|
|
20
20
|
// not `global` — after the rebind `global.console` would look up ferryGlobal.
|
|
21
21
|
const IIFE_WRAP_PREFIX = 'export default (function (__ctx) {\n' + ' const global = __ctx, window = __ctx, self = __ctx;\n' + ' let { requireModule, unRequireModule, $app_define$, $app_bootstrap$, $app_require$,\n' + ' console = globalThis.console } = __ctx;\n' + ' let { setTimeout, clearTimeout, setInterval, clearInterval } = __ctx;\n\n';
|
|
22
22
|
const IIFE_WRAP_SUFFIX = '\n});\n';
|
|
23
|
+
|
|
24
|
+
// `-s` strips debug info. `-m` (force module) is deliberately omitted: aiotjsc
|
|
25
|
+
// autodetects module-ness and the IIFE wrapper's `export default` already makes
|
|
26
|
+
// it detect module. Passing an explicit array also suppresses jsc's own
|
|
27
|
+
// `undefined → ["-ms"]` fallback, so this list is the full flag set.
|
|
28
|
+
const JSC_ARGS = ['-s'];
|
|
23
29
|
class Jsc {
|
|
24
30
|
constructor(projectPath, buildPath) {
|
|
25
31
|
this.projectPath = projectPath;
|
|
@@ -37,7 +43,7 @@ class Jsc {
|
|
|
37
43
|
this.assertJscVersion();
|
|
38
44
|
Jsc.wrapAllJsWithIife(buildPath);
|
|
39
45
|
const start = Date.now();
|
|
40
|
-
const msg = await module.default(buildPath,
|
|
46
|
+
const msg = await module.default(buildPath, JSC_ARGS);
|
|
41
47
|
const elapsed = Date.now() - start;
|
|
42
48
|
if (msg) {
|
|
43
49
|
_sharedUtils.ColorConsole.success({
|
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.16",
|
|
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.16",
|
|
23
|
+
"@aiot-toolkit/parser": "2.1.0-prender.16",
|
|
24
|
+
"@aiot-toolkit/shared-utils": "2.1.0-prender.16",
|
|
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.16",
|
|
32
32
|
"file-loader": "^6.2.0",
|
|
33
33
|
"fs-extra": "^11.2.0",
|
|
34
34
|
"jsrsasign": "^11.1.0",
|