154 lines
5.8 KiB
TypeScript
154 lines
5.8 KiB
TypeScript
import { describe } from "bun:test";
|
|
import { itBundled } from "./expectBundled";
|
|
|
|
describe("bundler", () => {
|
|
describe("compile with splitting", () => {
|
|
itBundled("compile/splitting/RelativePathsAcrossChunks", {
|
|
compile: true,
|
|
splitting: true,
|
|
backend: "cli",
|
|
files: {
|
|
"/src/app/entry.ts": /* js */ `
|
|
console.log('app entry');
|
|
import('../components/header').then(m => m.render());
|
|
`,
|
|
"/src/components/header.ts": /* js */ `
|
|
export async function render() {
|
|
console.log('header rendering');
|
|
const nav = await import('./nav/menu');
|
|
nav.show();
|
|
}
|
|
`,
|
|
"/src/components/nav/menu.ts": /* js */ `
|
|
export async function show() {
|
|
console.log('menu showing');
|
|
const items = await import('./items');
|
|
console.log('items:', items.list);
|
|
}
|
|
`,
|
|
"/src/components/nav/items.ts": /* js */ `
|
|
export const list = ['home', 'about', 'contact'].join(',');
|
|
`,
|
|
},
|
|
entryPoints: ["/src/app/entry.ts"],
|
|
outdir: "/build",
|
|
run: {
|
|
stdout: "app entry\nheader rendering\nmenu showing\nitems: home,about,contact",
|
|
},
|
|
});
|
|
|
|
for (const minify of [false, true]) {
|
|
itBundled(`compile/splitting/ImportMetaInSplitChunk${minify ? "+minify" : ""}`, {
|
|
compile: true,
|
|
splitting: true,
|
|
bytecode: true,
|
|
format: "esm",
|
|
...(minify ? { minifySyntax: true, minifyIdentifiers: true, minifyWhitespace: true } : {}),
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
const mod = await import("./worker.ts");
|
|
mod.run();
|
|
`,
|
|
"/worker.ts": /* js */ `
|
|
export function run() {
|
|
console.log(typeof import.meta.url === "string" ? "ok" : "fail");
|
|
console.log(typeof import.meta.dir === "string" ? "ok" : "fail");
|
|
}
|
|
`,
|
|
},
|
|
run: {
|
|
stdout: "ok\nok",
|
|
},
|
|
});
|
|
}
|
|
|
|
// The shared chunk contains a require()d (so __esm-wrapped) module with an
|
|
// unused import of a node builtin. Builtins are side-effect free, so that
|
|
// import is tree-shaken out of the printed chunk, but the module record
|
|
// built for --bytecode still listed it, under the import's original local
|
|
// name because the dead symbol was never renamed. When a live export of the
|
|
// chunk had the same name, its export entry turned into a re-export of the
|
|
// builtin, and the other chunk got fs/promises.rm instead of the local rm().
|
|
//
|
|
// The unused import binds every single-character name as well, so the
|
|
// collision also happens with whatever name the minifier gives `rm`.
|
|
//
|
|
// Only the bytecode builds have a record to get wrong, and only the
|
|
// splitting builds export `rm` across chunks; the other combinations pin
|
|
// the configurations that already worked.
|
|
const deadImportNames = [
|
|
"rm",
|
|
...Array.from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$", c => `rm as ${c}`),
|
|
];
|
|
for (const splitting of [false, true]) {
|
|
for (const bytecode of [false, true]) {
|
|
for (const minify of [false, true]) {
|
|
const suffix = (splitting ? "+splitting" : "") + (bytecode ? "+bytecode" : "") + (minify ? "+minify" : "");
|
|
itBundled(`compile/DeadExternalImportInWrappedModule${suffix}`, {
|
|
compile: true,
|
|
splitting,
|
|
bytecode,
|
|
format: "esm",
|
|
...(minify ? { minifySyntax: true, minifyIdentifiers: true, minifyWhitespace: true } : {}),
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import { rm } from "./shared.js";
|
|
console.log("entry:", rm());
|
|
await import("./page.js");
|
|
`,
|
|
"/page.js": /* js */ `
|
|
import { rm } from "./shared.js";
|
|
console.log("page:", rm());
|
|
`,
|
|
"/shared.js": /* js */ `
|
|
const { value } = require("./wrapped.js");
|
|
export function rm() {
|
|
return "local rm " + value;
|
|
}
|
|
`,
|
|
// A .js file on purpose: in TypeScript an unused import is dropped by
|
|
// the parser and never reaches the linker.
|
|
"/wrapped.js": /* js */ `
|
|
import { ${deadImportNames.join(", ")} } from "fs/promises";
|
|
export const value = 42;
|
|
`,
|
|
},
|
|
run: {
|
|
stdout: "entry: local rm 42\npage: local rm 42",
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// A split chunk gets its own bytecode + module record. Re-exports of
|
|
// externals in it are printed as `export * from`, or as imports plus the
|
|
// chunk's `export { ... }` tail; the record has to describe all of them in
|
|
// addition to the cross-chunk imports, or reading `fs` off the namespace
|
|
// throws a TDZ ReferenceError.
|
|
for (const minify of [false, true]) {
|
|
itBundled(`compile/splitting/ReExportExternalFromSplitChunk${minify ? "+minify" : ""}`, {
|
|
compile: true,
|
|
splitting: true,
|
|
bytecode: true,
|
|
format: "esm",
|
|
...(minify ? { minifySyntax: true, minifyIdentifiers: true, minifyWhitespace: true } : {}),
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
const mod = await import("./reexports.ts");
|
|
console.log(typeof mod.fs.readFileSync, typeof mod.join, typeof mod.rfs);
|
|
`,
|
|
"/reexports.ts": /* js */ `
|
|
export * from "node:path";
|
|
export * as fs from "node:fs";
|
|
export { readFileSync as rfs } from "node:fs";
|
|
`,
|
|
},
|
|
run: {
|
|
stdout: "function function function",
|
|
},
|
|
});
|
|
}
|
|
});
|
|
});
|