@cms-lab/reporter 1.2.1 → 1.2.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/README.md CHANGED
@@ -9,6 +9,16 @@ import { renderHtmlReport } from "@cms-lab/reporter";
9
9
  The CLI uses this package for `cms-lab scan --report`. The generated report is a
10
10
  self-contained HTML file with grouped diagnostics and client-side filters.
11
11
 
12
+ Use the share privacy mode when rendering a report that may be attached to a
13
+ public issue:
14
+
15
+ ```ts
16
+ renderHtmlReport(result, { privacy: "share" });
17
+ ```
18
+
19
+ Share-safe reports keep diagnostic codes, severity, route paths, and field paths
20
+ visible, but redact CMS source IDs and local project paths.
21
+
12
22
  ## Release History
13
23
 
14
24
  See the repository [changelog](https://github.com/i-afaqrashid/cms-lab/blob/main/CHANGELOG.md)
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@ type ReporterStatus = {
5
5
  format: "html";
6
6
  };
7
7
  declare function getReporterStatus(): ReporterStatus;
8
- declare function renderHtmlReport(result: ScanResult): string;
8
+ type HtmlReportPrivacy = "full" | "share";
9
+ type HtmlReportOptions = {
10
+ privacy?: HtmlReportPrivacy;
11
+ };
12
+ declare function renderHtmlReport(result: ScanResult, options?: HtmlReportOptions): string;
9
13
 
10
- export { type ReporterStatus, getReporterStatus, renderHtmlReport };
14
+ export { type HtmlReportOptions, type HtmlReportPrivacy, type ReporterStatus, getReporterStatus, renderHtmlReport };
package/dist/index.js CHANGED
@@ -18,9 +18,10 @@ function getReporterStatus() {
18
18
  format: "html"
19
19
  };
20
20
  }
