@guchho/banner
Copyright and license banners for Guchho JavaScript and CSS outputs.
#Copyright and license banner plugin for Guchho. A single plugin instance
puts a deterministic /*! ... */ legal comment at the top of every eligible
JavaScript and CSS file Guchho generates. It survives minification, coexists
with Guchho's built-in banner, and never touches HTML, JSON, source-map files,
or binary assets.
- Package:
@guchho/banner - Source: guchho/plugins/packages/banner
- Plugin API: Writing plugins · Hooks · Context
Installation
#npm install -D @guchho/banner
Requirements
#- Node.js 18 or newer. Guchho runs plugin hooks in the host process while a build runs.
- A Guchho that hosts plugin hooks. The plugin is a Guchho plugin - an
object with
buildStartandgenerateBundle- so it needs the engine that runs the plugin bridge. Older Guchho releases that treatedpluginsas a data descriptor will not run it.
Basic configuration
#import banner from "@guchho/banner";
export default {
build: {
entry: ["src/main.js", "src/styles.css"],
outdir: "dist",
},
plugins: [
banner({
style: "long",
packageJson: "./package.json",
}),
],
};
One instance handles every eligible JavaScript and CSS output of the build it belongs to. No separate JS and CSS plugin instances are needed.
How it works
#banner(options) validates the options immediately and hands back a plugin
object.
buildStartreads the package metadata once per build and renders the banner text. A missing file, unreadable file, invalid JSON, or non-object JSON fails the build with a plugin-named diagnostic viathis.error- the build stops, no output is written.generateBundleprepends that text to every eligible output:- JavaScript chunks - every generated
.js/.mjs/.cjs/.jsx/.ts/.tsxoutput. - CSS outputs, matched by their
.cssfile name. - Everything else is left alone: source maps, JSON, HTML, and binary assets never receive a banner.
- JavaScript chunks - every generated
The banner sits at the very top of each file, above Guchho's own build.banner
text and above any existing comment. It appears exactly once per file. The
none style produces an empty banner and the hook touches nothing.
JavaScript output support
#The banner is prepended to the very beginning of JavaScript outputs, above everything else, in every format Guchho generates.
- Works with both minified and unminified output.
- The banner uses the legal-comment form
/*! ... */, which Guchho's minifier preserves. - Minification is never disabled and its defaults are never changed.
- Directive prologues (
"use strict") are unaffected - the banner is a comment, not a statement.
CSS output support
#The banner is prepended to the very beginning of CSS outputs, above everything else.
- Works with minified and unminified CSS.
- Uses valid CSS block-comment syntax (the same text as JavaScript).
- Survives CSS minification without changing selectors, declarations,
at-rules, or
@charsethandling.
Supported banner styles
#| Style | Description |
|---|---|
long (default) |
Detailed multiline banner with copyright and license. |
short |
Compact, single-line copyright banner. |
minimal |
Package name and version only. |
compact |
Name, version, and license on one line, copyright below. |
standard |
Conventional banner with a License: label. |
production |
Clean banner for published libraries. |
full |
All available package metadata as labeled fields. |
legal |
Copyright and licensing focused, with LICENSE file reference. |
license |
Package name, version, and license reference only. |
author |
Package name, version, and author only. |
version |
Alias of minimal. |
dated |
Copyright with a configurable date line. |
boxed |
Decorative banner with = separator lines. |
separated |
Package info divided from legal details by a - line. |
none |
No banner is injected. |
long (default)
#/*!
* truehuman v0.0.6
* Copyright (c) 2026 Hemanta Gayen and contributors
*
* Released under the GPL-3.0 License
*/
short
#/*! truehuman v0.0.6 | Copyright (c) 2026 Hemanta Gayen and contributors | GPL-3.0 License */
minimal
#/*! my-library v1.0.0 */
compact
#/*!
* my-library v1.0.0 | MIT License
* Copyright (c) 2026 Your Name
*/
standard
#/*!
* my-library v1.0.0
* Copyright (c) 2026 Your Name
* License: MIT
*/
production
#/*!
* my-library v1.0.0
* Copyright (c) 2026 Your Name
* Licensed under the MIT License.
*/
full
#/*!
* Package: my-library
* Version: 1.0.0
* Description: A useful JavaScript library
* Author: Your Name
* Copyright (c) 2026 Your Name
* License: MIT
* Homepage: https://example.com
* Repository: https://github.com/example/my-library
*/
Only fields with valid values are included. Missing metadata produces no
undefined, null, or empty labels.
legal
#/*!
* my-library v1.0.0
* Copyright (c) 2026 Your Name
*
* Licensed under the MIT License.
* See the LICENSE file for details.
*/
license
#/*! my-library v1.0.0 | Released under the MIT License */
author
#/*! my-library v1.0.0 | By Your Name */
dated
#/*!
* my-library v1.0.0
* Copyright (c) 2026 Your Name
* License: MIT
* Date: 2026-10-11
*/
boxed
#/*!
* =================================
* my-library v1.0.0
* Copyright (c) 2026 Your Name
* License: MIT
* =================================
*/
separated
#/*!
* my-library v1.0.0
* ---------------------------------
* Copyright (c) 2026 Your Name
* License: MIT
*/
none
#Returns an empty banner string - no text is injected into any output.
All examples are illustrative: the content comes from your metadata, not from
these values. Missing fields are omitted and the remaining segments re-flow
cleanly - the plugin never emits undefined, null, or malformed comments, and
metadata containing newlines or */ is sanitized so the comment stays intact.
Identical inputs produce identical output.
Options
#| Option | Type | Default | Purpose |
|---|---|---|---|
style |
string |
"long" |
Banner formatting. One of the 15 supported styles. Any other value throws. |
packageJson |
string |
"./package.json" |
Package metadata file, relative to the config file's directory. |
date |
boolean | string |
false |
true for today's date (UTC), or an explicit date string like "2026-10-11". |
year |
number | string |
Current year at config-evaluation time | Copyright year. |
preserveExisting |
boolean |
false |
When true, an output that already starts with a /*! legal banner is left untouched. |
name |
string |
From package.json |
Override package name. |
version |
string |
From package.json |
Override package version. |
author |
string |
From package.json |
Override author. |
license |
string |
From package.json |
Override license. |
Unknown option names throw, so typos fail fast instead of being ignored. Explicit overrides are plain strings; an empty string clears that field (the segment is omitted).
Package metadata resolution
#- The file is read once per build, in the
buildStarthook - never per output file. - Relative
packageJsonpaths resolve againstprocess.cwd(), which Guchho sets to the configuration file's own directory when it evaluates the config. The plugin's own installation directory is never consulted. Absolute paths work as-is. - Fields used:
name,version,author(string or{ name }object -email/urlare ignored in banners),license,description,homepage,repository(string or{ url }object),contributors(array of strings or{ name }objects). - A missing file, unreadable file, invalid JSON, or non-object JSON fails the build with a plugin-named error (see Error handling). Missing fields are not: their segments are simply omitted.
- Non-string fields in
package.jsonare treated as missing. - A
versionwithout a leadingvgets one (3.1.4→v3.1.4); one that already has it is not double-prefixed. - A license value that already ends in
Licenseis not suffixed again.
Override precedence
#Explicit options always win over package.json, and overrides are applied after
the file is loaded:
banner({
year: 1999,
name: "other",
version: "9.9.9",
author: "Someone Else",
license: "MIT",
});
year has no package.json counterpart - it always comes from the option
(defaulting to the current year).
Built-in banner compatibility
#Guchho's own build.banner (a plain string for the JavaScript half) keeps
working unchanged. When both are configured, the plugin banner is prepended
above the built-in banner - the plugin text comes first because
generateBundle runs after the engine applied its own banner.
export default {
build: {
entry: "src/main.js",
outdir: "dist",
banner: "/*! Built-in */",
},
plugins: [banner({ year: 2026 })],
};
Minification behavior
#/*! ... */is the legal-comment form; Guchho's JavaScript and CSS minifiers preserve it.- The plugin never disables minification and never changes minification defaults.
- Unrelated comments are not touched by the plugin; any comment handling by Guchho's minifier is Guchho's own behavior.
Source-map behavior
#.mapfiles are never bannered: they arrive as JSON assets and the plugin skips every non-CSS asset.- Generating a source map together with a plugin banner places the banner ahead
of the mapping, which shifts the generated line numbers the same way any
prepended text does. If your tooling compares generated positions against
sourcesContent, prefer Guchho's built-inbanneroption, which the engine accounts for when emitting maps.
Error handling
#A broken configuration fails the build rather than silently producing a banner-less build:
- At plugin creation (
banner(...)is called, during config evaluation): invalidstyle, unknown option names, and wrong option types throw a plainErrorwhose message starts with@guchho/banner:. - In
buildStart(when the build runs): missing or unreadablepackageJson, invalid JSON, or a non-object JSON document callthis.error, failing the build with a plugin-named diagnostic that includes the resolved path.
@guchho/banner: cannot read package metadata at C:\project\nope.json (ENOENT)
Reproducible builds and the date option
#date: false(default): no clock reads other than the defaultyear. Identical inputs produce byte-identical banners.- The default
yearis the calendar year at config-evaluation time; pin it withyear: "2026"if your builds must be bit-stable across New Year. date: trueappendsBuilt YYYY-MM-DDin UTC to the banner. Output then changes once per UTC day - expected, and documented here rather than hidden.date: "2026-10-11"uses that exact string, producing deterministic output regardless of when the build runs.
Examples
#JavaScript-only build, short style, overridden metadata:
import banner from "@guchho/banner";
export default {
build: {
entry: "src/main.js",
outfile: "dist/app.js",
format: "cjs",
minify: true,
},
plugins: [
banner({
style: "short",
name: "my-app",
version: "1.4.0",
license: "MIT",
}),
],
};
CSS with a pinned year and the build date:
import banner from "@guchho/banner";
export default {
build: {
entry: "src/styles.css",
outdir: "dist",
minify: true,
},
plugins: [banner({ year: "2024-2026", date: true })],
};
Several builds in one config file (each element is its own build):
import banner from "@guchho/banner";
const plugin = [banner({ style: "long" })];
export default [
{ build: { entry: "src/main.js", outfile: "dist/app.esm.js", format: "esm" }, plugins: plugin },
{ build: { entry: "src/main.js", outfile: "dist/app.cjs.js", format: "cjs" }, plugins: plugin },
];
A ready-to-copy file lives at
packages/banner/examples/guchho.config.js.
Known limitations
#- The plugin host's
transformhook andthis.resolve/this.getFileNameare not wired yet in Guchho. A banner only needsgenerateBundle, so this plugin is unaffected. - The engine's own
banneroption stays the source-map–correct way to prepend text; the plugin's position is deliberately the top of the finished file. - The engine crashes (access violation, exit code
3221225477) on a single build that emits both a JS and a CSS source map. This is engine-side and reproduces without any plugin; per-type builds (JS-only with maps, or CSS without maps) are fine.
License
#MIT. Copyright © CodeHemu.