nuxt-layerscope 0.1.2 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ - Notes and the `--verbose` line are printed after the report, in every command, so they are the
6
+ last thing on screen. `note:` is highlighted in a terminal.
7
+ - `layerscope check` lists files with errors after files with only warnings, so the errors stay
8
+ next to the summary in a long report.
9
+ - The text report colors severities, rule names and the summary in a terminal. `NO_COLOR` and
10
+ `FORCE_COLOR` are honoured.
11
+
3
12
  ## 0.1.2
4
13
 
5
14
  - A note appears when `nuxt.config` is newer than the registry, because changes to its
package/dist/api.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { a as Context, c as Layer, d as Reference, f as ReferenceKind, g as SourceOption, h as Severity, i as BaselineResult, l as LayerRule, m as RuleName, n as AnalyzeResult, o as Edge, p as ResolutionSource, r as BaselineEntry, s as Finding, t as defineConfig, u as LayerscopeConfig } from "./define-DNRGLVG_.mjs";
2
2
  import { t as ProjectConfig } from "./effective-Dqg4EwH_.mjs";
3
+ import { styleText } from "node:util";
3
4
  //#region src/analyze/index.d.ts
4
5
  interface AnalyzeOptions {
5
6
  /** Defaults to `process.cwd()`. */
@@ -114,11 +115,15 @@ interface Registry {
114
115
  config?: ProjectConfig;
115
116
  }
116
117
  //#endregion
118
+ //#region src/utils/style.d.ts
119
+ /** Wraps text in terminal styles, e.g. `paint('red', 'error')`. */
120
+ type Paint = (format: Parameters<typeof styleText>[0], text: string) => string;
121
+ //#endregion
117
122
  //#region src/report/index.d.ts
118
123
  export declare const OUTPUT_FORMATS: readonly ['text', 'github', 'json'];
119
124
  type OutputFormat = (typeof OUTPUT_FORMATS)[number];
120
- /** Paths in the output are relative to `cwd`. */
121
- export declare function formatResult(result: AnalyzeResult, format: OutputFormat, cwd?: string): string;
125
+ /** Paths in the output are relative to `cwd`. Only the text format uses `paint`. */
126
+ export declare function formatResult(result: AnalyzeResult, format: OutputFormat, cwd?: string, paint?: Paint): string;
122
127
  //#endregion
123
128
  //#region src/report/json.d.ts
124
129
  /** Bumped on breaking changes to the JSON report shape. */
package/dist/api.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { l as REGISTRY_FILE, u as REGISTRY_VERSION, v as LayerscopeError } from "./layer-factory-DFh7nkFH.mjs";
2
2
  import { t as defineConfig } from "./define-l62B9fVk.mjs";
3
3
  import { i as createBaseline, n as BASELINE_FILE, r as BASELINE_VERSION, t as analyze } from "./analyze-QaYmBXyK.mjs";
4
- import { i as JSON_REPORT_VERSION, n as formatResult, t as OUTPUT_FORMATS } from "./report-BNW0GCl9.mjs";
4
+ import { i as JSON_REPORT_VERSION, n as formatResult, t as OUTPUT_FORMATS } from "./report-DShTCB59.mjs";
5
5
  import { r as buildGraph, t as findUnused } from "./unused-BtsPSfxb.mjs";
6
6
  export { BASELINE_FILE, BASELINE_VERSION, JSON_REPORT_VERSION, LayerscopeError, OUTPUT_FORMATS, REGISTRY_FILE, REGISTRY_VERSION, analyze, buildGraph, createBaseline, defineConfig, findUnused, formatResult };
package/dist/bin.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { v as LayerscopeError } from "./layer-factory-DFh7nkFH.mjs";
3
3
  import { i as plural, r as pascalCase } from "./strings-CrOjNZmB.mjs";
4
4
  import { a as writeBaseline, i as createBaseline, n as BASELINE_FILE, t as analyze } from "./analyze-QaYmBXyK.mjs";
5
- import { n as formatResult, r as isOutputFormat, t as OUTPUT_FORMATS } from "./report-BNW0GCl9.mjs";
5
+ import { a as paintFor, n as formatResult, r as isOutputFormat, t as OUTPUT_FORMATS } from "./report-DShTCB59.mjs";
6
6
  import { i as edgeStatus, n as GRAPH_LEVELS, r as buildGraph, t as findUnused } from "./unused-BtsPSfxb.mjs";
7
7
  import { relative, resolve } from "pathe";
8
8
  import { cac } from "cac";
@@ -20,14 +20,19 @@ function toSource(value) {
20
20
  if (source === void 0) throw new LayerscopeError(`Unknown source "${value}". Use ${SOURCE_OPTIONS.join(", ")}.`);
21
21
  return source;
22
22
  }
23
- /** Diagnostics go to stderr, so stdout stays machine-readable in every format. */
23
+ /**
24
+ * Diagnostics go to stderr, so stdout stays machine-readable in every format. Call it after the
25
+ * report is written: notes at the end are the ones a reader still sees when the report scrolled.
26
+ */
24
27
  function writeNotes(result, verbose) {
25
- const lines = result.notes.map((note) => `layerscope: note: ${note}`);
28
+ const paint = paintFor(process.stderr);
29
+ const lines = result.notes.map((note) => `${paint(["yellow", "bold"], "layerscope: note:")} ${note}`);
26
30
  if (verbose) {
27
31
  const kind = result.source === "registry" ? "the registry" : "the generated .d.ts files";
28
- lines.unshift(`layerscope: symbols from ${kind} (${relative(process.cwd(), result.sourceFile)})`);
32
+ const source = relative(process.cwd(), result.sourceFile);
33
+ lines.unshift(paint("dim", `layerscope: symbols from ${kind} (${source})`));
29
34
  }
30
- if (lines.length > 0) process.stderr.write(`${lines.join("\n")}\n`);
35
+ if (lines.length > 0) process.stderr.write(`${process.stdout.isTTY ? "\n" : ""}${lines.join("\n")}\n`);
31
36
  }
32
37
  //#endregion
33
38
  //#region src/commands/check.ts
@@ -38,11 +43,11 @@ async function updateBaseline(rootDir, flags) {
38
43
  prepare: flags.prepare,
39
44
  source: toSource(flags.source)
40
45
  });
