vite-plugin-taro 0.6.1 → 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.
- package/dist/node/plugins/h5/transform-app.js +1 -1
- package/dist/node/plugins/wx/dev/plugins.js +1 -1
- package/dist/node/plugins/wx/dev/wx-dev-options.js +1 -1
- package/dist/node/plugins/wx/{module.js → module/module.js} +2 -2
- package/dist/node/plugins/wx/placer/placement.d.ts +1 -0
- package/dist/node/plugins/wx/placer/placement.js +1 -1
- package/dist/node/plugins/wx/placer/placer.d.ts +17 -1
- package/dist/node/plugins/wx/placer/placer.js +25 -1
- package/dist/node/plugins/wx/plugins.js +1 -1
- package/dist/node/plugins/wx/render/capsule.d.ts +35 -3
- package/dist/node/plugins/wx/render/capsule.js +53 -12
- package/dist/node/plugins/wx/render/native.d.ts +20 -2
- package/dist/node/plugins/wx/render/native.js +539 -35
- package/dist/node/plugins/wx/render/system-js/string-editor.d.ts +24 -0
- package/dist/node/plugins/wx/render/system-js/string-editor.js +93 -0
- package/dist/node/plugins/wx/render/system-js/system-js.d.ts +23 -0
- package/dist/node/plugins/wx/render/system-js/system-js.js +602 -0
- package/dist/node/plugins/wx/render/transport.js +3 -3
- package/dist/node/plugins/wx/resolve/resolver.js +1 -1
- package/dist/node/plugins/wx/resolve/specialize-bootstrap.js +1 -1
- package/dist/node/plugins/wx/resolve/specialize-page-capsule.js +1 -1
- package/dist/node/utils/transform.d.ts +0 -3
- package/dist/node/utils/transform.js +0 -23
- package/package.json +8 -6
- package/src/node/plugins/h5/transform-app.ts +1 -1
- package/src/node/plugins/wx/dev/plugins.ts +1 -1
- package/src/node/plugins/wx/dev/wx-dev-options.ts +1 -1
- package/src/node/plugins/wx/{module.ts → module/module.ts} +2 -2
- package/src/node/plugins/wx/placer/placement.ts +1 -1
- package/src/node/plugins/wx/placer/placer.ts +27 -2
- package/src/node/plugins/wx/plugins.ts +1 -1
- package/src/node/plugins/wx/render/capsule.ts +53 -17
- package/src/node/plugins/wx/render/native.ts +670 -55
- package/src/node/plugins/wx/render/system-js/string-editor.ts +115 -0
- package/src/node/plugins/wx/render/system-js/system-js.ts +831 -0
- package/src/node/plugins/wx/render/transport.ts +3 -3
- package/src/node/plugins/wx/resolve/resolver.ts +1 -1
- package/src/node/plugins/wx/resolve/specialize-bootstrap.ts +1 -1
- package/src/node/plugins/wx/resolve/specialize-page-capsule.ts +1 -1
- package/src/node/utils/transform.ts +0 -30
- package/dist/node/plugins/wx/render/capsule-wrapper.d.ts +0 -3
- package/dist/node/plugins/wx/render/capsule-wrapper.js +0 -64
- package/src/node/plugins/wx/render/capsule-wrapper.ts +0 -86
- /package/dist/node/plugins/wx/{chunk-path.d.ts → module/chunk-path.d.ts} +0 -0
- /package/dist/node/plugins/wx/{chunk-path.js → module/chunk-path.js} +0 -0
- /package/dist/node/plugins/wx/{module.d.ts → module/module.d.ts} +0 -0
- /package/src/node/plugins/wx/{chunk-path.ts → module/chunk-path.ts} +0 -0
|
@@ -1,45 +1,549 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { resolveLogicalChunkReference, resolvePhysicalChunkReference } from '../chunk-path.js';
|
|
5
|
-
import { getWxEntryRole } from '../module.js';
|
|
6
|
-
/**
|
|
1
|
+
import { isReferenceIdentifier, ScopeTracker, walk } from 'oxc-walker';
|
|
2
|
+
import { RolldownMagicString } from 'rolldown';
|
|
3
|
+
import { parseSync } from 'rolldown/utils';
|
|
4
|
+
import { resolveLogicalChunkReference, resolvePhysicalChunkReference } from '../module/chunk-path.js';
|
|
5
|
+
import { getWxEntryRole } from '../module/module.js';
|
|
6
|
+
/**
|
|
7
|
+
* Materializes one final native/amphibious Rolldown chunk as executable WX CommonJS.
|
|
8
|
+
*
|
|
9
|
+
* The transform performs four semantic operations:
|
|
10
|
+
*
|
|
11
|
+
* 1. Static ESM imports are hoisted into source-order `require` calls. Named imports remain property reads from the required
|
|
12
|
+
* namespace so they observe current values. Default and namespace imports receive Babel-compatible CommonJS interop.
|
|
13
|
+
* 2. An import whose target owns a capsule entry is not passed to native `require`. It becomes
|
|
14
|
+
* `global.System.importSync(logicalChunkId)`, synchronously linking the capsule before the native lifecycle call.
|
|
15
|
+
* 3. Local exports are published at declaration and mutation points. Imported re-exports use getters, while assignments and
|
|
16
|
+
* updates notify every alias without changing expression completion values or accidentally matching shadowed bindings.
|
|
17
|
+
* 4. ESM top-level `this` becomes `undefined`. Direct imported calls and tags are explicitly unbound so converting an import
|
|
18
|
+
* into a namespace property does not introduce a false CommonJS receiver.
|
|
19
|
+
*
|
|
20
|
+
* Static dependency loading is emitted before the untouched module body because ESM evaluates dependencies before body
|
|
21
|
+
* statements regardless of where import declarations appear textually. Generated helper names are allocated against every
|
|
22
|
+
* source identifier. Source maps use the same range edits when requested; WX development disables them and keeps this path
|
|
23
|
+
* focused on startup latency.
|
|
24
|
+
*/
|
|
7
25
|
export function renderNative({ code, chunk, chunks, sourcemap }) {
|
|
8
|
-
|
|
26
|
+
const parsed = parseSync(chunk.fileName, code);
|
|
27
|
+
if (parsed.errors.length > 0) {
|
|
28
|
+
const diagnostics = parsed.errors.map((error) => error.message).join('; ');
|
|
29
|
+
throw new Error(`Failed to parse ${chunk.fileName} with Oxc: ${diagnostics}`);
|
|
30
|
+
}
|
|
31
|
+
// Analysis owns grammar validation, scope resolution, helper allocation, and physical-to-logical capsule classification.
|
|
32
|
+
// It completes before an editor exists, so failure cannot leak a partially rewritten chunk into Rolldown's output graph.
|
|
33
|
+
const model = analyzeNativeModule(parsed.program, chunk, chunks);
|
|
34
|
+
const editor = new RolldownMagicString(code, { filename: chunk.fileName });
|
|
35
|
+
// Expression edits split untouched source ranges first. Declaration replacement runs afterwards because MagicString must
|
|
36
|
+
// not split a range after that complete range has already been overwritten or removed.
|
|
37
|
+
rewriteExpressionSemantics(editor, model, chunk.fileName);
|
|
38
|
+
rewriteTopLevelThis(editor, model.program);
|
|
39
|
+
rewriteModuleDeclarations(editor, model);
|
|
40
|
+
const hasExports = model.exportNamesByLocal.size > 0;
|
|
41
|
+
const helpers = renderInteropHelpers(model);
|
|
42
|
+
const postfixTemp = model.usesPostfixTemp ? `var ${model.postfixTemp};` : '';
|
|
43
|
+
const imports = renderImports(model);
|
|
44
|
+
if (hasExports) {
|
|
45
|
+
editor.prepend(`"use strict";Object.defineProperty(exports,"__esModule",{value:true});${helpers}${postfixTemp}${imports}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
editor.prepend(`"use strict";${helpers}${postfixTemp}${imports}`);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
code: editor.toString(),
|
|
52
|
+
map: sourcemap ? createSourceMap(editor, chunk.fileName) : null
|
|
53
|
+
};
|
|
9
54
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Builds the immutable semantic model consumed by all rewrite passes.
|
|
57
|
+
*
|
|
58
|
+
* Import classification needs the complete rendered chunk graph: a relative path alone cannot reveal whether native require
|
|
59
|
+
* should execute the target or SystemJS should link it as a capsule. ScopeTracker is frozen after declaration collection so
|
|
60
|
+
* later reference walks can distinguish module cells from same-named parameters and nested locals.
|
|
61
|
+
*/
|
|
62
|
+
function analyzeNativeModule(program, chunk, chunks) {
|
|
63
|
+
const identifierNames = collectIdentifierNames(program);
|
|
64
|
+
const exportNamesByLocal = new Map();
|
|
65
|
+
const imports = [];
|
|
66
|
+
const importBindingsByLocal = new Map();
|
|
67
|
+
let hasDirectEval = false;
|
|
68
|
+
walk(program, {
|
|
69
|
+
enter(node) {
|
|
70
|
+
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'eval') {
|
|
71
|
+
hasDirectEval = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (hasDirectEval)
|
|
76
|
+
throw unsupported(chunk.fileName, 'direct eval');
|
|
77
|
+
for (const node of program.body) {
|
|
78
|
+
switch (node.type) {
|
|
79
|
+
case 'ImportDeclaration': {
|
|
80
|
+
requirePlainImport(node, chunk.fileName);
|
|
81
|
+
const capsule = getImportedCapsule(chunk.fileName, node.source.value, chunks);
|
|
82
|
+
if (capsule) {
|
|
83
|
+
const [specifier] = node.specifiers;
|
|
84
|
+
if (node.specifiers.length !== 1 || !specifier || specifier.type === 'ImportNamespaceSpecifier') {
|
|
85
|
+
throw new Error(`Expected one capsule value import from ${capsule.fileName} in ${chunk.fileName}`);
|
|
86
|
+
}
|
|
87
|
+
imports.push({
|
|
88
|
+
bindings: [],
|
|
89
|
+
capsuleBinding: {
|
|
90
|
+
imported: specifier.type === 'ImportDefaultSpecifier'
|
|
91
|
+
? 'default'
|
|
92
|
+
: moduleExportName(specifier.imported),
|
|
93
|
+
local: specifier.local.name,
|
|
94
|
+
logicalId: resolveLogicalChunkReference(chunk.fileName, node.source.value)
|
|
95
|
+
},
|
|
96
|
+
interop: 'none',
|
|
97
|
+
namespace: '',
|
|
98
|
+
reference: node.source.value
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const namespace = takeGeneratedName('__nativeImport', identifierNames);
|
|
103
|
+
const bindings = node.specifiers.map((specifier) => {
|
|
104
|
+
const binding = {
|
|
105
|
+
imported: specifier.type === 'ImportNamespaceSpecifier'
|
|
106
|
+
? null
|
|
107
|
+
: specifier.type === 'ImportDefaultSpecifier'
|
|
108
|
+
? 'default'
|
|
109
|
+
: moduleExportName(specifier.imported),
|
|
110
|
+
local: specifier.local.name,
|
|
111
|
+
namespace
|
|
112
|
+
};
|
|
113
|
+
importBindingsByLocal.set(binding.local, binding);
|
|
114
|
+
return binding;
|
|
115
|
+
});
|
|
116
|
+
imports.push({
|
|
117
|
+
bindings,
|
|
118
|
+
capsuleBinding: null,
|
|
119
|
+
interop: importInterop(node),
|
|
120
|
+
namespace,
|
|
121
|
+
reference: node.source.value
|
|
122
|
+
});
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case 'ExportNamedDeclaration':
|
|
126
|
+
collectFinalExports(node, exportNamesByLocal, chunk.fileName);
|
|
127
|
+
break;
|
|
128
|
+
case 'ExportDefaultDeclaration':
|
|
129
|
+
case 'ExportAllDeclaration':
|
|
130
|
+
throw unsupported(chunk.fileName, `source-level ${node.type}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const defaultInterop = takeGeneratedName('__nativeDefault', identifierNames);
|
|
134
|
+
const namespaceInterop = takeGeneratedName('__nativeNamespace', identifierNames);
|
|
135
|
+
const postfixTemp = takeGeneratedName('__nativePostfix', identifierNames);
|
|
136
|
+
const scopes = new ScopeTracker({ preserveExitedScopes: true });
|
|
137
|
+
walk(program, { scopeTracker: scopes });
|
|
138
|
+
scopes.freeze();
|
|
139
|
+
const usesPostfixTemp = hasPostfixExportUpdate(program, scopes, exportNamesByLocal);
|
|
12
140
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
141
|
+
defaultInterop,
|
|
142
|
+
exportNamesByLocal,
|
|
143
|
+
imports,
|
|
144
|
+
importBindingsByLocal,
|
|
145
|
+
namespaceInterop,
|
|
146
|
+
postfixTemp,
|
|
147
|
+
program,
|
|
148
|
+
scopes,
|
|
149
|
+
usesPostfixTemp
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Removes syntax that WX cannot parse and adds declaration-time CommonJS publication without regenerating other source.
|
|
154
|
+
* Import execution itself is emitted in the generated header; removing declarations here retains comments and all unrelated
|
|
155
|
+
* Rolldown output byte-for-byte. Final export lists publish only imported getters because local cells were already instrumented
|
|
156
|
+
* at their declarations and writes.
|
|
157
|
+
*/
|
|
158
|
+
function rewriteModuleDeclarations(editor, model) {
|
|
159
|
+
for (const node of model.program.body) {
|
|
160
|
+
switch (node.type) {
|
|
161
|
+
case 'ImportDeclaration':
|
|
162
|
+
// Static imports execute before every module body statement regardless of their textual position.
|
|
163
|
+
editor.remove(node.start, node.end);
|
|
164
|
+
break;
|
|
165
|
+
case 'ExportNamedDeclaration':
|
|
166
|
+
if (node.declaration) {
|
|
167
|
+
// Strip only the export keyword; declaration publication is handled at its initializer or end boundary.
|
|
168
|
+
editor.remove(node.start, node.declaration.start);
|
|
169
|
+
rewriteExportedDeclaration(editor, node.declaration, model.exportNamesByLocal);
|
|
19
170
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!importedChunk || getWxEntryRole(importedChunk) !== 'capsule') {
|
|
23
|
-
return;
|
|
171
|
+
else {
|
|
172
|
+
editor.overwrite(node.start, node.end, renderExportList(node.specifiers, model));
|
|
24
173
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
174
|
+
break;
|
|
175
|
+
case 'VariableDeclaration':
|
|
176
|
+
publishVariableInitializers(editor, node, model.exportNamesByLocal);
|
|
177
|
+
break;
|
|
178
|
+
case 'FunctionDeclaration':
|
|
179
|
+
if (node.id)
|
|
180
|
+
publishAfter(editor, node.end, node.id.name, model.exportNamesByLocal);
|
|
181
|
+
break;
|
|
182
|
+
case 'ClassDeclaration':
|
|
183
|
+
if (node.id)
|
|
184
|
+
publishAfter(editor, node.end, node.id.name, model.exportNamesByLocal);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Rewrites imported references and live-export mutations in one scope-aware O(n) pass.
|
|
191
|
+
*
|
|
192
|
+
* A named import such as `fn` becomes a namespace property read. In call or tag position it is wrapped as `(0, ns.fn)` to
|
|
193
|
+
* preserve ESM's unbound receiver; in `new fn()` it remains `ns.fn` because construction has no method receiver. Exported
|
|
194
|
+
* writes are instrumented only when ScopeTracker resolves the target to the root module declaration.
|
|
195
|
+
*/
|
|
196
|
+
function rewriteExpressionSemantics(editor, model, filename) {
|
|
197
|
+
// This mutable traversal stack exists only to see through explicit ParenthesizedExpression nodes around imported calls.
|
|
198
|
+
const ancestors = [];
|
|
199
|
+
walk(model.program, {
|
|
200
|
+
scopeTracker: model.scopes,
|
|
201
|
+
enter(node, parent) {
|
|
202
|
+
if (node.type === 'Identifier' &&
|
|
203
|
+
parent?.type !== 'ImportDeclaration' &&
|
|
204
|
+
isReferenceIdentifier(node, parent)) {
|
|
205
|
+
const binding = model.importBindingsByLocal.get(node.name);
|
|
206
|
+
const declaration = binding ? model.scopes.getDeclaration(node.name) : null;
|
|
207
|
+
if (binding && declaration?.type === 'Import') {
|
|
208
|
+
const context = importedReferenceContext(node, parent, ancestors);
|
|
209
|
+
const imported = importedExpression(binding);
|
|
210
|
+
const replacement = context === 'unbound' ? `(0,${imported})` : imported;
|
|
211
|
+
// Replacing `{ imported }` with `{ imported: namespace.imported }` preserves shorthand property keys.
|
|
212
|
+
editor.overwrite(node.start, node.end, parent?.type === 'Property' && parent.shorthand && parent.value === node
|
|
213
|
+
? `${node.name}:${replacement}`
|
|
214
|
+
: replacement);
|
|
30
215
|
}
|
|
31
|
-
const imported = types.isImportDefaultSpecifier(specifier)
|
|
32
|
-
? types.identifier('default')
|
|
33
|
-
: specifier.imported;
|
|
34
|
-
const importedConfig = types.memberExpression(createSyncImport(resolveLogicalChunkReference(fileName, reference)), types.cloneNode(imported), types.isStringLiteral(imported));
|
|
35
|
-
importPath.replaceWith(types.variableDeclaration('const', [
|
|
36
|
-
types.variableDeclarator(types.cloneNode(specifier.local), importedConfig)
|
|
37
|
-
]));
|
|
38
216
|
}
|
|
217
|
+
switch (node.type) {
|
|
218
|
+
case 'AssignmentExpression':
|
|
219
|
+
rewriteExportAssignment(editor, node.left, node.start, model, filename);
|
|
220
|
+
break;
|
|
221
|
+
case 'UpdateExpression':
|
|
222
|
+
rewriteExportUpdate(editor, node, model);
|
|
223
|
+
break;
|
|
224
|
+
case 'ForInStatement':
|
|
225
|
+
case 'ForOfStatement':
|
|
226
|
+
if (node.left.type !== 'VariableDeclaration') {
|
|
227
|
+
requireNoExportedPattern(node.left, model, filename);
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
ancestors.push(node);
|
|
232
|
+
},
|
|
233
|
+
leave() {
|
|
234
|
+
ancestors.pop();
|
|
39
235
|
}
|
|
40
|
-
};
|
|
236
|
+
});
|
|
41
237
|
}
|
|
42
|
-
/**
|
|
43
|
-
function
|
|
44
|
-
|
|
238
|
+
/** Prefixes a direct exported assignment; assignment operators already preserve their own completion value. */
|
|
239
|
+
function rewriteExportAssignment(editor, target, start, model, filename) {
|
|
240
|
+
if (target.type === 'Identifier') {
|
|
241
|
+
const names = exportedRootNames(target.name, model);
|
|
242
|
+
if (names.length > 0)
|
|
243
|
+
editor.prependLeft(start, exportAssignmentPrefix(names));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
requireNoExportedPattern(target, model, filename);
|
|
247
|
+
}
|
|
248
|
+
/** Publishes the updated cell while retaining the distinct prefix/postfix expression result. */
|
|
249
|
+
function rewriteExportUpdate(editor, update, model) {
|
|
250
|
+
if (update.argument.type !== 'Identifier')
|
|
251
|
+
return;
|
|
252
|
+
const names = exportedRootNames(update.argument.name, model);
|
|
253
|
+
if (names.length === 0)
|
|
254
|
+
return;
|
|
255
|
+
if (update.prefix) {
|
|
256
|
+
editor.prependLeft(update.start, exportAssignmentPrefix(names));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
editor.prependLeft(update.start, `(${model.postfixTemp}=`);
|
|
260
|
+
editor.appendRight(update.end, `,${exportAssignmentExpression(names, update.argument.name)},${model.postfixTemp})`);
|
|
261
|
+
}
|
|
262
|
+
/** Rejects destructuring writes because wrapping them would change their completion value. */
|
|
263
|
+
function requireNoExportedPattern(target, model, filename) {
|
|
264
|
+
const exported = bindingNames(target).filter((name) => exportedRootNames(name, model).length > 0);
|
|
265
|
+
if (exported.length > 0) {
|
|
266
|
+
throw unsupported(filename, `destructuring write to exported binding ${JSON.stringify(exported[0])}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function exportedRootNames(local, model) {
|
|
270
|
+
const names = model.exportNamesByLocal.get(local) ?? [];
|
|
271
|
+
if (names.length === 0)
|
|
272
|
+
return names;
|
|
273
|
+
return model.scopes.getDeclaration(local)?.scope === '' ? names : [];
|
|
274
|
+
}
|
|
275
|
+
/** Detects whether the generated declaration header needs one postfix completion-value cell. */
|
|
276
|
+
function hasPostfixExportUpdate(program, scopes, exportNamesByLocal) {
|
|
277
|
+
let found = false;
|
|
278
|
+
walk(program, {
|
|
279
|
+
scopeTracker: scopes,
|
|
280
|
+
enter(node) {
|
|
281
|
+
if (found || node.type !== 'UpdateExpression' || node.prefix || node.argument.type !== 'Identifier')
|
|
282
|
+
return;
|
|
283
|
+
const names = exportNamesByLocal.get(node.argument.name) ?? [];
|
|
284
|
+
if (names.length > 0 && scopes.getDeclaration(node.argument.name)?.scope === '')
|
|
285
|
+
found = true;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
return found;
|
|
289
|
+
}
|
|
290
|
+
/** Rewrites ESM top-level this, including arrow functions that lexically inherit it. */
|
|
291
|
+
function rewriteTopLevelThis(editor, program) {
|
|
292
|
+
let thisBoundaryDepth = 0;
|
|
293
|
+
walk(program, {
|
|
294
|
+
enter(node) {
|
|
295
|
+
if (isThisBoundary(node))
|
|
296
|
+
thisBoundaryDepth += 1;
|
|
297
|
+
if (node.type === 'ThisExpression' && thisBoundaryDepth === 0)
|
|
298
|
+
editor.overwrite(node.start, node.end, 'void 0');
|
|
299
|
+
},
|
|
300
|
+
leave(node) {
|
|
301
|
+
if (isThisBoundary(node))
|
|
302
|
+
thisBoundaryDepth -= 1;
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
/** Records aliases from Rolldown's normalized terminal `export { ... }` declaration. */
|
|
307
|
+
function collectFinalExports(declaration, exportNamesByLocal, filename) {
|
|
308
|
+
if (declaration.source || declaration.attributes.length > 0 || declaration.exportKind === 'type') {
|
|
309
|
+
throw unsupported(filename, 're-exports, export attributes, or type-only exports');
|
|
310
|
+
}
|
|
311
|
+
if (declaration.declaration) {
|
|
312
|
+
for (const local of declarationBindingNames(declaration.declaration, filename)) {
|
|
313
|
+
const names = exportNamesByLocal.get(local) ?? [];
|
|
314
|
+
names.push(local);
|
|
315
|
+
if (!exportNamesByLocal.has(local))
|
|
316
|
+
exportNamesByLocal.set(local, names);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
for (const specifier of declaration.specifiers) {
|
|
321
|
+
const local = moduleExportName(specifier.local);
|
|
322
|
+
const names = exportNamesByLocal.get(local) ?? [];
|
|
323
|
+
names.push(moduleExportName(specifier.exported));
|
|
324
|
+
if (!exportNamesByLocal.has(local))
|
|
325
|
+
exportNamesByLocal.set(local, names);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/** Applies publication edits to a declaration wrapped by `export`. */
|
|
329
|
+
function rewriteExportedDeclaration(editor, declaration, exportNamesByLocal) {
|
|
330
|
+
switch (declaration.type) {
|
|
331
|
+
case 'VariableDeclaration':
|
|
332
|
+
publishVariableInitializers(editor, declaration, exportNamesByLocal);
|
|
333
|
+
break;
|
|
334
|
+
case 'FunctionDeclaration':
|
|
335
|
+
case 'ClassDeclaration':
|
|
336
|
+
if (declaration.id)
|
|
337
|
+
publishAfter(editor, declaration.end, declaration.id.name, exportNamesByLocal);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/** Emits execute-time export assignments, using getters for imported cells to preserve live reads. */
|
|
342
|
+
function renderExportList(specifiers, model) {
|
|
343
|
+
return specifiers
|
|
344
|
+
.map((specifier) => {
|
|
345
|
+
const local = moduleExportName(specifier.local);
|
|
346
|
+
const exported = moduleExportName(specifier.exported);
|
|
347
|
+
const imported = model.importBindingsByLocal.get(local);
|
|
348
|
+
return imported
|
|
349
|
+
? `Object.defineProperty(exports,${JSON.stringify(exported)},{enumerable:true,get:function(){return ${importedExpression(imported)}}});`
|
|
350
|
+
: '';
|
|
351
|
+
})
|
|
352
|
+
.join('');
|
|
353
|
+
}
|
|
354
|
+
/** Publishes top-level initialized variables at their original initialization point. */
|
|
355
|
+
function publishVariableInitializers(editor, declaration, exportNamesByLocal) {
|
|
356
|
+
for (const declarator of declaration.declarations) {
|
|
357
|
+
if (declarator.id.type !== 'Identifier') {
|
|
358
|
+
const exported = bindingNames(declarator.id).filter((name) => exportNamesByLocal.has(name));
|
|
359
|
+
if (exported.length > 0)
|
|
360
|
+
throw unsupported('native chunk', 'exported destructuring declaration');
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const names = exportNamesByLocal.get(declarator.id.name) ?? [];
|
|
364
|
+
if (names.length === 0)
|
|
365
|
+
continue;
|
|
366
|
+
if (declarator.init) {
|
|
367
|
+
editor.prependLeft(declarator.init.start, exportAssignmentPrefix(names));
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
editor.appendRight(declarator.end, `=${exportAssignmentExpression(names, 'void 0')}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/** Publishes declaration values that become initialized only after their declaration executes. */
|
|
375
|
+
function publishAfter(editor, end, local, exportNamesByLocal) {
|
|
376
|
+
const names = exportNamesByLocal.get(local) ?? [];
|
|
377
|
+
if (names.length > 0)
|
|
378
|
+
editor.appendRight(end, `;${exportAssignmentExpression(names, local)};`);
|
|
379
|
+
}
|
|
380
|
+
function exportAssignmentPrefix(names) {
|
|
381
|
+
return names.reduce((prefix, name) => `exports[${JSON.stringify(name)}]=${prefix}`, '');
|
|
382
|
+
}
|
|
383
|
+
function exportAssignmentExpression(names, value) {
|
|
384
|
+
return `${exportAssignmentPrefix(names)}${value}`;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Imported functions are ESM references, not namespace methods. A direct call must therefore discard the generated
|
|
388
|
+
* namespace receiver with `(0, value)()`. The same comma expression preserves unbound optional calls and tag invocation;
|
|
389
|
+
* constructors remain plain references because `new` does not derive a receiver from a member expression.
|
|
390
|
+
*/
|
|
391
|
+
function importedReferenceContext(node, parent, ancestors) {
|
|
392
|
+
let expression = node;
|
|
393
|
+
let semanticParent = parent;
|
|
394
|
+
let ancestorIndex = ancestors.length - 1;
|
|
395
|
+
while (semanticParent?.type === 'ParenthesizedExpression' && semanticParent.expression === expression) {
|
|
396
|
+
expression = semanticParent;
|
|
397
|
+
ancestorIndex -= 1;
|
|
398
|
+
semanticParent = ancestors[ancestorIndex] ?? null;
|
|
399
|
+
}
|
|
400
|
+
if (semanticParent?.type === 'CallExpression' && semanticParent.callee === expression)
|
|
401
|
+
return 'unbound';
|
|
402
|
+
if (semanticParent?.type === 'NewExpression' && semanticParent.callee === expression)
|
|
403
|
+
return 'plain';
|
|
404
|
+
if (semanticParent?.type === 'TaggedTemplateExpression' && semanticParent.tag === expression)
|
|
405
|
+
return 'unbound';
|
|
406
|
+
return 'plain';
|
|
407
|
+
}
|
|
408
|
+
function importedExpression(binding) {
|
|
409
|
+
return binding.imported === null ? binding.namespace : memberExpression(binding.namespace, binding.imported);
|
|
410
|
+
}
|
|
411
|
+
function memberExpression(object, property) {
|
|
412
|
+
return /^[$A-Z_a-z][$\w]*$/.test(property) ? `${object}.${property}` : `${object}[${JSON.stringify(property)}]`;
|
|
413
|
+
}
|
|
414
|
+
function getImportedCapsule(fileName, reference, chunks) {
|
|
415
|
+
if (!reference.startsWith('./') && !reference.startsWith('../'))
|
|
416
|
+
return undefined;
|
|
417
|
+
const imported = chunks[resolvePhysicalChunkReference(fileName, reference)];
|
|
418
|
+
return imported && getWxEntryRole(imported) === 'capsule' ? imported : undefined;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Renders the dependency header in original import order.
|
|
422
|
+
*
|
|
423
|
+
* Side-effect imports always remain standalone `require` calls. Ordinary value imports share one required namespace per final
|
|
424
|
+
* import declaration, while cross-domain capsule imports synchronously ask SystemJS for the namespace under its package-neutral
|
|
425
|
+
* logical ID. Physical package placement remains the transport's responsibility.
|
|
426
|
+
*/
|
|
427
|
+
function renderImports(model) {
|
|
428
|
+
return model.imports
|
|
429
|
+
.map((importModel) => {
|
|
430
|
+
if (importModel.capsuleBinding) {
|
|
431
|
+
const { imported, local, logicalId } = importModel.capsuleBinding;
|
|
432
|
+
const namespace = `global.System.importSync(${JSON.stringify(logicalId)})`;
|
|
433
|
+
return `var ${local}=${memberExpression(namespace, imported)};`;
|
|
434
|
+
}
|
|
435
|
+
const requireCall = `require(${JSON.stringify(importModel.reference)})`;
|
|
436
|
+
if (importModel.bindings.length === 0)
|
|
437
|
+
return `${requireCall};`;
|
|
438
|
+
const importedValue = importModel.interop === 'default'
|
|
439
|
+
? `${model.defaultInterop}(${requireCall})`
|
|
440
|
+
: importModel.interop === 'namespace'
|
|
441
|
+
? `${model.namespaceInterop}(${requireCall})`
|
|
442
|
+
: requireCall;
|
|
443
|
+
return `var ${importModel.namespace}=${importedValue};`;
|
|
444
|
+
})
|
|
445
|
+
.join('');
|
|
446
|
+
}
|
|
447
|
+
/** Selects Babel-compatible wrapping for default and namespace imports from CommonJS values. */
|
|
448
|
+
function importInterop(declaration) {
|
|
449
|
+
if (declaration.specifiers.some((specifier) => specifier.type === 'ImportNamespaceSpecifier'))
|
|
450
|
+
return 'namespace';
|
|
451
|
+
if (declaration.specifiers.some((specifier) => specifier.type === 'ImportDefaultSpecifier'))
|
|
452
|
+
return 'default';
|
|
453
|
+
return 'none';
|
|
454
|
+
}
|
|
455
|
+
/** Emits interop helpers only when an analyzed import needs them. */
|
|
456
|
+
function renderInteropHelpers(model) {
|
|
457
|
+
const kinds = new Set(model.imports.map(({ interop }) => interop));
|
|
458
|
+
const defaultHelper = kinds.has('default')
|
|
459
|
+
? `function ${model.defaultInterop}(value){return value&&value.__esModule?value:{default:value};}`
|
|
460
|
+
: '';
|
|
461
|
+
const namespaceHelper = kinds.has('namespace')
|
|
462
|
+
? `function ${model.namespaceInterop}(value){if(value&&value.__esModule)return value;var namespace={default:value};if(value!==null&&(typeof value==="object"||typeof value==="function")){for(var key in value)if(key!=="default"&&Object.prototype.hasOwnProperty.call(value,key))Object.defineProperty(namespace,key,{enumerable:true,get:function(key){return function(){return value[key]}}(key)});}return namespace;}`
|
|
463
|
+
: '';
|
|
464
|
+
return `${defaultHelper}${namespaceHelper}`;
|
|
465
|
+
}
|
|
466
|
+
function requirePlainImport(declaration, filename) {
|
|
467
|
+
if (declaration.phase || declaration.attributes.length > 0 || declaration.importKind === 'type') {
|
|
468
|
+
throw unsupported(filename, 'import phases, attributes, or type-only imports');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/** Collects every occupied identifier before helper allocation. */
|
|
472
|
+
function collectIdentifierNames(program) {
|
|
473
|
+
const names = new Set();
|
|
474
|
+
walk(program, {
|
|
475
|
+
enter(node) {
|
|
476
|
+
if (node.type === 'Identifier')
|
|
477
|
+
names.add(node.name);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
return names;
|
|
481
|
+
}
|
|
482
|
+
function takeGeneratedName(base, used) {
|
|
483
|
+
let suffix = 0;
|
|
484
|
+
let candidate = base;
|
|
485
|
+
while (used.has(candidate)) {
|
|
486
|
+
suffix += 1;
|
|
487
|
+
candidate = `${base}${suffix}`;
|
|
488
|
+
}
|
|
489
|
+
used.add(candidate);
|
|
490
|
+
return candidate;
|
|
491
|
+
}
|
|
492
|
+
function moduleExportName(name) {
|
|
493
|
+
return name.type === 'Literal' ? String(name.value) : name.name;
|
|
494
|
+
}
|
|
495
|
+
function declarationBindingNames(declaration, filename) {
|
|
496
|
+
switch (declaration.type) {
|
|
497
|
+
case 'VariableDeclaration':
|
|
498
|
+
return declaration.declarations.flatMap(({ id }) => bindingNames(id));
|
|
499
|
+
case 'FunctionDeclaration':
|
|
500
|
+
case 'ClassDeclaration':
|
|
501
|
+
if (declaration.id)
|
|
502
|
+
return [declaration.id.name];
|
|
503
|
+
throw unsupported(filename, 'anonymous declaration export');
|
|
504
|
+
default:
|
|
505
|
+
throw unsupported(filename, `declaration export ${declaration.type}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function bindingNames(pattern) {
|
|
509
|
+
switch (pattern.type) {
|
|
510
|
+
case 'Identifier':
|
|
511
|
+
return [pattern.name];
|
|
512
|
+
case 'AssignmentPattern':
|
|
513
|
+
return bindingNames(pattern.left);
|
|
514
|
+
case 'ArrayPattern':
|
|
515
|
+
return pattern.elements.flatMap((element) => (element ? bindingNames(element) : []));
|
|
516
|
+
case 'ObjectPattern':
|
|
517
|
+
return pattern.properties.flatMap((property) => property.type === 'Property' ? bindingNames(property.value) : bindingNames(property.argument));
|
|
518
|
+
case 'RestElement':
|
|
519
|
+
return bindingNames(pattern.argument);
|
|
520
|
+
default:
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function isThisBoundary(node) {
|
|
525
|
+
return (node.type === 'FunctionDeclaration' ||
|
|
526
|
+
node.type === 'FunctionExpression' ||
|
|
527
|
+
node.type === 'ClassDeclaration' ||
|
|
528
|
+
node.type === 'ClassExpression');
|
|
529
|
+
}
|
|
530
|
+
function unsupported(filename, construct) {
|
|
531
|
+
return new Error(`Unsupported final Rolldown native chunk ${filename}: ${construct}`);
|
|
532
|
+
}
|
|
533
|
+
function createSourceMap(editor, filename) {
|
|
534
|
+
const generated = editor.generateMap({
|
|
535
|
+
file: filename,
|
|
536
|
+
hires: 'boundary',
|
|
537
|
+
includeContent: true,
|
|
538
|
+
source: filename
|
|
539
|
+
});
|
|
540
|
+
return {
|
|
541
|
+
version: generated.version,
|
|
542
|
+
file: generated.file,
|
|
543
|
+
sources: generated.sources,
|
|
544
|
+
sourcesContent: generated.sourcesContent,
|
|
545
|
+
names: generated.names,
|
|
546
|
+
mappings: generated.mappings,
|
|
547
|
+
...(generated.x_google_ignoreList ? { x_google_ignoreList: generated.x_google_ignoreList } : {})
|
|
548
|
+
};
|
|
45
549
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records non-overlapping range edits and renders them in one source-order pass.
|
|
3
|
+
*
|
|
4
|
+
* Final WX development chunks do not request source maps. RolldownMagicString's repeated relocation of hundreds of hoisted
|
|
5
|
+
* functions is considerably more expensive than the semantic analysis itself, so this editor keeps the same range operations
|
|
6
|
+
* while avoiding a mutable chunk graph when no mappings can be observed.
|
|
7
|
+
*/
|
|
8
|
+
export declare class StringEditor {
|
|
9
|
+
#private;
|
|
10
|
+
readonly original: string;
|
|
11
|
+
constructor(original: string);
|
|
12
|
+
/** Records a half-open replacement; semantic passes guarantee ranges are nested or disjoint. */
|
|
13
|
+
overwrite(start: number, end: number, content: string): void;
|
|
14
|
+
/** Removes a range and discards insertions that were owned only by that removed source. */
|
|
15
|
+
remove(start: number, end: number): void;
|
|
16
|
+
/** Inserts before prior insertions at a boundary, matching MagicString's prependLeft ordering. */
|
|
17
|
+
prependLeft(position: number, content: string): void;
|
|
18
|
+
/** Inserts after prior insertions at the left side of a boundary. */
|
|
19
|
+
appendLeft(position: number, content: string): void;
|
|
20
|
+
/** Matches the subset of appendRight ordering used by the capsule compiler. */
|
|
21
|
+
appendRight(position: number, content: string): void;
|
|
22
|
+
/** Materializes one source slice without mutating its edit journal, allowing functions to be rendered before relocation. */
|
|
23
|
+
render(start: number, end: number): string;
|
|
24
|
+
}
|