lighthouse-audit-utils 1.0.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
@@ -1,192 +1,258 @@
1
- # lighthouse-audit-utils
2
-
3
- Everything you'd do with a finished Lighthouse run, in one call: write the
4
- reports to disk, print the fix list to the terminal, and fail the run if a
5
- category scored below its threshold. Each step can be configured or disabled.
6
-
7
- ```bash
8
- npm install --save-dev lighthouse-audit-utils
9
- ```
10
-
11
- `lighthouse` is a peer dependency.
12
-
13
- ## Usage
14
-
15
- `runAudit` audits a URL and handles the result:
16
-
17
- ```ts
18
- import { runAudit } from 'lighthouse-audit-utils'
19
-
20
- await runAudit({
21
- lighthouseArgs: {
22
- url: 'https://example.com',
23
- flags: { port, output: ['html', 'json'] },
24
- },
25
- reports: { directory: 'lighthouse', name: 'desktop' },
26
- thresholds: { performance: 90 },
27
- })
28
- ```
29
-
30
- `lighthouseArgs` is handed to `lighthouse()`, in the order it takes them:
31
-
32
- | Option | Default | Description |
33
- | -------- | ---------- | ----------------------------------------------------- |
34
- | `url` | _required_ | The URL to audit |
35
- | `flags` | _none_ | Settings for the run, e.g. the `port` Lighthouse uses |
36
- | `config` | _none_ | Overrides the default config, e.g. `desktopConfig` |
37
-
38
- Everything else it takes is passed straight to `handleAuditResult` below, which returns `{ result, failures }`.
39
-
40
- ## `handleAuditResult`
41
-
42
- Handles a run you've already made yourself — this is what `runAudit` calls with the result from the actual `lighthouse()` audit:
43
-
44
- ```ts
45
- import lighthouse from 'lighthouse'
46
- import { handleAuditResult } from 'lighthouse-audit-utils'
47
-
48
- const result = await lighthouse(url, { port, output: ['html', 'json'] })
49
- if (!result) throw new Error('Lighthouse returned no result')
50
-
51
- await handleAuditResult({
52
- result,
53
- reports: { directory: 'lighthouse', name: 'desktop' },
54
- thresholds: { performance: 90 },
55
- })
56
- ```
57
-
58
- The three steps run in that order — reports, recommendations, thresholds — so a
59
- failing run still prints its fix list before throwing.
60
-
61
- | Option | Default | Description |
62
- | ----------------- | ---------- | ------------------------------------------------------------ |
63
- | `result` | _required_ | The full `RunnerResult` from a Lighthouse run |
64
- | `reports` | _none_ | Where to write the reports; omit to skip writing them |
65
- | `recommendations` | _none_ | Recommendation logging options, or `false` to skip |
66
- | `thresholds` | `100` | Minimum scores (0-100) one number for all, or per category |
67
- | `ignoreError` | `false` | Return the threshold failures rather than throwing on them |
68
-
69
-
70
- ```ts
71
- // just the log
72
- await handleAuditResult({ result, ignoreError: true })
73
-
74
- // just the thresholds
75
- await handleAuditResult({ result, recommendations: false })
76
- ```
77
-
78
- ### `reports`
79
-
80
- | Option | Default | Description |
81
- | ----------- | ---------- | -------------------------------------------------------------- |
82
- | `directory` | _required_ | Directory to write into; created if it doesn't exist |
83
- | `name` | _required_ | Base filename, e.g. `desktop` → `desktop.html`, `desktop.json` |
84
-
85
- Each report is written to `<directory>/<name>.<format>`, using the formats the
86
- run's `output` flag asked for.
87
-
88
- ### `thresholds`
89
-
90
- One number applies to every category; an object sets them individually. Any
91
- category you leave out has to score 100, so the strict case is the default:
92
-
93
- ```ts
94
- await handleAuditResult({ result }) // every category must score 100
95
- await handleAuditResult({ result, thresholds: 90 })
96
- await handleAuditResult({ result, thresholds: { performance: 90 } })
97
- ```
98
-
99
- Only the categories present that are scored in the lighthouse report are checked.
100
-
101
- ### `ignoreError`
102
-
103
- Returns the threshold failures instead of throwing an error, so you can decide what to do with
104
- them. `undefined` when everything passed.
105
-
106
- ### `recommendations`
107
-
108
- | Option | Default | Description |
109
- | ---------------- | -------------- | -------------------------------------------------------------- |
110
- | `label` | `reports.name` | Distinguishes runs of the same URL, e.g. `desktop`/`mobile` |
111
- | `maxItems` | `5` | Rows shown per audit before collapsing to "…and N more" |
112
- | `maxValueLength` | `120` | Max length of a single value before it's truncated with an "…" |
113
-
114
- Pass `recommendations: false` to skip the log entirely.
115
-
116
- #### Output
117
-
118
- The log is the same fix list the report UI shows — failing audits, their
119
- estimated savings, and the individual offending URLs/nodes — so a failing CI run
120
- is actionable without downloading and opening the HTML report. Audits are
121
- grouped by category and sorted by estimated savings, so the biggest wins come
122
- first:
123
-
124
- ```
125
- ───── Lighthouse recommendations: desktop — https://example.com/ ─────
126
-
127
- Performance: 87
128
- • Reduce unused JavaScript (Est savings of 1,010 KiB)
129
- unused-javascript · score 50
130
- - https://example.com/assets/vendor.css · Transfer Size: 24.1 KiB
131
- - https://example.com/assets/fonts.css · Transfer Size: 7.3 KiB
132
- - https://example.com/assets/dep-vendor-1.js · Transfer Size: 372 KiB · Est Savings: 343 KiB
133
- - https://example.com/assets/dep-vendor-2.js · Transfer Size: 176 KiB · Est Savings: 149 KiB
134
- - …and 2 more
135
- Properly size images (Potential savings of 96 KiB)
136
- uses-responsive-images · score 62
137
- - https://example.com/hero.png · Size: 142 KiB
138
- - …and 3 more
139
- • Render-blocking requests — est. savings: FCP 310 ms, LCP 310 ms
140
- render-blocking-insight · score 50
141
- - https://example.com/assets/index.css · Transfer Size: 12.4 KiB · Duration: 52 ms
142
-
143
- Accessibility: 100 nothing to fix
144
- ```
145
-
146
- ## Example: Playwright
147
-
148
- Lighthouse navigates over the CDP port itself, so launch a persistent context on
149
- that port and point `lighthouse` at it.
150
-
151
- ```ts
152
- import { desktopConfig } from 'lighthouse'
153
- import { runAudit } from 'lighthouse-audit-utils'
154
-
155
- await runAudit({
156
- lighthouseArgs: {
157
- url: page.url(),
158
- flags: { port, output: ['html', 'json'] },
159
- config: desktopConfig,
160
- },
161
- reports: { directory: testInfo.outputPath('lighthouse'), name: 'desktop' },
162
- thresholds: { performance: 90 },
163
- })
164
- ```
165
-
166
- ## Individual utilities
167
-
168
- The three steps are also exported on their own, each taking the report first and
169
- its options second:
170
-
171
- ```ts
172
- writeReports(result, { directory, name })
173
- logRecommendations(lhr, { label, maxItems, maxValueLength })
174
- checkAgainstThresholds(lhr, { thresholds, ignoreError })
175
- ```
176
-
177
- ## Development
178
-
179
- ```bash
180
- pnpm install
181
- pnpm build # tsup dist/ (types via tsc)
182
- pnpm start # tsup watch
183
- pnpm check # biome + tsc
184
- pnpm format # biome check --fix
185
- ```
186
-
187
- CI type-checks and builds against both supported peer majors, Lighthouse 12 and
188
- 13, on Node 22/24/26.
189
-
190
- ## License
191
-
192
- MIT
1
+ # lighthouse-audit-utils
2
+
3
+ Programmatic [Lighthouse](https://github.com/GoogleChrome/lighthouse) audit utilities for CI and performance testing: score-threshold checking, HTML/JSON report writing, and a recommendations logger (ie, what is seen in a lighthouse report UI), all from a finished Lighthouse run in one call. Each step can be configured or disabled.
4
+
5
+ Running the audits from Playwright? [`lighthouse-audit-utils/playwright`](#playwright)
6
+ ships the CDP wiring as a fixture, so a test can audit whatever page it's on —
7
+ handy for Lighthouse CI-style performance budgets inside a Playwright suite.
8
+
9
+ ```bash
10
+ npm install --save-dev lighthouse-audit-utils
11
+ ```
12
+
13
+ `lighthouse` is a peer dependency; `@playwright/test` is an optional one, needed
14
+ only if using the [Playwright entrypoint](#playwright).
15
+
16
+ ## Usage
17
+
18
+ `runAudit` audits a URL and handles the result:
19
+
20
+ ```ts
21
+ import { runAudit } from 'lighthouse-audit-utils'
22
+
23
+ await runAudit({
24
+ lighthouseArgs: {
25
+ url: 'https://example.com',
26
+ flags: { port, output: ['html', 'json'] },
27
+ },
28
+ reports: { directory: 'lighthouse', name: 'desktop' },
29
+ thresholds: { performance: 90 },
30
+ })
31
+ ```
32
+
33
+ `lighthouseArgs` is handed to `lighthouse()`, in the order it takes them:
34
+
35
+ | Option | Default | Description |
36
+ | -------- | ---------- | ----------------------------------------------------- |
37
+ | `url` | _required_ | The URL to audit |
38
+ | `flags` | _none_ | Settings for the run, e.g. the `port` Lighthouse uses |
39
+ | `config` | _none_ | Overrides the default config, e.g. `desktopConfig` |
40
+
41
+ Everything else it takes is passed straight to `handleAuditResult` below, which returns `{ result, failures }`.
42
+
43
+ ## `handleAuditResult`
44
+
45
+ Handles a run you've already made yourself — this is what `runAudit` calls with the result from the actual `lighthouse()` audit:
46
+
47
+ ```ts
48
+ import lighthouse from 'lighthouse'
49
+ import { handleAuditResult } from 'lighthouse-audit-utils'
50
+
51
+ const result = await lighthouse(url, { port, output: ['html', 'json'] })
52
+ if (!result) throw new Error('Lighthouse returned no result')
53
+
54
+ await handleAuditResult({
55
+ result,
56
+ reports: { directory: 'lighthouse', name: 'desktop' },
57
+ thresholds: { performance: 90 },
58
+ })
59
+ ```
60
+
61
+ The three steps run in that order — reports, recommendations, thresholds — so a
62
+ failing run still prints its recommendations before throwing.
63
+
64
+ | Option | Default | Description |
65
+ | ----------------- | ---------- | ------------------------------------------------------------ |
66
+ | `result` | _required_ | The full `RunnerResult` from a Lighthouse run |
67
+ | `reports` | _none_ | Where to write the reports; omit to skip writing them |
68
+ | `recommendations` | _none_ | Recommendation logging options, or `false` to skip |
69
+ | `thresholds` | `100` | Minimum scores (0-100) — one number for all, or per category |
70
+ | `ignoreError` | `false` | Return the threshold failures rather than throwing on them |
71
+
72
+ ```ts
73
+ // just the log
74
+ await handleAuditResult({ result, ignoreError: true })
75
+
76
+ // just the thresholds
77
+ await handleAuditResult({ result, recommendations: false })
78
+ ```
79
+
80
+ ### `reports`
81
+
82
+ | Option | Default | Description |
83
+ | ----------- | ---------- | -------------------------------------------------------------- |
84
+ | `directory` | _required_ | Directory to write into; created if it doesn't exist |
85
+ | `name` | _required_ | Base filename, e.g. `desktop` `desktop.html`, `desktop.json` |
86
+
87
+ Each report is written to `<directory>/<name>.<format>`, using the formats the
88
+ run's `output` flag asked for.
89
+
90
+ ### `thresholds`
91
+
92
+ One number applies to every category; an object sets them individually. Any
93
+ category you leave out has to score 100, so the strict case is the default:
94
+
95
+ ```ts
96
+ await handleAuditResult({ result }) // every category must score 100
97
+ await handleAuditResult({ result, thresholds: 90 })
98
+ await handleAuditResult({ result, thresholds: { performance: 90 } })
99
+ ```
100
+
101
+ Only the categories present that are scored in the lighthouse report are checked.
102
+
103
+ ### `ignoreError`
104
+
105
+ Returns the threshold failures instead of throwing an error, so you can decide what to do with
106
+ them. `undefined` when everything passed.
107
+
108
+ ### `recommendations`
109
+
110
+ | Option | Default | Description |
111
+ | ---------------- | -------------- | -------------------------------------------------------------- |
112
+ | `label` | `reports.name` | Distinguishes runs of the same URL, e.g. `desktop`/`mobile` |
113
+ | `maxItems` | `5` | Rows shown per audit before collapsing to "…and N more" |
114
+ | `maxValueLength` | `120` | Max length of a single value before it's truncated with an "…" |
115
+
116
+ Pass `recommendations: false` to skip the log entirely.
117
+
118
+ #### Output
119
+
120
+ The log is the same recommendations the report UI shows — failing audits, their
121
+ estimated savings, and the individual offending URLs/nodes so a failing CI run
122
+ is actionable without downloading and opening the HTML report. Audits are
123
+ grouped by category and sorted by estimated savings, so the biggest wins come
124
+ first:
125
+
126
+ ```
127
+ ───── Lighthouse recommendations: desktop — https://example.com/ ─────
128
+
129
+ Performance: 87
130
+ Reduce unused JavaScript (Est savings of 1,010 KiB)
131
+ unused-javascript · score 50
132
+ - https://example.com/assets/vendor.css · Transfer Size: 24.1 KiB
133
+ - https://example.com/assets/fonts.css · Transfer Size: 7.3 KiB
134
+ - https://example.com/assets/dep-vendor-1.js · Transfer Size: 372 KiB · Est Savings: 343 KiB
135
+ - https://example.com/assets/dep-vendor-2.js · Transfer Size: 176 KiB · Est Savings: 149 KiB
136
+ - …and 2 more
137
+ Properly size images (Potential savings of 96 KiB)
138
+ uses-responsive-images · score 62
139
+ - https://example.com/hero.png · Size: 142 KiB
140
+ - …and 3 more
141
+ • Render-blocking requests est. savings: FCP 310 ms, LCP 310 ms
142
+ render-blocking-insight · score 50
143
+ - https://example.com/assets/index.css · Transfer Size: 12.4 KiB · Duration: 52 ms
144
+
145
+ Accessibility: 100 — nothing to flag
146
+ ```
147
+
148
+ ## Individual utilities
149
+
150
+ The three steps are also exported on their own, each taking the report first and
151
+ its options second:
152
+
153
+ ```ts
154
+ writeReports(result, { directory, name })
155
+ logRecommendations(lhr, { label, maxItems, maxValueLength })
156
+ checkAgainstThresholds(lhr, { thresholds, ignoreError })
157
+ ```
158
+
159
+ ## Playwright
160
+
161
+ `lighthouse-audit-utils/playwright` ships the wiring as a fixture.
162
+
163
+ ```ts
164
+ // fixtures.ts
165
+ import { desktopConfig } from 'lighthouse'
166
+ import { withLighthouse } from 'lighthouse-audit-utils/playwright'
167
+
168
+ export const lighthouseTest = withLighthouse({
169
+ basePort: 9222,
170
+ lighthouseArgs: {
171
+ flags: { disableStorageReset: true, output: ['html', 'json'] },
172
+ config: {
173
+ extends: 'lighthouse:default',
174
+ settings: { skipAudits: ['color-contrast'] },
175
+ },
176
+ },
177
+ thresholds: { performance: 70 },
178
+ })
179
+
180
+ // a.spec.ts
181
+ lighthouseTest('home page', async ({ page, runAudit }) => {
182
+ await page.goto('/')
183
+ await runAudit({
184
+ name: 'desktop',
185
+ lighthouseArgs: { config: desktopConfig },
186
+ })
187
+ await runAudit({ name: 'mobile', thresholds: { performance: 60 } }) // merges over the fixture's value
188
+ })
189
+ ```
190
+
191
+ - Each call to `runAudit` audits whatever page the test is currently on and writes its reports to the test's output directory.
192
+ - Run `runAudit` more than once for more than one form factor
193
+ — wrap the calls in `test.step` if you want them grouped in the report.
194
+ - `withLighthouse(options, test)` takes the test to extend second, so you can layer
195
+ it onto your own fixtures; omit it to start from Playwright's `test`.
196
+
197
+ | Option | Required | Description |
198
+ | ---------------- | -------- | ---------------------------------------------------------------------------------------- |
199
+ | `basePort` | yes | First worker's CDP port; each further worker gets the next one up |
200
+ | `lighthouseArgs` | no | `flags` and `config` for `lighthouse()`; `url` and `flags.port` are set for you |
201
+ | `reports` | no | `(context) => { directory, name }`, or `false` to skip writing them |
202
+ | `launchOptions` | no | Merged into the persistent context launch, which already sets the CDP port and `baseURL` |
203
+
204
+ Plus everything [`runAudit`](#usage) from `lighthouse-audit-utils` takes —
205
+ `thresholds`, `ignoreError`, `recommendations`.
206
+
207
+ The `runAudit` fixture takes `name` — which names that run's reports, so two
208
+ audits in one test don't overwrite each other — and `lighthouseArgs`,
209
+ `thresholds`, `ignoreError` and `recommendations`, to overwrite the overall fixture's:
210
+
211
+ ```ts
212
+ const { result, failures } = await runAudit({
213
+ name: 'logged-in',
214
+ thresholds: { performance: 50 },
215
+ lighthouseArgs: { config: { settings: { onlyCategories: ['performance'] } } },
216
+ })
217
+ ```
218
+
219
+ `thresholds` merge when both are objects; anything else replaces, since a flat
220
+ number can't be partially overridden.
221
+
222
+ `context` is overridden to launch a persistent Chrome profile on the CDP port,
223
+ since Lighthouse navigates over that port itself rather than driving the
224
+ Playwright `page` — this way both see the same browser session.
225
+
226
+ ### Doing it by hand
227
+
228
+ ```ts
229
+ import { desktopConfig } from 'lighthouse'
230
+ import { runAudit } from 'lighthouse-audit-utils'
231
+
232
+ await runAudit({
233
+ lighthouseArgs: {
234
+ url: page.url(),
235
+ flags: { port, output: ['html', 'json'] },
236
+ config: desktopConfig,
237
+ },
238
+ reports: { directory: testInfo.outputPath('lighthouse'), name: 'desktop' },
239
+ thresholds: { performance: 90 },
240
+ })
241
+ ```
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ pnpm install
247
+ pnpm build # tsup → dist/ (types via tsc)
248
+ pnpm start # tsup watch
249
+ pnpm check # biome + tsc
250
+ pnpm format # biome check --fix
251
+ ```
252
+
253
+ CI type-checks and builds against both supported peer majors, Lighthouse 12 and
254
+ 13, on Node 22/24/26.
255
+
256
+ ## License
257
+
258
+ MIT