41
- writeNotes(result, flags.verbose === true);
42
46
  const file = resolve(rootDir, flags.baseline);
43
47
  writeBaseline(file, createBaseline(result.findings, rootDir));
44
48
  const count = plural(result.findings.length, "finding");
45
49
  process.stdout.write(`✔ Wrote ${count} to ${relative(process.cwd(), file)}\n`);
50
+ writeNotes(result, flags.verbose === true);
46
51
  return 0;
47
52
  }
48
53
  async function check(root, flags) {
@@ -57,8 +62,8 @@ async function check(root, flags) {
57
62
  source: toSource(flags.source),
58
63
  baseline: flags.baseline
59
64
  });
65
+ process.stdout.write(formatResult(result, format, process.cwd(), paintFor(process.stdout)));
60
66
  writeNotes(result, flags.verbose === true);
61
- process.stdout.write(formatResult(result, format));
62
67
  return result.findings.some((finding) => finding.severity === "error") ? 1 : 0;
63
68
  }
64
69
  function registerCheck(cli) {
@@ -162,8 +167,8 @@ async function graph(root, flags) {
162
167
  prepare: flags.prepare,
163
168
  source: toSource(flags.source)
164
169
  });
165
- writeNotes(result, flags.verbose === true);
166
170
  process.stdout.write(formatGraph(buildGraph(result, result.config, level), format));
171
+ writeNotes(result, flags.verbose === true);
167
172
  return 0;
168
173
  }
169
174
  function registerGraph(cli) {
@@ -216,8 +221,8 @@ async function unused(root, flags) {
216
221
  prepare: flags.prepare,
217
222
  source: toSource(flags.source)
218
223
  });
