vite-plugin-taro 0.6.0 → 0.6.2

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.
Files changed (68) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/node/plugins/h5/transform-app.js +1 -1
  4. package/dist/node/plugins/wx/dev/plugins.js +1 -1
  5. package/dist/node/plugins/wx/dev/wx-dev-options.js +22 -1
  6. package/dist/node/plugins/wx/module/chunk-path.d.ts +8 -0
  7. package/dist/node/plugins/wx/module/chunk-path.js +18 -0
  8. package/dist/node/plugins/wx/{module.js → module/module.js} +2 -2
  9. package/dist/node/plugins/wx/native/create-native-component-output.d.ts +3 -1
  10. package/dist/node/plugins/wx/native/create-native-component-output.js +3 -5
  11. package/dist/node/plugins/wx/output/files.d.ts +3 -2
  12. package/dist/node/plugins/wx/output/files.js +2 -2
  13. package/dist/node/plugins/wx/output/json.d.ts +1 -1
  14. package/dist/node/plugins/wx/output/json.js +1 -1
  15. package/dist/node/plugins/wx/placer/placement.d.ts +51 -0
  16. package/dist/node/plugins/wx/placer/placement.js +245 -0
  17. package/dist/node/plugins/wx/placer/placer.d.ts +76 -0
  18. package/dist/node/plugins/wx/placer/placer.js +147 -0
  19. package/dist/node/plugins/wx/plugins.js +17 -17
  20. package/dist/node/plugins/wx/render/capsule.d.ts +35 -3
  21. package/dist/node/plugins/wx/render/capsule.js +53 -12
  22. package/dist/node/plugins/wx/render/native.d.ts +20 -2
  23. package/dist/node/plugins/wx/render/native.js +539 -35
  24. package/dist/node/plugins/wx/render/system-js/string-editor.d.ts +24 -0
  25. package/dist/node/plugins/wx/render/system-js/string-editor.js +93 -0
  26. package/dist/node/plugins/wx/render/system-js/system-js.d.ts +23 -0
  27. package/dist/node/plugins/wx/render/system-js/system-js.js +602 -0
  28. package/dist/node/plugins/wx/render/transport.d.ts +7 -3
  29. package/dist/node/plugins/wx/render/transport.js +19 -10
  30. package/dist/node/plugins/wx/resolve/resolver.js +1 -1
  31. package/dist/node/plugins/wx/resolve/specialize-bootstrap.js +1 -1
  32. package/dist/node/plugins/wx/resolve/specialize-page-capsule.js +1 -1
  33. package/dist/node/utils/modules.d.ts +0 -2
  34. package/dist/node/utils/modules.js +0 -7
  35. package/dist/node/utils/transform.d.ts +0 -3
  36. package/dist/node/utils/transform.js +0 -23
  37. package/package.json +8 -6
  38. package/src/node/plugins/h5/transform-app.ts +1 -1
  39. package/src/node/plugins/wx/dev/plugins.ts +1 -1
  40. package/src/node/plugins/wx/dev/wx-dev-options.ts +22 -1
  41. package/src/node/plugins/wx/module/chunk-path.ts +22 -0
  42. package/src/node/plugins/wx/{module.ts → module/module.ts} +2 -2
  43. package/src/node/plugins/wx/native/create-native-component-output.ts +6 -5
  44. package/src/node/plugins/wx/output/files.ts +5 -3
  45. package/src/node/plugins/wx/output/json.ts +1 -2
  46. package/src/node/plugins/wx/placer/placement.ts +364 -0
  47. package/src/node/plugins/wx/placer/placer.ts +179 -0
  48. package/src/node/plugins/wx/plugins.ts +19 -19
  49. package/src/node/plugins/wx/render/capsule.ts +53 -17
  50. package/src/node/plugins/wx/render/native.ts +670 -55
  51. package/src/node/plugins/wx/render/system-js/string-editor.ts +115 -0
  52. package/src/node/plugins/wx/render/system-js/system-js.ts +831 -0
  53. package/src/node/plugins/wx/render/transport.ts +25 -9
  54. package/src/node/plugins/wx/resolve/resolver.ts +1 -1
  55. package/src/node/plugins/wx/resolve/specialize-bootstrap.ts +1 -1
  56. package/src/node/plugins/wx/resolve/specialize-page-capsule.ts +1 -1
  57. package/src/node/utils/modules.ts +0 -8
  58. package/src/node/utils/transform.ts +0 -30
  59. package/dist/node/plugins/wx/placement/placer.d.ts +0 -78
  60. package/dist/node/plugins/wx/placement/placer.js +0 -158
  61. package/dist/node/plugins/wx/placement/plan.d.ts +0 -46
  62. package/dist/node/plugins/wx/placement/plan.js +0 -210
  63. package/dist/node/plugins/wx/render/capsule-wrapper.d.ts +0 -3
  64. package/dist/node/plugins/wx/render/capsule-wrapper.js +0 -64
  65. package/src/node/plugins/wx/placement/placer.ts +0 -187
  66. package/src/node/plugins/wx/placement/plan.ts +0 -306
  67. package/src/node/plugins/wx/render/capsule-wrapper.ts +0 -86
  68. /package/dist/node/plugins/wx/{module.d.ts → module/module.d.ts} +0 -0
