aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/bundler/esbuild/ts.test.ts283
-rw-r--r--test/bundler/expectBundled.ts100
2 files changed, 310 insertions, 73 deletions
diff --git a/test/bundler/esbuild/ts.test.ts b/test/bundler/esbuild/ts.test.ts
index 7ad0453c0..5678edc1e 100644
--- a/test/bundler/esbuild/ts.test.ts
+++ b/test/bundler/esbuild/ts.test.ts
@@ -1,3 +1,4 @@
+import assert from "assert";
import { itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
@@ -7,9 +8,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// For debug, all files are written to $TEMP/bun-bundle-tests/ts
describe("bundler", () => {
- return;
itBundled("ts/TSDeclareConst", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare const require: any
@@ -20,9 +19,15 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("const foo"), 'does not include "const foo"');
+ },
});
itBundled("ts/TSDeclareLet", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare let require: any
@@ -33,9 +38,14 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ },
});
itBundled("ts/TSDeclareVar", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare var require: any
@@ -46,9 +56,14 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ },
});
itBundled("ts/TSDeclareClass", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare class require {}
@@ -59,14 +74,31 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("class"), 'does not include "class"');
+ },
});
itBundled("ts/TSDeclareClassFields", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
+ import './setup'
import './define-false'
import './define-true'
`,
+ "./setup.js": /* js */ `
+ globalThis.A = "global.A"
+ globalThis.a = "global.a"
+ globalThis.B = "global.B"
+ globalThis.b = "global.b"
+ globalThis.C = "global.C"
+ globalThis.c = "global.c"
+ globalThis.D = "global.D"
+ globalThis.d = "global.d"
+ `,
"/define-false/index.ts": /* ts */ `
class Foo {
a
@@ -79,7 +111,9 @@ describe("bundler", () => {
static [(() => null, C)]
static declare [(() => null, D)]
}
- (() => new Foo())()
+ const props = x => JSON.stringify({ ...Object.getOwnPropertyDescriptors(x), length: undefined, prototype: undefined })
+ console.log('Foo ', props(Foo))
+ console.log('new Foo', props(new Foo()))
`,
"/define-true/index.ts": /* ts */ `
class Bar {
@@ -93,19 +127,28 @@ describe("bundler", () => {
static [(() => null, C)]
static declare [(() => null, D)]
}
- (() => new Bar())()
+ const props = x => JSON.stringify({ ...Object.getOwnPropertyDescriptors(x), length: undefined, prototype: undefined })
+ console.log('Bar ', props(Bar))
+ console.log('new Bar', props(new Bar()))
`,
"/define-true/tsconfig.json": /* json */ `
{
- "compilerOptions": {
- "useDefineForClassFields": true
+ "compilerOptions": {
+ "useDefineForClassFields": true
+ }
}
- }
+ `,
+ },
+ run: {
+ stdout: `
+ Foo {"name":{"value":"Foo","writable":false,"enumerable":false,"configurable":true}}
+ new Foo {}
+ Bar {"name":{"value":"Bar","writable":false,"enumerable":false,"configurable":true},"A":{"writable":true,"enumerable":true,"configurable":true},"global.C":{"writable":true,"enumerable":true,"configurable":true}}
+ new Bar {"a":{"writable":true,"enumerable":true,"configurable":true},"global.c":{"writable":true,"enumerable":true,"configurable":true}}
`,
},
});
itBundled("ts/TSDeclareFunction", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare function require(): void
@@ -116,9 +159,15 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("function"), 'does not include "function"');
+ },
});
itBundled("ts/TSDeclareNamespace", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare namespace require {}
@@ -129,9 +178,15 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("namespace"), 'does not include "namespace"');
+ },
});
itBundled("ts/TSDeclareEnum", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare enum require {}
@@ -142,9 +197,15 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("enum"), 'does not include "enum"');
+ },
});
itBundled("ts/TSDeclareConstEnum", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
declare const enum require {}
@@ -155,9 +216,16 @@ describe("bundler", () => {
let foo = bar()
`,
},
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("foo = bar()"), 'includes "foo = bar()"');
+ assert(!api.readFile("/out.js").includes("require"), 'does not include "require"');
+ assert(!api.readFile("/out.js").includes("exports"), 'does not include "exports"');
+ assert(!api.readFile("/out.js").includes("module"), 'does not include "module"');
+ assert(!api.readFile("/out.js").includes("enum"), 'does not include "enum"');
+ assert(!api.readFile("/out.js").includes("const"), 'does not include "const"');
+ },
});
itBundled("ts/TSConstEnumComments", {
- // GENERATED
files: {
"/bar.ts": /* ts */ `
export const enum Foo {
@@ -171,7 +239,7 @@ describe("bundler", () => {
"%/*" = 1,
"*/%" = 2,
}
- console.log({
+ console.log(JSON.stringify({
'should have comments': [
Foo["%/*"],
Bar["%/*"],
@@ -180,93 +248,120 @@ describe("bundler", () => {
Foo["*/%"],
Bar["*/%"],
],
- });
+ }));
`,
},
entryPoints: ["/foo.ts"],
+ onAfterBundle(api) {
+ assert(!api.readFile("/out.js").match(/var|let|const/), "should have inlined all enum constants");
+ assert(!api.readFile("/out.js").match(/\*\/%/), "should not include '*/%' anywhere");
+ assert(
+ [...api.readFile("/out.js").matchAll(/1\s*\/\* %\/\* \*\//g)].length === 2,
+ "should have 2 comments for '1'",
+ );
+ assert(
+ [...api.readFile("/out.js").matchAll(/2\s*\/\*/g)].length === 0,
+ "should have 0 comments for '2' since */ will break the comment syntax",
+ );
+ },
+ run: {
+ stdout: `{"should have comments":[1,1],"should not have comments":[2,2]}`,
+ },
});
itBundled("ts/TSImportEmptyNamespace", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
- import {ns} from './ns.ts'
- function foo(): ns.type {}
+ import {REMOVE} from './ns.ts'
+ function foo(): REMOVE.type {}
foo();
`,
- "/ns.ts": `export namespace ns {}`,
+ "/ns.ts": `export namespace REMOVE { type type = number }`,
},
+ dce: true,
+ run: true,
});
itBundled("ts/TSImportMissingES6", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import fn, {x as a, y as b} from './foo'
console.log(fn(a, b))
`,
- "/foo.js": `export const x = 123`,
+ "/foo.js": `export const x = 123;`,
+ },
+ bundleErrors: {
+ "/entry.ts": [
+ `No matching export "default" in "foo.js" for import "default"`,
+ `No matching export "y" in "foo.js" for import "y"`,
+ ],
},
- /* TODO FIX expectedCompileLog: `entry.ts: ERROR: No matching export in "foo.js" for import "default"
- entry.ts: ERROR: No matching export in "foo.js" for import "y"
- `, */
});
itBundled("ts/TSImportMissingUnusedES6", {
- // GENERATED
files: {
"/entry.ts": `import fn, {x as a, y as b} from './foo'`,
"/foo.js": `export const x = 123`,
},
+ // goal for this test is there is no error. we dont really care about the output
});
itBundled("ts/TSExportMissingES6", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import * as ns from './foo'
- console.log(ns)
+ console.log(JSON.stringify(ns))
`,
"/foo.ts": `export {nope} from './bar'`,
"/bar.js": `export const yep = 123`,
},
+ run: {
+ stdout: `{}`,
+ },
});
itBundled("ts/TSImportMissingFile", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import {Something} from './doesNotExist.ts'
let foo = new Something
`,
},
- /* TODO FIX expectedScanLog: `entry.ts: ERROR: Could not resolve "./doesNotExist.ts"
- `, */
+ bundleErrors: {
+ "/entry.ts": [`Could not resolve: "./doesNotExist.ts"`],
+ },
});
itBundled("ts/TSImportTypeOnlyFile", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import {SomeType1} from './doesNotExist1.ts'
import {SomeType2} from './doesNotExist2.ts'
+ function bar() { return 2; }
let foo: SomeType1 = bar()
+ console.log(foo);
`,
},
+ run: {
+ stdout: "2",
+ },
});
itBundled("ts/TSExportEquals", {
- // GENERATED
files: {
"/a.ts": /* ts */ `
import b from './b.ts'
- console.log(b)
+ console.log(JSON.stringify(b))
`,
"/b.ts": /* ts */ `
export = [123, foo]
function foo() {}
`,
},
+ run: {
+ stdout: `[123,null]`,
+ },
});
itBundled("ts/TSExportNamespace", {
- // GENERATED
files: {
"/a.ts": /* ts */ `
import {Foo} from './b.ts'
- console.log(new Foo)
+ console.log(JSON.stringify(new Foo))
+ console.log(Foo.foo)
+ console.log(Foo.bar)
`,
"/b.ts": /* ts */ `
export class Foo {}
@@ -278,11 +373,13 @@ describe("bundler", () => {
}
`,
},
+ run: {
+ stdout: `{}\n1\n2`,
+ },
});
itBundled("ts/TSMinifyEnum", {
- // GENERATED
files: {
- "/a.ts": `enum Foo { A, B, C = Foo }`,
+ "/a.ts": `enum Foo { A, B, C = Foo }\ncapture(Foo)`,
"/b.ts": `export enum Foo { X, Y, Z = Foo }`,
},
entryPoints: ["/a.ts", "/b.ts"],
@@ -290,34 +387,107 @@ describe("bundler", () => {
minifyWhitespace: true,
minifyIdentifiers: true,
mode: "transform",
- });
- itBundled("ts/TSMinifyNestedEnum", {
- // GENERATED
- files: {
- "/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
- "/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
+ onAfterBundle(api) {
+ const a = api.readFile("/out/a.js");
+ api.writeFile("/out/a.edited.js", a.replace(/capture\((.*?)\)/, `export const Foo = $1`));
+ const b = api.readFile("/out/b.js");
+
+ // make sure the minification trick "enum[enum.K=V]=K" is used, but `enum`
+ assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.A=0]=["']A["']\b/), "should be using enum minification trick (1)");
+ assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.B=1]=["']B["']\b/), "should be using enum minification trick (2)");
+ assert(
+ a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.C=[a-zA-Z$]]=["']C["']\b/),
+ "should be using enum minification trick (3)",
+ );
+ assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.X=0]=["']X["']\b/), "should be using enum minification trick (4)");
+ assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Y=1]=["']Y["']\b/), "should be using enum minification trick (5)");
+ assert(
+ b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Z=[a-zA-Z$]]=["']Z["']\b/),
+ "should be using enum minification trick (6)",
+ );
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import {Foo as FooA} from './out/a.edited.js'
+ import {Foo as FooB} from './out/b.js'
+ import assert from 'assert';
+ assert.strictEqual(FooA.A, 0, 'a.ts Foo.A')
+ assert.strictEqual(FooA.B, 1, 'a.ts Foo.B')
+ assert.strictEqual(FooA.C, Foo, 'a.ts Foo.C')
+ assert.strictEqual(FooA[0], 'A', 'a.ts Foo[0]')
+ assert.strictEqual(FooA[1], 'B', 'a.ts Foo[1]')
+ assert.strictEqual(FooA[FooA], 'C', 'a.ts Foo[Foo]')
+ assert.strictEqual(FooB.X, 0, 'b.ts Foo.X')
+ assert.strictEqual(FooB.Y, 1, 'b.ts Foo.Y')
+ assert.strictEqual(FooB.Z, FooB, 'b.ts Foo.Z')
+ assert.strictEqual(FooB[0], 'X', 'b.ts Foo[0]')
+ assert.strictEqual(FooB[1], 'Y', 'b.ts Foo[1]')
+ assert.strictEqual(FooB[FooB], 'Z', 'b.ts Foo[Foo]')
+ `,
+ },
+ });
+ const TSMinifyNestedEnum = itBundled("ts/TSMinifyNestedEnum", {
+ files: {
+ "/a.ts": `function foo(arg) { enum Foo { A, B, C = Foo, D = arg } return Foo }\ncapture(foo)`,
+ "/b.ts": `export function foo(arg) { enum Foo { X, Y, Z = Foo, W = arg } return Foo }`,
},
entryPoints: ["/a.ts", "/b.ts"],
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
mode: "transform",
+ onAfterBundle(api) {
+ const a = api.readFile("/out/a.js");
+ api.writeFile("/out/a.edited.js", a.replace(/capture\((.*?)\)/, `export const Foo = $1`));
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import {foo as fooA} from './out/a.edited.js'
+ import {foo as fooB} from './out/b.js'
+ import assert from 'assert';
+ const S = Symbol('S')
+ const FooA = fooA(S)
+ const FooB = fooB(S)
+ assert.strictEqual(FooA.A, 0, 'a.ts Foo.A')
+ assert.strictEqual(FooA.B, 1, 'a.ts Foo.B')
+ assert.strictEqual(FooA.C, Foo, 'a.ts Foo.C')
+ assert.strictEqual(FooA.D, S, 'a.ts Foo.D')
+ assert.strictEqual(FooA[0], 'A', 'a.ts Foo[0]')
+ assert.strictEqual(FooA[1], 'B', 'a.ts Foo[1]')
+ assert.strictEqual(FooA[FooA], 'C', 'a.ts Foo[Foo]')
+ assert.strictEqual(FooA[S], 'D', 'a.ts Foo[S]')
+ assert.strictEqual(FooB.X, 0, 'b.ts Foo.X')
+ assert.strictEqual(FooB.Y, 1, 'b.ts Foo.Y')
+ assert.strictEqual(FooB.Z, FooB, 'b.ts Foo.Z')
+ assert.strictEqual(FooB.W, S, 'b.ts Foo.W')
+ assert.strictEqual(FooB[0], 'X', 'b.ts Foo[0]')
+ assert.strictEqual(FooB[1], 'Y', 'b.ts Foo[1]')
+ assert.strictEqual(FooB[FooB], 'Z', 'b.ts Foo[Foo]')
+ assert.strictEqual(FooB[S], 'W', 'b.ts Foo[S]')
+ `,
+ },
});
itBundled("ts/TSMinifyNestedEnumNoLogicalAssignment", {
- // GENERATED
files: {
- "/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
- "/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
+ "/a.ts": `function foo(arg) { enum Foo { A, B, C = Foo, D = arg } return Foo }\ncapture(foo)`,
+ "/b.ts": `export function foo(arg) { enum Foo { X, Y, Z = Foo, W = arg } return Foo }`,
},
entryPoints: ["/a.ts", "/b.ts"],
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
- outdir: "/",
mode: "transform",
+ unsupportedJSFeatures: ["logical-assignment"],
+ onAfterBundle(api) {
+ const a = api.readFile("/out/a.js");
+ assert(a.includes("A"), "a should not be empty");
+ assert(!a.includes("||="), "a should not use logical assignment");
+ const b = api.readFile("/out/b.js");
+ assert(b.includes("X"), "b should not be empty");
+ assert(!b.includes("||="), "b should not use logical assignment");
+ },
});
itBundled("ts/TSMinifyNestedEnumNoArrow", {
- // GENERATED
files: {
"/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
"/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
@@ -328,7 +498,17 @@ describe("bundler", () => {
minifyIdentifiers: true,
outdir: "/",
mode: "transform",
+ unsupportedJSFeatures: ["arrow"],
+ onAfterBundle(api) {
+ const a = api.readFile("/a.js");
+ assert(a.includes("A"), "a should not be empty");
+ assert(!a.includes("=>"), "a should not use arrow");
+ const b = api.readFile("/b.js");
+ assert(b.includes("X"), "b should not be empty");
+ assert(!b.includes("=>"), "b should not use arrow");
+ },
});
+ return;
itBundled("ts/TSMinifyNamespace", {
// GENERATED
files: {
@@ -377,6 +557,7 @@ describe("bundler", () => {
minifyIdentifiers: true,
outdir: "/",
mode: "transform",
+ unsupportedJSFeatures: ["logical-assignment"],
});
itBundled("ts/TSMinifyNamespaceNoArrow", {
// GENERATED
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 460e484b4..a1aa71c1e 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -146,6 +146,12 @@ export interface BundlerTestInput {
/** Used on tests in the esbuild suite that fail and skip. */
skipOnEsbuild?: boolean;
+ /** Compares output files from another test. Used for example in `ts/TSMinifyNestedEnumNoLogicalAssignment` because the output is exactly the same. */
+ matchesReference?: {
+ ref: BundlerTestRef;
+ files: string[];
+ };
+
/** Run after bundle happens but before runtime. */
onAfterBundle?(api: BundlerTestBundleAPI): void;
}
@@ -187,11 +193,25 @@ export interface BundlerTestRunOptions {
runtime?: "bun" | "node";
}
+export interface BundlerTestRef {
+ id: string;
+ options: BundlerTestInput;
+}
+
var testFiles = new Map();
-export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boolean) {
+function testRef(id: string, options: BundlerTestInput): BundlerTestRef {
+ return { id, options };
+}
+
+export function expectBundled(
+ id: string,
+ opts: BundlerTestInput,
+ dryRun = false,
+ ignoreFilter = false,
+): BundlerTestRef {
var { expect, it, test } = testForFile(callerSourceOrigin());
- if (FILTER && id !== FILTER) return;
+ if (!ignoreFilter && FILTER && id !== FILTER) return testRef(id, opts);
let {
assertNotPresent,
@@ -233,6 +253,7 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
treeShaking,
unsupportedCSSFeatures,
unsupportedJSFeatures,
+ matchesReference,
...unknownProps
} = opts;
@@ -288,10 +309,10 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
throw new Error("loader not implemented in bun build");
}
if (ESBUILD && skipOnEsbuild) {
- return;
+ return testRef(id, opts);
}
if (dryRun) {
- return;
+ return testRef(id, opts);
}
const root = path.join(outBase, id.replaceAll("/", path.sep));
@@ -452,14 +473,19 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
if (!success) {
if (!ESBUILD) {
const errorRegex = /^error: (.*?)\n(?:.*?\n\s*\^\s*\n(.*?)\n)?/gms;
- const allErrors = [...stderr!.toString("utf-8").matchAll(errorRegex)].map(([_str1, error, source]) => {
- if (!source) {
- return { error, file: "<bun>" };
- }
- const [_str2, fullFilename, line, col] = source.match(/bun-build-tests\/(.*):(\d+):(\d+)/)!;
- const file = fullFilename.slice(id.length + path.basename(outBase).length + 1);
- return { error, file, line, col };
- });
+ const allErrors = [...stderr!.toString("utf-8").matchAll(errorRegex)]
+ .map(([_str1, error, source]) => {
+ if (!source) {
+ if (error === "FileNotFound") {
+ return null;
+ }
+ return { error, file: "<bun>" };
+ }
+ const [_str2, fullFilename, line, col] = source.match(/bun-build-tests\/(.*):(\d+):(\d+)/)!;
+ const file = fullFilename.slice(id.length + path.basename(outBase).length + 1);
+ return { error, file, line, col };
+ })
+ .filter(Boolean) as any[];
if (allErrors.length === 0) {
console.log(stderr!.toString("utf-8"));
@@ -480,7 +506,7 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
for (const [file, errs] of Object.entries(files)) {
console.log(' "' + file + '": [');
for (const err of errs as any) {
- console.log(" `" + err.error + "`");
+ console.log(" `" + err.error + "`,");
}
console.log(" ],");
}
@@ -513,13 +539,13 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
throw new Error("Errors were expected while bundling:\n" + errorsLeft.map(formatError).join("\n"));
}
- return;
+ return testRef(id, opts);
}
throw new Error("Bundle Failed\n" + [...allErrors].map(formatError).join("\n"));
} else if (!expectedErrors) {
throw new Error("Bundle Failed\n" + stderr?.toString("utf-8"));
}
- return;
+ return testRef(id, opts);
} else if (expectedErrors) {
throw new Error("Errors were expected while bundling:\n" + expectedErrors.map(formatError).join("\n"));
}
@@ -547,7 +573,7 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
for (const [file, errs] of Object.entries(warningReference)) {
console.log(' "' + file + '": [');
for (const err of errs as any) {
- console.log(" `" + err.error + "`");
+ console.log(" `" + err.error + "`,");
}
console.log(" ],");
}
@@ -728,6 +754,31 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
onAfterBundle(api);
}
+ // check reference
+ if (matchesReference) {
+ const { ref } = matchesReference;
+ const theirRoot = path.join(outBase, ref.id.replaceAll("/", path.sep));
+ if (!existsSync(theirRoot)) {
+ expectBundled(ref.id, ref.options, false, true);
+ if (!existsSync(theirRoot)) {
+ console.log("Expected " + theirRoot + " to exist after running reference test");
+ throw new Error('Reference test "' + ref.id + '" did not succeed');
+ }
+ }
+ for (const file of matchesReference.files) {
+ const ours = path.join(root, file);
+ const theirs = path.join(theirRoot, file);
+ if (!existsSync(theirs)) throw new Error(`Reference test "${ref.id}" did not write ${file}`);
+ if (!existsSync(ours)) throw new Error(`Test did not write ${file}`);
+ try {
+ expect(readFileSync(ours).toString()).toBe(readFileSync(theirs).toString());
+ } catch (error) {
+ console.log("Expected reference test " + ref.id + "'s " + file + " to match ours");
+ throw error;
+ }
+ }
+ }
+
// Runtime checks!
if (run) {
const runs = Array.isArray(run) ? run : [run];
@@ -820,29 +871,34 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
}
}
}
+
+ return testRef(id, opts);
}
/** Shorthand for test and expectBundled. See `expectBundled` for what this does.
*/
-export function itBundled(id: string, opts: BundlerTestInput) {
+export function itBundled(id: string, opts: BundlerTestInput): BundlerTestRef {
+ const ref = testRef(id, opts);
const { it } = testForFile(callerSourceOrigin());
if (FILTER && id !== FILTER) {
- return;
+ return ref;
} else if (!FILTER) {
try {
- expectBundled(id, opts, true);
+ expectBundled(id, opts);
} catch (error) {
it.skip(id, () => {});
- return;
+ return ref;
}
}
it(id, () => expectBundled(id, opts));
+ return ref;
}
itBundled.skip = (id: string, opts: BundlerTestInput) => {
const { it } = testForFile(callerSourceOrigin());
- return it.skip(id, () => expectBundled(id, opts));
+ it.skip(id, () => expectBundled(id, opts));
+ return testRef(id, opts);
};
/** version of test that applies filtering */
@@ -855,7 +911,7 @@ export function bundlerTest(id: string, cb: () => void) {
}
bundlerTest.skip = (id: string, cb: any) => {
const { it } = testForFile(callerSourceOrigin());
- return it.skip(id, cb);
+ it.skip(id, cb);
};
function formatError(err: { file: string; error: string; line?: string; col?: string }) {