219
- writeNotes(result, flags.verbose === true);
220
224
  process.stdout.write(formatUnused(findUnused(result), format));
225
+ writeNotes(result, flags.verbose === true);
221
226
  return 0;
222
227
  }
223
228
  function registerUnused(cli) {
@@ -331,10 +336,13 @@ async function why(symbol, root, flags) {
331
336
  prepare: flags.prepare,
332
337
  source: toSource(flags.source)
333
338
  });
334
- writeNotes(result, flags.verbose === true);
335
339
  const targets = findUses(result, symbol, result.config);
336
- if (targets.length === 0) throw new LayerscopeError(`No uses of "${symbol}" found.`);
340
+ if (targets.length === 0) {
341
+ writeNotes(result, flags.verbose === true);
342
+ throw new LayerscopeError(`No uses of "${symbol}" found.`);
343
+ }
337
344
  process.stdout.write(formatWhy(symbol, targets, format));
345
+ writeNotes(result, flags.verbose === true);
338
346
  return 0;
339
347
  }
340
348
  function registerWhy(cli) {
package/dist/index.mjs CHANGED
@@ -106,7 +106,7 @@ function renderError(message) {
106
106
  //#region src/devtools/index.ts
107
107
  const DEVTOOLS_ROUTE = "/__layerscope";
108
108
  async function renderReport(nuxt, format) {
109
- const [{ analyze }, { formatResult }] = await Promise.all([import("./analyze-DVzyPH7N.mjs"), import("./report-DtAi0vPb.mjs")]);
109
+ const [{ analyze }, { formatResult }] = await Promise.all([import("./analyze-DVzyPH7N.mjs"), import("./report-CKhXT50W.mjs")]);
110
110
  const { rootDir, app } = nuxt.options;
111
111
  const result = await analyze({ rootDir });
112
112
  if (format === "json") return formatResult(result, "json", rootDir);
@@ -1,2 +1,2 @@
1
- import { n as formatResult, r as isOutputFormat, t as OUTPUT_FORMATS } from "./report-BNW0GCl9.mjs";
1
+ import { n as formatResult, r as isOutputFormat, t as OUTPUT_FORMATS } from "./report-DShTCB59.mjs";
2
2
  export { OUTPUT_FORMATS, formatResult, isOutputFormat };
@@ -1,6 +1,15 @@
1
1
  import { t as summarize } from "./summary-DiUYKMPe.mjs";
2
2
  import { i as plural } from "./strings-CrOjNZmB.mjs";
3
3
  import { relative } from "pathe";
4
+ import { styleText } from "node:util";
5
+ //#region src/utils/style.ts
6
+ /** No styles: what the report functions use unless the CLI passes a painter for its terminal. */
7
+ const plain = (_format, text) => text;
8
+ /** Styles only when `stream` is a terminal; `NO_COLOR` and `FORCE_COLOR` are honoured. */
9
+ function paintFor(stream) {
10
+ return (format, text) => styleText(format, text, { stream });
11
+ }
12
+ //#endregion
4
13
  //#region src/report/baseline.ts
5
14
  /** `layer-boundary useCart → web (×2)` */
6
15
  function describeEntry(entry) {
@@ -87,8 +96,8 @@ function formatJson(result, cwd) {
87
96
  //#endregion
88
97
  //#region src/report/text.ts
89
98
  const DETAIL_INDENT = " ".repeat(17);
90
- function formatFinding(finding, cwd) {
91
- return [` ${`${finding.line}:${finding.column}`.padEnd(8)}${finding.severity.padEnd(7)}${finding.message} ${finding.rule}`, ...findingDetails(finding, cwd).map((detail) => `${DETAIL_INDENT}${detail}`)].join("\n");
99
+ function formatFinding(finding, cwd, paint) {
100
+ return [` ${paint("dim", `${finding.line}:${finding.column}`.padEnd(8))}${paint(finding.severity === "error" ? "red" : "yellow", finding.severity) + " ".repeat(7 - finding.severity.length)}${finding.message} ${paint("dim", finding.rule)}`, ...findingDetails(finding, cwd).map((detail) => `${DETAIL_INDENT}${paint("dim", detail)}`)].join("\n");
92
101
  }
93
102
  function formatRemovable(baseline, cwd) {
94
103
  if (baseline.removable.length === 0) return null;
@@ -96,17 +105,22 @@ function formatRemovable(baseline, cwd) {
96
105
  const count = baseline.removable.length;
97
106
  return [`${relative(cwd, baseline.file)}: ${plural(count, "fixed entry", "fixed entries")}. ${UPDATE_HINT} ${count === 1 ? "it" : "them"}.`, ...entries].join("\n");
98
107
  }
99
- function formatFooter(result) {
108
+ function formatFooter(result, paint) {
100
109
  const suppressed = result.baseline?.suppressed.length ?? 0;
101
110
  const baselined = suppressed > 0 ? `, ${suppressed} more in the baseline` : "";
102
- if (result.findings.length === 0) return `✔ No problems in ${plural(result.files.length, "file")} across ${plural(result.layers.length, "layer")}${baselined}`;
111
+ if (result.findings.length === 0) return `${paint(["green", "bold"], `✔ No problems in ${plural(result.files.length, "file")} across ${plural(result.layers.length, "layer")}`)}${baselined}`;
103
112
  const { errors, warnings } = summarize(result.findings);
104
- return `✖ ${plural(result.findings.length, "problem")} (${plural(errors, "error")}, ${plural(warnings, "warning")})${baselined}`;
113
+ const counts = `✖ ${plural(result.findings.length, "problem")} (${plural(errors, "error")}, ${plural(warnings, "warning")})`;
114
+ return `${paint(["bold", errors > 0 ? "red" : "yellow"], counts)}${baselined}`;
115
+ }
116
+ function hasError(findings) {
117
+ return findings.some((finding) => finding.severity === "error");
105
118
  }
106
- function formatText(result, cwd) {
107
- const sections = [...Map.groupBy(result.findings, (finding) => relative(cwd, finding.file))].map(([file, findings]) => [file, ...findings.map((finding) => formatFinding(finding, cwd))].join("\n"));
119
+ /** Files with errors come last, next to the summary, where they are least likely to scroll away. */
120
+ function formatText(result, cwd, paint = plain) {
121
+ const sections = [...Map.groupBy(result.findings, (finding) => relative(cwd, finding.file))].toSorted(([, a], [, b]) => Number(hasError(a)) - Number(hasError(b))).map(([file, findings]) => [paint("underline", file), ...findings.map((finding) => formatFinding(finding, cwd, paint))].join("\n"));
108
122
  const removable = result.baseline === void 0 ? null : formatRemovable(result.baseline, cwd);
109
- return `${[...removable === null ? sections : [...sections, removable], formatFooter(result)].join("\n\n")}\n`;
123
+ return `${[...removable === null ? sections : [...sections, removable], formatFooter(result, paint)].join("\n\n")}\n`;
110
124
  }
111
125
  //#endregion
112
126
  //#region src/report/index.ts
@@ -123,9 +137,9 @@ const formatters = {
123
137
  function isOutputFormat(value) {
124
138
  return Object.hasOwn(formatters, value);
125
139
  }
126
- /** Paths in the output are relative to `cwd`. */
127
- function formatResult(result, format, cwd = process.cwd()) {
128
- return formatters[format](result, cwd);
140
+ /** Paths in the output are relative to `cwd`. Only the text format uses `paint`. */
141
+ function formatResult(result, format, cwd = process.cwd(), paint = plain) {
142
+ return formatters[format](result, cwd, paint);
129
143
  }
130
144
  //#endregion
131
- export { JSON_REPORT_VERSION as i, formatResult as n, isOutputFormat as r, OUTPUT_FORMATS as t };
145
+ export { paintFor as a, JSON_REPORT_VERSION as i, formatResult as n, isOutputFormat as r, OUTPUT_FORMATS as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-layerscope",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Layer boundary checks for Nuxt apps that see auto-imports",
5
5
  "keywords": [
6
6
  "architecture",