@@ -0,0 +1,602 @@
1
+ import { ScopeTracker, walk } from 'oxc-walker';
2
+ import { RolldownMagicString } from 'rolldown';
3
+ import { parseSync } from 'rolldown/utils';
4
+ import { StringEditor } from './string-editor.js';
5
+ /**
6
+ * Converts one final Rolldown ES chunk into System.register data without rebuilding its complete AST through Babel.
7
+ *
8
+ * The compiler intentionally accepts Rolldown's normalized final-chunk grammar rather than arbitrary source modules. Every
9
+ * unsupported module declaration fails before source edits begin, so a future Rolldown output change cannot be miscompiled.
10
+ */
11
+ export function transformSystemJs(options) {
12
+ const parseResult = parseSync(options.filename, options.code);
13
+ if (parseResult.errors.length > 0) {
14
+ const diagnostics = parseResult.errors.map((error) => error.message).join('; ');
15
+ throw new Error(`Failed to parse ${options.filename} with Oxc: ${diagnostics}`);
16
+ }
17
+ // Analysis and validation complete before an editor can publish output. A thrown unsupported-form error therefore fails
18
+ // the build transaction instead of returning a partially transformed capsule.
19
+ const model = analyzeModule(parseResult.program, options.filename);
20
+ if (!options.sourcemap) {
21
+ const editor = new StringEditor(options.code);
22
+ applyProgramEdits(editor, model);
23
+ applyExpressionEdits(editor, model, options);
24
+ return { code: assembleUnmappedRegistration(editor, model, options), map: null };
25
+ }
26
+ const editor = new RolldownMagicString(options.code, { filename: options.filename });
27
+ applyProgramEdits(editor, model);
28
+ applyExpressionEdits(editor, model, options);
29
+ assembleMappedRegistration(editor, model, options);
30
+ return {
31
+ code: editor.toString(),
32
+ map: createSourceMap(editor, options.filename)
33
+ };
34
+ }
35
+ /** Collects immutable compilation facts before any source range is changed. */
36
+ function analyzeModule(program, filename) {
37
+ // These journals are mutable only during this one linear analysis pass; all are exposed as readonly compilation facts.
38
+ const identifierNames = new Set();
39
+ const exportNamesByLocal = new Map();
40
+ const dependencyBySource = new Map();
41
+ const functions = [];
42
+ const hoistedVariables = [];
43
+ const importBindings = new Set();
44
+ const outerBindings = new Set();
45
+ // Boundary depth excludes declarations and await expressions owned by nested functions or classes from module analysis.
46
+ let moduleBoundaryDepth = 0;
47
+ let hasDirectEval = false;
48
+ let hasTopLevelAwait = false;
49
+ walk(program, {
50
+ enter(node, parent) {
51
+ if (node.type === 'Identifier')
52
+ identifierNames.add(node.name);
53
+ if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'eval') {
54
+ hasDirectEval = true;
55
+ }
56
+ if (isModuleBoundary(node))
57
+ moduleBoundaryDepth += 1;
58
+ if (node.type === 'AwaitExpression' && moduleBoundaryDepth === 0)
59
+ hasTopLevelAwait = true;
60
+ if (node.type === 'VariableDeclaration' && node.kind === 'var' && moduleBoundaryDepth === 0) {
61
+ const isForIterationBinding = (parent?.type === 'ForInStatement' || parent?.type === 'ForOfStatement') && parent.left === node;
62
+ hoistedVariables.push({ declaration: node, isForIterationBinding });
63
+ bindingNamesFromDeclaration(node).forEach((name) => {
64
+ outerBindings.add(name);
65
+ });
66
+ }
67
+ },
68
+ leave(node) {
69
+ if (isModuleBoundary(node))
70
+ moduleBoundaryDepth -= 1;
71
+ }
72
+ });
73
+ // Final Rolldown chunks normally expose imports and exports as direct Program children. Declaration exports and
74
+ // re-exports are intentionally not normalized here because accepting them would expand the semantic surface needlessly.
75
+ for (const node of program.body) {
76
+ switch (node.type) {
77
+ case 'ImportDeclaration':
78
+ collectImport(node, dependencyBySource, importBindings, outerBindings, filename);
79
+ break;
80
+ case 'ExportNamedDeclaration':
81
+ collectExports(node, exportNamesByLocal, filename);
82
+ break;
83
+ case 'ExportDefaultDeclaration':
84
+ case 'ExportAllDeclaration':
85
+ throw unsupported(filename, `source-level ${node.type}`);
86
+ case 'VariableDeclaration':
87
+ requireSupportedVariableKind(node, filename);
88
+ node.declarations
89
+ .flatMap((declaration) => bindingNames(declaration.id))
90
+ .forEach((name) => {
91
+ outerBindings.add(name);
92
+ });
93
+ break;
94
+ case 'FunctionDeclaration':
95
+ if (!node.id || !node.body || node.declare) {
96
+ throw unsupported(filename, 'anonymous or ambient function declaration');
97
+ }
98
+ functions.push(node);
99
+ outerBindings.add(node.id.name);
100
+ break;
101
+ case 'ClassDeclaration':
102
+ if (!node.id || node.declare)
103
+ throw unsupported(filename, 'anonymous or ambient class declaration');
104
+ outerBindings.add(node.id.name);
105
+ break;
106
+ }
107
+ }
108
+ requireNoDirectEval(hasDirectEval, filename);
109
+ const generatedNames = createGeneratedNames(identifierNames, dependencyBySource.size);
110
+ validateExportBindings(exportNamesByLocal, outerBindings, filename);
111
+ const scopes = createFrozenScopes(program);
112
+ return {
113
+ dependencies: [...dependencyBySource.values()],
114
+ exportNamesByLocal,
115
+ functions,
116
+ generatedNames,
117
+ hasTopLevelAwait,
118
+ hoistedVariables,
119
+ importBindings,
120
+ outerBindings,
121
+ program,
122
+ scopes
123
+ };
124
+ }
125
+ /** Merges same-source imports because one SystemJS dependency has exactly one setter. */
126
+ function collectImport(declaration, dependencyBySource, importBindings, outerBindings, filename) {
127
+ if (declaration.phase || declaration.attributes.length > 0 || declaration.importKind === 'type') {
128
+ throw unsupported(filename, 'import phases, attributes, or type-only imports');
129
+ }
130
+ const source = declaration.source.value;
131
+ // This map owns one mutable import list per source while declarations are folded; duplicate imports retain source order.
132
+ const dependency = dependencyBySource.get(source) ?? { source, imports: [] };
133
+ if (!dependencyBySource.has(source))
134
+ dependencyBySource.set(source, dependency);
135
+ for (const specifier of declaration.specifiers) {
136
+ importBindings.add(specifier.local.name);
137
+ outerBindings.add(specifier.local.name);
138
+ switch (specifier.type) {
139
+ case 'ImportDefaultSpecifier':
140
+ dependency.imports.push({ imported: 'default', local: specifier.local.name });
141
+ break;
142
+ case 'ImportNamespaceSpecifier':
143
+ dependency.imports.push({ imported: null, local: specifier.local.name });
144
+ break;
145
+ case 'ImportSpecifier':
146
+ if (specifier.importKind === 'type')
147
+ throw unsupported(filename, 'type-only import specifier');
148
+ dependency.imports.push({ imported: moduleExportName(specifier.imported), local: specifier.local.name });
149
+ break;
150
+ }
151
+ }
152
+ }
153
+ /** Records every public alias attached to one local binding. */
154
+ function collectExports(declaration, exportNamesByLocal, filename) {
155
+ if (declaration.declaration ||
156
+ declaration.source ||
157
+ declaration.attributes.length > 0 ||
158
+ declaration.exportKind === 'type') {
159
+ throw unsupported(filename, 'declaration exports, re-exports, export attributes, or type-only exports');
160
+ }
161
+ for (const specifier of declaration.specifiers) {
162
+ if (specifier.exportKind === 'type')
163
+ throw unsupported(filename, 'type-only export specifier');
164
+ const local = moduleExportName(specifier.local);
165
+ const exported = moduleExportName(specifier.exported);
166
+ // Aliases are accumulated in declaration order because nested live-binding calls must publish in the same order as Babel.
167
+ const names = exportNamesByLocal.get(local) ?? [];
168
+ names.push(exported);
169
+ if (!exportNamesByLocal.has(local))
170
+ exportNamesByLocal.set(local, names);
171
+ }
172
+ }
173
+ /** Rejects exports outside the direct normalized module cells supported by final Rolldown chunks. */
174
+ function validateExportBindings(exportNamesByLocal, outerBindings, filename) {
175
+ for (const local of exportNamesByLocal.keys()) {
176
+ if (!outerBindings.has(local)) {
177
+ throw unsupported(filename, `export of non-module binding ${JSON.stringify(local)}`);
178
+ }
179
+ }
180
+ }
181
+ /** Applies declaration-level edits whose semantics are known from direct Program children. */
182
+ function applyProgramEdits(editor, model) {
183
+ for (const node of model.program.body) {
184
+ switch (node.type) {
185
+ case 'ImportDeclaration':
186
+ editor.remove(node.start, node.end);
187
+ break;
188
+ case 'ExportNamedDeclaration':
189
+ editor.overwrite(node.start, node.end, renderImportedExports(node.specifiers, model));
190
+ break;
191
+ case 'VariableDeclaration':
192
+ transformTopLevelVariables(editor, node, model.generatedNames.exportBinding, model.exportNamesByLocal);
193
+ break;
194
+ case 'ClassDeclaration':
195
+ transformTopLevelClass(editor, node, model.generatedNames.exportBinding, model.exportNamesByLocal);
196
+ break;
197
+ }
198
+ }
199
+ const directDeclarations = new Set(model.program.body.flatMap((node) => (node.type === 'VariableDeclaration' ? [node] : [])));
200
+ model.hoistedVariables
201
+ .filter(({ declaration }) => !directDeclarations.has(declaration))
202
+ .forEach(({ declaration, isForIterationBinding }) => {
203
+ transformNestedHoistedVariables(editor, declaration, isForIterationBinding, model.generatedNames.exportBinding, model.exportNamesByLocal);
204
+ });
205
+ }
206
+ /** Changes module variables into assignments to declaration-scope cells shared with setters and hoisted functions. */
207
+ function transformTopLevelVariables(editor, declaration, exportBinding, exportNamesByLocal) {
208
+ const [first] = declaration.declarations;
209
+ if (!first) {
210
+ editor.remove(declaration.start, declaration.end);
211
+ return;
212
+ }
213
+ // `const value = init` becomes `(value = init)`: the declaration cell itself is emitted once in the registration scope.
214
+ editor.overwrite(declaration.start, first.start, '(');
215
+ editor.appendLeft(statementTerminatorStart(editor.original, declaration.end), ')');
216
+ transformVariableInitializers(editor, declaration, exportBinding, exportNamesByLocal);
217
+ }
218
+ /** Hoists module-scoped var declarations nested in statements without changing their control-flow position. */
219
+ function transformNestedHoistedVariables(editor, declaration, isForIterationBinding, exportBinding, exportNamesByLocal) {
220
+ const [first] = declaration.declarations;
221
+ if (!first) {
222
+ editor.remove(declaration.start, declaration.end);
223
+ return;
224
+ }
225
+ editor.overwrite(declaration.start, first.start, isForIterationBinding ? '' : '(');
226
+ if (!isForIterationBinding)
227
+ editor.appendLeft(statementTerminatorStart(editor.original, declaration.end), ')');
228
+ transformVariableInitializers(editor, declaration, exportBinding, exportNamesByLocal);
229
+ }
230
+ /** Publishes initialized exported cells at the exact initializer evaluation point. */
231
+ function transformVariableInitializers(editor, declaration, exportBinding, exportNamesByLocal) {
232
+ for (const declarator of declaration.declarations) {
233
+ if (!declarator.init)
234
+ continue;
235
+ const exportedBindings = bindingNames(declarator.id).flatMap((name) => (exportNamesByLocal.get(name) ?? []).map((exported) => ({ exported, local: name })));
236
+ if (exportedBindings.length === 0)
237
+ continue;
238
+ if (declarator.id.type === 'Identifier') {
239
+ // Nested calls publish every alias while preserving the initializer's completion value.
240
+ const names = exportNamesByLocal.get(declarator.id.name) ?? [];
241
+ editor.prependLeft(declarator.start, exportExpressionPrefix(exportBinding, names, ''));
242
+ editor.appendRight(declarator.end, exportExpressionSuffix(names));
243
+ continue;
244
+ }
245
+ editor.prependLeft(declarator.start, '(');
246
+ editor.appendRight(declarator.end, `,${exportedBindings.map(({ exported, local }) => exportCall(exportBinding, exported, local)).join(',')})`);
247
+ }
248
+ }
249
+ /** Turns a class declaration into the execute-time assignment required by cyclic SystemJS linking. */
250
+ function transformTopLevelClass(editor, declaration, exportBinding, exportNamesByLocal) {
251
+ if (!declaration.id)
252
+ return;
253
+ const names = exportNamesByLocal.get(declaration.id.name) ?? [];
254
+ editor.prependLeft(declaration.start, exportExpressionPrefix(exportBinding, names, `${declaration.id.name}=`));
255
+ editor.appendRight(declaration.end, exportExpressionSuffix(names));
256
+ }
257
+ /** Rewrites expression-level module semantics in one scope-aware O(n) traversal. */
258
+ function applyExpressionEdits(editor, model, options) {
259
+ const scopes = model.scopes;
260
+ // Function and class depth are mutable traversal cursors used only to identify lexical module `this` and top-level await.
261
+ let thisBoundaryDepth = 0;
262
+ walk(model.program, {
263
+ scopeTracker: scopes,
264
+ enter(node) {
265
+ if (isThisBoundary(node))
266
+ thisBoundaryDepth += 1;
267
+ switch (node.type) {
268
+ case 'ImportExpression':
269
+ // SystemJS owns dynamic loading; only string literals are canonicalized at build time.
270
+ if (node.options || node.phase)
271
+ throw unsupported(options.filename, 'dynamic import options or phases');
272
+ editor.overwrite(node.start, node.source.start, `${model.generatedNames.context}.import(`);
273
+ if (node.source.type === 'Literal' && typeof node.source.value === 'string') {
274
+ editor.overwrite(node.source.start, node.source.end, JSON.stringify(options.resolveReference(node.source.value, 'dynamic')));
275
+ }
276
+ break;
277
+ case 'MetaProperty':
278
+ if (node.meta.name === 'import' && node.property.name === 'meta') {
279
+ editor.overwrite(node.start, node.end, `${model.generatedNames.context}.meta`);
280
+ }
281
+ break;
282
+ case 'ThisExpression':
283
+ // ESM top-level `this` is undefined. Arrow functions inherit it, while normal functions/classes do not.
284
+ if (thisBoundaryDepth === 0)
285
+ editor.overwrite(node.start, node.end, 'void 0');
286
+ break;
287
+ case 'AssignmentExpression':
288
+ transformAssignment(editor, node.left, node.start, node.end, scopes, model, options.filename);
289
+ break;
290
+ case 'UpdateExpression':
291
+ transformUpdate(editor, node, scopes, model);
292
+ break;
293
+ case 'ForInStatement':
294
+ case 'ForOfStatement':
295
+ if (node.left.type !== 'VariableDeclaration') {
296
+ requireNoExportedPattern(node.left, scopes, model.exportNamesByLocal, options.filename);
297
+ }
298
+ break;
299
+ }
300
+ },
301
+ leave(node) {
302
+ if (isThisBoundary(node))
303
+ thisBoundaryDepth -= 1;
304
+ }
305
+ });
306
+ }
307
+ /** Wraps one assignment when its target is an exported root binding. */
308
+ function transformAssignment(editor, target, start, end, scopes, model, filename) {
309
+ if (target.type === 'Identifier') {
310
+ const names = exportedRootNames(target.name, scopes, model.exportNamesByLocal);
311
+ if (names.length === 0)
312
+ return;
313
+ editor.prependLeft(start, exportExpressionPrefix(model.generatedNames.exportBinding, names, ''));
314
+ editor.appendRight(end, exportExpressionSuffix(names));
315
+ return;
316
+ }
317
+ requireNoExportedPattern(target, scopes, model.exportNamesByLocal, filename);
318
+ }
319
+ /** Preserves prefix and postfix update values while publishing the next live binding. */
320
+ function transformUpdate(editor, update, scopes, model) {
321
+ if (update.argument.type !== 'Identifier')
322
+ return;
323
+ const names = exportedRootNames(update.argument.name, scopes, model.exportNamesByLocal);
324
+ if (names.length === 0)
325
+ return;
326
+ if (update.prefix) {
327
+ editor.prependLeft(update.start, exportExpressionPrefix(model.generatedNames.exportBinding, names, ''));
328
+ editor.appendRight(update.end, exportExpressionSuffix(names));
329
+ return;
330
+ }
331
+ // A postfix expression must return the old value, so publish the computed next cell before evaluating the original update.
332
+ const nextValue = `+${update.argument.name}${update.operator[0]}1`;
333
+ editor.prependLeft(update.start, `(${nestedExportExpression(model.generatedNames.exportBinding, names, nextValue)},`);
334
+ editor.appendRight(update.end, ')');
335
+ }
336
+ /** Fails rather than silently changing the completion value of an exported destructuring assignment. */
337
+ function requireNoExportedPattern(target, scopes, exportNamesByLocal, filename) {
338
+ const exported = assignmentNames(target).filter((name) => exportedRootNames(name, scopes, exportNamesByLocal).length > 0);
339
+ if (exported.length > 0) {
340
+ throw unsupported(filename, `destructuring write to exported binding ${JSON.stringify(exported[0])}`);
341
+ }
342
+ }
343
+ /** Renders the no-map fast path through one source-order edit journal. */
344
+ function assembleUnmappedRegistration(editor, model, options) {
345
+ const shell = createRegistrationShell(model, options);
346
+ const hoistedFunctions = model.functions
347
+ .map((declaration) => editor.render(declaration.start, declaration.end))
348
+ .join('');
349
+ model.functions.forEach((declaration) => {
350
+ editor.remove(declaration.start, declaration.end);
351
+ });
352
+ return `${shell.prefix}${editor.render(0, editor.original.length)}}};${hoistedFunctions}${shell.suffix}`;
353
+ }
354
+ /** Places mapped hoisted functions after the declaration return while retaining every original source segment. */
355
+ function assembleMappedRegistration(editor, model, options) {
356
+ const shell = createRegistrationShell(model, options);
357
+ // Relocation preserves source-map ownership for large hoisted function bodies when callers explicitly request maps.
358
+ for (const declaration of model.functions)
359
+ editor.move(declaration.start, declaration.end, editor.original.length);
360
+ editor.prepend(shell.prefix);
361
+ editor.prependLeft(editor.original.length, '}};');
362
+ editor.append(shell.suffix);
363
+ }
364
+ /** Creates the generated registration boundary shared by mapped and unmapped rendering. */
365
+ function createRegistrationShell(model, options) {
366
+ const dependencies = model.dependencies.map((dependency) => options.resolveReference(dependency.source, 'static'));
367
+ const setters = model.dependencies.map((dependency, index) => renderSetter(dependency, index, model.generatedNames));
368
+ const earlyExports = renderEarlyExports(model);
369
+ const declarations = model.outerBindings.size > 0 ? `var ${[...model.outerBindings].join(',')};` : '';
370
+ const execute = model.hasTopLevelAwait ? 'async function' : 'function';
371
+ // Imports, hoisted cells, and cyclically visible exports belong to declaration/link time. Original executable statements
372
+ // remain inside execute(), which becomes async only when the module itself owns a top-level await.
373
+ const declarationStart = `function(${model.generatedNames.exportBinding},${model.generatedNames.context}){"use strict";${declarations}${earlyExports}return {setters:[${setters.join(',')}],execute:${execute}(){`;
374
+ return options.format === 'system-register'
375
+ ? {
376
+ prefix: `System.register(${JSON.stringify(dependencies)},${declarationStart}`,
377
+ suffix: '})'
378
+ }
379
+ : {
380
+ prefix: `module.exports=[${JSON.stringify(dependencies)},${declarationStart}`,
381
+ suffix: '}]'
382
+ };
383
+ }
384
+ /** Produces declaration-time exports for functions and uninitialized variables, matching cyclic ESM availability. */
385
+ function renderEarlyExports(model) {
386
+ const functionNames = new Set(model.functions.flatMap((declaration) => (declaration.id ? [declaration.id.name] : [])));
387
+ const directVariables = model.program.body.flatMap((node) => (node.type === 'VariableDeclaration' ? [node] : []));
388
+ const nestedHoistedVariables = model.hoistedVariables
389
+ .map(({ declaration }) => declaration)
390
+ .filter((declaration) => !directVariables.includes(declaration));
391
+ const variablesInSourceOrder = [...directVariables, ...nestedHoistedVariables].sort((left, right) => left.start - right.start);
392
+ const entries = [];
393
+ for (const [local, exportedNames] of model.exportNamesByLocal) {
394
+ if (!functionNames.has(local))
395
+ continue;
396
+ for (const exported of exportedNames)
397
+ entries.push({ exported, value: local });
398
+ }
399
+ for (const declaration of variablesInSourceOrder) {
400
+ for (const declarator of declaration.declarations) {
401
+ if (declarator.init)
402
+ continue;
403
+ for (const local of bindingNames(declarator.id)) {
404
+ for (const exported of model.exportNamesByLocal.get(local) ?? []) {
405
+ entries.push({ exported, value: 'void 0' });
406
+ }
407
+ }
408
+ }
409
+ }
410
+ if (entries.length === 0)
411
+ return '';
412
+ if (entries.length === 1) {
413
+ const [entry] = entries;
414
+ return exportCallWith(model.generatedNames.exportBinding, entry.exported, entry.value);
415
+ }
416
+ const properties = entries.map(({ exported, value }) => `[${JSON.stringify(exported)}]:${value}`).join(',');
417
+ return `${model.generatedNames.exportBinding}({${properties}});`;
418
+ }
419
+ /** Keeps Babel's execute-time publication point for imported locals named by a final export list. */
420
+ function renderImportedExports(specifiers, model) {
421
+ return specifiers
422
+ .filter((specifier) => model.importBindings.has(moduleExportName(specifier.local)))
423
+ .map((specifier) => {
424
+ const local = moduleExportName(specifier.local);
425
+ return exportCallWith(model.generatedNames.exportBinding, moduleExportName(specifier.exported), local);
426
+ })
427
+ .join('');
428
+ }
429
+ /** Creates one dependency setter with the import cells consumed by execute-time code. */
430
+ function renderSetter(dependency, index, names) {
431
+ const moduleName = `${names.dependencyPrefix}${index}`;
432
+ const statements = dependency.imports.flatMap((binding) => {
433
+ const importedValue = binding.imported === null ? moduleName : memberExpression(moduleName, binding.imported);
434
+ return [`${binding.local}=${importedValue};`];
435
+ });
436
+ return `function(${moduleName}){${statements.join('')}}`;
437
+ }
438
+ /** Selects aliases only when the current traversal reference resolves to the root module declaration. */
439
+ function exportedRootNames(local, scopes, exportNamesByLocal) {
440
+ const names = exportNamesByLocal.get(local) ?? [];
441
+ if (names.length === 0)
442
+ return names;
443
+ const declaration = scopes.getDeclaration(local);
444
+ return declaration?.scope === '' ? names : [];
445
+ }
446
+ /** Builds the opening calls around an expression that follows directly in the original source. */
447
+ function exportExpressionPrefix(exportBinding, names, expressionPrefix) {
448
+ if (names.length === 0)
449
+ return expressionPrefix;
450
+ return names.reduce((prefix, exported) => `${exportCallPrefix(exportBinding, exported)}${prefix}`, expressionPrefix);
451
+ }
452
+ /** Closes nested export calls opened by exportExpressionPrefix. */
453
+ function exportExpressionSuffix(names) {
454
+ return ')'.repeat(names.length);
455
+ }
456
+ /** Renders nested export calls around a generated expression. */
457
+ function nestedExportExpression(exportBinding, names, expression) {
458
+ return names.reduce((value, exported) => `${exportCallPrefix(exportBinding, exported)}${value})`, expression);
459
+ }
460
+ /** Opens an export notification around an original expression whose source text follows. */
461
+ function exportCallPrefix(exportBinding, exported) {
462
+ return `${exportBinding}(${JSON.stringify(exported)},`;
463
+ }
464
+ /** Emits an export notification expression without imposing statement boundaries. */
465
+ function exportCall(exportBinding, exported, value) {
466
+ return `${exportBinding}(${JSON.stringify(exported)},${value})`;
467
+ }
468
+ /** Emits a complete export notification statement. */
469
+ function exportCallWith(exportBinding, exported, value) {
470
+ return `${exportBinding}(${JSON.stringify(exported)},${value});`;
471
+ }
472
+ /** Creates collision-free identifiers without requiring Babel's scope allocator. */
473
+ function createGeneratedNames(identifierNames, dependencyCount) {
474
+ // The used-name set is locally mutable because each selected helper reserves its name for the next helper.
475
+ const used = new Set(identifierNames);
476
+ const take = (base) => {
477
+ // The suffix cursor advances only on an actual source collision and never escapes this allocation call.
478
+ let suffix = 0;
479
+ let candidate = base;
480
+ while (used.has(candidate)) {
481
+ suffix += 1;
482
+ candidate = `${base}${suffix}`;
483
+ }
484
+ used.add(candidate);
485
+ return candidate;
486
+ };
487
+ const takeDependencyPrefix = (base) => {
488
+ // Prefix selection tests every concrete setter argument because the unsuffixed base may itself be collision-free.
489
+ let suffix = 0;
490
+ let candidate = base;
491
+ while (Array.from({ length: dependencyCount }, (_, index) => `${candidate}${index}`).some((name) => used.has(name))) {
492
+ suffix += 1;
493
+ candidate = `${base}${suffix}`;
494
+ }
495
+ Array.from({ length: dependencyCount }, (_, index) => `${candidate}${index}`).forEach((name) => {
496
+ used.add(name);
497
+ });
498
+ return candidate;
499
+ };
500
+ return {
501
+ context: take('__systemContext'),
502
+ exportBinding: take('__systemExport'),
503
+ dependencyPrefix: takeDependencyPrefix('__systemDependency')
504
+ };
505
+ }
506
+ /** Builds and freezes complete lexical declarations for scope-correct write detection. */
507
+ function createFrozenScopes(program) {
508
+ // ScopeTracker is intentionally mutable during its declaration pass, then frozen before every query traversal.
509
+ const scopes = new ScopeTracker({ preserveExitedScopes: true });
510
+ walk(program, { scopeTracker: scopes });
511
+ scopes.freeze();
512
+ return scopes;
513
+ }
514
+ /** Normalizes identifier and quoted module names to the runtime property string. */
515
+ function moduleExportName(name) {
516
+ return name.type === 'Literal' ? String(name.value) : name.name;
517
+ }
518
+ /** Uses dot access only when the imported name is a valid identifier. */
519
+ function memberExpression(object, property) {
520
+ return /^[$A-Z_a-z][$\w]*$/.test(property) ? `${object}.${property}` : `${object}[${JSON.stringify(property)}]`;
521
+ }
522
+ function bindingNamesFromDeclaration(declaration) {
523
+ return declaration.declarations.flatMap((declarator) => bindingNames(declarator.id));
524
+ }
525
+ function bindingNames(pattern) {
526
+ return patternNames(pattern);
527
+ }
528
+ function assignmentNames(pattern) {
529
+ return patternNames(pattern);
530
+ }
531
+ /** Extracts identifiers from binding and assignment patterns without treating computed keys as targets. */
532
+ function patternNames(pattern) {
533
+ switch (pattern.type) {
534
+ case 'Identifier':
535
+ return [pattern.name];
536
+ case 'AssignmentPattern':
537
+ return patternNames(pattern.left);
538
+ case 'ArrayPattern':
539
+ return pattern.elements.flatMap((element) => (element ? patternNames(element) : []));
540
+ case 'ObjectPattern':
541
+ return pattern.properties.flatMap((property) => property.type === 'Property' ? patternNames(property.value) : patternNames(property.argument));
542
+ case 'RestElement':
543
+ return patternNames(pattern.argument);
544
+ case 'TSAsExpression':
545
+ case 'TSSatisfiesExpression':
546
+ case 'TSNonNullExpression':
547
+ case 'TSTypeAssertion':
548
+ return patternNames(pattern.expression);
549
+ default:
550
+ return [];
551
+ }
552
+ }
553
+ function isFunction(node) {
554
+ return (node.type === 'FunctionDeclaration' ||
555
+ node.type === 'FunctionExpression' ||
556
+ node.type === 'ArrowFunctionExpression' ||
557
+ node.type === 'TSEmptyBodyFunctionExpression');
558
+ }
559
+ function isModuleBoundary(node) {
560
+ return isFunction(node) || node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
561
+ }
562
+ function isThisBoundary(node) {
563
+ return ((isFunction(node) && node.type !== 'ArrowFunctionExpression') ||
564
+ node.type === 'ClassDeclaration' ||
565
+ node.type === 'ClassExpression');
566
+ }
567
+ /** Babel rejects direct eval for SystemJS because renaming declaration cells cannot update evaluated source. */
568
+ function requireNoDirectEval(hasDirectEval, filename) {
569
+ if (hasDirectEval)
570
+ throw unsupported(filename, 'direct eval');
571
+ }
572
+ function requireSupportedVariableKind(declaration, filename) {
573
+ if (declaration.kind === 'using' || declaration.kind === 'await using' || declaration.declare) {
574
+ throw unsupported(filename, 'using or ambient variable declaration');
575
+ }
576
+ }
577
+ /** Places a closing edit before an optional semicolon owned by the declaration node. */
578
+ function statementTerminatorStart(code, end) {
579
+ return code[end - 1] === ';' ? end - 1 : end;
580
+ }
581
+ /** Makes a future Rolldown grammar change fail closed with the affected chunk named. */
582
+ function unsupported(filename, construct) {
583
+ return new Error(`Unsupported final Rolldown chunk ${filename}: ${construct}`);
584
+ }
585
+ /** Produces a Vite-compatible raw map that retains the complete pre-transform chunk as source content. */
586
+ function createSourceMap(editor, filename) {
587
+ const generated = editor.generateMap({
588
+ file: filename,
589
+ hires: 'boundary',
590
+ includeContent: true,
591
+ source: filename
592
+ });
593
+ return {
594
+ version: generated.version,
595
+ file: generated.file,
596
+ sources: generated.sources,
597
+ sourcesContent: generated.sourcesContent,
598
+ names: generated.names,
599
+ mappings: generated.mappings,
600
+ ...(generated.x_google_ignoreList ? { x_google_ignoreList: generated.x_google_ignoreList } : {})
601
+ };
602
+ }
@@ -1,17 +1,21 @@
1
1
  import type { Rolldown } from 'vite';
