Skip to content

Commit

Permalink
add banner, .d.ts, cli, make sourcemap compat, and add tests + docs (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
versecafe authored Oct 8, 2024
1 parent c071415 commit 62da730
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 16 deletions.
20 changes: 20 additions & 0 deletions docs/bundler/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,26 @@ $ bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file

{% /codetabs %}

### `banner`

A banner to be added to the final bundle, this can be a directive like "use client" for react or a comment block such as a license for the code.

{% codetabs %}

```ts#JavaScript
await Bun.build({
entrypoints: ['./index.tsx'],
outdir: './out',
banner: '"use client";'
})
```

```bash#CLI
$ bun build ./index.tsx --outdir ./out --banner "\"use client\";"
```

{% /codetabs %}

### `experimentalCss`

Whether to enable *experimental* support for bundling CSS files. Defaults to `false`.
Expand Down
4 changes: 2 additions & 2 deletions docs/bundler/vs-esbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
---
- `--banner`
- n/a
- Not supported
- `--banner`
- Only applies to js bundles
---
Expand Down
4 changes: 4 additions & 0 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,10 @@ declare module "bun" {
* @default false
*/
bytecode?: boolean;
/**
* Add a banner to the bundled code such as "use client";
*/
banner?: string;

/**
* **Experimental**
Expand Down
6 changes: 6 additions & 0 deletions src/bun.js/api/JSBundler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub const JSBundler = struct {
packages: options.PackagesOption = .bundle,
format: options.Format = .esm,
bytecode: bool = false,
banner: OwnedString = OwnedString.initEmpty(bun.default_allocator),
experimental_css: bool = false,

pub const List = bun.StringArrayHashMapUnmanaged(Config);
Expand Down Expand Up @@ -184,6 +185,11 @@ pub const JSBundler = struct {
has_out_dir = true;
}

if (try config.getOwnOptional(globalThis, "banner", ZigString.Slice)) |slice| {
defer slice.deinit();
try this.banner.appendSliceExact(slice.slice());
}

if (config.getOwnTruthy(globalThis, "sourcemap")) |source_map_js| {
if (bun.FeatureFlags.breaking_changes_1_2 and config.isBoolean()) {
if (source_map_js == .true) {
Expand Down
15 changes: 14 additions & 1 deletion src/bundler/bundle_v2.zig
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,8 @@ pub const BundleV2 = struct {
this.linker.options.emit_dce_annotations = bundler.options.emit_dce_annotations;
this.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations;

this.linker.options.banner = bundler.options.banner;

this.linker.options.experimental_css = bundler.options.experimental_css;

this.linker.options.source_maps = bundler.options.source_map;
Expand Down Expand Up @@ -1475,6 +1477,7 @@ pub const BundleV2 = struct {
bundler.options.emit_dce_annotations = config.emit_dce_annotations orelse !config.minify.whitespace;
bundler.options.ignore_dce_annotations = config.ignore_dce_annotations;
bundler.options.experimental_css = config.experimental_css;
bundler.options.banner = config.banner.toOwnedSlice();

bundler.configureLinker();
try bundler.configureDefines();
Expand Down Expand Up @@ -4598,6 +4601,7 @@ pub const LinkerContext = struct {
minify_whitespace: bool = false,
minify_syntax: bool = false,
minify_identifiers: bool = false,
banner: []const u8 = "",
experimental_css: bool = false,
source_maps: options.SourceMapOption = .none,
target: options.Target = .browser,
Expand Down Expand Up @@ -8751,7 +8755,16 @@ pub const LinkerContext = struct {
}
}

// TODO: banner
if (c.options.banner.len > 0) {
if (newline_before_comment) {
j.pushStatic("\n");
line_offset.advance("\n");
}
j.pushStatic(ctx.c.options.banner);
line_offset.advance(ctx.c.options.banner);
j.pushStatic("\n");
line_offset.advance("\n");
}

// Add the top-level directive if present (but omit "use strict" in ES
// modules because all ES modules are automatically in strict mode)
Expand Down
6 changes: 6 additions & 0 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ pub const Arguments = struct {
clap.parseParam("--outdir <STR> Default to \"dist\" if multiple files") catch unreachable,
clap.parseParam("--outfile <STR> Write to a file") catch unreachable,
clap.parseParam("--sourcemap <STR>? Build with sourcemaps - 'linked', 'inline', 'external', or 'none'") catch unreachable,
clap.parseParam("--banner <STR> Add a banner to the bundled output such as \"use client\"; for a bundle being used with RSCs") catch unreachable,
clap.parseParam("--format <STR> Specifies the module format to build to. Only \"esm\" is supported.") catch unreachable,
clap.parseParam("--root <STR> Root directory used for multiple entry points") catch unreachable,
clap.parseParam("--splitting Enable code splitting") catch unreachable,
Expand Down Expand Up @@ -778,6 +779,10 @@ pub const Arguments = struct {
ctx.bundler_options.public_path = public_path;
}

if (args.option("--banner")) |banner| {
ctx.bundler_options.banner = banner;
}

const experimental_css = args.flag("--experimental-css");
ctx.bundler_options.experimental_css = experimental_css;

Expand Down Expand Up @@ -1402,6 +1407,7 @@ pub const Command = struct {
emit_dce_annotations: bool = true,
output_format: options.Format = .esm,
bytecode: bool = false,
banner: []const u8 = "",
experimental_css: bool = false,
};

Expand Down
1 change: 1 addition & 0 deletions src/cli/build_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub const BuildCommand = struct {
this_bundler.options.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations;
this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;

this_bundler.options.banner = ctx.bundler_options.banner;
this_bundler.options.experimental_css = ctx.bundler_options.experimental_css;

this_bundler.options.output_dir = ctx.bundler_options.outdir;
Expand Down
36 changes: 36 additions & 0 deletions test/bundler/bundler_banner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe } from "bun:test";
import { itBundled } from "./expectBundled";

describe("bundler", () => {
itBundled("banner/CommentBanner", {
banner: "// developed with love in SF",
files: {
"/a.js": `console.log("Hello, world!")`,
},
onAfterBundle(api) {
api.expectFile("out.js").toContain("// developed with love in SF");
},
});
itBundled("banner/MultilineBanner", {
banner: `"use client";
// This is a multiline banner
// It can contain multiple lines of comments or code`,
files: {
/* js*/ "index.js": `console.log("Hello, world!")`,
},
onAfterBundle(api) {
api.expectFile("out.js").toContain(`"use client";
// This is a multiline banner
// It can contain multiple lines of comments or code`);
},
});
itBundled("banner/UseClientBanner", {
banner: '"use client";',
files: {
/* js*/ "index.js": `console.log("Hello, world!")`,
},
onAfterBundle(api) {
api.expectFile("out.js").toContain('"use client";');
},
});
});
19 changes: 6 additions & 13 deletions test/bundler/expectBundled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ export interface BundlerTestInput {
/** Temporary flag to mark failing tests as skipped. */
todo?: boolean;


// file options
files: Record<string, string | Buffer | Blob>;
/** Files to be written only after the bundle is done. */
Expand Down Expand Up @@ -515,9 +514,6 @@ function expectBundled(
if (!ESBUILD && mainFields) {
throw new Error("mainFields not implemented in bun build");
}
if (!ESBUILD && banner) {
throw new Error("banner not implemented in bun build");
}
if (!ESBUILD && inject) {
throw new Error("inject not implemented in bun build");
}
Expand Down Expand Up @@ -669,6 +665,7 @@ function expectBundled(
splitting && `--splitting`,
serverComponents && "--server-components",
outbase && `--root=${outbase}`,
banner && `--banner="${banner}"`, // TODO: --banner-css=*
ignoreDCEAnnotations && `--ignore-dce-annotations`,
emitDCEAnnotations && `--emit-dce-annotations`,
// inject && inject.map(x => ["--inject", path.join(root, x)]),
Expand Down Expand Up @@ -1532,7 +1529,7 @@ for (const [key, blob] of build.outputs) {
let result = out!.toUnixString().trim();

// no idea why this logs. ¯\_(ツ)_/¯
result = result.replace(`[EventLoop] enqueueTaskConcurrent(RuntimeTranspilerStore)\n`, '');
result = result.replace(`[EventLoop] enqueueTaskConcurrent(RuntimeTranspilerStore)\n`, "");

if (typeof expected === "string") {
expected = dedent(expected).trim();
Expand Down Expand Up @@ -1607,10 +1604,8 @@ export function itBundled(
id,
() => expectBundled(id, opts as any),
// sourcemap code is slow
(opts.snapshotSourceMap
? isDebug ? Infinity : 30_000
: isDebug ? 15_000 : 5_000)
* ((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1),
(opts.snapshotSourceMap ? (isDebug ? Infinity : 30_000) : isDebug ? 15_000 : 5_000) *
((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1),
);
}
return ref;
Expand All @@ -1622,10 +1617,8 @@ itBundled.only = (id: string, opts: BundlerTestInput) => {
id,
() => expectBundled(id, opts as any),
// sourcemap code is slow
(opts.snapshotSourceMap
? isDebug ? Infinity : 30_000
: isDebug ? 15_000 : 5_000)
* ((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1),
(opts.snapshotSourceMap ? (isDebug ? Infinity : 30_000) : isDebug ? 15_000 : 5_000) *
((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1),
);
};

Expand Down

0 comments on commit 62da730

Please sign in to comment.