diff options
Diffstat (limited to 'test/bundler/expectBundled.ts')
-rw-r--r-- | test/bundler/expectBundled.ts | 95 |
1 files changed, 89 insertions, 6 deletions
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 0b467dddf..3bffb4271 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -32,6 +32,16 @@ const DEBUG = process.env.BUN_BUNDLER_TEST_DEBUG; const FILTER = process.env.BUN_BUNDLER_TEST_FILTER; /** Path to the bun. TODO: Once bundler is merged, we should remove the `bun-debug` fallback. */ const BUN_EXE = (process.env.BUN_EXE && Bun.which(process.env.BUN_EXE)) ?? Bun.which("bun-debug") ?? bunExe(); +/** + * If set to true, run an alternate validation for tests which is much looser. + * We are only testing for: + * - bundler does not crash + * - output js has no syntax errors + * + * Defaults to true unless you are running a single test. + */ +const LOOSE = !process.env.BUN_BUNDLER_TEST_FILTER && process.env.BUN_BUNDLER_TEST_LOOSE !== "false"; +export const RUN_UNCHECKED_TESTS = LOOSE; const outBaseTemplate = path.join(tmpdir(), "bun-build-tests", `${ESBUILD ? "esbuild" : "bun"}-`); if (!existsSync(path.dirname(outBaseTemplate))) mkdirSync(path.dirname(outBaseTemplate), { recursive: true }); @@ -57,7 +67,7 @@ export interface BundlerTestInput { entryPointsRaw?: string[]; /** Defaults to bundle */ mode?: "bundle" | "transform"; - /** Used for `default/ErrorMessageCrashStdinIssue2913`. */ + /** Used for `default/ErrorMessageCrashStdinESBuildIssue2913`. */ stdin?: { contents: string; cwd: string }; /** Use when doing something weird with entryPoints and you need to check other output paths. */ outputPaths?: string[]; @@ -106,6 +116,7 @@ export interface BundlerTestInput { treeShaking?: boolean; unsupportedCSSFeatures?: string[]; unsupportedJSFeatures?: string[]; + /** if set to true or false, create or edit tsconfig.json to set compilerOptions.useDefineForClassFields */ useDefineForClassFields?: boolean; sourceMap?: boolean | "inline" | "external"; @@ -152,6 +163,9 @@ export interface BundlerTestInput { files: string[]; }; + /** Captures `capture()` function calls in the output. */ + capture?: string[]; + /** Run after bundle happens but before runtime. */ onAfterBundle?(api: BundlerTestBundleAPI): void; } @@ -167,6 +181,10 @@ export interface BundlerTestBundleAPI { appendFile(file: string, contents: string): void; expectFile(file: string): Expect; assertFileExists(file: string): void; + /** + * Finds all `capture(...)` calls and returns the strings within each function call. + */ + captureFile(file: string, fnName?: string): string[]; warnings: Record<string, { file: string; error: string; line?: string; col?: string }[]>; options: BundlerTestInput; @@ -220,6 +238,7 @@ export function expectBundled( banner, bundleErrors, bundleWarnings, + capture, dce, dceKeepMarkerCount, define, @@ -236,6 +255,7 @@ export function expectBundled( legalComments, loader, mainFields, + matchesReference, metafile, minifyIdentifiers, minifySyntax, @@ -255,7 +275,7 @@ export function expectBundled( treeShaking, unsupportedCSSFeatures, unsupportedJSFeatures, - matchesReference, + useDefineForClassFields, ...unknownProps } = opts; @@ -287,6 +307,12 @@ export 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 && mode === "transform") { + throw new Error("mode=transform not implemented in bun build"); + } if (!ESBUILD && metafile) { throw new Error("metafile not implemented in bun build"); } @@ -358,6 +384,26 @@ export function expectBundled( writeFileSync(filename, dedent(contents).replace(/\{\{root\}\}/g, root)); } + if (useDefineForClassFields !== undefined) { + if (existsSync(path.join(root, "tsconfig.json"))) { + try { + const tsconfig = JSON.parse(readFileSync(path.join(root, "tsconfig.json"), "utf8")); + tsconfig.compilerOptions = tsconfig.compilerOptions ?? {}; + tsconfig.compilerOptions.useDefineForClassFields = useDefineForClassFields; + writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify(tsconfig, null, 2)); + } catch (error) { + console.log( + "DEBUG NOTE: specifying useDefineForClassFields causes tsconfig.json to be parsed as JSON and not JSONC.", + ); + } + } else { + writeFileSync( + path.join(root, "tsconfig.json"), + JSON.stringify({ compilerOptions: { useDefineForClassFields } }, null, 2), + ); + } + } + // Run bun build cli. In the future we can move to using `Bun.Bundler` const cmd = ( !ESBUILD @@ -391,6 +437,7 @@ export function expectBundled( // keepNames && `--keep-names`, // mainFields && `--main-fields=${mainFields}`, // loader && Object.entries(loader).map(([k, v]) => ["--loader", `${k}=${v}`]), + mode === "transform" && "--transform", ] : [ ESBUILD_PATH, @@ -478,8 +525,9 @@ export function expectBundled( if (!success) { if (!ESBUILD) { + const errorText = stderr.toString("utf-8"); const errorRegex = /^error: (.*?)\n(?:.*?\n\s*\^\s*\n(.*?)\n)?/gms; - const allErrors = [...stderr!.toString("utf-8").matchAll(errorRegex)] + const allErrors = [...errorText.matchAll(errorRegex)] .map(([_str1, error, source]) => { if (!source) { if (error === "FileNotFound") { @@ -494,10 +542,16 @@ export function expectBundled( .filter(Boolean) as any[]; if (allErrors.length === 0) { - console.log(stderr!.toString("utf-8")); + console.log(errorText); } - if (stderr!.toString("utf-8").includes("Crash report saved to:")) { + if ( + errorText.includes("Crash report saved to:") || + errorText.includes("panic: reached unreachable code") || + errorText.includes("Panic: reached unreachable code") || + errorText.includes("Segmentation fault") || + errorText.includes("bun has crashed") + ) { throw new Error("Bun crashed during build"); } @@ -639,6 +693,15 @@ export function expectBundled( }, warnings: warningReference, options: opts, + captureFile: (file, fnName = "capture") => { + const fileContents = readFile(file); + const regex = new RegExp(`\\b${fnName}\\s*\\(((?:\\(\\))?.*?)\\)`, "g"); + const matches = [...fileContents.matchAll(regex)]; + if (matches.length === 0) { + throw new Error(`No ${fnName} calls found in ${file}`); + } + return matches.map(match => match[1]); + }, } satisfies BundlerTestBundleAPI; // DCE keep scan @@ -750,6 +813,11 @@ export function expectBundled( } } + if (capture) { + const captures = api.captureFile(path.relative(root, outfile ?? outputPaths[0])); + expect(captures).toEqual(capture); + } + // Write runtime files to disk as well as run the post bundle hook. for (const [file, contents] of Object.entries(runtimeFiles ?? {})) { mkdirSync(path.dirname(path.join(root, file)), { recursive: true }); @@ -907,7 +975,22 @@ export function itBundled(id: string, opts: BundlerTestInput): BundlerTestRef { } } - it(id, () => expectBundled(id, opts)); + if (LOOSE) { + try { + expectBundled(id, opts); + it(id, () => {}); + } catch (error: any) { + if (error.message === "Bun crashed during build") { + it(id, () => { + throw error; + }); + } else { + it.skip(id, () => {}); + } + } + } else { + it(id, () => expectBundled(id, opts)); + } return ref; } itBundled.skip = (id: string, opts: BundlerTestInput) => { |