@vmz/diagnostic 0.0.0 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,13 @@
1
- # @vmz/diagnostic
1
+ # `@vmz/diagnostic`
2
2
 
3
- Placeholder package (0.0.0). Reserved for the VMZ project.
3
+ Diagnostic layout for structured `code + args + span` rows.
4
+
5
+ - Caller injects `t` / catalog (official tables live in `@vmz/vmz`)
6
+ - No built-in language packs; no separate i18n package
7
+ - Deepen snippet / caret when the product CLI needs them
8
+
9
+ ```ts
10
+ import { formatDiagnostic } from '@vmz/diagnostic';
11
+
12
+ formatDiagnostic(row, { locale: 'en-US', catalog: productCatalog });
13
+ ```
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `@vmz/diagnostic` — catalog render + pretty printer (skeleton).
3
+ *
4
+ * Product path: N-API diagnostic wire → `t(code, args)` → layout.
5
+ */
6
+ /** Message id → template (`{name}` placeholders). */
7
+ export type LocaleCatalog = Record<string, string>;
8
+ /** Severity labels on the wire (kebab-case). */
9
+ export type Severity = 'error' | 'warning' | 'advice';
10
+ /** UTF-8 byte offset span (line/col only at render time). */
11
+ export type SourceSpan = {
12
+ start: number;
13
+ end: number;
14
+ };
15
+ /** Language-neutral diagnostic row (product wire shape). */
16
+ export type DiagnosticInput = {
17
+ path: string;
18
+ severity: Severity;
19
+ code: string;
20
+ args?: Record<string, string>;
21
+ /**
22
+ * Transitional wire prose when the product catalog has no entry for `code`.
23
+ * Prefer catalog templates; do not treat this as a second i18n source of truth.
24
+ */
25
+ message?: string;
26
+ span?: SourceSpan;
27
+ };
28
+ /** Optional offset → line/col context (aligned with compiler OffsetIndex later). */
29
+ export type PositionContext = {
30
+ lineCol(offset: number): {
31
+ line: number;
32
+ column: number;
33
+ };
34
+ };
35
+ export type FormatOptions = {
36
+ locale: string;
37
+ catalog: LocaleCatalog | ((locale: string) => LocaleCatalog);
38
+ sourceText?: string;
39
+ position?: PositionContext;
40
+ };
41
+ /**
42
+ * Resolve a catalog template with `{arg}` substitution.
43
+ * Missing keys fall back to `{{code}}` so wire identity stays visible.
44
+ */
45
+ export declare function t(code: string, args: Record<string, string> | undefined, catalog: LocaleCatalog): string;
46
+ /**
47
+ * Format one diagnostic for terminal / logs.
48
+ * Skeleton: single-line `path: severity[code]: message` — no snippet yet.
49
+ */
50
+ export declare function formatDiagnostic(d: DiagnosticInput, opts: FormatOptions): string;
51
+ /**
52
+ * Format many diagnostics, one per line.
53
+ */
54
+ export declare function formatDiagnostics(list: DiagnosticInput[], opts: FormatOptions): string;
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `@vmz/diagnostic` — catalog render + pretty printer (skeleton).
3
+ *
4
+ * Product path: N-API diagnostic wire → `t(code, args)` → layout.
5
+ */
6
+ /**
7
+ * Resolve a catalog template with `{arg}` substitution.
8
+ * Missing keys fall back to `{{code}}` so wire identity stays visible.
9
+ */
10
+ export function t(code, args, catalog) {
11
+ const template = catalog[code];
12
+ if (template == null) {
13
+ return `{{${code}}}`;
14
+ }
15
+ return template.replace(/\{([a-zA-Z0-9_.-]+)\}/g, (_m, name) => {
16
+ if (args && Object.prototype.hasOwnProperty.call(args, name)) {
17
+ return args[name] ?? '';
18
+ }
19
+ return `{${name}}`;
20
+ });
21
+ }
22
+ function resolveCatalog(locale, catalog) {
23
+ return typeof catalog === 'function' ? catalog(locale) : catalog;
24
+ }
25
+ /**
26
+ * Format one diagnostic for terminal / logs.
27
+ * Skeleton: single-line `path: severity[code]: message` — no snippet yet.
28
+ */
29
+ export function formatDiagnostic(d, opts) {
30
+ if (!d.code || typeof d.code !== 'string') {
31
+ throw new Error('@vmz/diagnostic: DiagnosticInput.code is required');
32
+ }
33
+ const catalog = resolveCatalog(opts.locale, opts.catalog);
34
+ const message = Object.prototype.hasOwnProperty.call(catalog, d.code)
35
+ ? t(d.code, d.args, catalog)
36
+ : d.message != null && String(d.message).length
37
+ ? String(d.message)
38
+ : t(d.code, d.args, catalog);
39
+ const where = formatWhere(d, opts);
40
+ const head = where ? `${where}: ` : '';
41
+ return `${head}${d.severity}[${d.code}]: ${message}`;
42
+ }
43
+ /**
44
+ * Format many diagnostics, one per line.
45
+ */
46
+ export function formatDiagnostics(list, opts) {
47
+ return list.map((d) => formatDiagnostic(d, opts)).join('\n');
48
+ }
49
+ function formatWhere(d, opts) {
50
+ const path = d.path || '';
51
+ if (!d.span || !opts.position) {
52
+ return path;
53
+ }
54
+ const { line, column } = opts.position.lineCol(d.span.start);
55
+ return path ? `${path}:${line}:${column}` : `${line}:${column}`;
56
+ }
package/package.json CHANGED
@@ -1,10 +1,38 @@
1
1
  {
2
2
  "name": "@vmz/diagnostic",
3
- "version": "0.0.0",
4
- "description": "VMZ placeholder ?not for production use.",
3
+ "version": "0.1.18",
4
+ "type": "module",
5
+ "description": "Language-neutral diagnostic catalog render and pretty printer",
5
6
  "license": "MIT",
6
- "private": false,
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
7
15
  "files": [
16
+ "dist",
8
17
  "README.md"
9
- ]
18
+ ],
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json"
24
+ },
25
+ "keywords": [
26
+ "vmz",
27
+ "diagnostic",
28
+ "i18n",
29
+ "pretty-print"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/doki-land/vmz-framework.git"
37
+ }
10
38
  }