Files
IOT_WEB/node_modules/nostics/dist/index.mjs
T
2026-08-21 15:36:24 +08:00

187 lines
6.2 KiB
JavaScript

//#region src/formatters/plain.ts
/**
* Renders a diagnostic into a multi-line, unicode-decorated string suitable
* for terminal output. The first line is `[<name>] <message>`; optional
* details (`fix`, `sources`, `docs`) follow with `├▶`/`╰▶` connectors.
*/
function formatDiagnostic(diagnostic) {
const header = `[${diagnostic.name}] ${diagnostic.message}`;
const details = [];
if (diagnostic.fix) details.push(`fix: ${diagnostic.fix}`);
if (diagnostic.sources?.length) details.push(`sources: ${diagnostic.sources.join(", ")}`);
if (diagnostic.docs) details.push(`see: ${diagnostic.docs}`);
if (details.length === 0) return header;
return [header, ...details.map((detail, i) => {
return `${i < details.length - 1 ? "├▶" : "╰▶"} ${detail}`;
})].join("\n");
}
//#endregion
//#region src/utils.ts
/**
* Transforms a value or a function that returns a value to a value.
*
* @param valFn either a value or a function that returns a value
* @param args arguments to pass to the function if `valFn` is a function
*
* @internal
*/
function toValueWithArgs(valFn, ...args) {
return typeof valFn === "function" ? valFn(...args) : valFn;
}
//#endregion
//#region src/diagnostic.ts
/**
* Creates a console reporter that renders each diagnostic with `formatter` and
* prints the result via `console[method]`. Both default sensibly (`'warn'` and
* {@link formatDiagnostic}); `method` can also be overridden per call through
* the reporter options.
*/
/* @__NO_SIDE_EFFECTS__ */
function createConsoleReporter({ method: defaultMethod = "warn", formatter = formatDiagnostic } = {}) {
return (diagnostic, { method = defaultMethod } = {}) => {
console[method](formatter(diagnostic));
};
}
const captureStackTrace = Error.captureStackTrace;
var Diagnostic = class Diagnostic extends Error {
name;
/**
* The diagnostic code, e.g. `MATH_E001`.
* Also appears as the `name` property.
*/
code;
/**
* URL to extended documentation for this diagnostic code.
* Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.
*/
docs;
/**
* Optional actionable instructions on how to resolve the problem.
*/
fix;
/**
* Locations in user code that contributed to this diagnostic, in
* `file:line:column` format. Relevant when the stack trace doesn't reflect
* the user's source (e.g. compilers, bundlers), otherwise redundant with the
* stack and should be omitted.
*/
sources;
/**
* Alias for {@link Error.message}: the reason this diagnostic was raised.
*/
get why() {
return this.message;
}
/**
* @param init structured initializer; `why` is required
* @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}
* so the top of the trace is the `new Diagnostic(...)` call site.
* `defineDiagnostics` passes its action method to strip its own frames too.
* Ignored on engines without `Error.captureStackTrace`.
*/
constructor(init, captureFrom = Diagnostic) {
super(init.why, { cause: init.cause });
this.code = this.name = init.code;
this.fix = init.fix;
this.docs = init.docs;
this.sources = init.sources;
captureStackTrace?.(this, captureFrom);
}
/**
* Converts the diagnostic into a serializable structured object.
*/
toJSON() {
return {
name: this.name,
why: this.why,
fix: this.fix,
docs: this.docs,
sources: this.sources,
cause: this.cause,
stack: this.stack
};
}
};
/**
* Resolves the docs URL for a code from a `docsBase` (string template or
* resolver function). Shared by {@link defineDiagnostics} and
* {@link defineProdDiagnostics}. Per-code `docs` overrides are handled by the
* caller; this only covers the `docsBase`-derived case.
*
* @internal
*/
function deriveDocs(docsBase, code) {
return typeof docsBase === "string" ? `${docsBase}/${code.toLowerCase()}` : docsBase?.(code);
}
/**
* Creates a typed diagnostics object from a set of code definitions. Each
* code becomes a callable {@link DiagnosticHandle}: invoke to report, or
* `throw` the result to raise. No `new` required, no proxy.
*/
/* @__NO_SIDE_EFFECTS__ */
function defineDiagnostics(options) {
const reporters = options.reporters ?? [];
const result = {};
const { docsBase } = options;
for (const code of Object.keys(options.codes)) {
const def = options.codes[code];
const docs = def.docs === false ? void 0 : def.docs || deriveDocs(docsBase, code);
const handle = (params = {}, reporterOptions = {}) => {
const diagnostic = new Diagnostic({
code,
why: toValueWithArgs(def.why, params),
fix: toValueWithArgs(def.fix, params),
docs,
cause: params.cause,
sources: params.sources
}, handle);
for (const reporter of reporters) reporter(diagnostic, reporterOptions);
return diagnostic;
};
result[code] = handle;
}
return result;
}
//#endregion
//#region src/prod-diagnostics.ts
/**
* Production counterpart to {@link defineDiagnostics}. Returns a `Proxy` that
* builds a minimal {@link Diagnostic} for any accessed code: the code becomes
* the instance `name`, `docs` is derived from `docsBase`, and `why` points to
* the docs URL when one exists (empty otherwise, so the thrown header is just
* the code). It carries no catalog text, so it stays tiny in a bundle.
*
* The strip plugin (`@nostics/unplugin`) can rewrite a `defineDiagnostics()`
* call into a `process.env.NODE_ENV === 'production'` ternary that selects this
* factory in production, dropping every `why`/`fix` string from the bundle.
*
* @example
* ```ts
* const diagnostics = defineProdDiagnostics({ docsBase: 'https://docs.example.com' })
* throw diagnostics.NUXT_B2011() // NUXT_B2011: https://docs.example.com/nuxt_b2011
* ```
*/
/* @__NO_SIDE_EFFECTS__ */
function defineProdDiagnostics(options = {}) {
const { docsBase, reporters = [] } = options;
return new Proxy({}, { get(_target, code) {
if (typeof code !== "string") return void 0;
const handle = (params = {}, reporterOptions = {}) => {
const docs = deriveDocs(docsBase, code);
const diagnostic = new Diagnostic({
code,
why: docs ?? "",
docs,
cause: params.cause,
sources: params.sources
}, handle);
for (const reporter of reporters) reporter(diagnostic, reporterOptions);
return diagnostic;
};
return handle;
} });
}
//#endregion
export { Diagnostic, createConsoleReporter, defineDiagnostics, defineProdDiagnostics, formatDiagnostic };
//# sourceMappingURL=index.mjs.map