21
- function renderHtmlReport(result) {
21
+ function renderHtmlReport(result, options = {}) {
22
22
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
23
23
  const diagnostics = result.diagnostics;
24
+ const privacy = options.privacy ?? "full";
24
25
  const status = result.summary.errors > 0 ? "failed" : "passed";
25
26
  const statusClass = result.summary.errors > 0 ? "fail" : result.summary.warnings > 0 ? "warn" : "ok";
26
27
  const grouped = groupDiagnostics(diagnostics);
@@ -411,6 +412,7 @@ function renderHtmlReport(result) {
411
412
  <span class="badge ${statusClass}">${escapeHtml(statusLabel(result))}</span>
412
413
  ${result.summary.warnings > 0 ? `<span class="badge warn">${result.summary.warnings} ${escapeHtml(plural(result.summary.warnings, "warning"))}</span>` : ""}
413
414
  ${result.summary.info > 0 ? `<span class="badge info">${result.summary.info} ${escapeHtml(plural(result.summary.info, "info item"))}</span>` : ""}
415
+ ${privacy === "share" ? `<span class="badge">Share-safe report</span>` : ""}
414
416
  </div>
415
417
  <div class="subtitle">
416
418
  ${escapeHtml(projectLabel(result))} \xB7 ${result.documents.length} ${escapeHtml(plural(result.documents.length, "document"))}
@@ -436,7 +438,7 @@ function renderHtmlReport(result) {
436
438
  ${grouped.map((group) => `<button class="chip" type="button" data-filter-kind="group" data-filter-value="${escapeHtml(group.label)}">${escapeHtml(group.label)} <span class="n">${group.diagnostics.length}</span></button>`).join("")}
437
439
  </div>
438
440
 
439
- ${diagnostics.length === 0 ? `<div class="empty-state">No diagnostics found.</div>` : grouped.map((group) => diagnosticsGroup(group)).join("")}
441
+ ${diagnostics.length === 0 ? `<div class="empty-state">No diagnostics found.</div>` : grouped.map((group) => diagnosticsGroup(group, result, privacy)).join("")}
440
442
 
441
443
  <div class="report-foot">
442
444
  <span>project: <span class="path-code">${escapeHtml(result.project.framework)} ${escapeHtml(result.project.router)}</span></span>
@@ -542,24 +544,45 @@ function groupForDiagnostic(diagnostic) {
542
544
  }
543
545
  return "other";
544
546
  }
545
- function diagnosticsGroup(group) {
547
+ function diagnosticsGroup(group, result, privacy) {
546
548
  return `<section class="diagnostic-group" data-diagnostic-group="${escapeHtml(group.label)}">
547
549
  <div class="group-header">
548
550
  <span>${escapeHtml(group.label)}</span> \xB7 <span class="count" data-visible-count>${group.diagnostics.length} ${escapeHtml(plural(group.diagnostics.length, "diagnostic"))}</span>
549
551
  </div>
550
- ${group.diagnostics.map((diagnostic, index) => diagnosticRow(diagnostic, group.label, index + 1)).join("")}
552
+ ${group.diagnostics.map((diagnostic, index) => diagnosticRow(diagnostic, group.label, index + 1, result, privacy)).join("")}
551
553
  </section>`;
552
554
  }
553
- function diagnosticRow(diagnostic, group, index) {
555
+ function diagnosticRow(diagnostic, group, index, result, privacy) {
556
+ const path = diagnostic.path ? redactReportValue(diagnostic.path, result, privacy) : void 0;
557
+ const message = redactReportValue(diagnostic.message, result, privacy);
558
+ const source = privacy === "share" ? diagnostic.source ? "[redacted CMS source]" : void 0 : diagnostic.source;
554
559
  return `<div class="diag ${escapeHtml(diagnostic.severity)}" data-diagnostic data-group="${escapeHtml(group)}" data-severity="${escapeHtml(diagnostic.severity)}">
555
560
  <div class="col-num">${index}</div>
556
561
  <div class="sev"><span class="marker"></span>${escapeHtml(diagnostic.severity)}</div>
557
562
  <div class="msg">
558
- ${diagnostic.path ? `<strong>${escapeHtml(diagnostic.path)}</strong> ` : ""}${escapeHtml(diagnostic.message)}
559
- <span class="ctx"><span class="diag-code">${escapeHtml(diagnostic.code)}</span>${diagnostic.source ? ` <span aria-hidden="true">\xB7</span> <span>${escapeHtml(diagnostic.source)}</span>` : ""}</span>
563
+ ${path ? `<strong>${escapeHtml(path)}</strong> ` : ""}${escapeHtml(message)}
564
+ <span class="ctx"><span class="diag-code">${escapeHtml(diagnostic.code)}</span>${source ? ` <span aria-hidden="true">\xB7</span> <span>${escapeHtml(source)}</span>` : ""}</span>
560
565
  </div>
561
566
  </div>`;
562
567
  }
568
+ function redactReportValue(value, result, privacy) {
569
+ if (privacy !== "share") {
570
+ return value;
571
+ }
572
+ let redacted = value;
573
+ const projectPaths = [
574
+ result.project.appDir,
575
+ result.project.pagesDir,
576
+ result.project.rootDir
577
+ ].filter((path) => Boolean(path)).sort((a, b) => b.length - a.length);
578
+ for (const projectPath of projectPaths) {
579
+ redacted = redacted.replaceAll(projectPath, "[redacted project path]");
580
+ }
581
+ return redacted.replaceAll(
582
+ /(?:\/Users|\/private\/var|\/var\/folders)\/[^\s<>"')]+/g,
583
+ "[redacted project path]"
584
+ ).replaceAll(/[A-Z]:\\Users\\[^\s<>"')]+/gi, "[redacted project path]");
585
+ }
563
586
  function capitalize(value) {
564
587
  return value.charAt(0).toUpperCase() + value.slice(1);
565
588
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cms-lab/reporter",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "description": "HTML report rendering for cms-lab.",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "@cms-lab/core": "1.2.1"
38
+ "@cms-lab/core": "1.2.3"
39
39
  },
40
40
  "author": "Afaq Rashid",
41
41
  "scripts": {