mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Bundles can be sync or async based on top level await (#4124)
Previously, bundles always utilised top level await, even if the bundled modules didn't require top level await. Now, analysis of the bundle is done and if none of the bundled modules are asynchronously executed, then the bundle as a whole will be synchronously executed. Fixes #4055 Fixes #4123
This commit is contained in:
parent
942e67c00b
commit
671f0b83be
4 changed files with 58 additions and 36 deletions
|
@ -94,14 +94,14 @@ test(async function bundleApiSources() {
|
||||||
"/bar.ts": `export const bar = "bar";\n`
|
"/bar.ts": `export const bar = "bar";\n`
|
||||||
});
|
});
|
||||||
assert(diagnostics == null);
|
assert(diagnostics == null);
|
||||||
assert(actual.includes(`__inst("foo")`));
|
assert(actual.includes(`__inst_s("foo")`));
|
||||||
assert(actual.includes(`__exp["bar"]`));
|
assert(actual.includes(`__exp["bar"]`));
|
||||||
});
|
});
|
||||||
|
|
||||||
test(async function bundleApiNoSources() {
|
test(async function bundleApiNoSources() {
|
||||||
const [diagnostics, actual] = await bundle("./cli/tests/subdir/mod1.ts");
|
const [diagnostics, actual] = await bundle("./cli/tests/subdir/mod1.ts");
|
||||||
assert(diagnostics == null);
|
assert(diagnostics == null);
|
||||||
assert(actual.includes(`__inst("mod1")`));
|
assert(actual.includes(`__inst_s("mod1")`));
|
||||||
assert(actual.includes(`__exp["printHello3"]`));
|
assert(actual.includes(`__exp["printHello3"]`));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,16 @@ export function buildBundle(
|
||||||
rootName = normalizeUrl(rootName)
|
rootName = normalizeUrl(rootName)
|
||||||
.replace(sharedPath, "")
|
.replace(sharedPath, "")
|
||||||
.replace(/\.\w+$/i, "");
|
.replace(/\.\w+$/i, "");
|
||||||
|
// If one of the modules requires support for top-level-await, TypeScript will
|
||||||
|
// emit the execute function as an async function. When this is the case we
|
||||||
|
// need to bubble up the TLA to the instantiation, otherwise we instantiate
|
||||||
|
// synchronously.
|
||||||
|
const hasTla = data.match(/execute:\sasync\sfunction\s/);
|
||||||
let instantiate: string;
|
let instantiate: string;
|
||||||
if (rootExports && rootExports.length) {
|
if (rootExports && rootExports.length) {
|
||||||
instantiate = `const __exp = await __inst("${rootName}");\n`;
|
instantiate = hasTla
|
||||||
|
? `const __exp = await __inst("${rootName}");\n`
|
||||||
|
: `const __exp = __inst_s("${rootName}");\n`;
|
||||||
for (const rootExport of rootExports) {
|
for (const rootExport of rootExports) {
|
||||||
if (rootExport === "default") {
|
if (rootExport === "default") {
|
||||||
instantiate += `export default __exp["${rootExport}"];\n`;
|
instantiate += `export default __exp["${rootExport}"];\n`;
|
||||||
|
@ -53,7 +60,9 @@ export function buildBundle(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instantiate = `await __inst("${rootName}");\n`;
|
instantiate = hasTla
|
||||||
|
? `await __inst("${rootName}");\n`
|
||||||
|
: `__inst_s("${rootName}");\n`;
|
||||||
}
|
}
|
||||||
return `${SYSTEM_LOADER}\n${data}\n${instantiate}`;
|
return `${SYSTEM_LOADER}\n${data}\n${instantiate}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
[WILDCARD]
|
[WILDCARD]
|
||||||
let System;
|
let System, __inst, __inst_s;
|
||||||
let __inst;
|
|
||||||
[WILDCARD]
|
[WILDCARD]
|
||||||
(() => {
|
(() => {
|
||||||
[WILDCARD]
|
[WILDCARD]
|
||||||
|
@ -16,7 +15,7 @@ System.register("mod1", ["subdir2/mod2"], function (exports_3, context_3) {
|
||||||
[WILDCARD]
|
[WILDCARD]
|
||||||
});
|
});
|
||||||
|
|
||||||
const __exp = await __inst("mod1");
|
const __exp = __inst_s("mod1");
|
||||||
export const returnsHi = __exp["returnsHi"];
|
export const returnsHi = __exp["returnsHi"];
|
||||||
export const returnsFoo2 = __exp["returnsFoo2"];
|
export const returnsFoo2 = __exp["returnsFoo2"];
|
||||||
export const printHello3 = __exp["printHello3"];
|
export const printHello3 = __exp["printHello3"];
|
||||||
|
|
|
@ -2,20 +2,16 @@
|
||||||
|
|
||||||
// This is a specialised implementation of a System module loader.
|
// This is a specialised implementation of a System module loader.
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// @ts-nocheck
|
||||||
let System;
|
/* eslint-disable */
|
||||||
let __inst;
|
|
||||||
|
let System, __inst, __inst_s;
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const mMap = new Map();
|
const mMap = new Map();
|
||||||
System = {
|
System = {
|
||||||
register(id, deps, f) {
|
register(id, d, f) {
|
||||||
mMap.set(id, {
|
mMap.set(id, { id, d, f, exp: {} });
|
||||||
id,
|
|
||||||
deps,
|
|
||||||
f,
|
|
||||||
exp: {}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,11 +24,10 @@ let __inst;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const gE = data => {
|
const gE = ({ exp }) => {
|
||||||
const { exp } = data;
|
return (id, v) => {
|
||||||
return (id, value) => {
|
const vs = typeof id === "string" ? { [id]: v } : id;
|
||||||
const values = typeof id === "string" ? { [id]: value } : id;
|
for (const [id, value] of Object.entries(vs)) {
|
||||||
for (const [id, value] of Object.entries(values)) {
|
|
||||||
Object.defineProperty(exp, id, {
|
Object.defineProperty(exp, id, {
|
||||||
value,
|
value,
|
||||||
writable: true,
|
writable: true,
|
||||||
|
@ -47,39 +42,58 @@ let __inst;
|
||||||
const enq = ids => {
|
const enq = ids => {
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
if (!iQ.includes(id)) {
|
if (!iQ.includes(id)) {
|
||||||
const { deps } = mMap.get(id);
|
const { d } = mMap.get(id);
|
||||||
iQ.push(id);
|
iQ.push(id);
|
||||||
enq(deps);
|
enq(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const dr = async main => {
|
const gRQ = main => {
|
||||||
const rQ = [];
|
const rQ = [];
|
||||||
let id;
|
let id;
|
||||||
while ((id = iQ.pop())) {
|
while ((id = iQ.pop())) {
|
||||||
const m = mMap.get(id);
|
const m = mMap.get(id),
|
||||||
const { f } = m;
|
{ f } = m;
|
||||||
if (!f) {
|
if (!f) return;
|
||||||
return;
|
rQ.push([m.d, f(gE(m), gC(m, id === main))]);
|
||||||
}
|
delete m.f;
|
||||||
rQ.push([m.deps, f(gE(m), gC(m, id === main))]);
|
|
||||||
m.f = undefined;
|
|
||||||
}
|
}
|
||||||
|
return rQ;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dr = async main => {
|
||||||
|
const rQ = gRQ(main);
|
||||||
let r;
|
let r;
|
||||||
while ((r = rQ.shift())) {
|
while ((r = rQ.shift())) {
|
||||||
const [deps, { execute, setters }] = r;
|
const [d, { execute, setters }] = r;
|
||||||
for (let i = 0; i < deps.length; i++) setters[i](mMap.get(deps[i])?.exp);
|
for (let i = 0; i < d.length; i++) setters[i](mMap.get(d[i])?.exp);
|
||||||
const e = execute();
|
const e = execute();
|
||||||
if (e) await e;
|
if (e) await e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dr_s = main => {
|
||||||
|
const rQ = gRQ(main);
|
||||||
|
let r;
|
||||||
|
while ((r = rQ.shift())) {
|
||||||
|
const [d, { execute, setters }] = r;
|
||||||
|
for (let i = 0; i < d.length; i++) setters[i](mMap.get(d[i])?.exp);
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
__inst = async id => {
|
__inst = async id => {
|
||||||
System = undefined;
|
System = __inst = __inst_s = undefined;
|
||||||
__inst = undefined;
|
|
||||||
enq([id]);
|
enq([id]);
|
||||||
await dr(id);
|
await dr(id);
|
||||||
return mMap.get(id)?.exp;
|
return mMap.get(id)?.exp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
__inst_s = id => {
|
||||||
|
System = __inst = __inst_s = undefined;
|
||||||
|
enq([id]);
|
||||||
|
dr_s(id);
|
||||||
|
return mMap.get(id)?.exp;
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Add table
Reference in a new issue