@fusion.dev/helix-diagnostics 0.140.0 → 0.142.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/README.md CHANGED
@@ -126,6 +126,54 @@ The address is always on a local host.
126
126
  `[::1]`, `*.localhost`, and `*.test`. It listens on `127.0.0.1`, so nothing
127
127
  else on your network reaches it, and production pages never send to it.
128
128
 
129
+ ## Scan your code
130
+
131
+ `helix scan` reads every stylesheet, template, and module in your repository,
132
+ judges them against the lint data of the Helix packages you installed, and
133
+ writes one run in the shape a page writes, with `source: "code"`:
134
+
135
+ ```sh
136
+ npx helix scan
137
+ npx helix scan --build dist --out reports/helix-diagnostics.json
138
+ ```
139
+
140
+ - **Each finding has its file and line**, and the kind and fingerprint a page
141
+ gives the same fault, so a dashboard joins the two.
142
+ - **It reads every file, and so every route.** A page sees the routes someone
143
+ opened, so a scan is the better count of what your project uses. An element
144
+ written once counts once, however many times it renders, and
145
+ `usage.counted` is `scanned`.
146
+ - **What only a page can settle is `unsettled`.** A hook set on a Helix tag
147
+ that does not have it works where an element with the hook is inside that
148
+ tag, and only the running page shows whether one is. The scan lists these
149
+ apart, each with the fingerprint the page's finding would have, and they
150
+ never fail the scan.
151
+ - **It reads** CSS, Sass, and Less files; HTML, Vue, Svelte, and Astro
152
+ templates; MDX outside its code samples; and JavaScript and TypeScript, for
153
+ JSX, Lit's `html` and `css` templates, and Angular's inline templates. A
154
+ file that does not parse, and a stylesheet in Sass's indented syntax or
155
+ Stylus, is listed in `coverage.unreadable`.
156
+ - **It leaves out** what `.gitignore` leaves out, the folders a build or a
157
+ package manager writes, and tests: a name with `.test.` or `.spec.`, and
158
+ anything under `__tests__`.
159
+ - **An attribute is checked where the code writes it as one.** In
160
+ `tone="primary"`, the name and the value are checked. Lit's `tone=${...}`
161
+ writes the attribute, so its name is checked. An expression in JSX, Svelte,
162
+ or Angular, and Vue's `:tone`, set a property where the element has one, so
163
+ none of them is checked.
164
+ - **With `--build`, it weighs your bundle**: what each Helix family and
165
+ package adds to it, from its source maps, as `performance.weight`. Turn
166
+ source maps on in your bundler, and pass the folder it writes the browser's
167
+ files to, such as `dist` or `.next/static`.
168
+ - **With `--out`, it writes its run to the CI file**, and keeps the runs
169
+ `helix diagnostics` wrote there. The relay keeps the scan's run the same
170
+ way, so the two share one file, in either order.
171
+
172
+ It reads `helix-diagnostics.json` from the project, then from the directory it
173
+ runs in, or the file `--config <path>` names, and refuses a setting that names
174
+ nothing, as the page does. It exits 1 on a finding at `error`, and 3 where the
175
+ project installs no Helix lint data. `--json` prints the run.
176
+
129
177
  ## Run it once, directly
130
178
 
131
179
  `diagnose()` is one run, which `initDiagnostics()` makes for you. Call it
@@ -169,9 +217,12 @@ A report is one run, in one shape for every sink, versioned by `schema`:
169
217
  severity, the tag and its family, a `category` (`styling`, `markup`,
170
218
  `integrity`, or `guideline`), an `owner` (`consumer`, or `helix` for a fault
171
219
  in the library), a `fingerprint` that stays the same from one run to the next
172
- for the same fault, and the rule's own text as evidence.
220
+ for the same fault, and the rule's own text as evidence. A scan's findings
221
+ also have the file's `line`.
173
222
  - **`performance`**: what the components cost, by tag, where you asked for it.
