first commit

This commit is contained in:
2026-10-07 11:08:05 +09:00
commit 6c7d3cc893
1811 changed files with 572221 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
//#region src/formatters/ansi.ts
/* @__NO_SIDE_EFFECTS__ */
function ansiFormatter(colors) {
return (d) => {
const header = `${colors.bold(colors.red(`[${d.name}]`))} ${d.message}`;
const details = [];
if (d.fix) details.push(`${colors.dim("fix:")} ${d.fix}`);
if (d.sources?.length) details.push(`${colors.dim("sources:")} ${d.sources.join(", ")}`);
if (d.docs) details.push(`${colors.dim("see:")} ${colors.cyan(d.docs)}`);
if (details.length === 0) return header;
return [header, ...details.map((detail, i) => {
return `${colors.dim(i < details.length - 1 ? "├▶" : "╰▶")} ${detail}`;
})].join("\n");
};
}
//#endregion
export { ansiFormatter };
//# sourceMappingURL=ansi.mjs.map
+194
View File
@@ -0,0 +1,194 @@
//#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;
/**
* Structured information associated with this diagnostic code.
*/
data;
/**
* 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;
this.data = init.data;
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,
data: this.data,
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,
data: toValueWithArgs(def.data, params)
}, 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 or data, 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
+126
View File
@@ -0,0 +1,126 @@
{
"name": "nostics",
"type": "module",
"version": "1.3.0",
"description": "Structured diagnostic code library",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"contributors": [
"Eduardo San Martin Morote <posva13@gmail.com>",
"Daniel Roe <daniel@roe.dev>"
],
"license": "MIT",
"homepage": "https://github.com/vercel-labs/nostics#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/vercel-labs/nostics.git"
},
"bugs": "https://github.com/vercel-labs/nostics/issues",
"keywords": [],
"sideEffects": false,
"exports": {
".": "./dist/index.mjs",
"./formatters/ansi": "./dist/formatters/ansi.mjs",
"./formatters/json": "./dist/formatters/json.mjs",
"./reporters/dev": "./dist/reporters/dev.mjs",
"./reporters/fetch": "./dist/reporters/fetch.mjs",
"./reporters/node": "./dist/reporters/node.mjs",
"./package.json": "./package.json"
},
"types": "./dist/index.d.mts",
"files": [
"dist",
"skills"
],
"devDependencies": {
"@posva/prompts": "^2.4.4",
"@size-limit/esbuild-why": "^14.0.0",
"@size-limit/preset-small-lib": "^14.0.0",
"@types/node": "^26.6.2",
"@types/semver": "^7.8.0",
"@vitest/coverage-v8": "5.0.1",
"conventional-changelog": "^8.1.3",
"conventional-changelog-angular": "^9.4.0",
"lint-staged": "^17.5.1",
"oxfmt": "^0.68.0",
"oxlint": "^1.80.0",
"oxlint-plugin-posva": "^0.0.2",
"publint": "^0.3.24",
"semver": "^7.8.5",
"simple-git-hooks": "^2.14.0",
"size-limit": "^14.0.0",
"tsdown": "^0.23.0",
"tsnapi": "^1.5.0",
"typescript": "^6.0.0",
"vite": "^8.3.0",
"vitest": "^5.0.1"
},
"simple-git-hooks": {
"pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm lint-staged"
},
"lint-staged": {
"*": [
"oxfmt --no-error-on-unmatched-pattern",
"oxlint --no-error-on-unmatched-pattern --fix"
]
},
"size-limit": [
{
"name": "defineDiagnostics from source",
"path": "src/index.ts",
"import": "{ defineDiagnostics }"
},
{
"name": "defineDiagnostics",
"path": "dist/index.mjs",
"import": "{ defineDiagnostics }"
},
{
"name": "defineProdDiagnostics",
"path": "dist/index.mjs",
"import": "{ defineProdDiagnostics }"
},
{
"name": "defineDiagnostics + createConsoleReporter",
"path": "dist/index.mjs",
"import": "{ defineDiagnostics, createConsoleReporter }"
},
{
"name": "defineProdDiagnostics + createConsoleReporter",
"path": "dist/index.mjs",
"import": "{ defineProdDiagnostics, createConsoleReporter }"
},
{
"name": "formatDiagnostic",
"path": "dist/index.mjs",
"import": "{ formatDiagnostic }"
},
{
"name": "full root",
"path": "dist/index.mjs",
"import": "*"
},
{
"name": "ansi formatter",
"path": "dist/formatters/ansi.mjs",
"import": "*"
}
],
"scripts": {
"build": "tsdown && pnpm --filter \"./packages/*\" run build",
"dev": "vitest",
"docs": "pnpm -C docs run dev",
"docs:build": "pnpm -C docs run build",
"play": "pnpm -C playground run dev",
"lint": "oxlint ${CI:+--deny-warnings} . && oxfmt --check .",
"lint:fix": "oxfmt && oxlint --fix .",
"fmt": "oxfmt",
"release": "node scripts/release.ts",
"size": "size-limit",
"test": "pnpm run build && pnpm run \"/^test:/\"",
"test:unit": "vitest run --coverage",
"test:types": "pnpm run typecheck",
"test:lint": "pnpm run lint",
"test:demo": "pnpm -C demo-lib run build && pnpm -C demo-lib run test && pnpm -C playground run build",
"typecheck": "tsc && pnpm --filter \"./packages/*\" run typecheck"
}
}