mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
--footer esbuild & rollup style! (#14396)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
@@ -1092,7 +1092,7 @@ $ bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file
|
||||
|
||||
### `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.
|
||||
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 %}
|
||||
|
||||
@@ -1108,11 +1108,29 @@ await Bun.build({
|
||||
$ bun build ./index.tsx --outdir ./out --banner "\"use client\";"
|
||||
```
|
||||
|
||||
### `footer`
|
||||
|
||||
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.
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#JavaScript
|
||||
await Bun.build({
|
||||
entrypoints: ['./index.tsx'],
|
||||
outdir: './out',
|
||||
footer: '// built with love in SF'
|
||||
})
|
||||
```
|
||||
|
||||
```bash#CLI
|
||||
$ bun build ./index.tsx --outdir ./out --footer="// built with love in SF"
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
### `experimentalCss`
|
||||
|
||||
Whether to enable *experimental* support for bundling CSS files. Defaults to `false`.
|
||||
Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`.
|
||||
|
||||
This supports bundling CSS files imported from JS, as well as CSS entrypoints.
|
||||
|
||||
|
||||
@@ -159,6 +159,12 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
|
||||
|
||||
---
|
||||
|
||||
- `--footer`
|
||||
- `--footer`
|
||||
- Only applies to js bundles
|
||||
|
||||
---
|
||||
|
||||
- `--certfile`
|
||||
- n/a
|
||||
- Not applicable
|
||||
@@ -195,12 +201,6 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
|
||||
|
||||
---
|
||||
|
||||
- `--footer`
|
||||
- n/a
|
||||
- Not supported
|
||||
|
||||
---
|
||||
|
||||
- `--global-name`
|
||||
- n/a
|
||||
- Not applicable, Bun does not support `iife` output at this time
|
||||
|
||||
6
packages/bun-types/bun.d.ts
vendored
6
packages/bun-types/bun.d.ts
vendored
@@ -1599,6 +1599,12 @@ declare module "bun" {
|
||||
* Add a banner to the bundled code such as "use client";
|
||||
*/
|
||||
banner?: string;
|
||||
/**
|
||||
* Add a footer to the bundled code such as a comment block like
|
||||
*
|
||||
* `// made with bun!`
|
||||
*/
|
||||
footer?: string;
|
||||
|
||||
/**
|
||||
* **Experimental**
|
||||
|
||||
@@ -73,6 +73,7 @@ pub const JSBundler = struct {
|
||||
format: options.Format = .esm,
|
||||
bytecode: bool = false,
|
||||
banner: OwnedString = OwnedString.initEmpty(bun.default_allocator),
|
||||
footer: OwnedString = OwnedString.initEmpty(bun.default_allocator),
|
||||
experimental_css: bool = false,
|
||||
|
||||
pub const List = bun.StringArrayHashMapUnmanaged(Config);
|
||||
@@ -190,6 +191,12 @@ pub const JSBundler = struct {
|
||||
try this.banner.appendSliceExact(slice.slice());
|
||||
}
|
||||
|
||||
|
||||
if (try config.getOptional(globalThis, "footer", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
try this.footer.appendSliceExact(slice.slice());
|
||||
}
|
||||
|
||||
if (config.getTruthy(globalThis, "sourcemap")) |source_map_js| {
|
||||
if (bun.FeatureFlags.breaking_changes_1_2 and config.isBoolean()) {
|
||||
if (source_map_js == .true) {
|
||||
|
||||
@@ -876,6 +876,7 @@ pub const BundleV2 = struct {
|
||||
this.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations;
|
||||
|
||||
this.linker.options.banner = bundler.options.banner;
|
||||
this.linker.options.footer = bundler.options.footer;
|
||||
|
||||
this.linker.options.experimental_css = bundler.options.experimental_css;
|
||||
|
||||
@@ -1478,6 +1479,7 @@ pub const BundleV2 = struct {
|
||||
bundler.options.ignore_dce_annotations = config.ignore_dce_annotations;
|
||||
bundler.options.experimental_css = config.experimental_css;
|
||||
bundler.options.banner = config.banner.toOwnedSlice();
|
||||
bundler.options.footer = config.footer.toOwnedSlice();
|
||||
|
||||
bundler.configureLinker();
|
||||
try bundler.configureDefines();
|
||||
@@ -4602,6 +4604,7 @@ pub const LinkerContext = struct {
|
||||
minify_syntax: bool = false,
|
||||
minify_identifiers: bool = false,
|
||||
banner: []const u8 = "",
|
||||
footer: []const u8 = "",
|
||||
experimental_css: bool = false,
|
||||
source_maps: options.SourceMapOption = .none,
|
||||
target: options.Target = .browser,
|
||||
@@ -8977,7 +8980,16 @@ pub const LinkerContext = struct {
|
||||
j.ensureNewlineAtEnd();
|
||||
// TODO: maybeAppendLegalComments
|
||||
|
||||
// TODO: footer
|
||||
if (c.options.footer.len > 0) {
|
||||
if (newline_before_comment) {
|
||||
j.pushStatic("\n");
|
||||
line_offset.advance("\n");
|
||||
}
|
||||
j.pushStatic(ctx.c.options.footer);
|
||||
line_offset.advance(ctx.c.options.footer);
|
||||
j.pushStatic("\n");
|
||||
line_offset.advance("\n");
|
||||
}
|
||||
|
||||
chunk.intermediate_output = c.breakOutputIntoPieces(
|
||||
worker.allocator,
|
||||
|
||||
@@ -263,6 +263,7 @@ pub const Arguments = struct {
|
||||
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("--footer <STR> Add a footer to the bundled output such as // built with bun!") 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,
|
||||
@@ -783,6 +784,10 @@ pub const Arguments = struct {
|
||||
ctx.bundler_options.banner = banner;
|
||||
}
|
||||
|
||||
if (args.option("--footer")) |footer| {
|
||||
ctx.bundler_options.footer = footer;
|
||||
}
|
||||
|
||||
const experimental_css = args.flag("--experimental-css");
|
||||
ctx.bundler_options.experimental_css = experimental_css;
|
||||
|
||||
@@ -1408,6 +1413,7 @@ pub const Command = struct {
|
||||
output_format: options.Format = .esm,
|
||||
bytecode: bool = false,
|
||||
banner: []const u8 = "",
|
||||
footer: []const u8 = "",
|
||||
experimental_css: bool = false,
|
||||
};
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ pub const BuildCommand = struct {
|
||||
this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
|
||||
|
||||
this_bundler.options.banner = ctx.bundler_options.banner;
|
||||
this_bundler.options.footer = ctx.bundler_options.footer;
|
||||
|
||||
this_bundler.options.experimental_css = ctx.bundler_options.experimental_css;
|
||||
|
||||
this_bundler.options.output_dir = ctx.bundler_options.outdir;
|
||||
|
||||
29
test/bundler/bundler_footer.test.ts
Normal file
29
test/bundler/bundler_footer.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { describe } from "bun:test";
|
||||
import { itBundled } from "./expectBundled";
|
||||
|
||||
describe("bundler", () => {
|
||||
itBundled("footer/CommentFooter", {
|
||||
footer: "// developed with love in SF",
|
||||
files: {
|
||||
"/a.js": `console.log("Hello, world!")`,
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("out.js").toEndWith('// developed with love in SF"\n');
|
||||
},
|
||||
});
|
||||
itBundled("footer/MultilineFooter", {
|
||||
footer: `/**
|
||||
* This is copyright of [...] ${new Date().getFullYear()}
|
||||
* do not redistribute without consent of [...]
|
||||
*/`,
|
||||
files: {
|
||||
"index.js": `console.log("Hello, world!")`,
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("out.js").toEndWith(`/**
|
||||
* This is copyright of [...] ${new Date().getFullYear()}
|
||||
* do not redistribute without consent of [...]
|
||||
*/\"\n`);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -146,6 +146,7 @@ export interface BundlerTestInput {
|
||||
alias?: Record<string, string>;
|
||||
assetNaming?: string;
|
||||
banner?: string;
|
||||
footer?: string;
|
||||
define?: Record<string, string | number>;
|
||||
|
||||
/** Use for resolve custom conditions */
|
||||
@@ -416,6 +417,7 @@ function expectBundled(
|
||||
external,
|
||||
packages,
|
||||
files,
|
||||
footer,
|
||||
format,
|
||||
globalName,
|
||||
inject,
|
||||
@@ -666,6 +668,7 @@ function expectBundled(
|
||||
serverComponents && "--server-components",
|
||||
outbase && `--root=${outbase}`,
|
||||
banner && `--banner="${banner}"`, // TODO: --banner-css=*
|
||||
footer && `--footer="${footer}"`,
|
||||
ignoreDCEAnnotations && `--ignore-dce-annotations`,
|
||||
emitDCEAnnotations && `--emit-dce-annotations`,
|
||||
// inject && inject.map(x => ["--inject", path.join(root, x)]),
|
||||
@@ -710,6 +713,7 @@ function expectBundled(
|
||||
metafile && `--metafile=${metafile}`,
|
||||
sourceMap && `--sourcemap=${sourceMap}`,
|
||||
banner && `--banner:js=${banner}`,
|
||||
footer && `--footer:js=${footer}`,
|
||||
legalComments && `--legal-comments=${legalComments}`,
|
||||
ignoreDCEAnnotations && `--ignore-annotations`,
|
||||
splitting && `--splitting`,
|
||||
|
||||
Reference in New Issue
Block a user