Skip to content

Commit 7996d06

Browse files
--footer esbuild & rollup style! (#14396)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
1 parent a234e06 commit 7996d06

File tree

9 files changed

+93
-9
lines changed

9 files changed

+93
-9
lines changed

docs/bundler/index.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ $ bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file
10921092

10931093
### `banner`
10941094

1095-
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.
1095+
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.
10961096

10971097
{% codetabs %}
10981098

@@ -1108,11 +1108,29 @@ await Bun.build({
11081108
$ bun build ./index.tsx --outdir ./out --banner "\"use client\";"
11091109
```
11101110

1111+
### `footer`
1112+
1113+
A footer to be added to the final bundle, this can be something like a comment block for a license or just a fun easter egg.
1114+
1115+
{% codetabs %}
1116+
1117+
```ts#JavaScript
1118+
await Bun.build({
1119+
entrypoints: ['./index.tsx'],
1120+
outdir: './out',
1121+
footer: '// built with love in SF'
1122+
})
1123+
```
1124+
1125+
```bash#CLI
1126+
$ bun build ./index.tsx --outdir ./out --footer="// built with love in SF"
1127+
```
1128+
11111129
{% /codetabs %}
11121130

11131131
### `experimentalCss`
11141132

1115-
Whether to enable *experimental* support for bundling CSS files. Defaults to `false`.
1133+
Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`.
11161134

11171135
This supports bundling CSS files imported from JS, as well as CSS entrypoints.
11181136

docs/bundler/vs-esbuild.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
159159

160160
---
161161

162+
- `--footer`
163+
- `--footer`
164+
- Only applies to js bundles
165+
166+
---
167+
162168
- `--certfile`
163169
- n/a
164170
- Not applicable
@@ -195,12 +201,6 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
195201

196202
---
197203

198-
- `--footer`
199-
- n/a
200-
- Not supported
201-
202-
---
203-
204204
- `--global-name`
205205
- n/a
206206
- Not applicable, Bun does not support `iife` output at this time

packages/bun-types/bun.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,12 @@ declare module "bun" {
15991599
* Add a banner to the bundled code such as "use client";
16001600
*/
16011601
banner?: string;
1602+
/**
1603+
* Add a footer to the bundled code such as a comment block like
1604+
*
1605+
* `// made with bun!`
1606+
*/
1607+
footer?: string;
16021608

16031609
/**
16041610
* **Experimental**

src/bun.js/api/JSBundler.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub const JSBundler = struct {
7373
format: options.Format = .esm,
7474
bytecode: bool = false,
7575
banner: OwnedString = OwnedString.initEmpty(bun.default_allocator),
76+
footer: OwnedString = OwnedString.initEmpty(bun.default_allocator),
7677
experimental_css: bool = false,
7778

7879
pub const List = bun.StringArrayHashMapUnmanaged(Config);
@@ -190,6 +191,12 @@ pub const JSBundler = struct {
190191
try this.banner.appendSliceExact(slice.slice());
191192
}
192193

194+
195+
if (try config.getOptional(globalThis, "footer", ZigString.Slice)) |slice| {
196+
defer slice.deinit();
197+
try this.footer.appendSliceExact(slice.slice());
198+
}
199+
193200
if (config.getTruthy(globalThis, "sourcemap")) |source_map_js| {
194201
if (bun.FeatureFlags.breaking_changes_1_2 and config.isBoolean()) {
195202
if (source_map_js == .true) {

src/bundler/bundle_v2.zig

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ pub const BundleV2 = struct {
876876
this.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations;
877877

878878
this.linker.options.banner = bundler.options.banner;
879+
this.linker.options.footer = bundler.options.footer;
879880

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

@@ -1478,6 +1479,7 @@ pub const BundleV2 = struct {
14781479
bundler.options.ignore_dce_annotations = config.ignore_dce_annotations;
14791480
bundler.options.experimental_css = config.experimental_css;
14801481
bundler.options.banner = config.banner.toOwnedSlice();
1482+
bundler.options.footer = config.footer.toOwnedSlice();
14811483

14821484
bundler.configureLinker();
14831485
try bundler.configureDefines();
@@ -4602,6 +4604,7 @@ pub const LinkerContext = struct {
46024604
minify_syntax: bool = false,
46034605
minify_identifiers: bool = false,
46044606
banner: []const u8 = "",
4607+
footer: []const u8 = "",
46054608
experimental_css: bool = false,
46064609
source_maps: options.SourceMapOption = .none,
46074610
target: options.Target = .browser,
@@ -8977,7 +8980,16 @@ pub const LinkerContext = struct {
89778980
j.ensureNewlineAtEnd();
89788981
// TODO: maybeAppendLegalComments
89798982

8980-
// TODO: footer
8983+
if (c.options.footer.len > 0) {
8984+
if (newline_before_comment) {
8985+
j.pushStatic("\n");
8986+
line_offset.advance("\n");
8987+
}
8988+
j.pushStatic(ctx.c.options.footer);
8989+
line_offset.advance(ctx.c.options.footer);
8990+
j.pushStatic("\n");
8991+
line_offset.advance("\n");
8992+
}
89818993

89828994
chunk.intermediate_output = c.breakOutputIntoPieces(
89838995
worker.allocator,

src/cli.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ pub const Arguments = struct {
263263
clap.parseParam("--outfile <STR> Write to a file") catch unreachable,
264264
clap.parseParam("--sourcemap <STR>? Build with sourcemaps - 'linked', 'inline', 'external', or 'none'") catch unreachable,
265265
clap.parseParam("--banner <STR> Add a banner to the bundled output such as \"use client\"; for a bundle being used with RSCs") catch unreachable,
266+
clap.parseParam("--footer <STR> Add a footer to the bundled output such as // built with bun!") catch unreachable,
266267
clap.parseParam("--format <STR> Specifies the module format to build to. Only \"esm\" is supported.") catch unreachable,
267268
clap.parseParam("--root <STR> Root directory used for multiple entry points") catch unreachable,
268269
clap.parseParam("--splitting Enable code splitting") catch unreachable,
@@ -783,6 +784,10 @@ pub const Arguments = struct {
783784
ctx.bundler_options.banner = banner;
784785
}
785786

787+
if (args.option("--footer")) |footer| {
788+
ctx.bundler_options.footer = footer;
789+
}
790+
786791
const experimental_css = args.flag("--experimental-css");
787792
ctx.bundler_options.experimental_css = experimental_css;
788793

@@ -1408,6 +1413,7 @@ pub const Command = struct {
14081413
output_format: options.Format = .esm,
14091414
bytecode: bool = false,
14101415
banner: []const u8 = "",
1416+
footer: []const u8 = "",
14111417
experimental_css: bool = false,
14121418
};
14131419

src/cli/build_command.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub const BuildCommand = struct {
9898
this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
9999

100100
this_bundler.options.banner = ctx.bundler_options.banner;
101+
this_bundler.options.footer = ctx.bundler_options.footer;
102+
101103
this_bundler.options.experimental_css = ctx.bundler_options.experimental_css;
102104

103105
this_bundler.options.output_dir = ctx.bundler_options.outdir;

test/bundler/bundler_footer.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { describe } from "bun:test";
2+
import { itBundled } from "./expectBundled";
3+
4+
describe("bundler", () => {
5+
itBundled("footer/CommentFooter", {
6+
footer: "// developed with love in SF",
7+
files: {
8+
"/a.js": `console.log("Hello, world!")`,
9+
},
10+
onAfterBundle(api) {
11+
api.expectFile("out.js").toEndWith('// developed with love in SF"\n');
12+
},
13+
});
14+
itBundled("footer/MultilineFooter", {
15+
footer: `/**
16+
* This is copyright of [...] ${new Date().getFullYear()}
17+
* do not redistribute without consent of [...]
18+
*/`,
19+
files: {
20+
"index.js": `console.log("Hello, world!")`,
21+
},
22+
onAfterBundle(api) {
23+
api.expectFile("out.js").toEndWith(`/**
24+
* This is copyright of [...] ${new Date().getFullYear()}
25+
* do not redistribute without consent of [...]
26+
*/\"\n`);
27+
},
28+
});
29+
});