2
2
  import { type AstTransformResult } from '../../../utils/transform.ts';
3
3
  /**
4
- * Materializes transport while Rolldown's preliminary hash placeholders are still active, so every injected physical
5
- * reference participates in final hash calculation instead of changing code after its filename has been fixed.
4
+ * Materializes transport while Rolldown's preliminary hash placeholders are still active. Each switch case deliberately has
5
+ * two IDs: the package-neutral preliminary filename without its `assets/` directory becomes the SystemJS registration
6
+ * identity, while the LTHP-selected assets/package-qualified filename becomes the literal native require path. Rolldown
7
+ * substitutes both hashes after this transform, so the
8
+ * generated transport code and its own content hash describe the exact files that `generateBundle` later materializes.
6
9
  *
7
10
  * This intentionally creates broad hash invalidation: changing one capsule can rename transport, then bootstrap, then
8
11
  * chunks that import bootstrap. A Mini Program ships one application package rather than independently cached HTTP
9
12
  * chunks, so honest content hashes and automatic graph linking are more valuable than minimizing that hash fan-out.
10
13
  */
11
- export declare function materializeTransport({ code, transportChunk, chunks, getLoadMode, sourcemap }: {
14
+ export declare function materializeTransport({ code, transportChunk, chunks, getLoadMode, getPhysicalChunkId, sourcemap }: {
12
15
  code: string;
13
16
  transportChunk: Rolldown.RenderedChunk;
14
17
  chunks: Readonly<Record<string, Rolldown.RenderedChunk>>;
15
18
  getLoadMode(chunk: Rolldown.RenderedChunk): 'sync' | 'async';
19
+ getPhysicalChunkId?: (chunk: Rolldown.RenderedChunk) => string;
16
20
  sourcemap?: boolean;
17
21
  }): Promise<AstTransformResult>;