nostics について
nostics は、安定したコードや修正案、ドキュメントへのリンクを持つ診断を扱う TypeScript ライブラリです。レポーターとシリアライズに対応し、本番ビルドから報告専用の診断を除去できます。
主な機能
- 01型付き診断コードの定義
- 02修正案とドキュメントリンクの付与
- 03レポーターによる診断出力
- 04プロセス間で渡す診断のシリアライズ
- 05フォーマッターと Vite ビルドプラグイン
活用シーン
- 01ビルドや実行時エラーへの安定コード付与
- 02診断記録のファイルまたは URL への送信
- 03Vite 開発中の診断収集
- 04本番バンドルから報告専用診断を除去
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vercel-labs/ nostics --skill nosticsインストール後、Claude Code で/nosticsを入力して読み込まれたか確認します。
同梱ファイルreferences/documentation-site.mdreferences/migration.md
詳細
nostics
すべてのエラー条件は、安定したコード、ドキュメント URL、実行可能な修正方法を持つ型付きの Diagnostic(Errorを継承)になります。toJSON()でシリアライズできます。
Diagnostic:name(コード)、message/why(補間されたテキスト)、fix?、docs?、sources?('file:line:column')、cause?、toJSON()。これをスローし、instanceof Diagnosticで捕捉し、プロセス間でtoJSON()を送信できます。
defineDiagnostics
コードごとに呼び出し可能なハンドルを返します。ハンドルを呼び出すと、新しいDiagnosticが生成され、すべてのレポーターが順番に実行され、その後にDiagnosticが返されます。返り値をthrowすると例外が発生します(レポーターは引き続き実行されるため、スローされたDiagnosticもレポートされます)。
import { createConsoleReporter, defineDiagnostics } from 'nostics'
const diagnostics = /*#__PURE__*/ defineDiagnostics({
docsBase: (code) => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
reporters: [/*#__PURE__*/ createConsoleReporter()],
codes: {
NUXT_B1001: {
why: 'Could not compile template.',
fix: 'Check the template for syntax errors.',
},
NUXT_B2011: {
why: (p: { src: string }) => `Invalid plugin "${p.src}". src option is required.`,
fix: 'Pass a string path or an object with a `src` to `addPlugin()`.',
},
NUXT_W9001: { why: 'message', docs: false }, // per-code: string overrides docsBase, false opts out
},
})
docsBasestring | (code) => string | undefined:文字列の場合は/${code.toLowerCase()}が追加されます。関数の場合は完全な URL(または省略する場合はundefined)を返します。codes:各定義にはwhy(string | (params) => string。唯一の必須フィールドであり、Error.messageになります)が必要です。fix(string | (params) => string)、docs(string | false)、data(object | (params) => object)は任意です。各コードで解決されたdataには型が付与され、toJSON()に含まれます。reporters:呼び出しごとに実行されます。省略可能です。optionsの型は交差され、必須のレポーターオプションは呼び出し箇所でも必須になります。コードが常にthrowされるカタログでは、省略する(または[]を渡す)ようにしてください。スローされたDiagnosticにはすでにメッセージが含まれているため、コンソールレポーターを使うと一度出力された後、捕捉されないエラーからも再度表示され、重複が目に見える形で発生します。レポーターが必要な警告のみのケースと、致命的な例外をスローするケースでは、別々のカタログを使用してください。- パラメーターの推論:
why、fix、dataのパラメーターは交差され、呼び出し箇所で必須になります。whyで{ src }が必要で、fixで{ date }が必要な場合、呼び出しには{ src, date }が必要です。
呼び出し箇所
diagnostics.NUXT_B1001() // no params: report only
diagnostics.NUXT_B2011({ src: '/plugins/bad.ts' }) // params first
diagnostics.NUXT_B2011({
src,
cause: originalError,
sources: ['nuxt.config.ts:42:3'],
}) // runtime fields merge in
diagnostics.NUXT_B2011({ src }, { method: 'error' }) // reporter options second
throw diagnostics.NUXT_B2011({ src }) // raise
cause/sourcesはparamsオブジェクトに含めます。sourcesは、JSのスタックがライブラリ内部を指すビルド/設定の診断で特に重要です。if (err instanceof Diagnostic)で捕捉してから、.name、.message、.fix、.docs、.dataを読み取ります。
定義時のdataはdefineProdDiagnostics()には含まれません。その.dataはundefinedです。そのため、開発版/本番版の三項演算子では、.dataの型はコードのdata型またはundefinedになります。
レポーター
(diagnostic: Diagnostic, options?: Opts) => void。必須のoptions型を宣言すると、呼び出し箇所で2番目の引数が必須になり、型が付与されます。
| レポーター | インポート | 説明 |
|---|---|---|
createConsoleReporter(options?) | nostics | console[method](formatter(d))。methodのデフォルトは'warn'('log'|'warn'|'error')、formatterのデフォルトはformatDiagnosticです。どちらもオプションで指定でき、methodは呼び出しごとに上書きすることもできます。 |
createFetchReporter(url) | nostics/reporters/fetch | 診断JSONをURLにPOSTします。失敗は無視されます。 |
createFileReporter(options?) | nostics/reporters/node | NDJSONをローカルファイル(デフォルトは.nostics.log)に追記します。 |
createDevReporter() | nostics/reporters/dev | import.meta.hot.send()を介して、toJSON()をVite開発サーバーに送信します。 |
import type { DiagnosticReporter } from 'nostics'
const sentryReporter: DiagnosticReporter = (d) =>
sentry.captureMessage(d.message, { tags: { code: d.name } })
const audited: DiagnosticReporter<{ priority: number }> = (d, o) =>
audit.log({ name: d.name, priority: o.priority })
// → audited makes diagnostics.X({...}, { priority: 1 }) required and type-checked.
フォーマッター
| フォーマッター | インポート | 説明 |
|---|---|---|
formatDiagnostic | nostics | Unicode 文字で装飾されたプレーン文字列です(組み込みのレポーターが使用します)。 |
ansiFormatter(colors) | nostics/formatters/ansi | 色付きです。Colors インターフェース(red/yellow/cyan/gray/bold/dim。それぞれ (s) => string)を受け取ります。 |
jsonFormatter | nostics/formatters/json | toJSON() を介した JSON.stringify(diagnostic) です。 |
formatDiagnostic の出力では、詳細の順序は fix → sources → see で固定され、欠落しているフィールドは省略されます。
[NUXT_B2011] Invalid plugin `/plugins/bad.ts`. src option is required.
├▶ fix: Pass a string path or an object with a `src` to `addPlugin()`.
├▶ sources: nuxt.config.ts:42:3
╰▶ see: https://nuxt.com/e/b2011
Vite プラグイン(@nostics/unplugin、開発依存関係)
@nostics/unplugin/strip-transform(ライブラリ作成者向け、ビルド最適化)と @nostics/unplugin/dev-server-collector(アプリ開発者向け、開発時の収集)があります。どちらも unplugin ベースで、.vite()、.webpack()、.rollup() などを使用できます。
nosticsStripはdefineDiagnostics()に/*#__PURE__*/を付け、裸の診断式文をNODE_ENVガードで囲み、本番環境でツリーシェイクできるようにします。オプションはpackageName?(デフォルトは'nostics')です。throw/return/代入は動作の一部であるため、そのまま残ります。追跡するには、相対インポートを使用し、カタログを直接エクスポートしてください。ファクトリーラッパーや深いバレルは使わないでください。- プラグインは任意です。カタログに手動でアノテーションを付ければ、ビルド変換なしでも同じ本番出力になります。
defineDiagnostics(の前と、その内部の各レポーターファクトリー呼び出しの前に/*#__PURE__*/を付け(ここにあるすべての例と同様)、レポート専用の各呼び出し箇所を開発用ガードで囲んでください(process.env.NODE_ENV !== 'production' && diagnostics.CODE(p))。アノテーションは常にソースに記述してください。レポート専用の呼び出し箇所がガードされておらず、それらに手を加えずに取り除きたい場合は、プラグインを使用してください。
- プラグインは任意です。カタログに手動でアノテーションを付ければ、ビルド変換なしでも同じ本番出力になります。
nosticsCollectorは、Vite の WebSocket 経由でcreateDevReporter()の診断を監視し、createFileReporterを介して NDJSON として書き込みます。Vite 専用です。オプションはlogFile?(デフォルトは.nostics.log)、debug?(デフォルトは!!process.env.DEBUG)です。
// vite.config.ts
import { nosticsStrip } from '@nostics/unplugin/strip-transform'
import { nosticsCollector } from '@nostics/unplugin/dev-server-collector'
export default defineConfig({
plugins: [nosticsStrip.vite(), nosticsCollector.vite()],
})
// src/diagnostics.ts — pair the collector with createDevReporter()
import { createConsoleReporter, defineDiagnostics } from 'nostics'
import { createDevReporter } from 'nostics/reporters/dev'
export const diagnostics = /*#__PURE__*/ defineDiagnostics({
reporters: [/*#__PURE__*/ createConsoleReporter(), /*#__PURE__*/ createDevReporter()],
codes: {/* ... */},
})
本番ビルド
- レポート専用の診断(裸の
diagnostics.X())は取り除かれるはずです。nosticsStripまたは手動のアノテーションでそれらを削除すると、未使用のカタログがツリーシェイクされます。 - 残る診断(throw/return/代入/引数)はそのまま残り、各診断からカタログ全体に到達可能になるため、すべての
why/fixが同梱されます。すべてのライブラリが本番環境で throw するわけではありません。レポートのみを行う場合は、ストリッピングだけで十分なので、ここで説明は終わりです。
ライブラリが本番環境で throw する場合は、定義時に NODE_ENV の三項演算子を使って defineProdDiagnostics を選択してください。これにより、利用側のバンドラーが開発用ブランチ(カタログ内のすべてのテキスト)を削除できます。
import { defineDiagnostics, defineProdDiagnostics } from 'nostics'
export const diagnostics =
process.env.NODE_ENV === 'production'
? /*#__PURE__*/ defineProdDiagnostics({ docsBase })
: /*#__PURE__*/ defineDiagnostics({
docsBase,
reporters: [/* ... */],
codes: {/* text */},
})
アクセスされたコードがインスタンスの name になり、docs は引き続き docsBase から導出されます。why はドキュメント URL が存在する場合はその URL を指し、存在しない場合は空になります。why/fix のテキストは同梱されません。デフォルトでは reporters はありません(そのため、残った throw がログを出力した後に未捕捉エラーとして再度表面化することはありません)。本番環境のテレメトリーを維持するには、reporters を渡してください。nosticsStrip は、この三項演算子を直接のカタログエクスポートと同様に追跡します。
規約
- コードは、安定した完全修飾の
PREFIX_XNNNN(Bはビルド、Rはランタイム、Cは設定、Dは非推奨)です。公開済みのコードを再利用したり、割り当て直したりしないでください。 - テストでは、メッセージや整形済みテキストではなく、診断コードを検証してください。スローされた
Diagnosticの.name、またはレポーターの出力に含まれる[CODE]を確認してください。 - 常に
whyを指定し、解決方法が分かっている場合はfixも指定してください(人間とエージェントにとって最も実用的なフィールドです)。ランタイム値にはパラメーター化されたテンプレートを使い、ファクトリーの外で文字列を連結しないでください。 whyは診断、fixは解決策です。両者を分け、重複させないでください。whyには問題点のみを記載し、fixには対処方法のみを記載します。レポーターは両方を出力するため、両方に含まれる文言は冗長です。1つの文に両方の内容が含まれている場合("A hash must start with '#'. Prefix it with '#'.")、文全体をwhyに入れてfixでも繰り返すのではなく、診断をwhyに、解決策をfixに分けてください。fixにもパラメーター関数((p) => ...)を指定できるため、値を含む解決策(use "#${p.hash}")はwhyに残さず、fixに移してください。- エラーを再スローするときは
causeを渡し、JS のスタックがユーザーのソースを反映していない場合はsourcesを渡してください。 - 大規模なカタログはドメインごとに分割してください(
diagnostics/build.ts、runtime.ts、config.ts)。それぞれindex.tsから再エクスポートし、各defineDiagnostics()で独自のコード範囲を使用しながらdocsBaseを共有します。
参考資料
- 既存ライブラリを nostics に移行する場合(ランタイムの動作を変えずに、
console.warn/console.error/warn()/スローされるErrorを診断コードに置き換える場合):references/migration.mdの手順を最初から最後まで実施してください。 - エラーコードのドキュメントサイトの構築(ページテンプレート、デプロイ、エージェントの最適化):
references/documentation-site.md。