English
English
Dark theme
Version 1.0.0 is a major release. This page takes a project from 0.6.1 to 1.0.0: first what can need action, each with a before/after snippet, then the changes in behaviour that need none but can surprise you. The plugin's migration guide on GitLab covers every edge case, and the changelog on GitLab lists every fix.
In the snippets, // 0.6.1 marks code written for 0.6.1 and // 1.0.0 its replacement. A text block shows what is printed, word for word. MARS-TECH there is the default prefix: yours shows instead.
stripInDeps stopped working.tsconfig includes your vite.config.ts (as tsconfig.node.json does under vue-tsc -b), the old option names are type errors: rename them, as listed in Renamed options.stripInDeps, it now strips nothing: replace it, as described in stripInDeps.MTPlugin, MTFormatContext, InstallStubOptions or GlobalAccess, those types are gone: see Removed and renamed types.Map, Set, RegExp, Date, bigint and class instances render differently, and a last argument that is not an options object is now printed: see Changed output and Options in the last argument.redact, three changes can need action (see Redaction): RegExp for the secret's shape;$1 convention): use a lookbehind;$debug prints nothing, with one warning, where it cannot read them: SSR in a process of its own or in workerd, Vitest 3's forks pools, and Vitest browser mode.Redaction is a safety net, not a vault: it catches a secret logged by mistake. Never log one on purpose.
node -v prints 20.19+ or 22.12+, locally and in every CI image.npm install -D @mars-tech/vite-debug-plugin@^1.0.0.vite dev once and read the lines that start with your prefix ([MARS-TECH] by default): each one says what to change. Reload any browser tab left open from before: a tab still running the 0.6.1 runtime forwards nothing to the terminal until it reloads.stripInDeps, as described in stripInDeps.globalName is a plain JavaScript identifier: mtDebug() now throws otherwise.$debug calls that pass a variable as the last argument (see Options in the last argument).redact: a RegExp beside each string entry the browser console must mask, a lookbehind for each capture group, and every RegExp linear-time (see Redaction).@vue/devtools-api if you want the Vue DevTools panel.vite build and read its warnings: each new one points at a call that would throw in production (see Production builds).| Requirement | 0.6.1 | 1.0.0 |
|---|---|---|
Node.js (engines) | >=20.19.0 | ^20.19.0 || >=22.12.0, tested on 20.19, 22.12 and 24 |
| Vite (peer dependency) | 6, 7 or 8 | unchanged |
@vue/devtools-api | not used | optional peer dependency, 6, 7 or 8 |
ESLint, for /eslint | 9 (flat config) and .eslintrc | 9 and 10 (flat config); .eslintrc on ESLint 9 and older |
Safari, with redactDefaults | no minimum | 16.4 or newer: the default patterns use lookbehind |
EBADENGINE warning, refuses to install with engine-strict, and Yarn 1 refuses by default. See Requirements.@vue/devtools-api is only needed for the Vue DevTools panel (Vue and Nuxt apps). Without it the integration is left out and nothing else changes. Pinia or Vue Router may already install it, but under pnpm's strict layout a transitive copy does not count: install it yourself (npm install -D @vue/devtools-api), then restart the dev server..eslintrc: see TypeScript and ESLint.@jridgewell/trace-mapping, finds each call's source location.The plugin reads every old key, maps it onto its new name when mtDebug() runs, and prints one warning per group of keys. The old keys keep working until 2.0.0.
| 0.6.1 | 1.0.0 | Notes |
|---|---|---|
browser | output.console | It only ever switched the browser console. |
terminal | output.terminal | false now also silences server-side output. |
showSourceLocation | showSource | Same name as the per-call option it sets the default for. |
allowDebugBuild | library.debugBuild | For library authors. |
libraryPrefix | library.brandPrefix | Without debugBuild, it now warns instead of doing nothing. |
testStub | test | testStub: false becomes test: false. |
testLog | test | testLog: true becomes test: 'log'. |
// 0.6.1
mtDebug({
browser: false,
showSourceLocation: false,
allowDebugBuild: true,
libraryPrefix: true,
testLog: true,
});[MARS-TECH] "allowDebugBuild" and "libraryPrefix" moved to "library" in 1.0.0. Use library: { debugBuild: true, brandPrefix?: true }. The old names still work until 2.0.0.
[MARS-TECH] "browser" moved to "output" in 1.0.0. Use output: { console, terminal, inspect, devtools }. The old name still works until 2.0.0.
[MARS-TECH] "showSourceLocation" moved to "showSource" in 1.0.0. It is the same switch as the per-call showSource option. The old name still works until 2.0.0.
[MARS-TECH] "testLog" moved to "test" in 1.0.0. Use test: 'stub' (default), test: 'log', or test: false. The old name still works until 2.0.0.// 1.0.0
mtDebug({
output: { console: false },
showSource: false,
library: { debugBuild: true, brandPrefix: true },
test: "log",
});output holds one switch per surface: console and terminal, and three new in 1.0: inspect (live values in the browser console), devtools (Vue DevTools) and file (an NDJSON log). The first four default to true; file is off unless you set it. With console, terminal and devtools all false and file off, the page gets a no-op $debug, so your calls still do not throw. See output.
terminal: false now silences the server side too: SSR and the Vite process, where 0.6.1 still printed SSR output. $debug also stays defined in the Vite process: in 0.6.1, a call from vite.config.ts or another plugin threw ReferenceError until an SSR module had loaded. If you relied on SSR output with terminal: false, leave output.terminal on: the same switch also covers the lines forwarded from the browser.
library is for library authors: allowDebugBuild: true, libraryPrefix: true becomes library: { debugBuild: true, brandPrefix: true }. libraryPrefix: true without allowDebugBuild did nothing in 0.6.1, silently. It still does nothing, and now says so:
[MARS-TECH] library.brandPrefix has no effect without library.debugBuild — the prefix shadow is only prepended to the debug/ variant that debugBuild emits.See Library authoring.
showSource, the plugin option, now has the same name as the per-call showSource option it sets the default for.
test turns two booleans into one option with three values: 'stub' (the default, a no-op $debug), 'log' (a real console logger) and false (register nothing). One combination changes what gets installed: { testStub: false, testLog: true } installed nothing in 0.6.1, and now maps to 'log', because an explicit testLog: true is read as the intent. If you really wanted nothing registered, write test: false. See Testing.
Development builds of 1.0 had flat inspect and devtools keys: they are mapped onto output with the same warning.
When a config sets an old key and its replacement, the replacement wins where they overlap, and the conflict gets its own warning:
// vite.config.js, half-migrated
mtDebug({ browser: false, output: { console: true } });[MARS-TECH] both "browser" and "output" are set; "output" wins where they overlap. Remove "browser".
[MARS-TECH] "browser" moved to "output" in 1.0.0. Use output: { console, terminal, inspect, devtools }. The old name still works until 2.0.0.The mapping happens at run time, for configs nothing type-checks: Vite never type-checks vite.config.ts itself. MTPluginOptions lists only the 1.0.0 names, so if a tsconfig includes your vite.config.ts, an old key fails the type check even though it still works at run time:
error TS2353: Object literal may only specify known properties, and 'browser' does not exist in type 'MTPluginOptions'.There is no typed spelling of the old names: rename the keys.
stripInDepsstripInDeps let a consuming app strip the $debug calls a dependency shipped in its published bundle. It is removed. Passing it warns and strips nothing:
// 0.6.1
mtDebug({ stripInDeps: ["@acme/cart-store"] });[MARS-TECH] the "stripInDeps" option was removed in 1.0.0. A dependency shipping $debug calls should be built with library: { debugBuild: true }, which needs no configuration in the consuming app — ask its author. See MIGRATION.md for the migration.MIGRATION.md is the plugin's migration guide, which this page follows. Dependencies are never rewritten now, so the dependency's calls reach your production bundle as written, and throw ReferenceError: $debug is not defined there.
The fix belongs to the library. Ask its author to build with library: { debugBuild: true } (see Library authoring): that build publishes a stripped variant for production and a debug/ variant for development, and the consuming app needs no configuration at all.
Until then, define a no-op $debug before the dependency runs, from a classic script (one without type="module"), which runs before every module of the page, however the bundler splits or orders your chunks:
<!-- index.html, in <head>, before any module script -->
<script src="/debug-stub.js"></script>// public/debug-stub.js — a classic script, so it runs before every module.
// In `vite dev` the plugin's runtime loads after it and replaces it.
{
const noop = () => {};
globalThis.$debug ??= Object.assign(noop, {
ns: () => noop,
enable: noop,
disable: noop,
setLevel: noop,
setPrefixFilter: noop,
assert: noop,
time: noop,
timeEnd: noop,
count: noop,
group: noop,
groupCollapsed: noop,
groupEnd: noop,
table: noop,
dir: noop,
});
}Files in public/ are copied to the build as they are, so the plugin never strips the stub, and an external <script src> raises no build warning. Do not import the stub from your entry module instead: when the dependency lands in a shared chunk, the bundler places that chunk's import first, and its top-level calls run before the stub. Unlike stripInDeps, the stub keeps the calls in your bundle and still evaluates their arguments: it only makes them harmless. For server-side rendering, run the same code first in the Node.js server that serves your production build.
| 0.6.1 | 1.0.0 |
|---|---|
MTPlugin from /helpers | Plugin from vite: MTPlugin was an alias of it |
MTFormatContext from /helpers | none: it described a format hook that never existed |
InstallStubOptions from /testing | MTInstallStubOptions, same shape |
GlobalAccess from /eslint | MTGlobalAccess, same shape |
// 1.0.0
import type { Plugin } from "vite";
import type { MTInstallStubOptions } from "@mars-tech/vite-debug-plugin/testing";
import type { MTGlobalAccess } from "@mars-tech/vite-debug-plugin/eslint";TypeScript points at the new names:
error TS2724: '"@mars-tech/vite-debug-plugin/testing"' has no exported member named 'InstallStubOptions'. Did you mean 'MTInstallStubOptions'?If your code used MTFormatContext as a shape, declare it locally. What the plugin really produces for one call is MTLogRecord, from /helpers (see New in 1.0).
The package no longer ships .map files: they were half of its size. The JavaScript in dist/ is not minified, so the plugin's own stack frames stay readable without them. A library.debugBuild build no longer writes .map files into debug/ either: the map it copied there described the stripped production chunk, so it pointed at the wrong lines. If the files field of your package.json names dist/debug/*.map, drop that entry.
mtDebugGlobals from @mars-tech/vite-debug-plugin/eslint now takes the same options object as mtDebugFlatConfig:
// 0.6.1
mtDebugGlobals("$log", "readonly");
// 1.0.0
mtDebugGlobals({ globalName: "$log", access: "readonly" });The positional form still works until 2.0.0. It prints no warning: its type is marked @deprecated, so editors strike it through. mtDebugGlobals() with no argument is unchanged.
The per-call { raw } option, which only development builds of 1.0 had, is a deprecated alias of { inspect }: it still works, with a one-time warning, until 2.0.0.
$debug now takes any number of payloads, like console.log, and substitutes printf directives (%s, %d, %o…). The last argument counts as options only when it is a plain object whose every key is an option name (tag, showSource, showStack, prefix, inspect); in 0.6.1, the third argument always was. The terminal lines below leave out the (file:line:col) that ends each line by default:
| Call | 0.6.1 | 1.0.0 |
|---|---|---|
$debug("info", "saved", { tag: "cart" }) | MARS-TECH Info [cart] - saved | MARS-TECH Info [cart] - saved |
$debug("info", "saved", undefined) | MARS-TECH Info - saved | MARS-TECH Info - saved undefined |
$debug("info", "saved", {}) | MARS-TECH Info - saved | MARS-TECH Info - saved {} |
$debug("info", "saved", { tag: "cart", retry: 1 }) | MARS-TECH Info [cart] - saved | MARS-TECH Info - saved {"tag":"cart","retry":1} |
$debug("info", "user", { id: 1 }) | MARS-TECH Info - user | MARS-TECH Info - user {"id":1} |
$debug("info", "%s scored %d", "Ann", 42) | MARS-TECH Info - %s scored %d | MARS-TECH Info - Ann scored 42 |
The second row is the one to look for: a wrapper that forwards optional options now prints undefined whenever its caller passed none. Pass them only when you have them:
// 0.6.1
import type { MTCallOptions } from "@mars-tech/vite-debug-plugin/helpers";
export function logSaved(options?: MTCallOptions) {
$debug("info", "saved", options);
}// 1.0.0
import type { MTCallOptions } from "@mars-tech/vite-debug-plugin/helpers";
export function logSaved(options?: MTCallOptions) {
if (options) $debug("info", "saved", options);
else $debug("info", "saved");
}The two-argument form is unchanged, and an options object keeps its autocomplete. A misspelt key ({ tagg: "x" }) is no longer ignored: it is logged, which shows you the typo. See Multiple values and printf.
$debug.enable(null)enable now accepts null: it drops the runtime namespace filter, including the copy saved in localStorage, and returns to the plugin's filter default (every namespace, unless you set one):
// 1.0.0
$debug.enable("cart*"); // only the cart namespaces, until cleared
$debug.enable(null); // back to the configured defaultIn 0.6.1, enable(null) was a type error, and from JavaScript it saved the text "null" as the filter: after the next reload, only a namespace named null printed.
redact and redactDefaults are a safety net, not a vault: they catch a secret that reaches a $debug call by mistake. What 1.0.0 guarantees is that the plugin never introduces a leak: every rule runs over every string a call hands the plugin, exactly as your code passed it, and over the line as printed; every match is masked whole; and nothing the plugin does afterwards brings a character of it back. A secret your code transformed before logging it (cut, encoded, escaped) is another string, outside that guarantee. See Redacting secrets.
A string entry is usually the secret itself. 0.6.1 wrote every entry into the runtime it served to the page, where any script on the dev origin could read it, and into the SSR runtime. 1.0.0 writes string entries into no module: they stay in the memory of the process that loads your config, and never leave the dev server. They mask the dev terminal, SSR output, the log file and test: 'log' outside a browser, never the browser console or Vue DevTools. To mask a value there too, add a RegExp for its shape: a RegExp is sent to the page as written, so it must never contain the secret.
// 1.0.0
mtDebug({
redact: [
process.env.STRIPE_KEY ?? "", // the value: terminal, SSR, log file, test: 'log'
/sk_live_[A-Za-z0-9]+/, // its shape: every surface, the browser included
],
});An empty or unset string is ignored, a number is matched as the literal it prints as, and any other value is ignored with a warning. In 0.6.1, an unset entry (undefined) or a number threw while the config loaded, and an empty string masked between every character. See String entries and RegExp entries.
The dev server now masks each line a page forwards with every rule, where 0.6.1 printed it as the page sent it. Your RegExp entries therefore also run in the Vite process, over text any script on the dev origin can send: keep them linear-time, as explained in Writing a RegExp entry.
0.6.1 re-emitted a pattern's first capture group in front of [redacted]. 1.0.0 masks the whole match, so a group keeps nothing: a pattern with a leading group now masks its key too, which is more than before, never less. To keep the text in front of a secret, match it with a lookbehind:
// 0.6.1
mtDebug({
redact: [/("password"\s*:\s*)"[^"]*"/], // $1 kept: only the value is masked
});// 1.0.0
mtDebug({
redact: [/(?<="password"\s*:\s*)"[^"]*"/], // a lookbehind: only the value is masked
});For $debug("info", { password: "hunter2" }), the 0.6.1 pattern now prints {[redacted]}; the lookbehind prints {"password":[redacted]}, as 0.6.1 did. A group that held the secret kept it in 0.6.1: /token=([0-9a-f]+)/ printed ?deadbeef01[redacted] for ?token=deadbeef01, where 1.0.0 prints ?[redacted]. The redactDefaults patterns are written as lookbehinds and print what they printed. A RegExp's sticky flag y is dropped.
$debug prints nothingWith string entries configured, each printer in Node.js reads them from the process that loads your config. Where a printer cannot, $debug prints nothing rather than print them unmasked, and says so once. 0.6.1 printed there, through the entries it wrote into the runtime.
With string entries, $debug prints nothing in | To see output there |
|---|---|
SSR in a process of its own, or in workerd (@cloudflare/vite-plugin) | a RegExp for each value's shape |
Vitest 3's forks and vmForks pools (forks is its default) | Vitest 4, or pool: "threads" |
| A Vitest project that runs in a browser, and Vitest's browser page | a RegExp for each value's shape |
| A worker thread started before the plugin | restart the worker |
The SSR runtime, for instance, says:
[MARS-TECH] $debug prints nothing in this process: the string entries of your redact option do not reach it, and it does not print what it cannot mask. They reach SSR in the Vite process and in a worker thread started after the plugin, and Vitest workers (the forks and vmForks pools from Vitest 4). Elsewhere - SSR in a process of its own, workerd - describe each such value with a RegExp for its shape instead.Without string entries, every printer prints, masked by your RegExp entries and the defaults. The rarer cases are listed in Where string entries cannot reach.
tag, a per-call prefix, the labels of count, time, timeEnd and group, an Error's message and stack, and the source path, then over the line as printed. 0.6.1 matched the printed message alone.", a \ or a newline is masked inside a logged object.test: 'log' redacts, with your redact and redactDefaults. 0.6.1 printed every secret there.RegExp entry or redactDefaults is configured: the browser console and Vue DevTools get the redacted text instead of the live object, and table() and dir() print the redacted summary. { inspect: true } opts one call back in, in the console only. See Live values in the browser.%d, %i or %f number that reads differently from its argument ("042917" prints 42917) is masked when a rule matches the argument. On a line from the browser, with string entries configured, the terminal and the log file mask such a number whatever its argument.redactDefaults covers more: Authorization: Basic credentials; the value of passphrase, x-api-key, private_key, credentials and cookie; a secret key's value in a [key, value] pair (a Map, Object.entries()), and in the JSON a logged string holds. One case masks less: an email glued to the end of another with no separator (a@x.com%2Cb@y.org) leaves the second one visible.server.forwardConsole left unset (Vite turns it on when it detects a coding agent), your own console.error and console.warn calls no longer reach the terminal through Vite's console forwarding; unhandled errors still do. A server.forwardConsole you set is kept. See Vite 8 console forwarding.Some payloads rendered through JSON.stringify, which drops what makes them worth logging. The one-line summary (the dev terminal, the browser console label, the log file, test: 'log') changed for these types:
| Payload | 0.6.1 | 1.0.0 |
|---|---|---|
new Map([["sku-1", 2], ["sku-2", 1]]) | {"sku-1":2,"sku-2":1} | Map(2) [["sku-1",2],["sku-2",1]] |
new Set(["a", "b"]) | ["a","b"] | Set(2) ["a","b"] |
/^tok_[a-z]+$/gi | {} | /^tok_[a-z]+$/gi |
new Date("2026-10-14T08:00:00.000Z") | "2026-10-14T08:00:00.000Z" | 2026-10-14T08:00:00.000Z |
123n | 123 | 123n |
new Cart(), with class Cart { items = 2 } | {"items":2} | Cart {"items":2} |
{ a: shared, b: shared }, with shared = { id: 1 } | {"a":{"id":1},"b":"[Circular]"} | {"a":{"id":1},"b":{"id":1}} |
A call made in the Vite process itself (from vite.config.ts, another plugin or same-process SSR) printed {} for a Map and a Set in 0.6.1; it now renders as above. Strings, numbers, plain objects, arrays and Errors render as before. A test that asserts on the old text needs updating. The payload rendering table lists the form for every type.
Errors stay expandable, as with console.log, unless a redactor withholds them. For the 0.6.1 one-line look, set output: { inspect: false }. See Live object inspection.$debug.group() opens a collapsible console.group in the browser instead of indenting the text. The terminal and test: 'log' still indent, and no longer repeat the label as a tag: $debug.group("checkout") prints MARS-TECH Debug - checkout, where 0.6.1 printed MARS-TECH Debug [checkout] - checkout.file:line:col is the original position in your .vue or .ts file, relative to the Vite root and counted from 1 (src/Cart.vue:12:3), where 0.6.1 printed a served URL or an absolute path. For a call the dev transform cannot record, see My log's file:line is a few lines off.@vue/devtools-api installed, a MARS-TECH tab appears in Vue DevTools. output: { devtools: false } turns it off. See Vue DevTools.None of these needs a code change.
enabled: true changes nothing, as in 0.6.1, and prints no warning. Unset already means "on in vite dev, stripped in vite build"; true never forced the runtime into a production build. For a debug build, use library: { debugBuild: true }.enabled: false now covers test: 'log' too: it falls back to the no-op stub, where 0.6.1 still installed the console logger under Vitest.globalName must be a plain JavaScript identifier that is not a reserved word ($debug, myDebug). Anything else, such as a dot, a space or a hyphen, now throws when mtDebug() runs; in 0.6.1 it silently broke the strip and a library's debug/ bundle.mtDebug() used to print every browser log twice. See Every log appears twice.plugins (under build.rollupOptions.plugins, or in a Rollup or Rolldown config), the plugin only strips, without a warning. 0.6.1 assumed vite dev there and shipped development code to production.levels and minLevel together still warn, but only when levels is not empty: an empty levels: [] defers to minLevel without a warning.colors[level].ansi overrides, where 0.6.1 printed the built-in colours.\r, C1 controls, bidi overrides. A string that already carries ANSI colours (from chalk, say) prints its codes as text. Tabs and newlines are kept.tag and prefix at 256, the source file at 1 024, the stack at 16 384 (past it, [stack omitted: N chars]). A long string logged in the browser is cut in the terminal.count() and time() remember up to 1 000 labels each, forgetting the least recently used, and the group indent stops growing at 32 levels.The NDJSON log file is new in 1.0 and has limits of its own:
output.fileMaxBytes sets another limit, in bytes; Infinity lifts it. See Sessions and size.vite-plugin-vue-devtools, Nuxt DevTools, Vite DevTools, devframes and TanStack Devtools. The terminal gets one warning naming the tool, and there is no override. A log an earlier session left is then not truncated either: delete it. If you need the log, run those tools in a separate dev session: in Nuxt, devtools: { enabled: false }; in Vite 8, no devtools: true and no DevTools() plugins..gitignore holding * into .mt-debug/, and into any log directory it creates, never over one that is there. That keeps the log out of git, and out of Tailwind CSS's automatic source detection. Next to @tailwindcss/vite, a log in a directory of your own whose .gitignore does not hold * is not opened, with one warning. @tailwindcss/postcss is not detected: add the file yourself..js, .ts, .jsx, .tsx, .mjs, .mts and .vue modules. 1.0.0 strips every module of your own source that names the global (.cjs, an inline <script type="module">, a virtual module, Svelte, Astro, MDX, VitePress pages), plus web workers and the scripts Vite copies instead of bundling (a service worker, a worklet, a ?url import). Calls there used to ship and throw.$debug that is not a call (const d = $debug), a computed method ($debug['time']()), a call in a classic inline <script> or an on* attribute, a copied script left as written (a ?raw import, a .ts file), and a file the strip cannot parse. Fix each one where it points.globalThis['$debug'](...) and window["$debug"] now strip like the dotted forms.$debug.ns('x') statement strips to void 0; a stored logger (const log = $debug.ns('x')) still strips to a callable no-op.stripInDeps.See What the strip covers, Method and chained forms, Web workers and Scripts Vite copies.
setPrefixFilter, groupCollapsed, table and dir no longer throw under Vitest.test: 'log' gives each Vitest project its own configuration, prints several payloads and printf, renders payloads like dev, and redacts.--browser included) gets what the dev page gets: your RegExp entries and redactDefaults, never string entries. With string entries configured, $debug prints nothing there.@mars-tech/vite-debug-plugin/testing/log is removed from setupFiles if you listed it under test: 'log': the plugin registers a module generated from your config instead. For a configured logger without the plugin, call installMtDebugConsole({ prefix, redact, … }) from /testing in your own setup file.See Seeing $debug output in tests.
library.brandPrefix, the consuming app sees no file:line:col for your library's calls, unless a call passes { showSource: true }. See Library logs carry no source location.redact is not shipped in debug/. The consuming app's configuration masks your library's output, or nothing does. Never pass a secret to $debug in library code.$debug payload get the same file names.umd and iife builds wrap the brandPrefix shadow in a block, so it no longer declares a page-wide $debug.$debug?.(…)) are removed from the production variant whatever the minifier or build.target, and template calls in debug/ reach the app's global $debug.Nothing below requires a change, but it is worth adopting:
mtDebug({ output: { file: true } }) writes every call, already masked, to .mt-debug/dev.ndjson under the Vite root. For security reasons, do not use it when your code logs secrets, even redacted ones: the log is a file on disk that any tool reading files can read. Keeping it from the page is a safety net, not a vault: the plugin blocks every known route from a page to the log, and a log you keep is safer outside the project (outside server.fs.allow, which in a monorepo is the workspace root), where Vite refuses every file. Run Vite 6.4.2, 7.3.2, 8.0.5 or later: earlier releases hand any file to a page script, your .env included. See Keeping the log from the page.mtDebug({ filter: "cart*" }), overridden at run time by ?mt-debug=, MT_DEBUG or $debug.enable("auth*"). See filter.mtDebug({ showStack: true }) attaches the redacted stack to every call that carries an Error. See showStack.$debug("info", "%s scored %d", name, score). See Multiple values and printf.$debug.groupCollapsed("checkout"), $debug.table(rows, ["name"]), $debug.dir(user). See Utility helpers.$debug("info", document.body) gives a node you can reveal in the Elements panel. See Live object inspection.MTLogRecord from /helpers is one call as the dev server prints it and writes it to the log file; MTLogLine, a line of that file, adds t, seq and origin.import type { MTPluginOptions } from "@mars-tech/vite-debug-plugin". See Subpath exports.installMtDebugConsole({ prefix: "APP", redactDefaults: true }) from /testing. See A logger in your own setup file../package.json is exported, for tools that read a dependency's manifest.CommonJS projects get CommonJS declarations. Under module: node16 (or nodenext before TypeScript 5.8), a CommonJS file importing the plugin saw TS1479. The require condition now points at .d.cts files: drop any @ts-ignore you added for it.
The /client reference is unchanged. Keep /// <reference types="@mars-tech/vite-debug-plugin/client" />. It now types the variadic call, enable(null) and the new helpers, under every moduleResolution.
If you implement MTDebug yourself (a typed stub, a custom global), add groupCollapsed, table and dir, and accept null in enable: a 0.6.1-shaped object no longer satisfies the type. The object the stripInDeps stub builds is a complete one.
MTDebugFn is overloaded: the typed (level, payload, options?) form and a variadic (level, ...payloads) form. A function you assign to it must accept any value after the level, so a third parameter typed MTCallOptions now fails with TS2322:
// 1.0.0
import type { MTDebugFn, MTLevel } from "@mars-tech/vite-debug-plugin/helpers";
export const log: MTDebugFn = (level: MTLevel, ...rest: unknown[]) => {};colors is typed MTColorOverrides: any subset of levels, each with any subset of label, css and ansi. mtDebug({ colors: { warning: { label: "Warning" } } }), a type error in 0.6.1, now type-checks.
MTCallOptions gains inspect, which forces or suppresses the live value for one call.
ESLint 10 removed .eslintrc. The flat config works on ESLint 9 and 10. If you move to 10, switch from mtDebugGlobals() in .eslintrc to the default export:
// eslint.config.js
import mtDebug from "@mars-tech/vite-debug-plugin/eslint";
export default [mtDebug];