You are reading the documentation for version 0.6, which is not the latest release.
Library authoring with allowDebugBuild
If you publish a Vite library that uses $debug internally, you want the published bundle to ship two parallel variants, so consumers get debug logs in dev without paying for them in production. The plugin handles both variants in a single build when you set allowDebugBuild: true:
// B's vite.config.ts (the library)
import mtDebug from "@mars-tech/vite-debug-plugin";
export default defineConfig({
plugins: [mtDebug({ allowDebugBuild: true })],
build: {
lib: { entry: "src/index.ts", formats: ["es", "cjs"] },
rollupOptions: { external: ["vue", "pinia"] },
sourcemap: true,
},
});Build output
After vite build the library's dist/ contains:
dist/
├── index.js ← standard variant, every $debug call stripped → DCE'd
├── index.cjs
├── index.d.ts ← single shared type file
├── index.js.map
├── index.cjs.map
└── debug/
├── index.js ← debug variant, $debug calls preserved + fallback shim prepended
├── index.cjs
├── index.js.map
└── index.cjs.mapConditional exports
Wire conditional exports in the library's package.json so Vite picks the right variant automatically:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": {
"import": "./dist/debug/index.js",
"require": "./dist/debug/index.cjs"
},
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}If you enable allowDebugBuild but forget to add the development condition to your package.json exports, the plugin emits a build warning with the exact snippet to paste. Otherwise the debug variant is emitted but never resolved, leaving consumers without dev logs.
Three-tier behavior
| Tier | Stage | What happens |
|---|---|---|
| A plugin (this package) | — | Provides the runtime and the dual-build transform. |
| B library (e.g. a Pinia plugin) | vite build | allowDebugBuild: true → dist/index.* stripped, dist/debug/index.* keeps calls + auto-prepended fallback shim. |
| C end-user app with A | vite (dev) | Vite picks the development condition → debug variant resolved → A's browser runtime defines the real $debug first → the fallback shim leaves it alone → logs appear. |
| C end-user app with A | vite build | Vite picks the default condition → standard variant resolved → zero $debug references in C's bundle, regardless of what C does in its own source. |
| C end-user app without A | vite (dev) | Debug variant resolved → the fallback shim installs a no-op $debug and emits a one-time console.warn advising to install A. |
| C end-user app without A | vite build | Standard variant resolved → already stripped → silent, no crash. |
Fallback shim in 0.6
The 0.6 fallback shim installs a bare function with no methods. In an app that does not load the plugin, base calls ($debug(level, payload)) are safe no-ops. But $debug.ns(...), assert, time/timeEnd, count, group/groupEnd, enable, disable, setLevel and setPrefixFilter throw a TypeError. In library code, either use only base calls, or build with libraryPrefix: true: its shadow guards every method except setPrefixFilter. Fixed in 1.0.
allowDebugBuild removes the need for consumers to know anything about this plugin. Library authors don't write a fallback shim by hand: the plugin auto-prepends it to every debug-variant entry chunk.
Optional peer dependency
Declare the plugin in B's package.json as an optional peer, so consumers that don't want logs can still install the library:
{
"peerDependencies": {
"@mars-tech/vite-debug-plugin": ">=0.2.0"
},
"peerDependenciesMeta": {
"@mars-tech/vite-debug-plugin": { "optional": true }
}
}Note
For libraries you can't rebuild with allowDebugBuild, the consuming app can strip a dependency's baked-in $debug calls at its own build with the stripInDeps option.
Branding library logs with libraryPrefix
By default a library's $debug calls render with the consuming app's prefix: the app's plugin instance installs the global $debug, and the library's bare calls inherit whatever prefix it was configured with. So a log from inside your library shows up as App Info …, giving the developer no signal that it came from your library.
Set libraryPrefix: true (alongside allowDebugBuild) to keep your library's logs branded with its own prefix wherever it's used:
// library vite.config.ts
plugins: [
mtDebug({ allowDebugBuild: true, libraryPrefix: true, prefix: "foo" }),
];Now every library log renders with foo regardless of the consuming app's own prefix (here the app is configured with prefix: "bar"):
foo Info [cart/action] - cart.add(Widget ×2) ← from the library
bar Info - user clicked checkout ← from the appHow it works: the debug variant gets a tiny module-local $debug shadow prepended to each chunk. It forwards to the app's real $debug but forces { prefix } as a last-write option, so the library's prefix cannot be overridden by the consumer or a per-call option. Your library code stays plain $debug(...): nothing to remember at each call site, nothing consumers can turn off. If the consuming app has no $debug at all, the shadow warns once and no-ops (and, unlike the 0.6 fallback shim, ns, assert and the helper methods are no-ops too; only setPrefixFilter is not guarded).
The branded forms are the base call, $debug.ns(...) and $debug.assert(...). Utility helpers (time, count, group) take no per-call options, so their output still shows the consumer's prefix.
To mute or isolate a branded library in the consuming app, see Prefix filtering.