correctly 0.0.0

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/dist/cli.mjs ADDED
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ import { d as discoverConfig, f as exclusions, m as loadProject, p as isIncluded, r as diagnosticViews, t as Engine, y as failure } from "./engine-BgWBnb1A.mjs";
3
+ import { parseArgs, styleText } from "node:util";
4
+ import { readFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { glob } from "tinyglobby";
7
+ import { fileURLToPath } from "node:url";
8
+ //#region src/cli/readable.ts
9
+ function createStyler(color) {
10
+ const apply = (format, text) => color === false ? text : styleText(format, text, {
11
+ stream: process.stdout,
12
+ ...color === true ? { validateStream: false } : {}
13
+ });
14
+ return {
15
+ bold: (text) => apply("bold", text),
16
+ bad: (text) => apply(["bold", "red"], text),
17
+ caret: (text) => apply(["bold", "yellow"], text),
18
+ code: (text) => apply("cyan", text),
19
+ dim: (text) => apply("dim", text),
20
+ success: (text) => apply(["bold", "green"], text),
21
+ warning: (text) => apply(["bold", "yellow"], text)
22
+ };
23
+ }
24
+ function displayPath(cwd, file) {
25
+ const relative = path.relative(cwd, file);
26
+ return relative && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative) ? relative : file;
27
+ }
28
+ function displaySchema(cwd, uri) {
29
+ const url = new URL(uri);
30
+ if (url.protocol !== "file:") return uri;
31
+ return `${displayPath(cwd, fileURLToPath(url))}${url.hash}`;
32
+ }
33
+ function countLabel(count, label) {
34
+ return `${count} ${label}${count === 1 ? "" : "s"}`;
35
+ }
36
+ function lastSelectedLine(diagnostic) {
37
+ const { start, end } = diagnostic.range;
38
+ return Math.max(start.line, end.line - (end.line > start.line && end.character === 0 ? 1 : 0));
39
+ }
40
+ function excerpt(source, diagnostic, diagnosedLines, styler) {
41
+ const start = diagnostic.range.start.line;
42
+ const end = Math.min(lastSelectedLine(diagnostic), source.length - 1);
43
+ if (start < 0 || start >= source.length) return void 0;
44
+ const visible = [];
45
+ if (start > 0 && !diagnosedLines.has(start - 1)) visible.push(start - 1);
46
+ visible.push(start);
47
+ if (end > start + 1) visible.push(void 0);
48
+ if (end > start) visible.push(end);
49
+ if (end + 1 < source.length && !diagnosedLines.has(end + 1) && (end + 1 < source.length - 1 || source[end + 1] !== "")) visible.push(end + 1);
50
+ const width = Math.max(...visible.map((line) => line === void 0 ? 1 : String(line + 1).length));
51
+ const output = [];
52
+ for (const line of visible) {
53
+ if (line === void 0) {
54
+ output.push(styler.dim(`${" ".repeat(width)} │ …`));
55
+ continue;
56
+ }
57
+ const text = source[line];
58
+ const selected = line >= start && line <= end;
59
+ const expanded = text.replaceAll(" ", " ").trimEnd();
60
+ output.push(`${styler.dim(`${String(line + 1).padStart(width)} │`)} ${selected ? expanded : styler.dim(expanded)}`);
61
+ if (!selected) continue;
62
+ const from = line === start ? diagnostic.range.start.character : 0;
63
+ const to = line === diagnostic.range.end.line ? diagnostic.range.end.character : text.length;
64
+ const caretStart = text.slice(0, from).replaceAll(" ", " ").length;
65
+ const caretWidth = Math.max(1, text.slice(from, to).replaceAll(" ", " ").length);
66
+ output.push(`${styler.dim(`${" ".repeat(width)} │`)} ${" ".repeat(caretStart)}${styler.caret("^".repeat(caretWidth))}`);
67
+ }
68
+ return {
69
+ lines: output,
70
+ width
71
+ };
72
+ }
73
+ function renderDiagnostic(issue, prefix, last, source, diagnosedLines, styler, shownRegions = /* @__PURE__ */ new Set()) {
74
+ const branch = last ? "└─" : "├─";
75
+ const continuation = `${prefix}${last ? " " : "│ "}`;
76
+ const output = [`${styler.dim(`${prefix}${branch}`)} ${styler.dim(`${issue.range.start.line + 1}:${issue.range.start.character + 1}`)} ${styler.code(issue.code)}`];
77
+ const region = source ? excerpt(source, issue, diagnosedLines, styler) : void 0;
78
+ const regionKey = `${issue.offset}:${issue.length}`;
79
+ if (region && !shownRegions.has(regionKey)) output.push(...region.lines.map((line) => `${styler.dim(continuation)}${line}`));
80
+ shownRegions.add(regionKey);
81
+ const indentation = `${continuation}${" ".repeat(region ? region.width + 1 : 0)}`;
82
+ output.push(`${styler.dim(continuation)}${" ".repeat(region ? region.width + 1 : 0)}${styler.dim("╰─")} ${issue.message}`);
83
+ for (const [index, branch] of issue.branches.entries()) {
84
+ const finalBranch = index === issue.branches.length - 1;
85
+ output.push(`${styler.dim(`${indentation}${finalBranch ? "└─" : "├─"}`)} ${styler.bold(branch.label)}`);
86
+ const children = branch.diagnostics.toSorted((a, b) => a.offset - b.offset);
87
+ for (const [childIndex, child] of children.entries()) output.push(...renderDiagnostic(child, `${indentation}${finalBranch ? " " : "│ "}`, childIndex === children.length - 1, source, diagnosedLines, styler, shownRegions));
88
+ }
89
+ return output;
90
+ }
91
+ function fileSection(group, options, styler) {
92
+ const cwd = options.cwd ?? process.cwd();
93
+ const diagnostics = diagnosticViews(group.result?.diagnostics ?? []).toSorted((a, b) => a.offset - b.offset);
94
+ const counts = [...diagnostics.length ? [countLabel(diagnostics.length, "error")] : [], ...group.failures.length ? [countLabel(group.failures.length, "failure")] : []];
95
+ const status = counts.join(", ") || (group.result?.coverage === "excluded" ? "excluded" : "valid");
96
+ const statusColor = counts.length ? styler.bad : group.result?.coverage === "excluded" ? styler.dim : styler.success;
97
+ let header = `${styler.bold(displayPath(cwd, group.file))} ${statusColor(status)}`;
98
+ if (group.result) {
99
+ const { mode, coverage, association } = group.result;
100
+ const format = mode.toUpperCase();
101
+ const schema = association?.schema;
102
+ const description = coverage === "excluded" ? format : schema ? `${format} against ${association.name} (${displaySchema(cwd, schema)})` : `${format} syntax only (${association ? `association: ${association.name}` : "no schema association"})`;
103
+ header += ` ${styler.dim(`· ${description}`)}`;
104
+ }
105
+ const output = [header];
106
+ const text = options.sources?.get(group.file);
107
+ const source = text === void 0 ? void 0 : text.split(/\r\n|\r|\n/);
108
+ const diagnosedLines = /* @__PURE__ */ new Set();
109
+ if (source) for (const diagnostic of group.result?.diagnostics ?? []) for (let line = diagnostic.range.start.line; line <= Math.min(lastSelectedLine(diagnostic), source.length - 1); line++) diagnosedLines.add(line);
110
+ const issues = [...diagnostics, ...group.failures];
111
+ for (const [index, issue] of issues.entries()) {
112
+ const last = index === issues.length - 1;
113
+ const branch = last ? "└─" : "├─";
114
+ if ("range" in issue) output.push(...renderDiagnostic(issue, "", last, source, diagnosedLines, styler));
115
+ else output.push(`${styler.dim(branch)} ${styler.bad(issue.code)}: ${issue.message}`);
116
+ if (!last) output.push(styler.dim("│"));
117
+ }
118
+ return output.join("\n");
119
+ }
120
+ function readableReport(report, options = {}) {
121
+ const styler = createStyler(options.color);
122
+ const groups = /* @__PURE__ */ new Map();
123
+ for (const result of report.files) groups.set(result.file, {
124
+ file: result.file,
125
+ result,
126
+ failures: [...result.failures]
127
+ });
128
+ const globalFailures = [];
129
+ for (const error of report.failures) {
130
+ if (!error.file) {
131
+ globalFailures.push(error);
132
+ continue;
133
+ }
134
+ let group = groups.get(error.file);
135
+ if (!group) {
136
+ group = {
137
+ file: error.file,
138
+ failures: []
139
+ };
140
+ groups.set(error.file, group);
141
+ }
142
+ group.failures.push(error);
143
+ }
144
+ const output = [...groups.values()].toSorted((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0).map((group) => fileSection(group, options, styler));
145
+ for (const error of globalFailures) output.push(`${styler.bad(error.code)}: ${error.message}`);
146
+ const errors = report.files.reduce((count, file) => count + diagnosticViews(file.diagnostics).length, 0);
147
+ const s = report.summary;
148
+ const status = report.exitCode === 2 ? `▲ Check failed with ${countLabel(s.failures, "failure")}` : report.exitCode === 1 ? `▲ Check found ${countLabel(errors, "error")} in ${countLabel(s.invalid, "file")}` : "✓ Check passed";
149
+ output.push([
150
+ styler.dim("─".repeat(56)),
151
+ "",
152
+ (report.exitCode === 2 ? styler.bad : report.exitCode === 1 ? styler.warning : styler.success)(status),
153
+ "",
154
+ `${s.checked} checked, ${s.schemaCovered} schema-covered, ${s.syntaxOnly} syntax-only, ${s.invalid} invalid, ${s.failures} failures`
155
+ ].join("\n"));
156
+ return output.join("\n\n");
157
+ }
158
+ //#endregion
159
+ //#region src/cli/check.ts
160
+ async function check(options = {}) {
161
+ const report = {
162
+ reportVersion: 1,
163
+ config: null,
164
+ files: [],
165
+ failures: [],
166
+ summary: {
167
+ checked: 0,
168
+ schemaCovered: 0,
169
+ syntaxOnly: 0,
170
+ invalid: 0,
171
+ failures: 0
172
+ },
173
+ exitCode: 0
174
+ };
175
+ try {
176
+ const cwd = path.resolve(options.cwd ?? process.cwd());
177
+ const configPath = options.config ? path.resolve(cwd, options.config) : await discoverConfig(cwd);
178
+ report.config = configPath;
179
+ const project = await loadProject(configPath);
180
+ const engine = new Engine(project, options.offline === void 0 ? {} : { offline: options.offline });
181
+ await engine.prepare();
182
+ const files = options.files?.length ? options.files.map((f) => path.resolve(cwd, f)) : await glob(project.config.files ?? ["**/*.json", "**/*.jsonc"], {
183
+ cwd: project.root,
184
+ ignore: exclusions(project),
185
+ absolute: true,
186
+ dot: true,
187
+ onlyFiles: true
188
+ });
189
+ for (const file of [...new Set(files)].sort()) try {
190
+ const included = isIncluded(project, file);
191
+ const text = included ? await readFile(file, "utf8") : "";
192
+ if (included) options.onRead?.(file, text);
193
+ report.files.push(await engine.validate(file, text));
194
+ } catch (error) {
195
+ report.failures.push(failure(error, file));
196
+ }
197
+ } catch (error) {
198
+ report.failures.push(failure(error));
199
+ }
200
+ const checked = report.files.filter((f) => f.coverage !== "excluded");
201
+ report.summary = {
202
+ checked: checked.length,
203
+ schemaCovered: checked.filter((f) => f.coverage === "schema").length,
204
+ syntaxOnly: checked.filter((f) => f.coverage === "syntax-only").length,
205
+ invalid: checked.filter((f) => f.diagnostics.length > 0).length,
206
+ failures: report.failures.length + report.files.reduce((n, f) => n + f.failures.length, 0)
207
+ };
208
+ report.exitCode = report.summary.failures ? 2 : report.summary.invalid ? 1 : 0;
209
+ return report;
210
+ }
211
+ //#endregion
212
+ //#region src/cli/main.ts
213
+ const HELP = `correctly check [files...] [--config path] [--offline] [--format readable|json]
214
+ correctly-lsp --stdio
215
+
216
+ Validate JSON/JSONC using associations in correctly.config.json.
217
+ Exit 0: valid; 1: document errors; 2: configuration, schema, or execution errors.
218
+ Formatting does not affect validation. See the package guide for configuration.
219
+ `;
220
+ async function run(args, cwd = process.cwd()) {
221
+ let json = args.includes("--format=json") || args.some((arg, i) => arg === "--format" && args[i + 1] === "json");
222
+ try {
223
+ const { values, positionals } = parseArgs({
224
+ args,
225
+ allowPositionals: true,
226
+ strict: true,
227
+ options: {
228
+ config: {
229
+ type: "string",
230
+ short: "c"
231
+ },
232
+ offline: { type: "boolean" },
233
+ format: {
234
+ type: "string",
235
+ default: "readable"
236
+ },
237
+ help: {
238
+ type: "boolean",
239
+ short: "h"
240
+ }
241
+ }
242
+ });
243
+ json = values.format === "json";
244
+ if (values.help || positionals.length === 0) return {
245
+ output: HELP,
246
+ exitCode: 0
247
+ };
248
+ if (positionals[0] !== "check") throw new Error(`Unknown command: ${positionals[0]}`);
249
+ if (values.format !== "json" && values.format !== "readable") throw new Error(`Unknown report format: ${values.format}`);
250
+ const sources = /* @__PURE__ */ new Map();
251
+ const report = await check({
252
+ cwd,
253
+ files: positionals.slice(1),
254
+ ...values.config ? { config: values.config } : {},
255
+ ...values.offline === void 0 ? {} : { offline: values.offline },
256
+ ...json ? {} : { onRead: (file, text) => {
257
+ sources.set(file, text);
258
+ } }
259
+ });
260
+ return {
261
+ output: json ? JSON.stringify(report, null, 2) : readableReport(report, {
262
+ cwd,
263
+ sources
264
+ }),
265
+ exitCode: report.exitCode
266
+ };
267
+ } catch (error) {
268
+ const report = {
269
+ reportVersion: 1,
270
+ config: null,
271
+ files: [],
272
+ failures: [failure(error)],
273
+ summary: {
274
+ checked: 0,
275
+ schemaCovered: 0,
276
+ syntaxOnly: 0,
277
+ invalid: 0,
278
+ failures: 1
279
+ },
280
+ exitCode: 2
281
+ };
282
+ return {
283
+ output: json ? JSON.stringify(report, null, 2) : readableReport(report, { cwd }),
284
+ exitCode: 2
285
+ };
286
+ }
287
+ }
288
+ const result = await run(process.argv.slice(2));
289
+ process.stdout.write(`${result.output}\n`);
290
+ process.exitCode = result.exitCode;
291
+ //#endregion
292
+ export { run };
293
+
294
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/readable.ts","../src/cli/check.ts","../src/cli/main.ts"],"sourcesContent":["import path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { styleText } from \"node:util\"\nimport type { Diagnostic, Failure, FileResult, Report } from \"../core/types.ts\"\nimport { diagnosticViews, type DiagnosticView } from \"../core/diagnostics.ts\"\n\nexport type ReadableOptions = {\n\tcwd?: string\n\tsources?: ReadonlyMap<string, string>\n\tcolor?: boolean\n}\n\n// Node's automatic color detection respects the output stream and NO_COLOR,\n// NODE_DISABLE_COLORS, and FORCE_COLOR. Use the same palette as Lasertag.\nfunction createStyler(color?: boolean) {\n\tconst apply = (format: Parameters<typeof styleText>[0], text: string) =>\n\t\tcolor === false\n\t\t\t? text\n\t\t\t: styleText(format, text, {\n\t\t\t\t\tstream: process.stdout,\n\t\t\t\t\t...(color === true ? { validateStream: false } : {}),\n\t\t\t\t})\n\treturn {\n\t\tbold: (text: string) => apply(\"bold\", text),\n\t\tbad: (text: string) => apply([\"bold\", \"red\"], text),\n\t\tcaret: (text: string) => apply([\"bold\", \"yellow\"], text),\n\t\tcode: (text: string) => apply(\"cyan\", text),\n\t\tdim: (text: string) => apply(\"dim\", text),\n\t\tsuccess: (text: string) => apply([\"bold\", \"green\"], text),\n\t\twarning: (text: string) => apply([\"bold\", \"yellow\"], text),\n\t}\n}\ntype Styler = ReturnType<typeof createStyler>\n\nfunction displayPath(cwd: string, file: string): string {\n\tconst relative = path.relative(cwd, file)\n\treturn relative &&\n\t\trelative !== \"..\" &&\n\t\t!relative.startsWith(`..${path.sep}`) &&\n\t\t!path.isAbsolute(relative)\n\t\t? relative\n\t\t: file\n}\n\nfunction displaySchema(cwd: string, uri: string): string {\n\tconst url = new URL(uri)\n\tif (url.protocol !== \"file:\") return uri\n\treturn `${displayPath(cwd, fileURLToPath(url))}${url.hash}`\n}\n\nfunction countLabel(count: number, label: string): string {\n\treturn `${count} ${label}${count === 1 ? \"\" : \"s\"}`\n}\n\nfunction lastSelectedLine(diagnostic: Diagnostic): number {\n\tconst { start, end } = diagnostic.range\n\treturn Math.max(\n\t\tstart.line,\n\t\tend.line - (end.line > start.line && end.character === 0 ? 1 : 0),\n\t)\n}\n\n// Like Lasertag, keep one context line on either side and collapse long ranges\n// to their first/last lines. Tabs are expanded equally in source and carets.\nfunction excerpt(\n\tsource: string[],\n\tdiagnostic: Diagnostic,\n\tdiagnosedLines: ReadonlySet<number>,\n\tstyler: Styler,\n) {\n\tconst start = diagnostic.range.start.line\n\tconst end = Math.min(lastSelectedLine(diagnostic), source.length - 1)\n\tif (start < 0 || start >= source.length) return undefined\n\tconst visible: (number | undefined)[] = []\n\tif (start > 0 && !diagnosedLines.has(start - 1)) visible.push(start - 1)\n\tvisible.push(start)\n\tif (end > start + 1) visible.push(undefined)\n\tif (end > start) visible.push(end)\n\tif (\n\t\tend + 1 < source.length &&\n\t\t!diagnosedLines.has(end + 1) &&\n\t\t(end + 1 < source.length - 1 || source[end + 1] !== \"\")\n\t)\n\t\tvisible.push(end + 1)\n\tconst width = Math.max(\n\t\t...visible.map((line) =>\n\t\t\tline === undefined ? 1 : String(line + 1).length,\n\t\t),\n\t)\n\tconst output: string[] = []\n\tfor (const line of visible) {\n\t\tif (line === undefined) {\n\t\t\toutput.push(styler.dim(`${\" \".repeat(width)} │ …`))\n\t\t\tcontinue\n\t\t}\n\t\tconst text = source[line]!\n\t\tconst selected = line >= start && line <= end\n\t\tconst expanded = text.replaceAll(\"\\t\", \" \").trimEnd()\n\t\toutput.push(\n\t\t\t`${styler.dim(`${String(line + 1).padStart(width)} │`)} ${selected ? expanded : styler.dim(expanded)}`,\n\t\t)\n\t\tif (!selected) continue\n\t\tconst from = line === start ? diagnostic.range.start.character : 0\n\t\tconst to =\n\t\t\tline === diagnostic.range.end.line\n\t\t\t\t? diagnostic.range.end.character\n\t\t\t\t: text.length\n\t\tconst caretStart = text.slice(0, from).replaceAll(\"\\t\", \" \").length\n\t\tconst caretWidth = Math.max(\n\t\t\t1,\n\t\t\ttext.slice(from, to).replaceAll(\"\\t\", \" \").length,\n\t\t)\n\t\toutput.push(\n\t\t\t`${styler.dim(`${\" \".repeat(width)} │`)} ${\" \".repeat(caretStart)}${styler.caret(\"^\".repeat(caretWidth))}`,\n\t\t)\n\t}\n\treturn { lines: output, width }\n}\n\ntype FileGroup = { file: string; result?: FileResult; failures: Failure[] }\n\nfunction renderDiagnostic(\n\tissue: DiagnosticView,\n\tprefix: string,\n\tlast: boolean,\n\tsource: string[] | undefined,\n\tdiagnosedLines: ReadonlySet<number>,\n\tstyler: Styler,\n\tshownRegions = new Set<string>(),\n): string[] {\n\tconst branch = last ? \"└─\" : \"├─\"\n\tconst continuation = `${prefix}${last ? \" \" : \"│ \"}`\n\tconst output = [\n\t\t`${styler.dim(`${prefix}${branch}`)} ${styler.dim(`${issue.range.start.line + 1}:${issue.range.start.character + 1}`)} ${styler.code(issue.code)}`,\n\t]\n\tconst region = source\n\t\t? excerpt(source, issue, diagnosedLines, styler)\n\t\t: undefined\n\tconst regionKey = `${issue.offset}:${issue.length}`\n\tif (region && !shownRegions.has(regionKey))\n\t\toutput.push(\n\t\t\t...region.lines.map((line) => `${styler.dim(continuation)}${line}`),\n\t\t)\n\tshownRegions.add(regionKey)\n\tconst indentation = `${continuation}${\" \".repeat(region ? region.width + 1 : 0)}`\n\toutput.push(\n\t\t`${styler.dim(continuation)}${\" \".repeat(region ? region.width + 1 : 0)}${styler.dim(\"╰─\")} ${issue.message}`,\n\t)\n\tfor (const [index, branch] of issue.branches.entries()) {\n\t\tconst finalBranch = index === issue.branches.length - 1\n\t\toutput.push(\n\t\t\t`${styler.dim(`${indentation}${finalBranch ? \"└─\" : \"├─\"}`)} ${styler.bold(branch.label)}`,\n\t\t)\n\t\tconst children = branch.diagnostics.toSorted((a, b) => a.offset - b.offset)\n\t\tfor (const [childIndex, child] of children.entries())\n\t\t\toutput.push(\n\t\t\t\t...renderDiagnostic(\n\t\t\t\t\tchild,\n\t\t\t\t\t`${indentation}${finalBranch ? \" \" : \"│ \"}`,\n\t\t\t\t\tchildIndex === children.length - 1,\n\t\t\t\t\tsource,\n\t\t\t\t\tdiagnosedLines,\n\t\t\t\t\tstyler,\n\t\t\t\t\tshownRegions,\n\t\t\t\t),\n\t\t\t)\n\t}\n\treturn output\n}\n\nfunction fileSection(\n\tgroup: FileGroup,\n\toptions: ReadableOptions,\n\tstyler: Styler,\n): string {\n\tconst cwd = options.cwd ?? process.cwd()\n\tconst diagnostics = diagnosticViews(group.result?.diagnostics ?? []).toSorted(\n\t\t(a, b) => a.offset - b.offset,\n\t)\n\tconst counts = [\n\t\t...(diagnostics.length ? [countLabel(diagnostics.length, \"error\")] : []),\n\t\t...(group.failures.length\n\t\t\t? [countLabel(group.failures.length, \"failure\")]\n\t\t\t: []),\n\t]\n\tconst status =\n\t\tcounts.join(\", \") ||\n\t\t(group.result?.coverage === \"excluded\" ? \"excluded\" : \"valid\")\n\tconst statusColor = counts.length\n\t\t? styler.bad\n\t\t: group.result?.coverage === \"excluded\"\n\t\t\t? styler.dim\n\t\t\t: styler.success\n\tlet header = `${styler.bold(displayPath(cwd, group.file))} ${statusColor(status)}`\n\tif (group.result) {\n\t\tconst { mode, coverage, association } = group.result\n\t\tconst format = mode.toUpperCase()\n\t\tconst schema = association?.schema\n\t\tconst description =\n\t\t\tcoverage === \"excluded\"\n\t\t\t\t? format\n\t\t\t\t: schema\n\t\t\t\t\t? `${format} against ${association.name} (${displaySchema(cwd, schema)})`\n\t\t\t\t\t: `${format} syntax only (${association ? `association: ${association.name}` : \"no schema association\"})`\n\t\theader += ` ${styler.dim(`· ${description}`)}`\n\t}\n\tconst output = [header]\n\tconst text = options.sources?.get(group.file)\n\tconst source = text === undefined ? undefined : text.split(/\\r\\n|\\r|\\n/)\n\tconst diagnosedLines = new Set<number>()\n\tif (source)\n\t\tfor (const diagnostic of group.result?.diagnostics ?? []) {\n\t\t\tfor (\n\t\t\t\tlet line = diagnostic.range.start.line;\n\t\t\t\tline <= Math.min(lastSelectedLine(diagnostic), source.length - 1);\n\t\t\t\tline++\n\t\t\t)\n\t\t\t\tdiagnosedLines.add(line)\n\t\t}\n\tconst issues: (DiagnosticView | Failure)[] = [\n\t\t...diagnostics,\n\t\t...group.failures,\n\t]\n\tfor (const [index, issue] of issues.entries()) {\n\t\tconst last = index === issues.length - 1\n\t\tconst branch = last ? \"└─\" : \"├─\"\n\t\tif (\"range\" in issue) {\n\t\t\toutput.push(\n\t\t\t\t...renderDiagnostic(issue, \"\", last, source, diagnosedLines, styler),\n\t\t\t)\n\t\t} else\n\t\t\toutput.push(\n\t\t\t\t`${styler.dim(branch)} ${styler.bad(issue.code)}: ${issue.message}`,\n\t\t\t)\n\t\tif (!last) output.push(styler.dim(\"│\"))\n\t}\n\treturn output.join(\"\\n\")\n}\n\nexport function readableReport(\n\treport: Report,\n\toptions: ReadableOptions = {},\n): string {\n\tconst styler = createStyler(options.color)\n\tconst groups = new Map<string, FileGroup>()\n\tfor (const result of report.files)\n\t\tgroups.set(result.file, {\n\t\t\tfile: result.file,\n\t\t\tresult,\n\t\t\tfailures: [...result.failures],\n\t\t})\n\tconst globalFailures: Failure[] = []\n\tfor (const error of report.failures) {\n\t\tif (!error.file) {\n\t\t\tglobalFailures.push(error)\n\t\t\tcontinue\n\t\t}\n\t\tlet group = groups.get(error.file)\n\t\tif (!group) {\n\t\t\tgroup = { file: error.file, failures: [] }\n\t\t\tgroups.set(error.file, group)\n\t\t}\n\t\tgroup.failures.push(error)\n\t}\n\tconst output = [...groups.values()]\n\t\t.toSorted((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : 0))\n\t\t.map((group) => fileSection(group, options, styler))\n\tfor (const error of globalFailures)\n\t\toutput.push(`${styler.bad(error.code)}: ${error.message}`)\n\tconst errors = report.files.reduce(\n\t\t(count, file) => count + diagnosticViews(file.diagnostics).length,\n\t\t0,\n\t)\n\tconst s = report.summary\n\tconst status =\n\t\treport.exitCode === 2\n\t\t\t? `▲ Check failed with ${countLabel(s.failures, \"failure\")}`\n\t\t\t: report.exitCode === 1\n\t\t\t\t? `▲ Check found ${countLabel(errors, \"error\")} in ${countLabel(s.invalid, \"file\")}`\n\t\t\t\t: \"✓ Check passed\"\n\toutput.push(\n\t\t[\n\t\t\tstyler.dim(\"─\".repeat(56)),\n\t\t\t\"\",\n\t\t\t(report.exitCode === 2\n\t\t\t\t? styler.bad\n\t\t\t\t: report.exitCode === 1\n\t\t\t\t\t? styler.warning\n\t\t\t\t\t: styler.success)(status),\n\t\t\t\"\",\n\t\t\t`${s.checked} checked, ${s.schemaCovered} schema-covered, ${s.syntaxOnly} syntax-only, ${s.invalid} invalid, ${s.failures} failures`,\n\t\t].join(\"\\n\"),\n\t)\n\treturn output.join(\"\\n\\n\")\n}\n","import { readFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { glob } from \"tinyglobby\"\nimport {\n\tdiscoverConfig,\n\texclusions,\n\tisIncluded,\n\tloadProject,\n} from \"../core/config.ts\"\nimport { Engine } from \"../core/engine.ts\"\nimport { failure, type Report } from \"../core/types.ts\"\n\nexport type CheckOptions = {\n\tcwd?: string\n\tconfig?: string\n\toffline?: boolean\n\tfiles?: string[]\n\tonRead?: (file: string, text: string) => void\n}\nexport async function check(options: CheckOptions = {}): Promise<Report> {\n\tconst report: Report = {\n\t\treportVersion: 1,\n\t\tconfig: null,\n\t\tfiles: [],\n\t\tfailures: [],\n\t\tsummary: {\n\t\t\tchecked: 0,\n\t\t\tschemaCovered: 0,\n\t\t\tsyntaxOnly: 0,\n\t\t\tinvalid: 0,\n\t\t\tfailures: 0,\n\t\t},\n\t\texitCode: 0,\n\t}\n\ttry {\n\t\tconst cwd = path.resolve(options.cwd ?? process.cwd())\n\t\tconst configPath = options.config\n\t\t\t? path.resolve(cwd, options.config)\n\t\t\t: await discoverConfig(cwd)\n\t\treport.config = configPath\n\t\tconst project = await loadProject(configPath)\n\t\tconst engine = new Engine(\n\t\t\tproject,\n\t\t\toptions.offline === undefined ? {} : { offline: options.offline },\n\t\t)\n\t\tawait engine.prepare()\n\t\tconst files = options.files?.length\n\t\t\t? options.files.map((f) => path.resolve(cwd, f))\n\t\t\t: await glob(project.config.files ?? [\"**/*.json\", \"**/*.jsonc\"], {\n\t\t\t\t\tcwd: project.root,\n\t\t\t\t\tignore: exclusions(project),\n\t\t\t\t\tabsolute: true,\n\t\t\t\t\tdot: true,\n\t\t\t\t\tonlyFiles: true,\n\t\t\t\t})\n\t\tfor (const file of [...new Set(files)].sort()) {\n\t\t\ttry {\n\t\t\t\tconst included = isIncluded(project, file)\n\t\t\t\tconst text = included ? await readFile(file, \"utf8\") : \"\"\n\t\t\t\tif (included) options.onRead?.(file, text)\n\t\t\t\treport.files.push(await engine.validate(file, text))\n\t\t\t} catch (error) {\n\t\t\t\treport.failures.push(failure(error, file))\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\treport.failures.push(failure(error))\n\t}\n\tconst checked = report.files.filter((f) => f.coverage !== \"excluded\")\n\treport.summary = {\n\t\tchecked: checked.length,\n\t\tschemaCovered: checked.filter((f) => f.coverage === \"schema\").length,\n\t\tsyntaxOnly: checked.filter((f) => f.coverage === \"syntax-only\").length,\n\t\tinvalid: checked.filter((f) => f.diagnostics.length > 0).length,\n\t\tfailures:\n\t\t\treport.failures.length +\n\t\t\treport.files.reduce((n, f) => n + f.failures.length, 0),\n\t}\n\treport.exitCode = report.summary.failures ? 2 : report.summary.invalid ? 1 : 0\n\treturn report\n}\n\nexport { readableReport } from \"./readable.ts\"\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { check, readableReport } from \"./check.ts\"\nimport { failure, type Report } from \"../core/types.ts\"\n\nconst HELP = `correctly check [files...] [--config path] [--offline] [--format readable|json]\ncorrectly-lsp --stdio\n\nValidate JSON/JSONC using associations in correctly.config.json.\nExit 0: valid; 1: document errors; 2: configuration, schema, or execution errors.\nFormatting does not affect validation. See the package guide for configuration.\n`\n\nexport async function run(\n\targs: string[],\n\tcwd = process.cwd(),\n): Promise<{ output: string; exitCode: number }> {\n\tlet json =\n\t\targs.includes(\"--format=json\") ||\n\t\targs.some((arg, i) => arg === \"--format\" && args[i + 1] === \"json\")\n\ttry {\n\t\tconst { values, positionals } = parseArgs({\n\t\t\targs,\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: {\n\t\t\t\tconfig: { type: \"string\", short: \"c\" },\n\t\t\t\toffline: { type: \"boolean\" },\n\t\t\t\tformat: { type: \"string\", default: \"readable\" },\n\t\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t\t},\n\t\t})\n\t\tjson = values.format === \"json\"\n\t\tif (values.help || positionals.length === 0)\n\t\t\treturn { output: HELP, exitCode: 0 }\n\t\tif (positionals[0] !== \"check\")\n\t\t\tthrow new Error(`Unknown command: ${positionals[0]}`)\n\t\tif (values.format !== \"json\" && values.format !== \"readable\")\n\t\t\tthrow new Error(`Unknown report format: ${values.format}`)\n\t\tconst sources = new Map<string, string>()\n\t\tconst report = await check({\n\t\t\tcwd,\n\t\t\tfiles: positionals.slice(1),\n\t\t\t...(values.config ? { config: values.config } : {}),\n\t\t\t...(values.offline === undefined ? {} : { offline: values.offline }),\n\t\t\t...(json\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tonRead: (file: string, text: string) => {\n\t\t\t\t\t\t\tsources.set(file, text)\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t})\n\t\treturn {\n\t\t\toutput: json\n\t\t\t\t? JSON.stringify(report, null, 2)\n\t\t\t\t: readableReport(report, { cwd, sources }),\n\t\t\texitCode: report.exitCode,\n\t\t}\n\t} catch (error) {\n\t\tconst report: Report = {\n\t\t\treportVersion: 1,\n\t\t\tconfig: null,\n\t\t\tfiles: [],\n\t\t\tfailures: [failure(error)],\n\t\t\tsummary: {\n\t\t\t\tchecked: 0,\n\t\t\t\tschemaCovered: 0,\n\t\t\t\tsyntaxOnly: 0,\n\t\t\t\tinvalid: 0,\n\t\t\t\tfailures: 1,\n\t\t\t},\n\t\t\texitCode: 2,\n\t\t}\n\t\treturn {\n\t\t\toutput: json\n\t\t\t\t? JSON.stringify(report, null, 2)\n\t\t\t\t: readableReport(report, { cwd }),\n\t\t\texitCode: 2,\n\t\t}\n\t}\n}\n\nconst result = await run(process.argv.slice(2))\nprocess.stdout.write(`${result.output}\\n`)\nprocess.exitCode = result.exitCode\n"],"mappings":"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"}
@@ -0,0 +1,183 @@
1
+ import { AnySchema, ValidateFunction } from "ajv";
2
+ import { Node } from "jsonc-parser";
3
+ //#region src/core/types.d.ts
4
+ export type Mode = "json" | "jsonc";
5
+ export type Position = {
6
+ line: number;
7
+ character: number;
8
+ };
9
+ export type Range = {
10
+ start: Position;
11
+ end: Position;
12
+ };
13
+ export type Diagnostic = {
14
+ code: string;
15
+ message: string;
16
+ pointer: string;
17
+ offset: number;
18
+ length: number;
19
+ range: Range;
20
+ context?: {
21
+ parent: number;
22
+ label: string;
23
+ };
24
+ };
25
+ export type Failure = {
26
+ code: string;
27
+ message: string;
28
+ file?: string;
29
+ };
30
+ export declare class CorrectlyError extends Error {
31
+ readonly code: string;
32
+ constructor(code: string, message: string);
33
+ }
34
+ export declare function failure(error: unknown, file?: string): Failure;
35
+ export type Association = {
36
+ index: number;
37
+ name: string;
38
+ schema: string | null;
39
+ mode: Mode;
40
+ };
41
+ export type FileResult = {
42
+ file: string;
43
+ mode: Mode;
44
+ coverage: "schema" | "syntax-only" | "excluded";
45
+ association: Association | null;
46
+ diagnostics: Diagnostic[];
47
+ failures: Failure[];
48
+ };
49
+ export type Report = {
50
+ reportVersion: 1;
51
+ config: string | null;
52
+ files: FileResult[];
53
+ failures: Failure[];
54
+ summary: {
55
+ checked: number;
56
+ schemaCovered: number;
57
+ syntaxOnly: number;
58
+ invalid: number;
59
+ failures: number;
60
+ };
61
+ exitCode: 0 | 1 | 2;
62
+ };
63
+ //#endregion
64
+ //#region src/core/parse.d.ts
65
+ export type SourceSpan = {
66
+ offset: number;
67
+ length: number;
68
+ };
69
+ export type ParsedDocument = {
70
+ value: unknown;
71
+ diagnostics: Diagnostic[];
72
+ locate: (pointer: string, key?: boolean) => SourceSpan | undefined;
73
+ };
74
+ export type ParsedJsonDocument = ParsedDocument & {
75
+ root: Node | undefined;
76
+ };
77
+ export declare function pointer(parts: readonly (string | number)[]): string;
78
+ export declare function positionAt(text: string, offset: number): Position;
79
+ export declare function diagnostic(text: string, code: string, message: string, jsonPointer?: string, offset?: number, length?: number): Diagnostic;
80
+ export declare function parseDocument(text: string, mode: Mode): ParsedJsonDocument;
81
+ export declare function locate(root: Node | undefined, jsonPointer: string, key?: boolean): Node | undefined;
82
+ //#endregion
83
+ //#region src/core/config.d.ts
84
+ export declare const CONFIG_NAME = "correctly.config.json";
85
+ export declare const DEFAULT_EXCLUDES: string[];
86
+ export type ProjectConfig = {
87
+ files?: string[];
88
+ exclude?: string[];
89
+ associations: {
90
+ name?: string;
91
+ files: string[];
92
+ schema: string | null;
93
+ mode?: Mode;
94
+ }[];
95
+ remote?: {
96
+ offline?: boolean;
97
+ cacheDir?: string;
98
+ timeoutMs?: number;
99
+ maxBytes?: number;
100
+ maxRequests?: number;
101
+ };
102
+ };
103
+ export type Project = {
104
+ configPath: string;
105
+ root: string;
106
+ config: ProjectConfig;
107
+ };
108
+ export type ReadText = (uri: string) => Promise<string>;
109
+ export declare const readText: ReadText;
110
+ export declare function contains(root: string, file: string): boolean;
111
+ export declare function discoverConfig(start: string, boundary?: string, virtualFiles?: ReadonlySet<string>): Promise<string>;
112
+ export declare function loadProject(configPath: string, read?: ReadText): Promise<Project>;
113
+ export declare function schemaUri(value: string, root: string): string;
114
+ export declare function exclusions(project: Project): string[];
115
+ export declare function isIncluded(project: Project, file: string): boolean;
116
+ export declare function associationFor(project: Project, file: string): Association | null;
117
+ export declare function modeFor(file: string): Mode;
118
+ //#endregion
119
+ //#region src/core/schemas.d.ts
120
+ export type Dialect = "draft7" | "2020-12";
121
+ export declare const DIALECTS: {
122
+ readonly draft7: "http://json-schema.org/draft-07/schema#";
123
+ readonly "2020-12": "https://json-schema.org/draft/2020-12/schema";
124
+ };
125
+ export type SchemaResource = {
126
+ uri: string;
127
+ schema: AnySchema;
128
+ dialect: Dialect;
129
+ };
130
+ export type StoreOptions = {
131
+ offline?: boolean;
132
+ read?: ReadText;
133
+ fetch?: typeof fetch;
134
+ signal?: AbortSignal;
135
+ };
136
+ export declare class SchemaStore {
137
+ private readonly options;
138
+ readonly resources: Map<string, SchemaResource>;
139
+ private readonly pending;
140
+ private readonly cacheDir;
141
+ private readonly offline;
142
+ private readonly timeoutMs;
143
+ private readonly maxBytes;
144
+ private readonly maxRequests;
145
+ private readonly read;
146
+ private readonly request;
147
+ private requests;
148
+ constructor(project: Project, options?: StoreOptions);
149
+ load(value: string, inherited?: Dialect): Promise<SchemaResource>;
150
+ private loadResource;
151
+ private register;
152
+ private normalize;
153
+ private remote;
154
+ }
155
+ export declare function localSchemaPath(uri: string): string | undefined;
156
+ //#endregion
157
+ //#region src/core/engine.d.ts
158
+ export declare class Engine {
159
+ readonly project: Project;
160
+ readonly store: SchemaStore;
161
+ private readonly compiled;
162
+ constructor(project: Project, options?: StoreOptions);
163
+ validator(uri: string): Promise<ValidateFunction>;
164
+ private compile;
165
+ prepare(): Promise<void>;
166
+ validate(file: string, text: string): Promise<FileResult>;
167
+ }
168
+ //#endregion
169
+ //#region src/core/diagnostics.d.ts
170
+ type DiagnosticView = Diagnostic & {
171
+ branches: {
172
+ label: string;
173
+ diagnostics: DiagnosticView[];
174
+ }[];
175
+ };
176
+ export declare function diagnosticViews(diagnostics: readonly Diagnostic[]): DiagnosticView[];
177
+ export declare function diagnosticDetails(view: DiagnosticView): {
178
+ label: string;
179
+ diagnostic: DiagnosticView;
180
+ }[];
181
+ //#endregion
182
+ export type { DiagnosticView };
183
+ //# sourceMappingURL=core.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/parse.ts","../src/core/config.ts","../src/core/schemas.ts","../src/core/engine.ts","../src/core/diagnostics.ts"],"mappings":";;;YAAY;YACA;EAAa;EAAc;;YAC3B;EAAU,OAAO;EAAU,KAAK;;YAChC;EACX;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;IAAY;IAAgB;;;YAEjB;EAAY;EAAc;EAAiB;;qBAC1C,uBAAuB;WAC1B;EACT,YAAY,cAAc;;wBAMX,QAAQ,gBAAgB,gBAAgB;YAO5C;EACX;EACA;EACA;EACA,MAAM;;YAEK;EACX;EACA,MAAM;EACN;EACA,aAAa;EACb,aAAa;EACb,UAAU;;YAEC;EACX;EACA;EACA,OAAO;EACP,UAAU;EACV;IACC;IACA;IACA;IACA;IACA;;EAED;;;;YC3CW;EAAe;EAAgB;;YAC/B;EACX;EACA,aAAa;EACb,SAAS,iBAAiB,kBAAkB;;YAEjC,qBAAqB;EAAmB,MAAM;;wBAE1C,QAAQ;wBAMR,WAAW,cAAc,iBAAiB;wBAiB1C,WACf,cACA,cACA,iBACA,sBACA,iBACA,kBACE;wBAgBa,cAAc,cAAc,MAAM,OAAO;wBA8EzC,OACf,MAAM,kBACN,qBACA,gBACE;;;qBC1IU;qBACA;YAOD;EACX;EACA;EACA;IACC;IACA;IACA;IACA,OAAO;;EAER;IACC;IACA;IACA;IACA;IACA;;;YAGU;EACX;EACA;EACA,QAAQ;;YAEG,YAAY,gBAAgB;qBAC3B,UAAU;wBAKP,SAAS,cAAc;wBASjB,eACrB,eACA,mBACA,eAAc,sBACZ;wBAuBmB,YACrB,oBACA,OAAM,WACJ,QAAQ;wBAwCK,UAAU,eAAe;wBAoBzB,WAAW,SAAS;wBAkBpB,WAAW,SAAS,SAAS;wBAY7B,eACf,SAAS,SACT,eACE;wBAoBa,QAAQ,eAAe;;;YC5L3B;qBACC;WACZ;WACA;;YAwDW;EACX;EACA,QAAQ;EACR,SAAS;;YAQE;EACX;EACA,OAAO;EACP,eAAe;EACf,SAAS;;qBAGG;mBACK;WACR,WAAS,YAAA;mBACD;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;UACT;EACR,YAAY,SAAS,SAAS,UAAS;EAejC,KACL,eACA,YAAW,UACT,QAAQ;UAeG;UA2DN;UAOA;UAoEM;;wBA8GC,gBAAgB;;;qBCzPnB;WACH,SAAS;WACT,OAAO;mBACC;EACjB,YAAY,SAAS,SAAS,UAAS;EAKvC,UAAU,cAAc,QAAQ;UASlB;EAwDR,WAAW;EAYX,SAAS,cAAc,eAAe,QAAQ;;;;KCjNzC,iBAAiB;EAC5B;IAAY;IAAe,aAAa;;;wBAiEzB,gBACf,sBAAsB,eACpB;wBA8Ba,kBACf,MAAM;EACF;EAAe,YAAY"}
package/dist/core.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { C as pointer, S as parseDocument, _ as schemaUri, a as SchemaStore, b as diagnostic, c as DEFAULT_EXCLUDES, d as discoverConfig, f as exclusions, g as readText, h as modeFor, i as DIALECTS, l as associationFor, m as loadProject, n as diagnosticDetails, o as localSchemaPath, p as isIncluded, r as diagnosticViews, s as CONFIG_NAME, t as Engine, u as contains, v as CorrectlyError, w as positionAt, x as locate, y as failure } from "./engine-BgWBnb1A.mjs";
2
+ export { CONFIG_NAME, CorrectlyError, DEFAULT_EXCLUDES, DIALECTS, Engine, SchemaStore, associationFor, contains, diagnostic, diagnosticDetails, diagnosticViews, discoverConfig, exclusions, failure, isIncluded, loadProject, localSchemaPath, locate, modeFor, parseDocument, pointer, positionAt, readText, schemaUri };