Skip to content

Commit 8c1e92a

Browse files
committed
fix(pack): script external loadScript
1 parent 45fc528 commit 8c1e92a

26 files changed

Lines changed: 310 additions & 50 deletions

File tree

crates/pack-core/js/src/umd/runtime-base.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,36 @@ function loadChunkByUrl(
133133
}
134134
browserContextPrototype.L = loadChunkByUrl;
135135

136+
const loadedScripts = new Map<string, Promise<void>>();
137+
138+
/**
139+
* Load an external script by creating a <script> tag.
140+
* This is used for script externals that need to be loaded from CDN or other external sources.
141+
*/
142+
function loadScript(
143+
this: TurbopackBrowserBaseContext<Module>,
144+
scriptUrl: string,
145+
): Promise<void> {
146+
// Return cached promise if script is already loading or loaded
147+
let promise = loadedScripts.get(scriptUrl);
148+
if (promise) {
149+
return promise;
150+
}
151+
152+
promise = new Promise<void>((resolve, reject) => {
153+
const script = document.createElement("script");
154+
script.src = scriptUrl;
155+
script.onload = () => resolve();
156+
script.onerror = () =>
157+
reject(new Error(`Failed to load script: ${scriptUrl}`));
158+
document.head.appendChild(script);
159+
});
160+
161+
loadedScripts.set(scriptUrl, promise);
162+
return promise;
163+
}
164+
browserContextPrototype.S = loadScript;
165+
136166
// Do not make this async. React relies on referential equality of the returned Promise.
137167
function loadChunkByUrlInternal(
138168
sourceType: SourceType,

crates/pack-core/js/src/umd/runtime-types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type DynamicExport = (
5353
) => void;
5454

5555
type LoadChunkByUrl = (chunkUrl: ChunkUrl) => Promise<any> | undefined;
56+
type LoadScript = (scriptUrl: string) => Promise<void>;
5657

5758
type ModuleCache<M> = Record<ModuleId, M>;
5859
// TODO properly type values here
@@ -119,6 +120,7 @@ interface TurbopackBaseContext<M> {
119120
n: ExportNamespace;
120121
m: Module;
121122
L: LoadChunkByUrl;
123+
S: LoadScript;
122124
c: ModuleCache<M>;
123125
M: ModuleFactories;
124126
g: typeof globalThis;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"config": {
3+
"entry": [
4+
{
5+
"import": "input/index.ts",
6+
"name": "main"
7+
}
8+
],
9+
"externals": {
10+
"lodash": "script _@https://gw.alipayobjects.com/os/lib/lodash/4.17.21/lodash.min.js"
11+
}
12+
}
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const func = async () => {
2+
// @ts-ignore
3+
const _ = await import('lodash');
4+
console.log(Object.keys(_.default.omit({ a: 1 }, 'a')).length === 0);
5+
};
6+
func();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(globalThis["TURBOPACK"] || (globalThis["TURBOPACK"] = [])).push([typeof document === "object" ? document.currentScript : undefined,
2+
20, ((__turbopack_context__) => {
3+
"use strict";
4+
5+
return __turbopack_context__.a(async (__turbopack_handle_async_dependencies__, __turbopack_async_result__) => { try {
6+
7+
const mod = await (async () => {
8+
if (typeof globalThis["_"] !== 'undefined') {
9+
return globalThis["_"];
10+
}
11+
await __turbopack_context__.S("https://gw.alipayobjects.com/os/lib/lodash/4.17.21/lodash.min.js");
12+
if (typeof globalThis["_"] !== 'undefined') {
13+
return globalThis["_"];
14+
}
15+
const error = new Error('Loading script failed.\n(missing: "https://gw.alipayobjects.com/os/lib/lodash/4.17.21/lodash.min.js")');
16+
error.name = 'ScriptExternalLoadError';
17+
error.type = 'missing';
18+
error.request = "https://gw.alipayobjects.com/os/lib/lodash/4.17.21/lodash.min.js";
19+
throw error;
20+
})();
21+
22+
__turbopack_context__.v(mod);
23+
__turbopack_async_result__();
24+
} catch(e) { __turbopack_async_result__(e); } }, true);}),
25+
]);

crates/pack-tests/tests/snapshot/externals/async-script-externals/output/_externals____2fc6bb03.js.map

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pack-tests/tests/snapshot/externals/async-script-externals/output/_root-of-the-server___fa01a871.js

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pack-tests/tests/snapshot/externals/async-script-externals/output/_root-of-the-server___fa01a871.js.map

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(globalThis["TURBOPACK"] || (globalThis["TURBOPACK"] = [])).push([
2+
typeof document === "object" ? document.currentScript : undefined,
3+
{"otherChunks":["_root-of-the-server___fa01a871.js"],"runtimeModuleIds":[6]}
4+
]);
5+
// Dummy runtime

crates/pack-tests/tests/snapshot/externals/async-script-externals/output/main.js.map

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)