diff options
author | 2023-05-11 17:42:54 -0400 | |
---|---|---|
committer | 2023-05-11 14:42:54 -0700 | |
commit | 6a163cf933542506354dc836bd92693bcae5939b (patch) | |
tree | 2eaeee9cddeb930792b96de6ed040a9877ebc318 | |
parent | 02cad591f8c56f801fb9ccc480bf9547484144c2 (diff) | |
download | bun-6a163cf933542506354dc836bd92693bcae5939b.tar.gz bun-6a163cf933542506354dc836bd92693bcae5939b.tar.zst bun-6a163cf933542506354dc836bd92693bcae5939b.zip |
bundler tests and improve `Bun.build` return type (#2833)
* importstar_ts
* tests
* run acorn test suite
* bench tweaks
* test
* bun.build tests very incomplete
* remove dataurl and base64 loaders from tests since they dont work yet
* tests
* stuff
* stuff
* add errors and array of blobs
* work so far
* docs
* requested changes
* fix overwrite docs
* remove this file
46 files changed, 1100 insertions, 299 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index cc79067f4..e196eda42 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "request": "launch", "name": "bun test", "program": "bun-debug", - "args": ["wiptest", "${file}"], + "args": ["test", "${file}"], "cwd": "${workspaceFolder}", "env": { "FORCE_COLOR": "1" @@ -30,7 +30,7 @@ "request": "launch", "name": "bun test (all)", "program": "bun-debug", - "args": ["wiptest"], + "args": ["test"], "cwd": "${workspaceFolder}/test", "env": { "FORCE_COLOR": "1", @@ -201,7 +201,7 @@ "--splitting", "bun", "/Users/jarred/Code/bun-rsc/components/Message.tsx", - "/Users/jarred/Code/bun-rsc/components/Button.tsx", + "/Users/jarred/Code/bun-rsc/components/Button.tsx" ], "cwd": "/Users/jarred/Code/bun-rsc", "console": "internalConsole", diff --git a/bench/react-hello-world/react-hello-world.jsx b/bench/react-hello-world/react-hello-world.jsx index eac44d266..ae889f811 100644 --- a/bench/react-hello-world/react-hello-world.jsx +++ b/bench/react-hello-world/react-hello-world.jsx @@ -1,5 +1,5 @@ // to run this: -// NODE_ENV=production bun --jsx-production react-hello-world.jsx +// NODE_ENV=production bun react-hello-world.jsx // Make sure you're using react-dom@18.3.0 or later. // Currently that is available at react-dom@next (which is installed in this repository) diff --git a/bench/sqlite/deno.js b/bench/sqlite/deno.js index 821cf5149..cc69a5288 100644 --- a/bench/sqlite/deno.js +++ b/bench/sqlite/deno.js @@ -1,4 +1,4 @@ -import { Database } from "https://deno.land/x/sqlite3@0.8.0/mod.ts"; +import { Database } from "https://deno.land/x/sqlite3@0.9.1/mod.ts"; import { run, bench } from "../node_modules/mitata/src/cli.mjs"; const db = new Database("./src/northwind.sqlite"); diff --git a/docs/api/globals.md b/docs/api/globals.md index 891f13fc9..370dd3800 100644 --- a/docs/api/globals.md +++ b/docs/api/globals.md @@ -80,7 +80,7 @@ Bun implements the following globals. --- -- `BuildError` +- `BuildMessage` - Bun - @@ -272,7 +272,7 @@ Bun implements the following globals. --- -- `ResolveError` +- `ResolveMessage` - Bun - diff --git a/docs/bundler/migration.md b/docs/bundler/migration.md index aac37479c..bfe5a63bc 100644 --- a/docs/bundler/migration.md +++ b/docs/bundler/migration.md @@ -1,3 +1,7 @@ +{% callout %} +**Note** — Available in the Bun v0.6.0 nightly. Run `bun upgrade --canary` to try it out. +{% /callout %} + Bun's bundler API is inspired heavily by [esbuild](https://esbuild.github.io/). Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API. There are a few behavioral differences to note. @@ -31,7 +35,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `--bundle` - n/a -- Not necessary, `bun build` always bundles. +- Bun always bundles, use `--transpile` to disable this behavior. --- @@ -101,7 +105,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `--platform` - `--target` -- Renamed to `--target` for consistency with tsconfig +- Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. --- @@ -137,7 +141,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `--allow-overwrite` - n/a -- Overwriting is always allowed +- Overwriting is never allowed --- @@ -449,7 +453,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `allowOverwrite` - n/a -- Always `true` +- Always `false` --- @@ -476,7 +480,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `bundle` - n/a -- Always `true` +- Always `true`. Use [`Bun.Transpiler`](/docs/api/transpiler) to transpile without bundling. --- @@ -769,7 +773,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `platform` - `target` -- Supports `"bun"`, `"node"`, and `"browser"` (the default) +- Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. --- diff --git a/docs/cli/build.md b/docs/cli/build.md index 01262944d..f7113ae9f 100644 --- a/docs/cli/build.md +++ b/docs/cli/build.md @@ -1,7 +1,3 @@ -{% callout %} -**Note** — Available in the Bun v0.6.0 nightly. Run `bun upgrade --canary` to try it out. -{% /callout %} - Bun's fast native bundler is now in beta. It can be used via the `bun build` CLI command or the `Bun.build()` JavaScript API. {% codetabs group="a" %} @@ -221,13 +217,11 @@ The behavior described in this table can be overridden with [plugins](/docs/bund {% codetabs group="a" %} -```ts#JavaScript +```ts #JavaScript const result = await Bun.build({ entrypoints: ['./index.ts'] -}); // => Promise - -await result; -// => { outputs: Array<{ path: string; result: Blob }> } +}); +// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash#CLI @@ -249,35 +243,36 @@ const result = await Bun.build({ entrypoints: ['./index.ts'], outdir: './out' }); - -result; -// => { outputs: Array<{ path: string; result: BunFile }> } +// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash#CLI $ bun build --entrypoints ./index.ts --outdir ./out -# the bundle will be printed to stdout -# ... +# a summary of bundled files will be printed to stdout ``` {% /codetabs %} -When `outdir` is specified: +If `outdir` is not passed to the JavaScript API, bundled code will not be written to disk. Bundled files are returned in an array of `BuildArtifact` objects. These objects are Blobs with extra properties. -- The JavaScript API will write the generated bundles to the appropriate location in `outdir`. The result of the `Bun.build()` call will contain `BunFile` instances corresponding to the new files. +```ts +const result = await Bun.build({ + entrypoints: ['./index.ts'] +}); - ```ts - const result = await Bun.build({ - /* ... */ - }); - // => { outputs: Array<{ path: string; result: BunFile }> } +for (const result of result.outputs) { + // Can be consumed as blobs + await result.text(); - for (const { path, result } of result.outputs) { - console.log(`Wrote file: ${path}`); - } - ``` + // Bun will set Content-Type and Etag headers + new Response(result); + + // Can be written manually, but you should use `outdir` in this case. + Bun.write(path.join("out", result.path), result); +} +``` -- The CLI will print a summary of the written files. The bundled code will not be written to `stdout`. +When `outdir` is set, the `.path` on `BuildArtifact` will be the absolute path to where it was written to. ### `target` @@ -320,7 +315,7 @@ Depending on the target, Bun will apply different module resolution rules and op --- - `node` -- For generating bundles that are intended to be run by Node.js. Prioritizes the `"node"` export condition when resolving imports. In the future, this will automatically polyfill the `Bun` global and other built-in `bun:*` modules, though this is not yet implemented. +- For generating bundles that are intended to be run by Node.js. Prioritizes the `"node"` export condition when resolving imports, and outputs `.mjs`. In the future, this will automatically polyfill the `Bun` global and other built-in `bun:*` modules, though this is not yet implemented. {% /table %} @@ -332,7 +327,7 @@ Currently the bundler only supports one module format: `"esm"`. Support for `"cj {% codetabs %} -```ts#Bun.build +```ts#JavaScript await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', @@ -344,7 +339,7 @@ await Bun.build({ $ bun build ./index.tsx --outdir ./out --format esm ``` -{% /codetabs %} --> +{% /codetabs %} <!-- ### `bundling` @@ -479,7 +474,7 @@ n/a Bun implements a univeral plugin system for both Bun's runtime and bundler. Refer to the [plugin documentation](/docs/bundler/plugins) for complete documentation. -### `manifest` +<!-- ### `manifest` Whether to return a build manifest in the result of `Bun.build`. @@ -538,7 +533,7 @@ export type ImportKind = {% /details %} -By design, the manifest is a simple JSON object that can easily be serialized or written to disk. It is also compatible with esbuild's [`metafile`](https://esbuild.github.io/api/#metafile) format. +By design, the manifest is a simple JSON object that can easily be serialized or written to disk. It is also compatible with esbuild's [`metafile`](https://esbuild.github.io/api/#metafile) format. --> ### `sourcemap` @@ -860,7 +855,7 @@ $ bun build ./index.tsx --outdir ./out --entry-naming "[dir]/[name].[ext]" --chu {% /codetabs %} -<!-- ### `root` +### `root` The root directory of the project. @@ -949,7 +944,7 @@ By specifying `.` as `root`, the generated file structure will look like this: └── pages └── index.js └── settings.js -``` --> +``` ### `publicPath` @@ -1056,19 +1051,63 @@ $ bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file {% /codetabs %} + +## Error handling + +`Bun.build` only throws if invalid options are provided. You should read the `success` and `logs` properties of the result to determine whether the build was successful. + +```ts +const result = await Bun.build({ + entrypoints: ['./index.tsx'], + outdir: './out', +}); + +if(!result.success) { + console.error("Build failed"); + for (const message of result.logs) { + // Bun will pretty print the message object + console.error(message); + } +} +``` + +Each message is either a `BuildMessage` or `ResolveMessage` object, which can be used to trace what problems happened in the build. + +```ts +class BuildMessage { + name: string; + position?: Position; + message: string; + level: "error" | "warning" | "info" | "debug" | "verbose"; +} + +class ResolveMessage extends BuildMessage { + code: string; + referrer: string; + specifier: string; + importKind: ImportKind; +} +``` + +If you want to throw an error from a failed build, consider passing the logs to an `AggregateError`. If uncaught, Bun will pretty print the contained messages nicely. + +```ts +if (!result.success) { + throw new AggregateError(result.logs, "Build failed"); +} +``` + ## Reference ```ts interface Bun { - build(options: BuildOptions): Promise<{ - outputs: Array<{ path: string; result: Blob | BunFile }>; - manifest?: BuildManifest; - }>; + build(options: BuildOptions): Promise<BuildOutput>; } interface BuildOptions { entrypoints: string[]; // required outdir?: string; // default: no write (in-memory only) + format?: "esm"; // later: "cjs" | "iife" target?: "browser" | "bun" | "node"; // "browser" splitting?: boolean; // true plugins?: BunPlugin[]; // [] // See https://bun.sh/docs/bundler/plugins @@ -1094,6 +1133,34 @@ interface BuildOptions { }; } +interface BuildOutput { + outputs: BuildArtifact[]; + success: boolean; + logs: Array<BuildMessage | ResolveMessage>; +} + +interface BuildArtifact extends Blob { + path: string; + loader: Loader; + hash?: string; + kind: "entry-point" | "chunk" | "asset" | "sourecemap"; + sourcemap?: BuildArtifact; +} + +type Loader = + | "js" + | "jsx" + | "ts" + | "tsx" + | "json" + | "toml" + | "file" + | "napi" + | "wasm" + | "text"; +``` + +<!-- interface BuildManifest { inputs: { [path: string]: { @@ -1120,5 +1187,4 @@ interface BuildManifest { exports: string[]; }; }; -} -``` +} --> diff --git a/docs/runtime/index.md b/docs/runtime/index.md index b97deb122..90d200006 100644 --- a/docs/runtime/index.md +++ b/docs/runtime/index.md @@ -196,8 +196,7 @@ The following Web APIs are partially or completely supported. --- - Errors -- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) [`ResolveError`](https://developer.mozilla.org/en-US/docs/Web/API/ResolveError) - [`BuildError`](https://developer.mozilla.org/en-US/docs/Web/API/BuildError) +- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) --- diff --git a/docs/runtime/web-apis.md b/docs/runtime/web-apis.md index 82108de31..e1d95fa9f 100644 --- a/docs/runtime/web-apis.md +++ b/docs/runtime/web-apis.md @@ -69,8 +69,7 @@ The following Web APIs are partially or completely supported. --- - Errors -- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) [`ResolveError`](https://developer.mozilla.org/en-US/docs/Web/API/ResolveError) - [`BuildError`](https://developer.mozilla.org/en-US/docs/Web/API/BuildError) +- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) --- diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 27d933988..0fc63f911 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -105,7 +105,7 @@ declare module "bun" { /** * Synchronously resolve a `moduleId` as though it were imported from `parent` * - * On failure, throws a `ResolveError` + * On failure, throws a `ResolveMessage` */ // tslint:disable-next-line:unified-signatures export function resolveSync(moduleId: string, parent: string): string; @@ -113,7 +113,7 @@ declare module "bun" { /** * Resolve a `moduleId` as though it were imported from `parent` * - * On failure, throws a `ResolveError` + * On failure, throws a `ResolveMessage` * * For now, use the sync version. There is zero performance benefit to using this async version. It exists for future-proofing. */ @@ -1019,13 +1019,13 @@ declare module "bun" { sourcemap: null; } - function build(config: BuildConfig): Promise<{ - outputs: Map< - string, - BuildArtifact | AssetBuildArtifact | SourceMapBuildArtifact - >; - logs: Array<BuildError | ResolveError>; - }>; + interface BuildOutput { + outputs: Array<BuildArtifact | AssetBuildArtifact | SourceMapBuildArtifact>; + success: boolean; + logs: Array<BuildMessage | ResolveMessage>; + } + + function build(config: BuildConfig): Promise<BuildOutput>; /** * **0** means the message was **dropped** @@ -2542,7 +2542,13 @@ declare module "bun" { export type Target = /** - * The default environment when using `bun run` or `bun` to load a script + * For generating bundles that are intended to be run by the Bun runtime. In many cases, + * it isn't necessary to bundle server-side code; you can directly execute the source code + * without modification. However, bundling your server code can reduce startup times and + * improve running performance. + * + * All bundles generated with `target: "bun"` are marked with a special `// @bun` pragma, which + * indicates to the Bun runtime that there's no need to re-transpile the file before execution. */ | "bun" /** @@ -2553,6 +2559,7 @@ declare module "bun" { * The plugin will be applied to browser builds */ | "browser"; + /** https://bun.sh/docs/bundler/loaders */ type Loader = | "js" @@ -2564,7 +2571,6 @@ declare module "bun" { | "file" | "napi" | "wasm" - | "dataurl" | "text"; interface PluginConstraints { diff --git a/packages/bun-types/globals.d.ts b/packages/bun-types/globals.d.ts index 8413d2540..5a00d5ebb 100644 --- a/packages/bun-types/globals.d.ts +++ b/packages/bun-types/globals.d.ts @@ -252,13 +252,13 @@ interface ImportMeta { /** * Resolve a module ID the same as if you imported it * - * On failure, throws a `ResolveError` + * On failure, throws a `ResolveMessage` */ resolve(moduleId: string): Promise<string>; /** * Resolve a `moduleId` as though it were imported from `parent` * - * On failure, throws a `ResolveError` + * On failure, throws a `ResolveMessage` */ // tslint:disable-next-line:unified-signatures resolve(moduleId: string, parent: string): Promise<string>; @@ -2864,12 +2864,12 @@ interface Position { offset: number; } -interface ResolveError { +declare class ResolveMessage { + readonly name: "ResolveMessage"; readonly position: Position | null; readonly code: string; readonly message: string; readonly referrer: string; - readonly name: string; readonly specifier: string; readonly importKind: | "entry_point" @@ -2882,23 +2882,27 @@ interface ResolveError { | "at_conditional" | "url" | "internal"; + readonly level: "error" | "warning" | "info" | "debug" | "verbose"; toString(): string; } -declare var ResolveError: { - readonly protoype: ResolveError; -}; - -interface BuildError { +declare class BuildMessage { + readonly name: "BuildMessage"; readonly position: Position | null; readonly message: string; - readonly name: string; + readonly level: "error" | "warning" | "info" | "debug" | "verbose"; } -declare var BuildError: { - readonly protoype: BuildError; -}; +/** + * @deprecated Renamed to `BuildMessage` + */ +declare var BuildError: typeof BuildMessage; + +/** + * @deprecated Renamed to `ResolveMessage` + */ +declare var ResolveError: typeof ResolveMessage; // Declare "static" methods in Error interface ErrorConstructor { diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 80f776fca..16a540212 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -179,24 +179,36 @@ pub const JSBundler = struct { if (naming.isString()) { if (try config.getOptional(globalThis, "naming", ZigString.Slice)) |slice| { defer slice.deinit(); + if (!strings.hasPrefixComptime(slice.slice(), "./")) { + this.names.owned_entry_point.appendSliceExact("./") catch unreachable; + } this.names.owned_entry_point.appendSliceExact(slice.slice()) catch unreachable; this.names.entry_point.data = this.names.owned_entry_point.list.items; } } else if (naming.isObject()) { if (try naming.getOptional(globalThis, "entry", ZigString.Slice)) |slice| { defer slice.deinit(); + if (!strings.hasPrefixComptime(slice.slice(), "./")) { + this.names.owned_entry_point.appendSliceExact("./") catch unreachable; + } this.names.owned_entry_point.appendSliceExact(slice.slice()) catch unreachable; this.names.entry_point.data = this.names.owned_entry_point.list.items; } if (try naming.getOptional(globalThis, "chunk", ZigString.Slice)) |slice| { defer slice.deinit(); + if (!strings.hasPrefixComptime(slice.slice(), "./")) { + this.names.owned_chunk.appendSliceExact("./") catch unreachable; + } this.names.owned_chunk.appendSliceExact(slice.slice()) catch unreachable; this.names.chunk.data = this.names.owned_chunk.list.items; } if (try naming.getOptional(globalThis, "asset", ZigString.Slice)) |slice| { defer slice.deinit(); + if (!strings.hasPrefixComptime(slice.slice(), "./")) { + this.names.owned_asset.appendSliceExact("./") catch unreachable; + } this.names.owned_asset.appendSliceExact(slice.slice()) catch unreachable; this.names.asset.data = this.names.owned_asset.list.items; } diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 2ac6948d1..a6d23dbd0 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -200,7 +200,7 @@ pub const TransformTask = struct { const error_value: JSValue = brk: { if (this.err) |err| { if (!this.log.hasAny()) { - break :brk JSC.JSValue.fromRef(JSC.BuildError.create( + break :brk JSC.JSValue.fromRef(JSC.BuildMessage.create( this.global, bun.default_allocator, logger.Msg{ diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 9ebc7f594..aab880453 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -11,8 +11,8 @@ const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; const C = bun.C; const JavaScript = @import("./javascript.zig"); -const ResolveError = JavaScript.ResolveError; -const BuildError = JavaScript.BuildError; +const ResolveMessage = JavaScript.ResolveMessage; +const BuildMessage = JavaScript.BuildMessage; const JSC = @import("root").bun.JSC; const WebCore = @import("./webcore.zig"); const Test = @import("./test/jest.zig"); @@ -2200,7 +2200,7 @@ const MD5_SHA1 = JSC.API.Bun.Crypto.MD5_SHA1; const FFI = JSC.FFI; pub const JSPrivateDataPtr = TaggedPointerUnion(.{ AttributeIterator, - BuildError, + BuildMessage, Comment, DebugServer, DebugSSLServer, @@ -2215,7 +2215,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{ LazyPropertiesObject, ModuleNamespace, - ResolveError, + ResolveMessage, Router, Server, diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 66b63f6ca..6ed97f4b9 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -2163,14 +2163,14 @@ pub const ZigConsoleClient = struct { if (CAPI.JSObjectGetPrivate(value.asRef())) |private_data_ptr| { const priv_data = JSPrivateDataPtr.from(private_data_ptr); switch (priv_data.tag()) { - .BuildError => { - const build_error = priv_data.as(JS.BuildError); - build_error.msg.writeFormat(writer_, enable_ansi_colors) catch {}; + .BuildMessage => { + const build_log = priv_data.as(JS.BuildMessage); + build_log.msg.writeFormat(writer_, enable_ansi_colors) catch {}; return; }, - .ResolveError => { - const resolve_error = priv_data.as(JS.ResolveError); - resolve_error.msg.writeFormat(writer_, enable_ansi_colors) catch {}; + .ResolveMessage => { + const resolve_log = priv_data.as(JS.ResolveMessage); + resolve_log.msg.writeFormat(writer_, enable_ansi_colors) catch {}; return; }, else => {}, @@ -3108,7 +3108,7 @@ pub const ZigConsoleClient = struct { // const resolve = ModuleLoader.resolve(global, input, module) catch |err| { // return ErrorableJSValue.errFmt( // err, -// "ResolveError: {s} while resolving \"{s}\"\nfrom \"{s}\"", +// "ResolveMessage: {s} while resolving \"{s}\"\nfrom \"{s}\"", // .{ // @errorName(err), // input, diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 67f016ad2..8bf13314b 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -95,8 +95,8 @@ pub const GlobalClasses = [_]type{ Bun.Class, WebCore.Crypto.Class, EventListenerMixin.addEventListener(VirtualMachine), - BuildError.Class, - ResolveError.Class, + BuildMessage.Class, + ResolveMessage.Class, // Fetch.Class, js_ast.Macro.JSNode.BunJSXCallbackFunction, @@ -1219,7 +1219,7 @@ pub const VirtualMachine = struct { } } - const printed = ResolveError.fmt( + const printed = ResolveMessage.fmt( jsc_vm.allocator, specifier.slice(), source.slice(), @@ -1239,7 +1239,7 @@ pub const VirtualMachine = struct { }; { - res.* = ErrorableZigString.err(err, @ptrCast(*anyopaque, ResolveError.create(global, VirtualMachine.get().allocator, msg, source.slice()))); + res.* = ErrorableZigString.err(err, @ptrCast(*anyopaque, ResolveMessage.create(global, VirtualMachine.get().allocator, msg, source.slice()))); } return; @@ -1349,7 +1349,7 @@ pub const VirtualMachine = struct { }; }; { - ret.* = ErrorableResolvedSource.err(err, @ptrCast(*anyopaque, BuildError.create(globalThis, globalThis.allocator(), msg))); + ret.* = ErrorableResolvedSource.err(err, @ptrCast(*anyopaque, BuildMessage.create(globalThis, globalThis.allocator(), msg))); } return; }, @@ -1357,8 +1357,8 @@ pub const VirtualMachine = struct { 1 => { const msg = log.msgs.items[0]; ret.* = ErrorableResolvedSource.err(err, switch (msg.metadata) { - .build => BuildError.create(globalThis, globalThis.allocator(), msg).?, - .resolve => ResolveError.create( + .build => BuildMessage.create(globalThis, globalThis.allocator(), msg).?, + .resolve => ResolveMessage.create( globalThis, globalThis.allocator(), msg, @@ -1374,8 +1374,8 @@ pub const VirtualMachine = struct { for (log.msgs.items, 0..) |msg, i| { errors[i] = switch (msg.metadata) { - .build => BuildError.create(globalThis, globalThis.allocator(), msg).?, - .resolve => ResolveError.create( + .build => BuildMessage.create(globalThis, globalThis.allocator(), msg).?, + .resolve => ResolveMessage.create( globalThis, globalThis.allocator(), msg, @@ -1638,8 +1638,8 @@ pub const VirtualMachine = struct { // When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException. // This is for: - // - BuildError - // - ResolveError + // - BuildMessage + // - ResolveMessage // If there were multiple errors, it could be contained in an AggregateError. // In that case, this function becomes recursive. // In all other cases, we will convert it to a ZigException. @@ -1737,9 +1737,9 @@ pub const VirtualMachine = struct { const private_data_ptr = JSPrivateDataPtr.from(value); switch (private_data_ptr.tag()) { - .BuildError => { + .BuildMessage => { defer Output.flush(); - var build_error = private_data_ptr.as(BuildError); + var build_error = private_data_ptr.as(BuildMessage); if (!build_error.logged) { build_error.msg.writeFormat(writer, allow_ansi_color) catch {}; writer.writeAll("\n") catch {}; @@ -1753,9 +1753,9 @@ pub const VirtualMachine = struct { } return true; }, - .ResolveError => { + .ResolveMessage => { defer Output.flush(); - var resolve_error = private_data_ptr.as(ResolveError); + var resolve_error = private_data_ptr.as(ResolveMessage); if (!resolve_error.logged) { resolve_error.msg.writeFormat(writer, allow_ansi_color) catch {}; resolve_error.logged = true; @@ -2350,7 +2350,7 @@ pub const EventListenerMixin = struct { } }; -pub const ResolveError = struct { +pub const ResolveMessage = struct { msg: logger.Msg, allocator: std.mem.Allocator, referrer: ?Fs.Path = null, @@ -2375,8 +2375,8 @@ pub const ResolveError = struct { } } - pub fn toStringFn(this: *ResolveError, ctx: js.JSContextRef) js.JSValueRef { - var text = std.fmt.allocPrint(default_allocator, "ResolveError: {s}", .{this.msg.data.text}) catch return null; + pub fn toStringFn(this: *ResolveMessage, ctx: js.JSContextRef) js.JSValueRef { + var text = std.fmt.allocPrint(default_allocator, "ResolveMessage: {s}", .{this.msg.data.text}) catch return null; var str = ZigString.init(text); str.setOutputEncoding(); if (str.isUTF8()) { @@ -2390,7 +2390,7 @@ pub const ResolveError = struct { pub fn toString( // this - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, // function _: js.JSObjectRef, @@ -2406,8 +2406,8 @@ pub const ResolveError = struct { switch (kind) { js.JSType.kJSTypeString => { if (js.JSObjectGetPrivate(obj)) |priv| { - if (JSPrivateDataPtr.from(priv).is(ResolveError)) { - var this = JSPrivateDataPtr.from(priv).as(ResolveError); + if (JSPrivateDataPtr.from(priv).is(ResolveMessage)) { + var this = JSPrivateDataPtr.from(priv).as(ResolveMessage); return this.toStringFn(ctx); } } @@ -2419,9 +2419,9 @@ pub const ResolveError = struct { } pub const Class = NewClass( - ResolveError, + ResolveMessage, .{ - .name = "ResolveError", + .name = "ResolveMessage", .read_only = true, }, .{ @@ -2457,6 +2457,10 @@ pub const ResolveError = struct { .get = getPosition, .ro = true, }, + .level = .{ + .get = getLevel, + .ro = true, + }, }, ); @@ -2466,8 +2470,8 @@ pub const ResolveError = struct { msg: logger.Msg, referrer: string, ) js.JSObjectRef { - var resolve_error = allocator.create(ResolveError) catch unreachable; - resolve_error.* = ResolveError{ + var resolve_error = allocator.create(ResolveMessage) catch unreachable; + resolve_error.* = ResolveMessage{ .msg = msg.clone(allocator) catch unreachable, .allocator = allocator, .referrer = Fs.Path.init(referrer), @@ -2478,27 +2482,27 @@ pub const ResolveError = struct { } pub fn getCode( - _: *ResolveError, + _: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return ZigString.static(comptime @as(string, @tagName(JSC.Node.ErrorCode.ERR_MODULE_NOT_FOUND))).toValue(ctx).asObjectRef(); + return ZigString.static(comptime @as(string, @tagName(JSC.Node.ErrorCode.ERR_MODULE_NOT_FOUND))).toValueGC(ctx).asObjectRef(); } pub fn getPosition( - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return BuildError.generatePositionObject(this.msg, ctx); + return BuildMessage.generatePositionObject(this.msg, ctx); } pub fn getMessage( - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, @@ -2508,7 +2512,7 @@ pub const ResolveError = struct { } pub fn getSpecifier( - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, @@ -2518,17 +2522,17 @@ pub const ResolveError = struct { } pub fn getImportKind( - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return ZigString.init(@tagName(this.msg.metadata.resolve.import_kind)).toValue(ctx.ptr()).asRef(); + return ZigString.init(this.msg.metadata.resolve.import_kind.label()).toValueGC(ctx.ptr()).asRef(); } pub fn getReferrer( - this: *ResolveError, + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, @@ -2542,31 +2546,41 @@ pub const ResolveError = struct { } pub fn getName( - _: *ResolveError, + _: *ResolveMessage, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.static("ResolveMessage").toValueGC(ctx.ptr()).asRef(); + } + + pub fn getLevel( + this: *ResolveMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return ZigString.static("ResolveError").toValue(ctx.ptr()).asRef(); + return ZigString.init(this.msg.kind.string()).toValueGC(ctx.ptr()).asRef(); } - pub fn finalize(this: *ResolveError) void { + pub fn finalize(this: *ResolveMessage) void { this.msg.deinit(bun.default_allocator); } }; -pub const BuildError = struct { +pub const BuildMessage = struct { msg: logger.Msg, // resolve_result: Resolver.Result, allocator: std.mem.Allocator, logged: bool = false, pub const Class = NewClass( - BuildError, - .{ .name = "BuildError", .read_only = true, .ts = .{ + BuildMessage, + .{ .name = "BuildMessage", .read_only = true, .ts = .{ .class = .{ - .name = "BuildError", + .name = "BuildMessage", }, } }, .{ @@ -2587,11 +2601,15 @@ pub const BuildError = struct { .get = getPosition, .ro = true, }, + .level = .{ + .get = getLevel, + .ro = true, + }, }, ); - pub fn toStringFn(this: *BuildError, ctx: js.JSContextRef) js.JSValueRef { - var text = std.fmt.allocPrint(default_allocator, "BuildError: {s}", .{this.msg.data.text}) catch return null; + pub fn toStringFn(this: *BuildMessage, ctx: js.JSContextRef) js.JSValueRef { + var text = std.fmt.allocPrint(default_allocator, "BuildMessage: {s}", .{this.msg.data.text}) catch return null; var str = ZigString.init(text); str.setOutputEncoding(); if (str.isUTF8()) { @@ -2605,7 +2623,7 @@ pub const BuildError = struct { pub fn toString( // this - this: *BuildError, + this: *BuildMessage, ctx: js.JSContextRef, // function _: js.JSObjectRef, @@ -2621,8 +2639,8 @@ pub const BuildError = struct { switch (kind) { js.JSType.kJSTypeString => { if (js.JSObjectGetPrivate(obj)) |priv| { - if (JSPrivateDataPtr.from(priv).is(BuildError)) { - var this = JSPrivateDataPtr.from(priv).as(BuildError); + if (JSPrivateDataPtr.from(priv).is(BuildMessage)) { + var this = JSPrivateDataPtr.from(priv).as(BuildMessage); return this.toStringFn(ctx); } } @@ -2639,8 +2657,8 @@ pub const BuildError = struct { msg: logger.Msg, // resolve_result: *const Resolver.Result, ) js.JSObjectRef { - var build_error = allocator.create(BuildError) catch unreachable; - build_error.* = BuildError{ + var build_error = allocator.create(BuildMessage) catch unreachable; + build_error.* = BuildMessage{ .msg = msg.clone(allocator) catch unreachable, // .resolve_result = resolve_result.*, .allocator = allocator, @@ -2652,7 +2670,7 @@ pub const BuildError = struct { } pub fn getPosition( - this: *BuildError, + this: *BuildMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, @@ -2707,24 +2725,33 @@ pub const BuildError = struct { } pub fn getMessage( - this: *BuildError, + this: *BuildMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return ZigString.init(this.msg.data.text).toValue(ctx.ptr()).asRef(); + return ZigString.init(this.msg.data.text).toValueGC(ctx.ptr()).asRef(); } - const BuildErrorName = "BuildError"; pub fn getName( - _: *BuildError, + _: *BuildMessage, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return ZigString.static("BuildMessage").toValueGC(ctx.ptr()).asRef(); + } + + pub fn getLevel( + this: *BuildMessage, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return ZigString.init(BuildErrorName).toValue(ctx.ptr()).asRef(); + return ZigString.init(this.msg.kind.string()).toValueGC(ctx.ptr()).asRef(); } }; diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index acd591089..2f48570c4 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -1265,14 +1265,14 @@ pub const JestPrettyFormat = struct { if (CAPI.JSObjectGetPrivate(value.asRef())) |private_data_ptr| { const priv_data = JSPrivateDataPtr.from(private_data_ptr); switch (priv_data.tag()) { - .BuildError => { - const build_error = priv_data.as(JS.BuildError); - build_error.msg.writeFormat(writer_, enable_ansi_colors) catch {}; + .BuildMessage => { + const build_log = priv_data.as(JS.BuildMessage); + build_log.msg.writeFormat(writer_, enable_ansi_colors) catch {}; return; }, - .ResolveError => { - const resolve_error = priv_data.as(JS.ResolveError); - resolve_error.msg.writeFormat(writer_, enable_ansi_colors) catch {}; + .ResolveMessage => { + const resolve_log = priv_data.as(JS.ResolveMessage); + resolve_log.msg.writeFormat(writer_, enable_ansi_colors) catch {}; return; }, else => {}, diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 883152c71..3a61408e0 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1201,32 +1201,28 @@ pub const BundleV2 = struct { switch (this.result) { .pending => unreachable, .err => { + root_obj.put(globalThis, JSC.ZigString.static("outputs"), JSC.JSValue.createEmptyArray(globalThis, 0)); root_obj.put( globalThis, - JSC.ZigString.static("outputs"), - JSC.JSMap.create( - globalThis, - ), + JSC.ZigString.static("success"), + JSC.JSValue.jsBoolean(false), ); - root_obj.put( globalThis, JSC.ZigString.static("logs"), - this.log.toJS(globalThis, bun.default_allocator, "Errors while building"), + this.log.toJSArray(globalThis, bun.default_allocator), ); }, .value => |*build| { var output_files: []options.OutputFile = build.output_files.items; - const output_files_js = JSC.JSMap.create(globalThis); + const output_files_js = JSC.JSValue.createEmptyArray(globalThis, output_files.len); if (output_files_js == .zero) { @panic("Unexpected pending JavaScript exception in JSBundleCompletionTask.onComplete. This is a bug in Bun."); } - var outputs = JSC.JSMap.fromJS(output_files_js) orelse @panic("Unexpected pending JavaScript exception in JSBundleCompletionTask.onComplete. This is a bug in Bun."); - defer build.output_files.deinit(); var to_assign_on_sourcemap: JSC.JSValue = .zero; - for (output_files) |*output_file| { + for (output_files, 0..) |*output_file, i| { defer bun.default_allocator.free(output_file.input.text); defer bun.default_allocator.free(output_file.path); const result = output_file.toJS( @@ -1268,23 +1264,19 @@ pub const BundleV2 = struct { to_assign_on_sourcemap = result; } - outputs.set( - globalThis, - JSC.ZigString.fromUTF8(output_file.input.text).toValueGC(globalThis), - result, - ); + output_files_js.putIndex(globalThis, @intCast(u32, i), result); } + root_obj.put(globalThis, JSC.ZigString.static("outputs"), output_files_js); root_obj.put( globalThis, - JSC.ZigString.static("outputs"), - output_files_js, + JSC.ZigString.static("success"), + JSC.JSValue.jsBoolean(true), ); - root_obj.put( globalThis, JSC.ZigString.static("logs"), - this.log.toJS(globalThis, bun.default_allocator, "Errors while building"), + this.log.toJSArray(globalThis, bun.default_allocator), ); }, } diff --git a/src/cli.zig b/src/cli.zig index 171593246..ff4c847c6 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -204,7 +204,7 @@ pub const Arguments = struct { clap.parseParam("--chunk-naming <STR> Customize chunk filenames. Defaults to \"[name]-[hash].[ext]\"") catch unreachable, clap.parseParam("--asset-naming <STR> Customize asset filenames. Defaults to \"[name]-[hash].[ext]\"") catch unreachable, clap.parseParam("--server-components Enable React Server Components (experimental)") catch unreachable, - clap.parseParam("--transform Single file transform, do not bundle") catch unreachable, + clap.parseParam("--transpile Transpile file only, do not bundle") catch unreachable, }; // TODO: update test completions @@ -477,7 +477,7 @@ pub const Arguments = struct { ctx.bundler_options.minify_identifiers = minify_flag or args.flag("--minify-identifiers"); if (cmd == .BuildCommand) { - ctx.bundler_options.transform_only = args.flag("--transform"); + ctx.bundler_options.transform_only = args.flag("--transpile"); if (args.option("--outdir")) |outdir| { if (outdir.len > 0) { diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index f0cf14475..e6c9b6d55 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -180,7 +180,7 @@ pub const BuildCommand = struct { } if (ctx.bundler_options.outfile.len == 0 and output_files.len == 1 and ctx.bundler_options.outdir.len == 0) { - // if --transform is passed, it won't have an output dir + // if --transpile is passed, it won't have an output dir if (output_files[0].value == .buffer) try writer.writeAll(output_files[0].value.buffer.bytes); break :dump; diff --git a/src/js_ast.zig b/src/js_ast.zig index 275d1509c..1f5d771a6 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -9699,7 +9699,7 @@ pub const Macro = struct { node.updateSymbolsMap(Visitor, this.visitor); return _entry.value_ptr.*; }, - .ResolveError, .BuildError => { + .ResolveMessage, .BuildMessage => { this.macro.vm.runErrorHandler(value, null); return error.MacroFailed; }, diff --git a/src/linker.zig b/src/linker.zig index 8ca36dada..6e99dbb5b 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -41,7 +41,7 @@ const Runtime = @import("./runtime.zig").Runtime; const URL = @import("url.zig").URL; const JSC = @import("root").bun.JSC; const PluginRunner = bun.bundler.PluginRunner; -pub const CSSResolveError = error{ResolveError}; +pub const CSSResolveError = error{ResolveMessage}; pub const OnImportCallback = *const fn (resolve_result: *const Resolver.Result, import_record: *ImportRecord, origin: URL) void; @@ -795,7 +795,7 @@ pub const Linker = struct { else => {}, } - if (had_resolve_errors) return error.ResolveError; + if (had_resolve_errors) return error.ResolveMessage; result.ast.externals = try externals.toOwnedSlice(); // if (result.ast.needs_runtime and (result.ast.runtime_import_record_id == null or import_records.items.len == 0)) { diff --git a/src/logger.zig b/src/logger.zig index 1481eb9b7..42c033c25 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -432,6 +432,13 @@ pub const Msg = struct { }; } + pub fn toJS(this: Msg, globalObject: *bun.JSC.JSGlobalObject, allocator: std.mem.Allocator) JSC.JSValue { + return switch (this.metadata) { + .build => JSC.BuildMessage.create(globalObject, allocator, this).?.value(), + .resolve => JSC.ResolveMessage.create(globalObject, allocator, this, "").?.value(), + }; + } + pub fn count(this: *const Msg, builder: *StringBuilder) void { this.data.count(builder); if (this.notes) |notes| { @@ -709,16 +716,16 @@ pub const Log = struct { 0 => return JSC.JSValue.jsUndefined(), 1 => { const msg = msgs[0]; - return JSC.JSValue.fromRef(JSC.BuildError.create(global, allocator, msg)); + return JSC.JSValue.fromRef(JSC.BuildMessage.create(global, allocator, msg)); }, else => { for (msgs[0..count], 0..) |msg, i| { switch (msg.metadata) { .build => { - errors_stack[i] = JSC.BuildError.create(global, allocator, msg).?; + errors_stack[i] = JSC.BuildMessage.create(global, allocator, msg).?; }, .resolve => { - errors_stack[i] = JSC.ResolveError.create(global, allocator, msg, "").?; + errors_stack[i] = JSC.ResolveMessage.create(global, allocator, msg, "").?; }, } } @@ -729,6 +736,20 @@ pub const Log = struct { } } + pub fn toJSArray(this: Log, global: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSC.JSValue { + const msgs: []const Msg = this.msgs.items; + var errors_stack: [256]*anyopaque = undefined; + + const count = @intCast(u16, @min(msgs.len, errors_stack.len)); + var arr = JSC.JSValue.createEmptyArray(global, count); + + for (msgs[0..count], 0..) |msg, i| { + arr.putIndex(global, @intCast(u32, i), msg.toJS(global, allocator)); + } + + return arr; + } + pub fn cloneTo(self: *Log, other: *Log) !void { var notes_count: usize = 0; diff --git a/src/runtime/errors.ts b/src/runtime/errors.ts index e2973db91..c5968e422 100644 --- a/src/runtime/errors.ts +++ b/src/runtime/errors.ts @@ -1,6 +1,6 @@ // @ts-nocheck -var __BuildError; -var __ResolveError; +var __BuildLog; +var __ResolveLog; var __ImportKind; { enum ImportKind { @@ -37,13 +37,13 @@ var __ImportKind; importKind: ImportKind; } - class BuildError extends Error { + class BuildMessage extends Error { constructor(data: BuildErrorImplementation) { super(data.message); this.name = data.name; this.data = data; } - data: BuildErrorImplementation; + data: BuildLogImplementation; get position() { return this.data.position; @@ -54,7 +54,7 @@ var __ImportKind; } } - class ResolveError extends BuildError { + class ResolveMessage extends BuildMessage { constructor(data: ResolveErrorImplementation) { super(data); this.name = data.name; @@ -71,9 +71,9 @@ var __ImportKind; } } - __ResolveError = ResolveError; - __BuildError = BuildError; + __ResolveLog = ResolveMessage; + __BuildLog = BuildMessage; __ImportKind = ImportKind; } -export { __ResolveError as ResolveError, __BuildError as BuildError, __ImportKind as ImportKind }; +export { __ResolveLog as ResolveMessage, __BuildLog as BuildMessage, __ImportKind as ImportKind }; diff --git a/test/bundler/__snapshots__/build.test.ts.snap b/test/bundler/__snapshots__/build.test.ts.snap new file mode 100644 index 000000000..e94e83fc2 --- /dev/null +++ b/test/bundler/__snapshots__/build.test.ts.snap @@ -0,0 +1,85 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`Bun.build outdir + reading out blobs works 1`] = ` +"var __create = Object.create; +var __descs = Object.getOwnPropertyDescriptors; +var __defProp = Object.defineProperty; +var __getProtoOf = Object.getPrototypeOf; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; +var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); + +// fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => { + { + return fn; + } + } +}); +function fn(a) { + return a + 42; +} +var init_fn = __esm(() => { +}); + +// fixtures/trivial/index.js +var NS = Promise.resolve().then(() => (init_fn(), exports_fn)); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; + +exports[`Bun.build new Response(BuildArtifact): response text 1`] = ` +"var __create = Object.create; +var __descs = Object.getOwnPropertyDescriptors; +var __defProp = Object.defineProperty; +var __getProtoOf = Object.getPrototypeOf; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; +var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); + +// fixtures/trivial/fn.js +var exports_fn = {}; +__export(exports_fn, { + fn: () => { + { + return fn; + } + } +}); +function fn(a) { + return a + 42; +} +var init_fn = __esm(() => { +}); + +// fixtures/trivial/index.js +var NS = Promise.resolve().then(() => (init_fn(), exports_fn)); +NS.then(({ fn: fn2 }) => { + console.log(fn2(42)); +}); +" +`; diff --git a/test/bundler/acorn.patch b/test/bundler/acorn.patch new file mode 100644 index 000000000..a13af5a28 --- /dev/null +++ b/test/bundler/acorn.patch @@ -0,0 +1,94 @@ +diff --git a/acorn-loose/rollup.config.mjs b/acorn-loose/rollup.config.mjs +index 83eb7af..9b51c12 100644 +--- a/acorn-loose/rollup.config.mjs ++++ b/acorn-loose/rollup.config.mjs +@@ -1,5 +1,3 @@ +-import buble from "@rollup/plugin-buble" +- + export default { + external: ["acorn"], + input: "acorn-loose/src/index.js", +@@ -17,6 +15,5 @@ export default { + } + ], + plugins: [ +- buble({transforms: {dangerousForOf: true}}) + ] + } +diff --git a/acorn-walk/rollup.config.mjs b/acorn-walk/rollup.config.mjs +index d78ec05..11d5904 100644 +--- a/acorn-walk/rollup.config.mjs ++++ b/acorn-walk/rollup.config.mjs +@@ -1,5 +1,3 @@ +-import buble from "@rollup/plugin-buble" +- + export default { + input: "acorn-walk/src/index.js", + output: [ +@@ -14,6 +12,5 @@ export default { + } + ], + plugins: [ +- buble({transforms: {dangerousForOf: true}}) + ] + } +diff --git a/package.json b/package.json +index 2171590..75ae6b4 100644 +--- a/package.json ++++ b/package.json +@@ -27,9 +27,8 @@ + "build:walk": "rollup -c acorn-walk/rollup.config.mjs", + "generate": "node bin/generate-identifier-regex.js", + "lint": "eslint .", +- "prepare": "npm run test", + "pretest": "npm run build:main && npm run build:loose", +- "test": "node test/run.js && npm run lint", ++ "test": "node test/run.js", + "test:test262": "node bin/run_test262.js" + }, + "devDependencies": { +diff --git a/test/run.js b/test/run.js +index 84f1b18..039040f 100644 +--- a/test/run.js ++++ b/test/run.js +@@ -1,5 +1,8 @@ +-(function() { +- var driver = require("./driver.js") ++(async function () { ++ var acorn = await import("../acorn/dist/acorn.mjs"); ++ var acorn_loose = await import("../acorn-loose/dist/acorn-loose.mjs"); ++ globalThis.acorn = acorn; ++ var driver = require("./driver.js"); + require("./tests.js"); + require("./tests-harmony.js"); + require("./tests-es7.js"); +@@ -26,8 +29,6 @@ + require("./tests-numeric-separators.js"); + require("./tests-class-features-2022.js"); + require("./tests-module-string-names.js"); +- var acorn = require("../acorn") +- var acorn_loose = require("../acorn-loose") + + var htmlLog = typeof document === "object" && document.getElementById('log'); + var htmlGroup = htmlLog; +diff --git a/test/tests-trailing-commas-in-func.js b/test/tests-trailing-commas-in-func.js +index 049e575..80390f2 100644 +--- a/test/tests-trailing-commas-in-func.js ++++ b/test/tests-trailing-commas-in-func.js +@@ -799,4 +799,3 @@ testFail("export function foo(,) { }", "Unexpected token (1:20)", {ecmaVersion: + + testFail("(a,)", "Unexpected token (1:3)", {ecmaVersion: 7}) + testFail("(a,)", "Unexpected token (1:3)", {ecmaVersion: 8}) +- +diff --git a/test/tests.js b/test/tests.js +index 0272265..8ea387d 100644 +--- a/test/tests.js ++++ b/test/tests.js +@@ -4,7 +4,6 @@ + if (typeof exports !== "undefined") { + var driver = require("./driver.js"); + var test = driver.test, testFail = driver.testFail, testAssert = driver.testAssert; +- var acorn = require("../acorn"); + } + + test("import ''", { diff --git a/test/bundler/acorn.sh b/test/bundler/acorn.sh new file mode 100755 index 000000000..17a5b83de --- /dev/null +++ b/test/bundler/acorn.sh @@ -0,0 +1,47 @@ +set -e +cd $(dirname $(realpath $0)) + +if [ -z "$BUN_EXE" ]; then + BUN_EXE=$(which bun-debug 2>/dev/null || which bun 2>/dev/null) +fi +export BUN_EXE + +commit=96c721dbf89d0ccc3a8c7f39e69ef2a6a3c04dfa + +if [ ! -d "github/acorn" ]; then + mkdir -p github/acorn + cd github/acorn + git init + git remote add origin https://github.com/acornjs/acorn.git + git fetch --depth 1 origin $commit + git -c advice.detachedHead=false checkout FETCH_HEAD +else + cd github/acorn + git reset --hard $commit +fi + +patch -p1 < ../../acorn.patch + +rm -rf node_modules +BUN_DEBUG_QUIET_LOGS=1 bun i + +# test 1: bundle and minify +bun build acorn/src/index.js --target=node --minify > acorn/dist/acorn.mjs +bun build acorn-loose/src/index.js --target=node --minify > acorn-loose/dist/acorn-loose.mjs +node test/run.js + +# test 2: minify every source file +minify_in_place() { + for file in $(find $1 -name '*.js'); do + echo "minifying $file" + bun build $file --target=node --minify --external '*' > $file.min.js + bun build $file.min.js > /dev/null + mv $file.min.js $file + done +} +minify_in_place acorn/src +minify_in_place acorn-loose/src +# oddity, the minified stuff here wont be handled by rollup for syntax error, but theres no syntax errors in the files +bun build acorn/src/index.js --target=node --minify > acorn/dist/acorn.mjs +bun build acorn-loose/src/index.js --target=node --minify > acorn-loose/dist/acorn-loose.mjs +node test/run.js diff --git a/test/bundler/build.test.ts b/test/bundler/build.test.ts new file mode 100644 index 000000000..3478d07cd --- /dev/null +++ b/test/bundler/build.test.ts @@ -0,0 +1,240 @@ +import { test, expect, describe } from "bun:test"; +import { readFileSync } from "fs"; +import { bunEnv, bunExe } from "harness"; +import { join } from "path"; + +describe("Bun.build", () => { + test("invalid options throws", async () => { + expect(() => Bun.build({} as any)).toThrow(); + expect(() => + Bun.build({ + entrypoints: [], + } as any), + ).toThrow(); + expect(() => + Bun.build({ + entrypoints: ["hello"], + format: "invalid", + } as any), + ).toThrow(); + expect(() => + Bun.build({ + entrypoints: ["hello"], + target: "invalid", + } as any), + ).toThrow(); + expect(() => + Bun.build({ + entrypoints: ["hello"], + sourcemap: "invalid", + } as any), + ).toThrow(); + }); + + test("returns errors properly", async () => { + Bun.gc(true); + const build = await Bun.build({ + entrypoints: [join(import.meta.dir, "does-not-exist.ts")], + }); + expect(build.outputs).toHaveLength(0); + expect(build.logs).toHaveLength(1); + expect(build.logs[0]).toBeInstanceOf(BuildMessage); + expect(build.logs[0].message).toMatch(/ModuleNotFound/); + expect(build.logs[0].name).toBe("BuildMessage"); + expect(build.logs[0].position).toEqual(null); + expect(build.logs[0].level).toEqual("error"); + Bun.gc(true); + }); + + test("returns output files", async () => { + Bun.gc(true); + const build = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + }); + expect(build.outputs).toHaveLength(1); + expect(build.logs).toHaveLength(0); + Bun.gc(true); + }); + + test("rebuilding busts the directory entries cache", () => { + Bun.gc(true); + const { exitCode, stderr } = Bun.spawnSync({ + cmd: [bunExe(), join(import.meta.dir, "bundler-reloader-script.ts")], + env: bunEnv, + stderr: "pipe", + stdout: "inherit", + }); + if (stderr.byteLength > 0) { + throw new Error(stderr.toString()); + } + expect(exitCode).toBe(0); + Bun.gc(true); + }); + + test("outdir + reading out blobs works", async () => { + Bun.gc(true); + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + outdir: "/tmp/bun-build-test-read-out", + }); + expect(await x.outputs.values().next().value?.text()).toMatchSnapshot(); + Bun.gc(true); + }); + + test("BuildArtifact properties", async () => { + Bun.gc(true); + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + }); + const [blob] = x.outputs; + expect(blob).toBeTruthy(); + expect(blob.type).toBe("text/javascript;charset=utf-8"); + expect(blob.size).toBeGreaterThan(1); + expect(blob.path).toBe("./index.js"); + expect(blob.hash).toBeTruthy(); + expect(blob.hash).toMatchSnapshot("hash"); + expect(blob.kind).toBe("entry-point"); + expect(blob.loader).toBe("jsx"); + expect(blob.sourcemap).toBe(null); + Bun.gc(true); + }); + + test("BuildArtifact properties + entry.naming", async () => { + Bun.gc(true); + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + naming: { + entry: "hello", + }, + }); + const [blob] = x.outputs; + expect(blob).toBeTruthy(); + expect(blob.type).toBe("text/javascript;charset=utf-8"); + expect(blob.size).toBeGreaterThan(1); + expect(blob.path).toBe("./hello"); + expect(blob.hash).toBeTruthy(); + expect(blob.hash).toMatchSnapshot("hash"); + expect(blob.kind).toBe("entry-point"); + expect(blob.loader).toBe("jsx"); + expect(blob.sourcemap).toBe(null); + Bun.gc(true); + }); + + test("BuildArtifact properties sourcemap", async () => { + Bun.gc(true); + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + sourcemap: "external", + }); + const [blob, map] = x.outputs; + expect(blob.type).toBe("text/javascript;charset=utf-8"); + expect(blob.size).toBeGreaterThan(1); + expect(blob.path).toBe("./index.js"); + expect(blob.hash).toBeTruthy(); + expect(blob.hash).toMatchSnapshot("hash index.js"); + expect(blob.kind).toBe("entry-point"); + expect(blob.loader).toBe("jsx"); + expect(blob.sourcemap).toBe(map); + + expect(map.type).toBe("application/json;charset=utf-8"); + expect(map.size).toBeGreaterThan(1); + expect(map.path).toBe("./index.js.map"); + expect(map.hash).toBeTruthy(); + expect(map.hash).toMatchSnapshot("hash index.js.map"); + expect(map.kind).toBe("sourcemap"); + expect(map.loader).toBe("file"); + expect(map.sourcemap).toBe(null); + Bun.gc(true); + }); + + // test("BuildArtifact properties splitting", async () => { + // Bun.gc(true); + // const x = await Bun.build({ + // entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + // splitting: true, + // }); + // expect(x.outputs).toHaveLength(2); + // const [indexBlob, chunkBlob] = x.outputs; + + // expect(indexBlob).toBeTruthy(); + // expect(indexBlob.type).toBe("text/javascript;charset=utf-8"); + // expect(indexBlob.size).toBeGreaterThan(1); + // expect(indexBlob.path).toBe("/index.js"); + // expect(indexBlob.hash).toBeTruthy(); + // expect(indexBlob.hash).toMatchSnapshot("hash index.js"); + // expect(indexBlob.kind).toBe("entry-point"); + // expect(indexBlob.loader).toBe("jsx"); + // expect(indexBlob.sourcemap).toBe(null); + + // expect(chunkBlob).toBeTruthy(); + // expect(chunkBlob.type).toBe("text/javascript;charset=utf-8"); + // expect(chunkBlob.size).toBeGreaterThan(1); + // expect(chunkBlob.path).toBe(`/foo-${chunkBlob.hash}.js`); + // expect(chunkBlob.hash).toBeTruthy(); + // expect(chunkBlob.hash).toMatchSnapshot("hash foo.js"); + // expect(chunkBlob.kind).toBe("chunk"); + // expect(chunkBlob.loader).toBe("jsx"); + // expect(chunkBlob.sourcemap).toBe(null); + // Bun.gc(true); + // }); + + test("Bun.write(BuildArtifact)", async () => { + Bun.gc(true); + console.log("Bun.write(BuildArtifact)"); + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + }); + console.log(x); + Bun.write("/tmp/bun-build-test-write.js", x.outputs[0]); + expect(readFileSync("/tmp/bun-build-test-write.js", "utf-8")).toMatchSnapshot(); + Bun.gc(true); + }); + + test("new Response(BuildArtifact)", async () => { + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/trivial/index.js")], + }); + const response = new Response(x.outputs.values().next().value!); + expect(await response.text()).toMatchSnapshot("response text"); + expect(response.headers.get("content-type")).toBe("text/javascript;charset=utf-8"); + expect(response.headers.get("content-length")).toBeGreaterThan(1); + expect(response.headers.get("content-length")).toMatchSnapshot("content-length"); + expect(response.headers.get("etag")).toBeTruthy(); + expect(response.headers.get("etag")).toMatchSnapshot("content-etag"); + }); + + test("BuildArtifact with assets", async () => { + const x = await Bun.build({ + entrypoints: [join(import.meta.dir, "./fixtures/with-assets/index.js")], + loader: { + ".blob": "file", + ".png": "file", + }, + }); + console.log(x); + const [blob, asset] = x.outputs; + expect(blob).toBeTruthy(); + expect(blob instanceof Blob).toBe(true); + expect(blob.type).toBe("text/javascript;charset=utf-8"); + expect(blob.size).toBeGreaterThan(1); + expect(blob.path).toBe("/index.js"); + expect(blob.hash).toBeTruthy(); + expect(blob.hash).toMatchSnapshot(); + expect(blob.kind).toBe("entry-point"); + expect(blob.loader).toBe("jsx"); + expect(blob.sourcemap).toBe(null); + throw new Error("test was not fully written"); + }); + + // test("errors are returned as an array", async () => { + // const x = await Bun.build({ + // entrypoints: [join(import.meta.dir, "does-not-exist.ts")], + // }); + // expect(x.errors).toHaveLength(1); + // expect(x.errors[0].message).toMatch(/ModuleNotFound/); + // expect(x.errors[0].name).toBe("BuildMessage"); + // expect(x.errors[0].position).toEqual(null); + // expect(x.warnings).toHaveLength(0); + // expect(x.logs).toHaveLength(0); + // }); +}); diff --git a/test/bundler/bundler-reloader-script.ts b/test/bundler/bundler-reloader-script.ts index 4a04ab448..28604206a 100644 --- a/test/bundler/bundler-reloader-script.ts +++ b/test/bundler/bundler-reloader-script.ts @@ -27,7 +27,7 @@ closeSync(maxfd); const { outputs: second } = await Bun.build({ entrypoints: [input], }); -const text = await second?.[0]?.result?.text(); +const text = await second.values().next().value?.text(); if (!text?.includes?.(" = 1")) { throw new Error("Expected text to include ' = 1', but received\n\n" + text); diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts index 30721637f..9bbee8e40 100644 --- a/test/bundler/bundler_edgecase.test.ts +++ b/test/bundler/bundler_edgecase.test.ts @@ -51,7 +51,7 @@ describe("bundler", () => { }); itBundled("edgecase/BunPluginTreeShakeImport", { notImplemented: true, - // This only appears at runtime and not with bun build, even with --transform + // This only appears at runtime and not with bun build, even with --transpile files: { "/entry.ts": /* js */ ` import { A, B } from "./somewhere-else"; @@ -636,4 +636,133 @@ describe("bundler", () => { `, }, }); + itBundled("edgecase/ExportDefaultUndefined", { + files: { + "/entry.ts": /* ts */ ` + export const a = 1; + `, + }, + target: "bun", + }); + itBundled("edgecase/RuntimeExternalRequire", { + files: { + "/entry.ts": /* ts */ ` + console.log(require("hello-1").type); + `, + }, + external: ["hello-1"], + target: "bun", + runtimeFiles: { + "/node_modules/hello-1/require.js": `export const type = "require";`, + "/node_modules/hello-1/package.json": /* json */ ` + { + "type": "module", + "exports": { + ".": { + "require": "./require.js", + } + } + } + `, + }, + run: { + stdout: ` + require + `, + }, + }); + itBundled("edgecase/RuntimeExternalImport", { + files: { + "/entry.ts": /* ts */ ` + import { type as a1 } from 'hello-1'; + import { type as a2 } from 'hello-2'; + import { type as a3 } from 'hello-3'; + console.log(a1, a2, a3); + + const b1 = require('hello-1').type; + const b2 = require('hello-2').type; + const b3 = require('hello-3').type; + console.log(b1, b2, b3); + `, + }, + external: ["hello-1", "hello-2", "hello-3"], + target: "bun", + runtimeFiles: { + "/node_modules/hello-1/node.js": `export const type = "node";`, + "/node_modules/hello-1/bun.js": `export const type = "bun";`, + "/node_modules/hello-1/package.json": /* json */ ` + { + "type": "module", + "exports": { + ".": { + "node": "./node.js", + "bun": "./bun.js" + } + } + } + `, + "/node_modules/hello-2/node.js": `export const type = "node";`, + "/node_modules/hello-2/bun.js": `export const type = "bun";`, + "/node_modules/hello-2/package.json": /* json */ ` + { + "type": "module", + "exports": { + ".": { + "bun": "./bun.js", + "node": "./node.js" + } + } + } + `, + "/node_modules/hello-3/import.js": `export const type = "import";`, + "/node_modules/hello-3/require.js": `exports.type = "require";`, + "/node_modules/hello-3/package.json": /* json */ ` + { + "type": "module", + "exports": { + ".": { + "require": "./require.js", + "import": "./import.js", + } + } + } + `, + }, + run: { + stdout: ` + bun bun import + bun bun import + `, + }, + }); + itBundled("edgecase/RuntimeExternalImport2", { + files: { + "/entry.ts": /* ts */ ` + import t from 'hello'; + console.log(t); + `, + }, + external: ["hello"], + target: "bun", + runtimeFiles: { + "/node_modules/hello/index.js": /* js */ ` + export const hello = "Hello World"; + `, + }, + run: { + stdout: "Hello World", + }, + }); + itBundled("edgecase/AssetPublicPath", { + files: { + "/entry.ts": /* ts */ ` + import hello from "./hello.file"; + console.log(hello); + `, + "/hello.file": "Hello World", + }, + outdir: "/out", + publicPath: "/www", + run: {}, + }); }); diff --git a/test/bundler/bundler_reloadable.test.ts b/test/bundler/bundler_reloadable.test.ts deleted file mode 100644 index e05e76666..000000000 --- a/test/bundler/bundler_reloadable.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { test, expect } from "bun:test"; -import { bunEnv, bunExe } from "harness"; -import { join } from "path"; - -test("rebuilding busts the directory entries cache", () => { - const { exitCode, stderr } = Bun.spawnSync({ - cmd: [bunExe(), join(import.meta.dir, "bundler-reloader-script.ts")], - env: bunEnv, - stderr: "pipe", - stdout: "inherit", - }); - if (stderr.byteLength > 0) { - throw new Error(stderr.toString()); - } - - expect(exitCode).toBe(0); -}); diff --git a/test/bundler/esbuild/importstar_ts.test.ts b/test/bundler/esbuild/importstar_ts.test.ts index 394c4260f..45ae1cbb3 100644 --- a/test/bundler/esbuild/importstar_ts.test.ts +++ b/test/bundler/esbuild/importstar_ts.test.ts @@ -1,15 +1,13 @@ -import { test, describe } from "bun:test"; -import { RUN_UNCHECKED_TESTS, itBundled } from "../expectBundled"; +import assert from "assert"; +import { itBundled, testForFile } from "../expectBundled"; +var { describe, test, expect } = testForFile(import.meta.path); // Tests ported from: // https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_ts_test.go // For debug, all files are written to $TEMP/bun-bundle-tests/ts - describe("bundler", () => { - return; - itBundled("ts/TSImportStarUnused", { - // GENERATED + itBundled("importstar_ts/Unused", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -18,20 +16,20 @@ describe("bundler", () => { `, "/foo.ts": `export const foo = 123`, }, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarCapture", { - // GENERATED + itBundled("importstar_ts/Capture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, "/foo.ts": `export const foo = 123`, }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarNoCapture", { - // GENERATED + itBundled("importstar_ts/NoCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -40,9 +38,9 @@ describe("bundler", () => { `, "/foo.ts": `export const foo = 123`, }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarExportImportStarUnused", { - // GENERATED + itBundled("importstar_ts/ExportImportStarUnused", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' @@ -55,9 +53,9 @@ describe("bundler", () => { export {ns} `, }, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarExportImportStarNoCapture", { - // GENERATED + itBundled("importstar_ts/ExportImportStarNoCapture", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' @@ -70,14 +68,14 @@ describe("bundler", () => { export {ns} `, }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarExportImportStarCapture", { - // GENERATED + itBundled("importstar_ts/ExportImportStarCapture", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, "/foo.ts": `export const foo = 123`, "/bar.ts": /* ts */ ` @@ -85,9 +83,9 @@ describe("bundler", () => { export {ns} `, }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarExportStarAsUnused", { - // GENERATED + itBundled("importstar_ts/ExportStarAsUnused", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' @@ -98,8 +96,7 @@ describe("bundler", () => { "/bar.ts": `export * as ns from './foo'`, }, }); - itBundled("ts/TSImportStarExportStarAsNoCapture", { - // GENERATED + itBundled("importstar_ts/ExportStarAsNoCapture", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' @@ -109,21 +106,21 @@ describe("bundler", () => { "/foo.ts": `export const foo = 123`, "/bar.ts": `export * as ns from './foo'`, }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarExportStarAsCapture", { - // GENERATED + itBundled("importstar_ts/ExportStarAsCapture", { files: { "/entry.ts": /* ts */ ` import {ns} from './bar' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, "/foo.ts": `export const foo = 123`, "/bar.ts": `export * as ns from './foo'`, }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarExportStarUnused", { - // GENERATED + itBundled("importstar_ts/ExportStarUnused", { files: { "/entry.ts": /* ts */ ` import * as ns from './bar' @@ -133,9 +130,9 @@ describe("bundler", () => { "/foo.ts": `export const foo = 123`, "/bar.ts": `export * from './foo'`, }, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarExportStarNoCapture", { - // GENERATED + itBundled("importstar_ts/ExportStarNoCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './bar' @@ -145,21 +142,21 @@ describe("bundler", () => { "/foo.ts": `export const foo = 123`, "/bar.ts": `export * from './foo'`, }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarExportStarCapture", { - // GENERATED + itBundled("importstar_ts/ExportStarCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './bar' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, "/foo.ts": `export const foo = 123`, "/bar.ts": `export * from './foo'`, }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarCommonJSUnused", { - // GENERATED + itBundled("importstar_ts/CommonJSUnused", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -168,20 +165,20 @@ describe("bundler", () => { `, "/foo.ts": `exports.foo = 123`, }, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarCommonJSCapture", { - // GENERATED + itBundled("importstar_ts/CommonJSCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, "/foo.ts": `exports.foo = 123`, }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarCommonJSNoCapture", { - // GENERATED + itBundled("importstar_ts/CommonJSNoCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -190,9 +187,9 @@ describe("bundler", () => { `, "/foo.ts": `exports.foo = 123`, }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarAndCommonJS", { - // GENERATED + itBundled("importstar_ts/TSAndCommonJS", { files: { "/entry.js": /* js */ ` import * as ns from './foo' @@ -201,9 +198,9 @@ describe("bundler", () => { `, "/foo.ts": `export const foo = 123`, }, + run: { stdout: "123 123" }, }); - itBundled("ts/TSImportStarNoBundleUnused", { - // GENERATED + itBundled("importstar_ts/NoBundleUnused", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -211,21 +208,28 @@ describe("bundler", () => { console.log(foo) `, }, + target: "bun", bundling: false, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarNoBundleCapture", { - // GENERATED + itBundled("importstar_ts/NoBundleCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, }, + target: "bun", bundling: false, + runtimeFiles: { + "/foo.js": ` + export const foo = 123 + `, + }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarNoBundleNoCapture", { - // GENERATED + itBundled("importstar_ts/NoBundleNoCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -233,10 +237,16 @@ describe("bundler", () => { console.log(ns.foo, ns.foo, foo) `, }, + target: "bun", bundling: false, + runtimeFiles: { + "/foo.js": ` + export const foo = 123 + `, + }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSImportStarMangleNoBundleUnused", { - // GENERATED + itBundled("importstar_ts/MangleNoBundleUnused", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -245,22 +255,33 @@ describe("bundler", () => { `, }, minifySyntax: true, + target: "bun", bundling: false, + runtimeFiles: { + "/foo.js": ` + export const foo = 123 + `, + }, + run: { stdout: "234" }, }); - itBundled("ts/TSImportStarMangleNoBundleCapture", { - // GENERATED + itBundled("importstar_ts/MangleNoBundleCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' let foo = 234 - console.log(ns, ns.foo, foo) + console.log(JSON.stringify(ns), ns.foo, foo) `, }, minifySyntax: true, bundling: false, + runtimeFiles: { + "/foo.js": ` + export const foo = 123 + `, + }, + run: { stdout: '{"foo":123} 123 234' }, }); - itBundled("ts/TSImportStarMangleNoBundleNoCapture", { - // GENERATED + itBundled("importstar_ts/MangleNoBundleNoCapture", { files: { "/entry.ts": /* ts */ ` import * as ns from './foo' @@ -270,9 +291,14 @@ describe("bundler", () => { }, minifySyntax: true, bundling: false, + runtimeFiles: { + "/foo.js": ` + export const foo = 123 + `, + }, + run: { stdout: "123 123 234" }, }); - itBundled("ts/TSReExportTypeOnlyFileES6", { - // GENERATED + itBundled("importstar_ts/ReExportTypeOnlyFileES6", { files: { "/entry.ts": /* ts */ ` import * as ns from './re-export' @@ -286,7 +312,7 @@ describe("bundler", () => { `, "/types1.ts": /* ts */ ` export interface Foo {} - export type Bar = number + export type Bar = number; console.log('some code') `, "/types2.ts": /* ts */ ` @@ -296,7 +322,7 @@ describe("bundler", () => { `, "/types3.ts": /* ts */ ` export {Foo} from "./type" - console.log('some code') + console.log('some code'); `, "/values.ts": `export let foo = 123`, "/type.ts": `export type Foo = number`, diff --git a/test/bundler/esbuild/loader.test.ts b/test/bundler/esbuild/loader.test.ts index 628fc4ca8..a2376a3ce 100644 --- a/test/bundler/esbuild/loader.test.ts +++ b/test/bundler/esbuild/loader.test.ts @@ -327,6 +327,7 @@ describe("bundler", () => { // }, // outbase: "/src", // }); + return; itBundled("loader/FileRelativePathAssetNamesJS", { // GENERATED files: { diff --git a/test/bundler/esbuild/splitting.test.ts b/test/bundler/esbuild/splitting.test.ts index fa38e80ea..39410d2cb 100644 --- a/test/bundler/esbuild/splitting.test.ts +++ b/test/bundler/esbuild/splitting.test.ts @@ -65,6 +65,16 @@ describe("bundler", () => { assertNotPresent: { "/out/entry.js": "123", }, + onAfterBundle(api) { + const files = readdirSync(api.outdir); + assert.strictEqual( + files.length, + 2, + "should have 2 files: entry.js and foo-[hash].js, found [" + files.join(", ") + "]", + ); + assert(files.includes("entry.js"), "has entry.js"); + assert(!files.includes("foo.js"), "does not have foo.js"); + }, run: { file: "/out/entry.js", stdout: "123", diff --git a/test/bundler/esbuild/ts.test.ts b/test/bundler/esbuild/ts.test.ts index cafba8f5f..531b1b288 100644 --- a/test/bundler/esbuild/ts.test.ts +++ b/test/bundler/esbuild/ts.test.ts @@ -1026,6 +1026,7 @@ describe("bundler", () => { } } } + console.log(Foo, Bar) `, }, bundling: false, @@ -1757,44 +1758,93 @@ describe("bundler", () => { expect(api.readFile("/out.js").trim()).toBe(""); }, }); - itBundled("ts/SiblingNamespace", { + itBundled("ts/SiblingNamespaceLet", { + notImplemented: true, files: { "/let.ts": /* ts */ ` export namespace x { export let y = 123 } export namespace x { export let z = y } `, + }, + entryPoints: ["/let.ts"], + bundling: false, + runtimeFiles: { + "/test.js": /* js */ ` + import assert from 'assert' + const m = (await import('./out.js')).x + assert(m.x === m.z, "it worked") + `, + }, + }); + itBundled("ts/SiblingNamespaceFunction", { + notImplemented: true, + files: { "/function.ts": /* ts */ ` export namespace x { export function y() {} } export namespace x { export let z = y } `, - "/class.ts": /* ts */ ` + }, + entryPoints: ["/function.ts"], + bundling: false, + runtimeFiles: { + "/test.js": /* js */ ` + import assert from 'assert' + const m = (await import('./out.js')).x + assert(m.x === m.z, "it worked worked") + `, + }, + }); + itBundled("ts/SiblingNamespaceClass", { + notImplemented: true, + files: { + "/let.ts": /* ts */ ` export namespace x { export class y {} } export namespace x { export let z = y } `, + }, + entryPoints: ["/function.ts"], + bundling: false, + runtimeFiles: { + "/test.js": /* js */ ` + import assert from 'assert' + const m = (await import('./out.js')).x + assert(m.x === m.z, "it worked worked") + `, + }, + }); + itBundled("ts/SiblingNamespaceNamespace", { + notImplemented: true, + files: { "/namespace.ts": /* ts */ ` export namespace x { export namespace y { 0 } } export namespace x { export let z = y } `, + }, + entryPoints: ["/namespace.ts"], + bundling: false, + runtimeFiles: { + "/test.js": /* js */ ` + import assert from 'assert' + const m = (await import('./out.js')).x + assert(m.x === m.z, "it worked worked") + `, + }, + }); + itBundled("ts/SiblingNamespaceEnum", { + notImplemented: true, + files: { "/enum.ts": /* ts */ ` export namespace x { export enum y {} } export namespace x { export let z = y } `, }, - entryPoints: ["/let.ts", "/function.ts", "/class.ts", "/namespace.ts", "/enum.ts"], + entryPoints: ["/enum.ts"], bundling: false, runtimeFiles: { "/test.js": /* js */ ` import assert from 'assert' - const test_let = (await import('./let.js')).x - assert(test_let.x === test_let.x, "let.ts worked") - const test_function = (await import('./function.js')).x - assert(test_function.x === test_function.x, "function.ts worked") - const test_class = (await import('./class.js')).x - assert(test_class.x === test_class.x, "class.ts worked") - const test_namespace = (await import('./namespace.js')).x - assert(test_namespace.x === test_namespace.x, "namespace.ts worked") - const test_enum = (await import('./enum.js')).x - assert(test_enum.x === test_enum.x, "enum.ts worked") + const m = (await import('./out.js')).x + assert(m.x === m.z, "it worked.ts worked") `, }, }); diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index c5f41701b..d17c9de3e 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -381,9 +381,8 @@ function expectBundled( if (bundleWarnings === true) bundleWarnings = {}; const useOutFile = outfile ? true : outdir ? false : entryPoints.length === 1; - if (bundling === false) { - // https://github.com/oven-sh/bun/issues/2821 - external = ["*"]; + if (bundling === false && entryPoints.length > 1) { + throw new Error("bundling:false only supports a single entry point"); } if (!ESBUILD && format !== "esm") { throw new Error("formats besides esm not implemented in bun build"); @@ -515,6 +514,7 @@ function expectBundled( "build", ...entryPaths, ...(entryPointsRaw ?? []), + bundling === false ? "--transpile" : [], outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`, define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]), `--target=${target}`, diff --git a/test/bundler/fixtures/trivial/fn.js b/test/bundler/fixtures/trivial/fn.js new file mode 100644 index 000000000..12c72fccb --- /dev/null +++ b/test/bundler/fixtures/trivial/fn.js @@ -0,0 +1,3 @@ +export function fn(a) { + return a + 42; +} diff --git a/test/fixtures/bundle/trivial/index.js b/test/bundler/fixtures/trivial/index.js index 15a8c7af4..15a8c7af4 100644 --- a/test/fixtures/bundle/trivial/index.js +++ b/test/bundler/fixtures/trivial/index.js diff --git a/test/bundler/fixtures/trivial/ts.ts b/test/bundler/fixtures/trivial/ts.ts new file mode 100644 index 000000000..d700762ec --- /dev/null +++ b/test/bundler/fixtures/trivial/ts.ts @@ -0,0 +1,5 @@ +const hi = import("./fn.js"); + +hi.then(({ fn }) => { + console.log(fn(42)); +}); diff --git a/test/bundler/fixtures/with-assets/hello.csv b/test/bundler/fixtures/with-assets/hello.csv new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/bundler/fixtures/with-assets/hello.csv diff --git a/test/bundler/fixtures/with-assets/img.png b/test/bundler/fixtures/with-assets/img.png new file mode 120000 index 000000000..894185f67 --- /dev/null +++ b/test/bundler/fixtures/with-assets/img.png @@ -0,0 +1 @@ +/data/archive/capture/2023/05/2023-05-10_13-18-34i.png
\ No newline at end of file diff --git a/test/bundler/fixtures/with-assets/index.js b/test/bundler/fixtures/with-assets/index.js new file mode 100644 index 000000000..324e238de --- /dev/null +++ b/test/bundler/fixtures/with-assets/index.js @@ -0,0 +1,4 @@ +import asset1 from "./hello.csv"; +import asset2 from "./img.png"; +capture1(asset1); +capture2(asset2); diff --git a/test/fixtures/bundle/trivial/fn.js b/test/fixtures/bundle/trivial/fn.js deleted file mode 100644 index 467f11c0b..000000000 --- a/test/fixtures/bundle/trivial/fn.js +++ /dev/null @@ -1,3 +0,0 @@ -export function fn() { - return 42; -} diff --git a/test/js/bun/resolve/import-meta.test.js b/test/js/bun/resolve/import-meta.test.js index b21465bbb..5771aeb30 100644 --- a/test/js/bun/resolve/import-meta.test.js +++ b/test/js/bun/resolve/import-meta.test.js @@ -43,7 +43,7 @@ it("require with a query string works on dynamically created content", () => { try { require("./bar.js?query=123.js"); } catch (e) { - expect(e.name).toBe("ResolveError"); + expect(e.name).toBe("ResolveMessage"); } mkdirSync("/tmp/bun-test-import-meta-dynamic-dir", { recursive: true }); diff --git a/test/js/bun/resolve/resolve-error.test.ts b/test/js/bun/resolve/resolve-error.test.ts index cddd2c051..d2114190f 100644 --- a/test/js/bun/resolve/resolve-error.test.ts +++ b/test/js/bun/resolve/resolve-error.test.ts @@ -1,6 +1,6 @@ import { expect, it, describe } from "bun:test"; -describe("ResolveError", () => { +describe("ResolveMessage", () => { it("position object does not segfault", async () => { try { await import("./file-importing-nonexistent-file.js"); diff --git a/test/js/bun/resolve/resolve-test.js b/test/js/bun/resolve/resolve-test.js index bdc0f94b6..17e2b4eb4 100644 --- a/test/js/bun/resolve/resolve-test.js +++ b/test/js/bun/resolve/resolve-test.js @@ -26,9 +26,9 @@ it("#imports", async () => { await import.meta.resolve("#foo"); throw new Error("Test failed"); } catch (exception) { - expect(exception instanceof ResolveError).toBe(true); + expect(exception instanceof ResolveMessage).toBe(true); expect(exception.referrer).toBe(import.meta.path); - expect(exception.name).toBe("ResolveError"); + expect(exception.name).toBe("ResolveMessage"); } // Chcek that package-json-imports/#foo doesn't work @@ -36,9 +36,9 @@ it("#imports", async () => { await import.meta.resolve("package-json-imports/#foo"); throw new Error("Test failed"); } catch (exception) { - expect(exception instanceof ResolveError).toBe(true); + expect(exception instanceof ResolveMessage).toBe(true); expect(exception.referrer).toBe(import.meta.path); - expect(exception.name).toBe("ResolveError"); + expect(exception.name).toBe("ResolveMessage"); } }); @@ -94,14 +94,14 @@ it("import.meta.resolve", async () => { join(import.meta.path, "../resolve-typescript-file.tsx"), ); - // throws a ResolveError on failure + // throws a ResolveMessage on failure try { await import.meta.resolve("THIS FILE DOESNT EXIST"); throw new Error("Test failed"); } catch (exception) { - expect(exception instanceof ResolveError).toBe(true); + expect(exception instanceof ResolveMessage).toBe(true); expect(exception.referrer).toBe(import.meta.path); - expect(exception.name).toBe("ResolveError"); + expect(exception.name).toBe("ResolveMessage"); } }); diff --git a/test/tsconfig.json b/test/tsconfig.json index 7d6e245cc..67f706cdf 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -43,9 +43,5 @@ ] } }, - "exclude": [ - "fixtures", - "snapshots", - "js/deno" - ] + "exclude": ["bundler/fixtures", "snapshots", "js/deno"] } |