test/bundler/expectBundled.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export interface BundlerTestInput {
146146
alias?: Record<string, string>;
147147
assetNaming?: string;
148148
banner?: string;
149+
footer?: string;
149150
define?: Record<string, string | number>;
150151

151152
/** Use for resolve custom conditions */
@@ -416,6 +417,7 @@ function expectBundled(
416417
external,
417418
packages,
418419
files,
420+
footer,
419421
format,
420422
globalName,
421423
inject,
@@ -666,6 +668,7 @@ function expectBundled(
666668
serverComponents && "--server-components",
667669
outbase && `--root=${outbase}`,
668670
banner && `--banner="${banner}"`, // TODO: --banner-css=*
671+
footer && `--footer="${footer}"`,
669672
ignoreDCEAnnotations && `--ignore-dce-annotations`,
670673
emitDCEAnnotations && `--emit-dce-annotations`,
671674
// inject && inject.map(x => ["--inject", path.join(root, x)]),
@@ -710,6 +713,7 @@ function expectBundled(
710713
metafile && `--metafile=${metafile}`,
711714
sourceMap && `--sourcemap=${sourceMap}`,
712715
banner && `--banner:js=${banner}`,
716+
footer && `--footer:js=${footer}`,
713717
legalComments && `--legal-comments=${legalComments}`,
714718
ignoreDCEAnnotations && `--ignore-annotations`,
715719
splitting && `--splitting`,

0 commit comments

Comments
 (0)