diff options
Diffstat (limited to 'test/bundler/expectBundled.ts')
-rw-r--r-- | test/bundler/expectBundled.ts | 174 |
1 files changed, 98 insertions, 76 deletions
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index e029787c8..f6ad423f9 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -75,13 +75,13 @@ export interface BundlerTestInput { // bundler options alias?: Record<string, string>; - assetNames?: string; + assetNaming?: string; banner?: string; define?: Record<string, string | number>; /** Default is "[name].[ext]" */ - entryNames?: string; + entryNaming?: string; /** Default is "[name]-[hash].[ext]" */ - chunkNames?: string; + chunkNaming?: string; extensionOrder?: string[]; /** Replaces "{{root}}" with the file root */ external?: string[]; @@ -91,11 +91,10 @@ export interface BundlerTestInput { ignoreDCEAnnotations?: boolean; inject?: string[]; jsx?: { - factory?: string; - fragment?: string; - automaticRuntime?: boolean; - development?: boolean; - preserve?: boolean; + runtime?: "automatic" | "classic"; + importSource?: string; // for automatic + factory?: string; // for classic + fragment?: string; // for classic }; outbase?: string; /** Defaults to `/out.js` */ @@ -103,7 +102,7 @@ export interface BundlerTestInput { /** Defaults to `/out` */ outdir?: string; /** Defaults to "browser". "bun" is set to "node" when using esbuild. */ - platform?: "bun" | "node" | "neutral" | "browser"; + target?: "bun" | "node" | "browser"; publicPath?: string; keepNames?: boolean; legalComments?: "none" | "inline" | "eof" | "linked" | "external"; @@ -215,7 +214,7 @@ export interface BundlerTestRunOptions { /** Pass args to bun itself (before the filename) */ bunArgs?: string[]; /** match exact stdout */ - stdout?: string; + stdout?: string | RegExp; /** partial match stdout (toContain()) */ partialStdout?: string; /** match exact error message, example "ReferenceError: Can't find variable: bar" */ @@ -253,21 +252,23 @@ function expectBundled( ignoreFilter = false, ): Promise<BundlerTestRef> | BundlerTestRef { var { expect, it, test } = testForFile(callerSourceOrigin()); - if (!ignoreFilter && FILTER && id !== FILTER) return testRef(id, opts); + if (!ignoreFilter && FILTER && !filterMatches(id)) return testRef(id, opts); let { + notImplemented, bundleWarnings, bundleErrors, banner, backend, assertNotPresent, capture, - chunkNames, + assetNaming, + chunkNaming, cjs2esm, dce, dceKeepMarkerCount, define, - entryNames, + entryNaming, entryPoints, entryPointsRaw, env, @@ -292,7 +293,7 @@ function expectBundled( outdir, outfile, outputPaths, - platform, + target, plugins, publicPath, run, @@ -321,7 +322,7 @@ function expectBundled( // Resolve defaults for options and some related things mode ??= "bundle"; - platform ??= "browser"; + target ??= "browser"; format ??= "esm"; entryPoints ??= entryPointsRaw ? [] : [Object.keys(files)[0]]; if (run === true) run = {}; @@ -333,9 +334,6 @@ function expectBundled( if (!ESBUILD && format !== "esm") { throw new Error("formats besides esm not implemented in bun build"); } - if (!ESBUILD && platform === "neutral") { - throw new Error("platform=neutral not implemented in bun build"); - } if (!ESBUILD && metafile) { throw new Error("metafile not implemented in bun build"); } @@ -357,9 +355,6 @@ function expectBundled( if (!ESBUILD && mainFields) { throw new Error("mainFields not implemented in bun build"); } - if (!ESBUILD && loader) { - throw new Error("loader not implemented in bun build"); - } if (!ESBUILD && sourceMap) { throw new Error("sourceMap not implemented in bun build"); } @@ -369,11 +364,13 @@ function expectBundled( if (!ESBUILD && inject) { throw new Error("inject not implemented in bun build"); } - if (!ESBUILD && publicPath) { - throw new Error("publicPath not implemented in bun build"); - } - if (!ESBUILD && chunkNames) { - throw new Error("chunkNames is not implemented in bun build"); + if (!ESBUILD && loader) { + const loaderValues = [...new Set(Object.values(loader))]; + const supportedLoaderTypes = ["js", "jsx", "ts", "tsx", "css", "json", "text", "file"]; + const unsupportedLoaderTypes = loaderValues.filter(x => !supportedLoaderTypes.includes(x)); + if (unsupportedLoaderTypes.length) { + throw new Error(`loader '${unsupportedLoaderTypes.join("', '")}' not implemented in bun build`); + } } if (ESBUILD && skipOnEsbuild) { return testRef(id, opts); @@ -413,8 +410,8 @@ function expectBundled( } if (outdir) { - entryNames ??= "[name].[ext]"; - chunkNames ??= "[name]-[hash].[ext]"; + entryNaming ??= "[name].[ext]"; + chunkNaming ??= "[name]-[hash].[ext]"; } // Option validation @@ -469,31 +466,32 @@ function expectBundled( ...(entryPointsRaw ?? []), mode === "bundle" ? [outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`] : [], define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]), - `--platform=${platform}`, + `--target=${target}`, external && external.map(x => ["--external", x]), minifyIdentifiers && `--minify-identifiers`, minifySyntax && `--minify-syntax`, minifyWhitespace && `--minify-whitespace`, globalName && `--global-name=${globalName}`, // inject && inject.map(x => ["--inject", path.join(root, x)]), - jsx.preserve && "--jsx=preserve", - jsx.automaticRuntime === false && "--jsx=classic", - jsx.factory && `--jsx-factory=${jsx.factory}`, - jsx.fragment && `--jsx-fragment=${jsx.fragment}`, - jsx.development === false && `--jsx-production`, - // metafile && `--metafile=${metafile}`, + // jsx.preserve && "--jsx=preserve", + jsx.runtime && ["--jsx-runtime", jsx.runtime], + jsx.factory && ["--jsx-factory", jsx.factory], + jsx.fragment && ["--jsx-fragment", jsx.fragment], + jsx.importSource && ["--jsx-import-source", jsx.importSource], + // metafile && `--manifest=${metafile}`, // sourceMap && `--sourcemap${sourceMap !== true ? `=${sourceMap}` : ""}`, - entryNames && entryNames !== "[name].[ext]" && [`--entry-names`, entryNames], - // chunkNames && chunkNames !== "[name]-[hash].[ext]" && [`--chunk-names`, chunkNames], + entryNaming && entryNaming !== "[name].[ext]" && [`--entry-naming`, entryNaming], + chunkNaming && chunkNaming !== "[name]-[hash].[ext]" && [`--chunk-naming`, chunkNaming], + assetNaming && assetNaming !== "[name]-[hash].[ext]" && [`--asset-naming`, chunkNaming], // `--format=${format}`, // legalComments && `--legal-comments=${legalComments}`, splitting && `--splitting`, - // treeShaking && `--tree-shaking`, + // treeShaking === false && `--no-tree-shaking`, // ?? // outbase && `--outbase=${outbase}`, // keepNames && `--keep-names`, // mainFields && `--main-fields=${mainFields}`, - // loader && Object.entries(loader).map(([k, v]) => ["--loader", `${k}=${v}`]), - // publicPath && `--public-path=${publicPath}`, + loader && Object.entries(loader).map(([k, v]) => ["--loader", `${k}:${v}`]), + publicPath && `--public-path=${publicPath}`, mode === "transform" && "--transform", ] : [ @@ -501,7 +499,7 @@ function expectBundled( mode === "bundle" && "--bundle", outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`, `--format=${format}`, - `--platform=${platform === "bun" ? "node" : platform}`, + `--platform=${target === "bun" ? "node" : target}`, minifyIdentifiers && `--minify-identifiers`, minifySyntax && `--minify-syntax`, minifyWhitespace && `--minify-whitespace`, @@ -509,15 +507,18 @@ function expectBundled( external && external.map(x => `--external:${x}`), inject && inject.map(x => `--inject:${path.join(root, x)}`), define && Object.entries(define).map(([k, v]) => `--define:${k}=${v}`), - jsx.automaticRuntime && "--jsx=automatic", - jsx.preserve && "--jsx=preserve", + `--jsx=${jsx.runtime ?? "automatic"}`, + // jsx.preserve && "--jsx=preserve", jsx.factory && `--jsx-factory=${jsx.factory}`, jsx.fragment && `--jsx-fragment=${jsx.fragment}`, - jsx.development && `--jsx-dev`, - entryNames && entryNames !== "[name].[ext]" && `--entry-names=${entryNames.replace(/\.\[ext]$/, "")}`, - chunkNames && - chunkNames !== "[name]-[hash].[ext]" && - `--chunk-names=${chunkNames.replace(/\.\[ext]$/, "")}`, + env?.NODE_ENV !== "production" && `--jsx-dev`, + entryNaming && entryNaming !== "[name].[ext]" && `--entry-names=${entryNaming.replace(/\.\[ext]$/, "")}`, + chunkNaming && + chunkNaming !== "[name]-[hash].[ext]" && + `--chunk-names=${chunkNaming.replace(/\.\[ext]$/, "")}`, + assetNaming && + assetNaming !== "[name]-[hash].[ext]" && + `--asset-names=${assetNaming.replace(/\.\[ext]$/, "")}`, metafile && `--metafile=${metafile}`, sourceMap && `--sourcemap${sourceMap !== true ? `=${sourceMap}` : ""}`, banner && `--banner:js=${banner}`, @@ -758,33 +759,35 @@ function expectBundled( syntax: minifySyntax, }, naming: { - entrypoint: useOutFile ? path.basename(outfile!) : entryNames, - chunk: chunkNames, + entry: useOutFile ? path.basename(outfile!) : entryNaming, + chunk: chunkNaming, + asset: assetNaming, }, plugins: pluginArray, treeShaking, outdir: buildOutDir, sourcemap: sourceMap === true ? "external" : sourceMap || "none", splitting, - target: platform === "neutral" ? "browser" : platform, + target, + publicPath, } as BuildConfig; if (DEBUG) { if (_referenceFn) { const x = _referenceFn.toString().replace(/^\s*expect\(.*$/gm, "// $&"); const debugFile = `import path from 'path'; - import assert from 'assert'; - const {plugins} = (${x})({ root: ${JSON.stringify(root)} }); - const options = ${JSON.stringify({ ...buildConfig, plugins: undefined }, null, 2)}; - options.plugins = typeof plugins === "function" ? [{ name: "plugin", setup: plugins }] : plugins; - const build = await Bun.build(options); - if (build.logs) { - throw build.logs; - } - for (const blob of build.outputs) { - await Bun.write(path.join(options.outdir, blob.path), blob.result); - } - `; +import assert from 'assert'; +const {plugins} = (${x})({ root: ${JSON.stringify(root)} }); +const options = ${JSON.stringify({ ...buildConfig, plugins: undefined }, null, 2)}; +options.plugins = typeof plugins === "function" ? [{ name: "plugin", setup: plugins }] : plugins; +const build = await Bun.build(options); +if (build.logs) { + throw build.logs; +} +for (const blob of build.outputs) { + await Bun.write(path.join(options.outdir, blob.path), blob.result); +} +`; writeFileSync(path.join(root, "run.js"), debugFile); } else { console.log("TODO: generate run.js, currently only works if options are wrapped in a function"); @@ -795,14 +798,12 @@ function expectBundled( const build = await Bun.build(buildConfig); Bun.gc(true); + console.log(build); + if (build.logs) { console.log(build.logs); throw new Error("TODO: handle build logs, but we should make this api nicer"); } - - for (const blob of build.outputs) { - await Bun.write(path.join(buildOutDir, blob.path), blob.result); - } } else { await esbuild.build({ bundle: true, @@ -896,7 +897,7 @@ function expectBundled( } } else { // entryNames makes it so we cannot predict the output file - if (!entryNames || entryNames === "[name].[ext]") { + if (!entryNaming || entryNaming === "[name].[ext]") { for (const fullpath of outputPaths) { if (!existsSync(fullpath)) { throw new Error("Bundle was not written to disk: " + fullpath); @@ -1027,7 +1028,7 @@ function expectBundled( if (file) { file = path.join(root, file); } else if (entryPaths.length === 1) { - file = outfile; + file = outfile ?? outputPaths[0]; } else { throw new Error(prefix + "run.file is required when there is more than one entrypoint."); } @@ -1042,6 +1043,7 @@ function expectBundled( env: { ...bunEnv, FORCE_COLOR: "0", + IS_TEST_RUNNER: "1", }, stdio: ["ignore", "pipe", "pipe"], }); @@ -1100,18 +1102,34 @@ function expectBundled( if (run.stdout !== undefined) { const result = stdout!.toString("utf-8").trim(); - const expected = dedent(run.stdout).trim(); - if (expected !== result) { - console.log({ file }); + if (typeof run.stdout === "string") { + const expected = dedent(run.stdout).trim(); + if (expected !== result) { + console.log(`runtime failed file=${file}`); + console.log(`reference stdout:`); + console.log(result); + console.log(`---`); + } + expect(result).toBe(expected); + } else { + if (!run.stdout.test(result)) { + console.log(`runtime failed file=${file}`); + console.log(`reference stdout:`); + console.log(result); + console.log(`---`); + } + expect(result).toMatch(run.stdout); } - expect(result).toBe(expected); } if (run.partialStdout !== undefined) { const result = stdout!.toString("utf-8").trim(); const expected = dedent(run.partialStdout).trim(); if (!result.includes(expected)) { - console.log({ file }); + console.log(`runtime failed file=${file}`); + console.log(`reference stdout:`); + console.log(result); + console.log(`---`); } expect(result).toContain(expected); } @@ -1137,7 +1155,7 @@ export function itBundled( const ref = testRef(id, opts); const { it } = testForFile(callerSourceOrigin()); - if (FILTER && id !== FILTER) { + if (FILTER && !filterMatches(id)) { return ref; } else if (!FILTER) { try { @@ -1165,7 +1183,7 @@ export function itBundled( return ref; } itBundled.skip = (id: string, opts: BundlerTestInput) => { - if (FILTER && id !== FILTER) { + if (FILTER && !filterMatches(id)) { return testRef(id, opts); } const { it } = testForFile(callerSourceOrigin()); @@ -1176,3 +1194,7 @@ itBundled.skip = (id: string, opts: BundlerTestInput) => { function formatError(err: { file: string; error: string; line?: string; col?: string }) { return `${err.file}${err.line ? " :" + err.line : ""}${err.col ? ":" + err.col : ""}: ${err.error}`; } + +function filterMatches(id: string) { + return FILTER === id || FILTER + "Dev" === id || FILTER + "Prod" === id; +} |