Skip to content

Commit 2136bb4

Browse files
committed
fix(core): defer external modules until dependency evaluation
1 parent 9a94c3b commit 2136bb4

2 files changed

Lines changed: 64 additions & 14 deletions

File tree

packages/core/src/server/runner/systemJs.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,20 @@ type SystemJsModuleState =
126126
| 'evaluated'
127127
| 'failed';
128128

129+
type SystemJsEvaluationDependency =
130+
| {
131+
kind: 'bundle';
132+
module: SystemJsModuleNode;
133+
}
134+
| {
135+
kind: 'external';
136+
metadata?: SystemJsImportMetadata;
137+
setter: (namespace: Namespace) => void;
138+
specifier: string;
139+
};
140+
129141
type SystemJsModuleNode = {
130-
dependencies: SystemJsModuleNode[];
142+
dependencies: SystemJsEvaluationDependency[];
131143
error?: unknown;
132144
evaluationPromise?: Promise<void>;
133145
execute?: SystemJsDeclaration['execute'];
@@ -418,22 +430,17 @@ class SystemJsEvaluator {
418430
await this.#instantiate(dependency);
419431
}
420432
dependency.importers.push({ setter });
421-
moduleNode.dependencies.push(dependency);
433+
moduleNode.dependencies.push({ kind: 'bundle', module: dependency });
422434
setter(dependency.namespace);
423435
continue;
424436
}
425437

426-
const namespace = await this.#runExternalModule(
438+
moduleNode.dependencies.push({
439+
kind: 'external',
440+
metadata: registration.importMetadata[index],
441+
setter,
427442
specifier,
428-
moduleNode.id,
429-
);
430-
setter(
431-
this.#processImport(
432-
namespace,
433-
specifier,
434-
registration.importMetadata[index],
435-
),
436-
);
443+
});
437444
}
438445
moduleNode.state = 'instantiated';
439446
})().catch((error) => {
@@ -574,9 +581,24 @@ class SystemJsEvaluator {
574581
moduleNode.state = 'evaluating';
575582
const promise = (async () => {
576583
for (const dependency of moduleNode.dependencies) {
577-
if (!nextAncestors.has(dependency.id)) {
578-
await this.#evaluateModule(dependency, nextAncestors);
584+
if (dependency.kind === 'bundle') {
585+
if (!nextAncestors.has(dependency.module.id)) {
586+
await this.#evaluateModule(dependency.module, nextAncestors);
587+
}
588+
continue;
579589
}
590+
591+
const namespace = await this.#runExternalModule(
592+
dependency.specifier,
593+
moduleNode.id,
594+
);
595+
dependency.setter(
596+
this.#processImport(
597+
namespace,
598+
dependency.specifier,
599+
dependency.metadata,
600+
),
601+
);
580602
}
581603
await moduleNode.execute?.();
582604
moduleNode.state = 'evaluated';

packages/core/tests/systemJsRunner.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,31 @@ test('reports a error for a missing static external export', async () => {
136136
name: 'SyntaxError',
137137
});
138138
});
139+
140+
test('evaluates bundle dependencies before later external dependencies', async () => {
141+
const stateKey = '__rsbuildSystemJsEvaluationOrder';
142+
const externalId = `data:text/javascript,${encodeURIComponent(`
143+
const key = ${JSON.stringify(stateKey)};
144+
if (globalThis[key] !== 'bundle') {
145+
throw new Error('bundle dependency was not evaluated first');
146+
}
147+
globalThis[key] += ':external';
148+
`)}`;
149+
const runner = createSystemJsRunner([
150+
[
151+
'entry.mjs',
152+
`import './polyfill.mjs';
153+
import ${JSON.stringify(externalId)};
154+
export const order = globalThis[${JSON.stringify(stateKey)}];`,
155+
],
156+
['polyfill.mjs', `globalThis[${JSON.stringify(stateKey)}] = 'bundle';`],
157+
]);
158+
159+
try {
160+
await expect(runner.run('entry.mjs')).resolves.toMatchObject({
161+
order: 'bundle:external',
162+
});
163+
} finally {
164+
delete (globalThis as Record<string, unknown>)[stateKey];
165+
}
166+
});

0 commit comments

Comments
 (0)