174
223
  See [Performance](#performance).
224
+ - **`unsettled`**: from a scan, the faults only a page can settle. See
225
+ [Scan your code](#scan-your-code).
175
226
  - **`runtime`**: what the library reported going wrong as it ran on the page,
176
227
  from its first load. One entry per distinct event, with its `kind`, its
177
228
  `tag`, the sentence that says what to do, how many times it was reported,
@@ -284,6 +335,9 @@ that has not asked pays one property read per element. Layout shift and long
284
335
  tasks are measured in Chromium only. Elsewhere `measured` says so, and their
285
336
  numbers are left out.
286
337
 
338
+ What each family adds to your bundle is measured from the code, by
339
+ `helix scan --build`, as `weight`. See [Scan your code](#scan-your-code).
340
+
287
341
  ## Configure it
288
342
 
289
343
  Every setting can be given in three places, and a later place wins, field by
@@ -357,6 +411,10 @@ A fault found on many pages is one line, with the pages it was on. `error`
357
411
  findings fail the job by default; pass `failOn: 'warn'` to fail on warnings
358
412
  too, or `'never'` to report without failing.
359
413
 
414
+ `ciSink` writes the whole file, so to add a run from your code, run
415
+ `helix scan --out` with the same file after it. The scan keeps the runs it
416
+ finds there.
417
+
360
418
  ## Versions
361
419
 
362
420
  `@fusion.dev/helix-core` lists its version on the page as it loads, and every
package/dist/index.d.ts CHANGED
@@ -35,7 +35,7 @@ export { fingerprintOf, normalize } from './identity.js';
35
35
  export { type Diagnostics, INSTANCE, type InitOptions, initDiagnostics } from './init.js';
36
36
  export type { LintData, LintTag, ManifestSource, Published, Tier } from './manifest.js';
37
37
  export { index, lintOf, nearest } from './manifest.js';
38
- export type { Category, Configuration, Coverage, Environment, Finding, FindingCategory, Kind, Layer, Origin, Owner, Report, Severity, Unreadable, Usage, } from './report.js';
38
+ export type { Category, Configuration, Coverage, Environment, Finding, FindingCategory, Kind, Layer, Origin, Owner, Report, Severity, Unreadable, Usage, Weight, } from './report.js';
39
39
  export { CATEGORIES, KINDS } from './report.js';
40
40
  export { type Override, type Rules, SHIPPED_RULES } from './rules.js';
41
41
  export { TIMING, type Timing } from './schedule.js';
package/dist/report.d.ts CHANGED
@@ -13,9 +13,10 @@
13
13
  /** Where a rule was read from, so a reader can go and look at it. */
14
14
  export interface Origin {
15
15
  /**
16
- * The rule's own text, which is the receipt.
16
+ * The rule's own text, which is the receipt. For a scan's finding about
17
+ * an attribute, the opening tag's text.
17
18
  *
18
- * **There is no line number and there cannot be one.** CSSOM exposes
19
+ * **A page has no line number and cannot have one.** CSSOM exposes
19
20
  * `cssText` and an index within the sheet; the byte offset in the authored
20
21
  * file is not in the object model at any level. A line was asked for, and
21
22
  * this is the honest substitute: the text of the rule is what a
@@ -23,9 +24,18 @@ export interface Origin {
23
24
  * line number would not.
24
25
  */
25
26
  cssText: string;
26
- /** Position within the sheet's own rule list, for a sheet with repeats. */
27
+ /**
28
+ * Position within the sheet's own rule list, for a sheet with repeats. For
29
+ * a scan's finding about an attribute, the element's position in the file.
30
+ */
27
31
  index: number;
28
- /** The sheet's `href`, or a description of why it has none. */
32
+ /**
33
+ * The line the rule's first declaration, or the element's opening tag, is
34
+ * on, counting from 1. A scan reads the file, so its findings have one. A
35
+ * page's never do.
36
+ */
37
+ line?: number;
38
+ /** The sheet's `href`, or a description of why it has none. For a scan, the file's path. */
29
39
  sheet: string;
30
40
  }
31
41
  /**
@@ -109,6 +119,23 @@ export type Identified = Omit<Finding, 'decidedBy' | 'severity'>;
109
119
  export declare const CATEGORIES: readonly ['assist', 'configuration', 'coverage', 'findings', 'performance', 'runtime', 'usage'];
110
120
  /** A kind of telemetry. */
111
121
  export type Category = (typeof CATEGORIES)[number];
122
+ /**
123
+ * What each Helix family adds to a build, measured by `helix scan --build`
124
+ * from the build's source maps, in bytes before compression.
125
+ */
126
+ export interface Weight {
127
+ /** The bytes from each family's folder, across the tiers. */
128
+ families: Record<string, number>;
129
+ /** The bundle files read with a source map, and without one, which cannot be split. */
130
+ files: {
131
+ mapped: number;
132
+ unmapped: number;
133
+ };
134
+ /** The bytes from each Helix package. */
135
+ packages: Record<string, number>;
136
+ /** Every byte in the bundle files read, Helix or not. */
137
+ total: number;
138
+ }
112
139
  /**
113
140
  * What the components cost on a page, by tag, where a project asked for it.
114
141
  */
@@ -140,6 +167,12 @@ export interface PerformanceReport {
140
167
  /** The layout shift its elements moved, summed as the browser scores it. */
141
168
  shift: number;
142
169
  }>;
170
+ /**
171
+ * What each family adds to the build, from a scan given the build. A scan
172
+ * measures nothing in a browser, so its `measured` is false and its `tags`
173
+ * are empty.
174
+ */
175
+ weight?: Weight;
143
176
  }
144
177
  /**
145
178
  * A runtime event as a run holds it: what `@fusion.dev/helix-core` reported
@@ -168,9 +201,14 @@ export interface RuntimeEntry {
168
201
  }
169
202
  /** Where a run was declared to happen. Undeclared is never sent. */
170
203
  export type Environment = 'ci' | 'development' | 'production';
171
- /** A sheet the collector could not read, and the reason. */
204
+ /**
205
+ * A sheet the collector could not read, and the reason. For a scan, a file
206
+ * that did not parse is `threw`, and a stylesheet in a language the scan does
207
+ * not read, such as Sass's indented syntax or Stylus, is `unsupported`.
208
+ */
172
209
  export interface Unreadable {
173
- reason: 'cross-origin' | 'detached' | 'threw';
210
+ reason: 'cross-origin' | 'detached' | 'threw' | 'unsupported';
211
+ /** The sheet's `href`, or for a scan, the file's path. */
174
212
  sheet: string;
175
213
  }
176
214
  /**
@@ -183,6 +221,8 @@ export interface Unreadable {
183
221
  * hole this reports rather than steps over.
184
222
  */
185
223
  export interface Coverage {
224
+ /** Files read, for a scan: stylesheets, templates, and modules. */
225
+ files?: number;
186
226
  /** Sheets whose rules were read end to end. */
187
227
  read: number;
188
228
  /** Shadow roots entered, including nested ones. */
@@ -214,9 +254,10 @@ export interface Usage {
214
254
  * How the elements were counted. `watched`: every element that rendered
215
255
  * since `watch()` began, including the ones that have gone, such as a
216
256
  * closed dialog. `walked`: the elements in the page at the moment of the
217
- * run, which misses those.
257
+ * run, which misses those. `scanned`: each element written in the code, by
258
+ * `helix scan`, counted once however many times it renders.
218
259
  */
219
- counted: 'walked' | 'watched';
260
+ counted: 'scanned' | 'walked' | 'watched';
220
261
  /** Each hook the page's stylesheets set, by tag, counted per declaration. */
221
262
  hooks: Record<string, Record<string, number>>;
222
263
  /**
@@ -321,6 +362,12 @@ export interface Report {
321
362
  * first load: one entry per distinct event, counted.
322
363
  */
323
364
  runtime: RuntimeEntry[];
365
+ /**
366
+ * The faults a scan found and only a page can settle, such as a hook set
367
+ * on an element for one inside it, each with the fingerprint the page's
368
+ * finding would have. A scan's alone.
369
+ */
370
+ unsettled?: Identified[];
324
371
  /**
325
372
  * The project's own name and labels, as its configuration gives them. The
326
373
  * key is never written into a report.
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * release, so the two never disagree; a spec beside this holds them to it.
7
7
  * Nothing else in the running code knows which release it is.
8
8
  */
9
- export declare const VERSION = "0.140.0";
9
+ export declare const VERSION = "0.142.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.140.0';
1
+ export const VERSION = '0.142.0';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-diagnostics",
3
3
  "description": "Finds the Helix mistakes that are valid CSS: dead parts, misnamed hooks, two copies of the library.",
4
- "version": "0.140.0",
4
+ "version": "0.142.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "bugs": "https://github.com/project-fusion/fusion/issues",
35
35
  "devDependencies": {
36
- "@fusion.dev/helix-testing": "0.140.0",
36
+ "@fusion.dev/helix-testing": "0.142.0",
37
37
  "@types/bun": "^1.3.0"
38
38
  },
39
39
  "homepage": "https://fusion.dev",