ng-checkup 0.11.1 → 1.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/README.md +85 -10
- package/dist/cache.d.ts +1 -0
- package/dist/cache.js.map +1 -1
- package/dist/checks/architecture.js +31 -0
- package/dist/checks/architecture.js.map +1 -1
- package/dist/checks/context.d.ts +6 -0
- package/dist/checks/context.js.map +1 -1
- package/dist/checks/security.js +43 -5
- package/dist/checks/security.js.map +1 -1
- package/dist/churn.d.ts +25 -0
- package/dist/churn.js +44 -0
- package/dist/churn.js.map +1 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.js +173 -10
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +9 -1
- package/dist/config.js.map +1 -1
- package/dist/history.d.ts +66 -0
- package/dist/history.js +103 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +50 -9
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +3 -3
- package/dist/init.js +3 -3
- package/dist/report/history.d.ts +17 -0
- package/dist/report/history.js +138 -0
- package/dist/report/history.js.map +1 -0
- package/dist/report/html.d.ts +5 -0
- package/dist/report/html.js +1 -1
- package/dist/report/html.js.map +1 -1
- package/dist/report/site.d.ts +19 -0
- package/dist/report/site.js +32 -0
- package/dist/report/site.js.map +1 -0
- package/dist/report/terminal.d.ts +11 -0
- package/dist/report/terminal.js +2 -1
- package/dist/report/terminal.js.map +1 -1
- package/dist/site.d.ts +105 -0
- package/dist/site.js +252 -0
- package/dist/site.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/vulnerabilities.d.ts +54 -0
- package/dist/vulnerabilities.js +116 -0
- package/dist/vulnerabilities.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,9 +23,11 @@ npx ng-checkup
|
|
|
23
23
|
- [Configuration](#configuration)
|
|
24
24
|
- [Baseline: adopt it on legacy code](#baseline-adopt-it-on-legacy-code)
|
|
25
25
|
- [Watch mode](#watch-mode)
|
|
26
|
+
- [History](#history)
|
|
26
27
|
- [Upgrade plan](#upgrade-plan)
|
|
27
28
|
- [AngularJS and hybrid apps](#angularjs-and-hybrid-apps)
|
|
28
29
|
- [Primo VE to NDE](#primo-ve-to-nde)
|
|
30
|
+
- [A live site](#a-live-site)
|
|
29
31
|
- [Monorepos](#monorepos)
|
|
30
32
|
- [Client report](#client-report)
|
|
31
33
|
- [What it checks](#what-it-checks)
|
|
@@ -116,6 +118,8 @@ npx ng-checkup init --ci gitlab # pick the CI instead of detecting it (github
|
|
|
116
118
|
| `ignore` | paths left out of the analysis; a path matches everything below it, `*` stays within a folder, `**` crosses folders | |
|
|
117
119
|
| `baseline` | the baseline file, relative to the config file (default `ng-checkup.baseline.json` in the root) | |
|
|
118
120
|
| `plan` | add the upgrade plan to every run | `--plan` |
|
|
121
|
+
| `only` | report only these areas, by [check id](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md) (`["security", "dependencies"]`) | `--only` |
|
|
122
|
+
| `skip` | leave these areas out of the report, the score and the baseline | `--skip` |
|
|
119
123
|
|
|
120
124
|
An unknown key or a wrong type stops the run with an error, so a typo cannot silently switch off a CI threshold. Use `--config path/to/file.json` for a file elsewhere.
|
|
121
125
|
|
|
@@ -157,6 +161,35 @@ Score 72 -> 76 (+4 since the watch started)
|
|
|
157
161
|
|
|
158
162
|
Only the changed files are parsed again, and the registry is asked again only when `package.json` or a lockfile changes, so a run after a save takes a fraction of the first one. Keep it open next to the editor while working through a refactor; run `npx ng-checkup baseline` in another terminal to lock a drop in, and the next run compares against it.
|
|
159
163
|
|
|
164
|
+
## History
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npx ng-checkup history --report history.html
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
ng-checkup 1.0.0 history of angular-realworld-example-app, 19 points (shortened)
|
|
172
|
+
|
|
173
|
+
date commit Angular score lines
|
|
174
|
+
2023-10-30 0d28f5c 16.2.11 60 1 407
|
|
175
|
+
2023-12-19 bcc6d42 17.0.7 64 1 360
|
|
176
|
+
2024-05-24 932dff0 18.0.0 68 1 441
|
|
177
|
+
2025-05-29 a420aa2 20.0.0 72 1 401
|
|
178
|
+
2025-11-10 987b634 20.3.9 76 1 356
|
|
179
|
+
2026-01-30 6809ae9 21.1.1 91 1 471
|
|
180
|
+
2026-10-23 now 21.2.12 91 1 709
|
|
181
|
+
|
|
182
|
+
Moved most since the first point
|
|
183
|
+
-27 *ngIf / *ngFor / [ngSwitch] uses (27 -> 0)
|
|
184
|
+
-16 Components with eager change detection (16 -> 0)
|
|
185
|
+
-9 Components and services without a spec (27 -> 18)
|
|
186
|
+
-7 Explicit any in application code (7 -> 0)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
How the score and every measure moved over the last months, without having to collect it as you go. It takes the last commit of each month from git (`--points 12` months by default; a month without a commit is skipped), extracts it into a temporary folder with `git archive`, runs the checkup offline on it, and ends with the working tree as it is, uncommitted changes included. Your checkout is never touched. The HTML report has a chart of the score and one per measure that moved most; `.md` and `.json` work too.
|
|
190
|
+
|
|
191
|
+
It is the slide for "what did the refactoring buy us", and the first thing to look at on a codebase you inherit: whether the debt is shrinking or growing. A shallow clone has no history, so in CI fetch it in full (`fetch-depth: 0`).
|
|
192
|
+
|
|
160
193
|
## Upgrade plan
|
|
161
194
|
|
|
162
195
|
```bash
|
|
@@ -222,6 +255,27 @@ Libraries on Ex Libris Primo VE customize it with an AngularJS package, and thos
|
|
|
222
255
|
|
|
223
256
|
A `js/custom.js` built from the view's other files and copies outside the view folders are not counted twice. With `--plan`, the plan has four phases (decide what NDE still needs and set up the module, branding and home pages, rebuild the hooks, test in proxy mode and switch view by view), and the client report puts them in its roadmap.
|
|
224
257
|
|
|
258
|
+
## A live site
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npx ng-checkup url https://app.example.com
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
ng-checkup https://my.example-saas.de/ Login
|
|
266
|
+
Read the page and 5 scripts (36.7 MB) from my.example-saas.de
|
|
267
|
+
|
|
268
|
+
AngularJS 1.8.3 seen in vendor.bundle.js
|
|
269
|
+
React 19.2.3 seen in react.bundle.js
|
|
270
|
+
|
|
271
|
+
high AngularJS 1.8.3 is end of life since January 2022
|
|
272
|
+
It runs next to React, so a migration is under way. Until it is finished, the app ships a framework without security fixes.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Which framework a public web app runs, without its code: the page and the scripts of the same site it loads, including the chunks they import, up to 12 requests. It recognises AngularJS and Angular with their versions (from the license header, the version object or `new Version("…")` in the bundle, and `ng-version`), whether zone.js is loaded, and React, Vue, Next.js and Nuxt, also several at once. Third-party scripts are skipped (a chat widget built with React does not make a site a React app), except AngularJS from a public CDN. It reads only what any browser downloads: nothing is submitted, nothing behind a sign-in is reached. `--json` prints the result for scripts.
|
|
276
|
+
|
|
277
|
+
Useful before a first conversation: what a company's product runs on, how far behind it is, and whether a migration has started.
|
|
278
|
+
|
|
225
279
|
## Monorepos
|
|
226
280
|
|
|
227
281
|
In an Angular CLI workspace with several projects or an Nx workspace, the report adds a table with one row per project, worst score first (here [nx-examples](https://github.com/nrwl/nx-examples), shortened):
|
|
@@ -272,14 +326,14 @@ The codebase is named by its repository and commit, never by a path on your disk
|
|
|
272
326
|
| [**Change detection**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#change-detection) | zoneless or zone.js, OnPush coverage, `NgZone`, zone-only test helpers | zone.js with less than half the components on OnPush |
|
|
273
327
|
| [**Components & templates**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#components) | standalone, signal inputs and outputs, built-in control flow, `@defer` | average adoption below 40% |
|
|
274
328
|
| [**State management**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#state) | classic NgRx, NgRx signal store, signals, `BehaviorSubject` services | the removed `@Effect()` decorator |
|
|
275
|
-
| [**Architecture**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#architecture) | eager vs. lazy routes (following `loadChildren` into lazy modules), files over 500 and 1 000 lines, constructor injection, class-based guards and interceptors | ten eager routes and none lazy, or three files over 1 000 lines |
|
|
329
|
+
| [**Architecture**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#architecture) | eager vs. lazy routes (following `loadChildren` into lazy modules), files over 500 and 1 000 lines, hotspots (large files changed most often, from git), constructor injection, class-based guards and interceptors | ten eager routes and none lazy, or three files over 1 000 lines |
|
|
276
330
|
| [**Testing**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#testing) | Karma / Jest / Vitest, specs next to components and services, Protractor / Playwright / Cypress | Karma, Protractor or no specs |
|
|
277
331
|
| [**Type safety**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#type-safety) | `strictNullChecks`, `strictTemplates`, `noImplicitAny`, `any`, `x!`, `@ts-ignore`, `$any()` | both `strictNullChecks` and `strictTemplates` off |
|
|
278
|
-
| [**Security**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#security) | `bypassSecurityTrust*`, HTML written straight into the DOM (`innerHTML =`, `insertAdjacentHTML`, `Renderer2.setProperty`), `eval`, `[innerHTML]` bindings | HTML written into the DOM past the sanitizer, or `eval` |
|
|
332
|
+
| [**Security**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#security) | known vulnerabilities of the dependencies (from [osv.dev](https://osv.dev), AngularJS included), `bypassSecurityTrust*`, HTML written straight into the DOM (`innerHTML =`, `insertAdjacentHTML`, `Renderer2.setProperty`), `eval`, `[innerHTML]` bindings | a high or critical advisory in a runtime dependency, HTML written into the DOM past the sanitizer, or `eval` |
|
|
279
333
|
| [**Circular dependencies**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#circular-deps) | import cycles through relative paths, barrels and tsconfig `paths` | more than three groups or ten files in loops |
|
|
280
334
|
| [**Bundle size**](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md#bundle) | the initial bundle from the last `ng build --stats-json` (esbuild or webpack) against the build's budget, its largest packages, lazy chunks | over the `initial` error budget (1 MB by default) |
|
|
281
335
|
|
|
282
|
-
Each finding says why it matters and which `ng generate` migration fixes it. **[Every check, its grading and its fix →](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md)**
|
|
336
|
+
`--only security,dependencies` reports just those areas, and `--skip bundle` leaves one out; the score is then the average of what is left. Each finding says why it matters and which `ng generate` migration fixes it. **[Every check, its grading and its fix →](https://github.com/stefankuburovic/ng-checkup/blob/main/docs/checks.md)**
|
|
283
337
|
|
|
284
338
|
Defaults that changed between versions are applied per version: a component without `standalone` is standalone from Angular 19, one without `changeDetection` is OnPush from 22, and an app without a zone provider is zoneless from 21.
|
|
285
339
|
|
|
@@ -308,6 +362,8 @@ Defaults that changed between versions are applied per version: a component with
|
|
|
308
362
|
Usage: ng-checkup [path | repository] [options]
|
|
309
363
|
ng-checkup init [path] [init options]
|
|
310
364
|
ng-checkup baseline [path] [--force]
|
|
365
|
+
ng-checkup url <address> [--json] [--offline]
|
|
366
|
+
ng-checkup history [path] [--points <n>] [--report <file>] [--json]
|
|
311
367
|
|
|
312
368
|
Arguments:
|
|
313
369
|
path Repository root (default: current directory)
|
|
@@ -342,8 +398,11 @@ Options:
|
|
|
342
398
|
--fail-on <grade> Exit with code 1 if any area is graded <grade> or worse
|
|
343
399
|
(poor or fair), or with regression if a measure is
|
|
344
400
|
worse than the baseline (for CI)
|
|
401
|
+
--only <ids> Report only these areas, comma-separated check ids
|
|
402
|
+
(security,testing); the rest stay out of the score
|
|
403
|
+
--skip <ids> Leave these areas out of the report and the score
|
|
345
404
|
--max-examples <n> Example files listed per finding (default 5)
|
|
346
|
-
--offline
|
|
405
|
+
--offline Nothing online: no npm registry, no osv.dev lookup
|
|
347
406
|
--config <file> Config file (default: ng-checkup.config.json in the root)
|
|
348
407
|
--no-color Plain output (also off when NO_COLOR is set or not a TTY)
|
|
349
408
|
-h, --help Show this help
|
|
@@ -356,6 +415,23 @@ Init options (ng-checkup init):
|
|
|
356
415
|
|
|
357
416
|
Baseline options (ng-checkup baseline):
|
|
358
417
|
--force Accept measures that went up since the last baseline
|
|
418
|
+
|
|
419
|
+
Url (ng-checkup url <address>):
|
|
420
|
+
Which framework a public web app runs, from what a
|
|
421
|
+
browser downloads: AngularJS and Angular with their
|
|
422
|
+
versions, zone.js, React, Vue, Next.js, Nuxt. Reads
|
|
423
|
+
the page and up to 12 scripts of the same site
|
|
424
|
+
--json Print the result as JSON
|
|
425
|
+
|
|
426
|
+
History (ng-checkup history [path]):
|
|
427
|
+
How the score and the measures moved over the last
|
|
428
|
+
months: checks out one commit per month into a
|
|
429
|
+
temporary folder (the working tree is never touched),
|
|
430
|
+
runs the checkup offline on each, and ends with now.
|
|
431
|
+
Needs the full git history, not a shallow clone
|
|
432
|
+
--points <n> Months back (default 12)
|
|
433
|
+
-r, --report <file> Write it as .html (with charts), .md or .json
|
|
434
|
+
--json Print it as JSON
|
|
359
435
|
```
|
|
360
436
|
|
|
361
437
|
Exit codes: `0` done, `1` `--fail-on` tripped, `2` usage error (including `--fail-on regression` without a baseline). Areas graded `info` never fail a run.
|
|
@@ -382,7 +458,7 @@ jobs:
|
|
|
382
458
|
runs-on: ubuntu-latest
|
|
383
459
|
steps:
|
|
384
460
|
- uses: actions/checkout@v4
|
|
385
|
-
- uses: stefankuburovic/ng-checkup@
|
|
461
|
+
- uses: stefankuburovic/ng-checkup@v1
|
|
386
462
|
with:
|
|
387
463
|
fail-on: regression # needs a committed baseline: npx ng-checkup baseline
|
|
388
464
|
sarif: true
|
|
@@ -411,7 +487,7 @@ No `npm ci` needed: versions come from the lockfile. Inputs: `path` (a folder in
|
|
|
411
487
|
|
|
412
488
|
```yaml
|
|
413
489
|
include:
|
|
414
|
-
- component: $CI_SERVER_FQDN/stefankuburovic/ng-checkup/ng-checkup@
|
|
490
|
+
- component: $CI_SERVER_FQDN/stefankuburovic/ng-checkup/ng-checkup@1
|
|
415
491
|
inputs:
|
|
416
492
|
fail-on: regression
|
|
417
493
|
```
|
|
@@ -420,7 +496,7 @@ On a self-managed GitLab, which cannot include components from gitlab.com, use t
|
|
|
420
496
|
|
|
421
497
|
```yaml
|
|
422
498
|
include:
|
|
423
|
-
- remote: https://raw.githubusercontent.com/stefankuburovic/ng-checkup/
|
|
499
|
+
- remote: https://raw.githubusercontent.com/stefankuburovic/ng-checkup/v1/gitlab/ng-checkup.yml
|
|
424
500
|
|
|
425
501
|
ng-checkup:
|
|
426
502
|
variables:
|
|
@@ -468,11 +544,11 @@ for (const check of report.checks) {
|
|
|
468
544
|
await writeFile('checkup.md', renderMarkdown(report));
|
|
469
545
|
```
|
|
470
546
|
|
|
471
|
-
`runCheckup` never writes anything. Its options mirror the flags (`offline`, `plan`, `project`, `ignore`, `maxExamples`), plus `cache` from `createAnalysisCache()` to make repeated runs incremental. `renderHtml`, `renderMarkdown`, `renderSarif`, `overallScore`, `topPriorities`, `loadConfig`, `planInit` (what `init` would change, without writing it) and the baseline functions (`createBaseline`, `compareWithBaseline`, `readBaseline`, `serializeBaseline`) are exported too, along with the `Report`, `CheckResult`, `Finding` and `Grade` types. `Report` is also the schema of the JSON output.
|
|
547
|
+
`runCheckup` never writes anything. Its options mirror the flags (`offline`, `plan`, `project`, `ignore`, `only`, `skip`, `maxExamples`), plus `cache` from `createAnalysisCache()` to make repeated runs incremental. `renderHtml`, `renderMarkdown`, `renderSarif`, `overallScore`, `topPriorities`, `loadConfig`, `planInit` (what `init` would change, without writing it), `collectHistory` with `renderHistoryHtml` and `renderHistoryMarkdown`, and the baseline functions (`createBaseline`, `compareWithBaseline`, `readBaseline`, `serializeBaseline`) are exported too, along with the `Report`, `CheckResult`, `Finding` and `Grade` types. `Report` is also the schema of the JSON output.
|
|
472
548
|
|
|
473
549
|
## What leaves your machine
|
|
474
550
|
|
|
475
|
-
Package names and versions,
|
|
551
|
+
Package names and versions, to two public services. To the npm registry: the latest `@angular/core` version (three-second timeout); for every third-party package that depends on Angular, its public metadata, to find the release that supports the next Angular and whether it is deprecated (eight at a time, four-second timeout each); and, when `node_modules` is missing, the published tarballs of the `@angular/*`, `@ngrx/*` and `rxjs` versions your code imports, of which only `package.json` and `.d.ts` files are kept, in `~/.cache/ng-checkup` (`NG_CHECKUP_CACHE` moves it). To [osv.dev](https://osv.dev): the direct dependencies with their exact versions, to look up known vulnerabilities (eight-second timeout). `--offline` skips all of it and uses only what is already cached; the report then compares against the newest major this release knows and says so. A remote repository argument also clones it from its host. No source, no file names, no telemetry. The tool reads files and writes only the reports you ask for.
|
|
476
552
|
|
|
477
553
|
## How it works
|
|
478
554
|
|
|
@@ -502,7 +578,6 @@ Package names and versions, and only to the public npm registry: the latest `@an
|
|
|
502
578
|
## Roadmap
|
|
503
579
|
|
|
504
580
|
- Per-project baselines for monorepos
|
|
505
|
-
- Change frequency from git history, to rank large files by how often they change
|
|
506
581
|
- A VS Code extension showing findings inline
|
|
507
582
|
|
|
508
583
|
Ideas and bug reports are welcome in [issues](https://github.com/stefankuburovic/ng-checkup/issues).
|
package/dist/cache.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface AnalysisCache {
|
|
|
25
25
|
deprecations: DeprecationIndex;
|
|
26
26
|
typings: import('./typings.js').TypingsSource;
|
|
27
27
|
compatibility: PackageCompatibility[];
|
|
28
|
+
vulnerabilities?: import('./vulnerabilities.js').VulnerabilityScan;
|
|
28
29
|
latestAngular: {
|
|
29
30
|
major: number;
|
|
30
31
|
source: 'registry' | 'bundled';
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkCjC,sBAAsB;AACtB,MAAM,UAAU,mBAAmB;IACjC,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AAC9B,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,CAAC,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,iCAAiC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtK,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAwB,KAAgC,EAAE,IAAY,EAAE,OAAgB,EAAE,GAAG,GAAG,IAAI;IAC7H,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC,KAAU,CAAC;IACtD,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -3,6 +3,14 @@ import { applicationFiles, formatCount, percent, plural } from './context.js';
|
|
|
3
3
|
const LARGE_FILE = 500;
|
|
4
4
|
/** Lines of code from which a file counts against the grade. */
|
|
5
5
|
const VERY_LARGE_FILE = 1000;
|
|
6
|
+
/**
|
|
7
|
+
* A hotspot is at least this large… The thresholds keep the list to files
|
|
8
|
+
* that matter on a large app (on a 1 600-file app, 200 lines and 5 commits
|
|
9
|
+
* named 147 files, which is a list nobody acts on).
|
|
10
|
+
*/
|
|
11
|
+
const HOTSPOT_LINES = 300;
|
|
12
|
+
/** …and was changed in at least this many commits of the last year. */
|
|
13
|
+
const HOTSPOT_COMMITS = 10;
|
|
6
14
|
/**
|
|
7
15
|
* How the code is put together: lazy loading, oversized files, and the
|
|
8
16
|
* dependency-injection and guard style.
|
|
@@ -57,6 +65,26 @@ export function architectureCheck(context) {
|
|
|
57
65
|
count: large.length,
|
|
58
66
|
});
|
|
59
67
|
}
|
|
68
|
+
// Hotspots: large files that also change often, ranked by size times commits.
|
|
69
|
+
const churn = typeof context.churn === 'object' ? context.churn : undefined;
|
|
70
|
+
const hotspots = churn
|
|
71
|
+
? files
|
|
72
|
+
.map((file) => ({ file, commits: churn.files.get(file.path) ?? 0 }))
|
|
73
|
+
.filter(({ file, commits }) => file.linesOfCode >= HOTSPOT_LINES && commits >= HOTSPOT_COMMITS)
|
|
74
|
+
.sort((left, right) => right.file.linesOfCode * right.commits - left.file.linesOfCode * left.commits)
|
|
75
|
+
: [];
|
|
76
|
+
if (hotspots.length > 0) {
|
|
77
|
+
findings.push({
|
|
78
|
+
severity: hotspots.some(({ file }) => file.linesOfCode >= LARGE_FILE) ? 'medium' : 'low',
|
|
79
|
+
message: `Hotspots: ${hotspots.length > 5 ? 'the ' : ''}large files changed most often${hotspots.length > 5 ? ` (top 5 of ${formatCount(hotspots.length)})` : ''}`,
|
|
80
|
+
detail: `${hotspots
|
|
81
|
+
.slice(0, 5)
|
|
82
|
+
.map(({ file, commits }) => `${file.path.split('/').pop()} (${formatCount(file.linesOfCode)} lines, ${plural(commits, 'commit')})`)
|
|
83
|
+
.join(', ')}, over the last year of history. A file that is both large and edited all the time costs its size on every change; these are where splitting or a refactor pays back first, more than the largest file nobody touches.`,
|
|
84
|
+
files: hotspots.slice(0, max).map(({ file }) => ({ path: file.path })),
|
|
85
|
+
count: hotspots.length,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
60
88
|
if (classGuards.length > 0) {
|
|
61
89
|
findings.push({
|
|
62
90
|
severity: 'low',
|
|
@@ -95,6 +123,9 @@ export function architectureCheck(context) {
|
|
|
95
123
|
{ label: 'Large files', value: formatCount(large.length), hint: `over ${formatCount(LARGE_FILE)} lines` },
|
|
96
124
|
{ label: 'Constructor injection', value: formatCount(constructorInjections.length), hint: `classes; ${formatCount(injectCalls)} inject() calls` },
|
|
97
125
|
{ label: 'Class guards', value: formatCount(classGuards.length), hint: 'and interceptors' },
|
|
126
|
+
churn
|
|
127
|
+
? { label: 'Hotspots', value: formatCount(hotspots.length), hint: `from ${plural(churn.commits, 'commit')} in the last year` }
|
|
128
|
+
: { label: 'Hotspots', value: '-', hint: context.churn === 'shallow clone' ? 'shallow clone: no history (fetch-depth: 0)' : `${context.churn}` },
|
|
98
129
|
],
|
|
99
130
|
findings,
|
|
100
131
|
measures: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/checks/architecture.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAwB,MAAM,cAAc,CAAC;AAEpG,0DAA0D;AAC1D,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,gEAAgE;AAChE,MAAM,eAAe,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/checks/architecture.ts"],"names":[],"mappings":"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"}
|
package/dist/checks/context.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { AngularJsFacts } from '../angularjs.js';
|
|
2
2
|
import type { BundleStats } from '../bundle.js';
|
|
3
|
+
import type { Churn, ChurnUnavailable } from '../churn.js';
|
|
3
4
|
import type ts from 'typescript';
|
|
4
5
|
import type { PackageCompatibility } from '../compatibility.js';
|
|
5
6
|
import type { DeprecationIndex } from '../deprecations.js';
|
|
6
7
|
import type { DependencyTangle } from '../graph.js';
|
|
7
8
|
import type { TypingsSource } from '../typings.js';
|
|
9
|
+
import type { VulnerabilityScan } from '../vulnerabilities.js';
|
|
8
10
|
import type { SourceFileFacts, TemplateFact } from '../scan.js';
|
|
9
11
|
import type { CheckResult, FileRef } from '../types.js';
|
|
10
12
|
import type { WorkspaceInfo } from '../workspace.js';
|
|
@@ -37,6 +39,10 @@ export interface AnalysisContext {
|
|
|
37
39
|
bundles: BundleStats[];
|
|
38
40
|
/** Installed third-party packages that depend on Angular. Empty without node_modules. */
|
|
39
41
|
compatibility: PackageCompatibility[];
|
|
42
|
+
/** Commits per file over the last year from git, or why there is no history to read (see `readChurn`). */
|
|
43
|
+
churn: Churn | ChurnUnavailable;
|
|
44
|
+
/** Known vulnerabilities of the direct dependencies, from osv.dev. Undefined offline or when OSV did not answer. */
|
|
45
|
+
vulnerabilities?: VulnerabilityScan;
|
|
40
46
|
/** Newest Angular major, and whether it came from the registry or the tool's bundled fallback. */
|
|
41
47
|
latestAngular: {
|
|
42
48
|
major: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/checks/context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/checks/context.ts"],"names":[],"mappings":"AA0DA,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,KAAwB,EAAE,GAAG,IAAc;IACpE,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAC5F,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CAAC,KAAwB,EAAE,GAAG,IAAc;IACnE,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAAC,KAAwB,EAAE,OAAiB;IACxE,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7F,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAa;IACjD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAU,GAAG,GAAG,QAAQ,GAAG;IACjF,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AACxE,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,IAAI,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAkB;IACtE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;AAC7C,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,OAAwB;IACvD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC7E,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,SAAS,CAAC,OAAwB;IAChD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACvD,CAAC"}
|
package/dist/checks/security.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { compareVersions, severityRank } from '../vulnerabilities.js';
|
|
1
2
|
import { applicationFiles, formatCount, plural } from './context.js';
|
|
2
3
|
/**
|
|
3
4
|
* The places where the code steps around Angular's built-in XSS protection.
|
|
@@ -55,21 +56,58 @@ export function securityCheck(context) {
|
|
|
55
56
|
...examples(innerHtml.map((template) => ({ path: template.path }))),
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
|
-
const
|
|
59
|
+
const scan = context.vulnerabilities;
|
|
60
|
+
const runtime = scan?.packages.filter((entry) => !entry.dev) ?? [];
|
|
61
|
+
const tooling = scan?.packages.filter((entry) => entry.dev) ?? [];
|
|
62
|
+
const runtimeCount = runtime.reduce((sum, entry) => sum + entry.vulnerabilities.length, 0);
|
|
63
|
+
const severe = runtime.reduce((sum, entry) => sum + entry.vulnerabilities.filter((vulnerability) => severityRank(vulnerability.severity) >= 3).length, 0);
|
|
64
|
+
const moderate = runtime.some((entry) => entry.vulnerabilities.some((vulnerability) => vulnerability.severity === 'moderate'));
|
|
65
|
+
if (runtimeCount > 0) {
|
|
66
|
+
findings.unshift({
|
|
67
|
+
severity: severe > 0 ? 'high' : moderate ? 'medium' : 'low',
|
|
68
|
+
message: `${plural(runtimeCount, 'known vulnerability', 'known vulnerabilities')} in ${plural(runtime.length, 'dependency', 'dependencies')}${severe > 0 ? ` (${formatCount(severe)} high or critical)` : ''}`,
|
|
69
|
+
detail: `${runtime
|
|
70
|
+
.slice(0, 6)
|
|
71
|
+
.map((entry) => {
|
|
72
|
+
const cves = [...new Set(entry.vulnerabilities.flatMap((vulnerability) => vulnerability.cves))];
|
|
73
|
+
const fixes = [...new Set(entry.vulnerabilities.map((vulnerability) => vulnerability.fixedIn).filter((fixed) => fixed !== undefined))];
|
|
74
|
+
const unfixed = entry.vulnerabilities.some((vulnerability) => vulnerability.fixedIn === undefined);
|
|
75
|
+
const fix = unfixed ? (fixes.length > 0 ? 'some without a fixed version' : 'no fixed version') : `all fixed by ${fixes.sort(compareVersions).pop()}`;
|
|
76
|
+
return `\`${entry.name}\` ${entry.version}: ${plural(entry.vulnerabilities.length, 'advisory', 'advisories')}${cves.length > 0 ? ` (${cves.slice(0, 3).join(', ')}${cves.length > 3 ? ', …' : ''})` : ''}, ${fix}`;
|
|
77
|
+
})
|
|
78
|
+
.join('; ')}${runtime.length > 6 ? `; and ${plural(runtime.length - 6, 'more dependency', 'more dependencies')}` : ''}. Direct dependencies at their exact versions, from the advisories in osv.dev; the full dependency tree is \`npm audit\`'s job.`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (tooling.length > 0) {
|
|
82
|
+
findings.push({
|
|
83
|
+
severity: 'low',
|
|
84
|
+
message: `${plural(tooling.reduce((sum, entry) => sum + entry.vulnerabilities.length, 0), 'known vulnerability', 'known vulnerabilities')} in development tooling`,
|
|
85
|
+
detail: `${tooling.map((entry) => `\`${entry.name}\` ${entry.version}`).join(', ')}. Not shipped to users, but they run on developer machines and in CI.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const codeGrade = files.length === 0 ? 'info' : unsafeDom.length > 0 || dynamicCode.length > 0 ? 'poor' : bypass.length > 0 ? 'fair' : 'good';
|
|
89
|
+
// Known vulnerabilities grade an app even without TypeScript to read (an AngularJS app on 1.8.3).
|
|
90
|
+
const grade = severe > 0 ? 'poor' : moderate && codeGrade !== 'poor' ? 'fair' : codeGrade === 'info' && scan && scan.checked > 0 ? (runtimeCount > 0 ? 'fair' : 'good') : codeGrade;
|
|
91
|
+
const vulnerabilitySummary = runtimeCount > 0 ? `; ${plural(runtimeCount, 'known vulnerability', 'known vulnerabilities')} in dependencies` : '';
|
|
59
92
|
return {
|
|
60
93
|
id: 'security',
|
|
61
94
|
title: 'Security',
|
|
62
95
|
grade,
|
|
63
96
|
summary: files.length === 0
|
|
64
|
-
?
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
? runtimeCount > 0
|
|
98
|
+
? `No TypeScript application code to check; ${plural(runtimeCount, 'known vulnerability', 'known vulnerabilities')} in ${plural(runtime.length, 'dependency', 'dependencies')}.`
|
|
99
|
+
: 'No application code to check.'
|
|
100
|
+
: codeGrade === 'good'
|
|
101
|
+
? `No sanitizer bypasses, direct DOM HTML writes or eval in ${plural(files.length, 'file')}${innerHtmlCount > 0 ? `; ${plural(innerHtmlCount, 'sanitized [innerHTML] binding')}` : ''}${vulnerabilitySummary}.`
|
|
102
|
+
: `${plural(unsafeDom.length, 'direct DOM HTML write')}, ${plural(dynamicCode.length, 'eval')}, ${plural(bypass.length, 'sanitizer bypass', 'sanitizer bypasses')}${vulnerabilitySummary}.`,
|
|
68
103
|
metrics: [
|
|
69
104
|
{ label: 'Direct DOM HTML writes', value: formatCount(unsafeDom.length), hint: 'innerHTML =, insertAdjacentHTML' },
|
|
70
105
|
{ label: 'Sanitizer bypasses', value: formatCount(bypass.length), hint: 'bypassSecurityTrust*' },
|
|
71
106
|
{ label: 'eval / new Function', value: formatCount(dynamicCode.length) },
|
|
72
107
|
{ label: '[innerHTML] bindings', value: formatCount(innerHtmlCount), hint: 'sanitized by Angular' },
|
|
108
|
+
scan
|
|
109
|
+
? { label: 'Known vulnerabilities', value: formatCount(runtimeCount), hint: `in ${plural(scan.checked, 'direct dependency', 'direct dependencies')} checked` }
|
|
110
|
+
: { label: 'Known vulnerabilities', value: 'not checked', hint: 'offline' },
|
|
73
111
|
],
|
|
74
112
|
findings,
|
|
75
113
|
measures: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/checks/security.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/checks/security.ts"],"names":[],"mappings":"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"}
|
package/dist/churn.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How often each file changed recently, from git, as `AnalysisContext.churn`. */
|
|
2
|
+
export interface Churn {
|
|
3
|
+
/** The window, as git understands it (`12.months`). */
|
|
4
|
+
since: string;
|
|
5
|
+
/** Commits in the window that touched the analysed folder, merges left out. */
|
|
6
|
+
commits: number;
|
|
7
|
+
/** Commits per report path (relative to the analysed root). */
|
|
8
|
+
files: Map<string, number>;
|
|
9
|
+
}
|
|
10
|
+
/** Why there is no churn to report. */
|
|
11
|
+
export type ChurnUnavailable = 'not a git repository' | 'shallow clone' | 'git not installed';
|
|
12
|
+
/**
|
|
13
|
+
* Commits per file over the last `since`, for the files under `root`.
|
|
14
|
+
*
|
|
15
|
+
* A file that is both large and changed often is where a refactoring pays
|
|
16
|
+
* off first: it is read and edited all the time, so its size costs on every
|
|
17
|
+
* change. Static analysis cannot see that; the history can. `--relative`
|
|
18
|
+
* keeps paths relative to `root` and limits the log to it, so a checkup of
|
|
19
|
+
* one folder of a larger repository counts that folder only.
|
|
20
|
+
*
|
|
21
|
+
* @returns The reason instead when there is no usable history: not a git
|
|
22
|
+
* repository, or a shallow clone (the remote mode and CI checkouts with
|
|
23
|
+
* `fetch-depth: 1`), whose history is one commit and would rank nothing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readChurn(root: string, since?: string): Churn | ChurnUnavailable;
|
package/dist/churn.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Commits per file over the last `since`, for the files under `root`.
|
|
4
|
+
*
|
|
5
|
+
* A file that is both large and changed often is where a refactoring pays
|
|
6
|
+
* off first: it is read and edited all the time, so its size costs on every
|
|
7
|
+
* change. Static analysis cannot see that; the history can. `--relative`
|
|
8
|
+
* keeps paths relative to `root` and limits the log to it, so a checkup of
|
|
9
|
+
* one folder of a larger repository counts that folder only.
|
|
10
|
+
*
|
|
11
|
+
* @returns The reason instead when there is no usable history: not a git
|
|
12
|
+
* repository, or a shallow clone (the remote mode and CI checkouts with
|
|
13
|
+
* `fetch-depth: 1`), whose history is one commit and would rank nothing.
|
|
14
|
+
*/
|
|
15
|
+
export function readChurn(root, since = '12.months') {
|
|
16
|
+
const git = (...args) => execFileSync('git', ['-C', root, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 256 * 1024 * 1024, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
17
|
+
try {
|
|
18
|
+
if (git('rev-parse', '--is-inside-work-tree').trim() !== 'true')
|
|
19
|
+
return 'not a git repository';
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return error.code === 'ENOENT' ? 'git not installed' : 'not a git repository';
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
if (git('rev-parse', '--is-shallow-repository').trim() === 'true')
|
|
26
|
+
return 'shallow clone';
|
|
27
|
+
const log = git('log', `--since=${since}`, '--no-merges', '--relative', '--format=%x01', '--name-only', '--', '.');
|
|
28
|
+
const files = new Map();
|
|
29
|
+
let commits = 0;
|
|
30
|
+
for (const block of log.split('\u0001')) {
|
|
31
|
+
const paths = block.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
32
|
+
if (paths.length === 0)
|
|
33
|
+
continue;
|
|
34
|
+
commits++;
|
|
35
|
+
for (const path of new Set(paths))
|
|
36
|
+
files.set(path, (files.get(path) ?? 0) + 1);
|
|
37
|
+
}
|
|
38
|
+
return { since, commits, files };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return 'not a git repository';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=churn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"churn.js","sourceRoot":"","sources":["../src/churn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAelD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAK,GAAG,WAAW;IACzD,MAAM,GAAG,GAAG,CAAC,GAAG,IAAc,EAAU,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAClO,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM;YAAE,OAAO,sBAAsB,CAAC;IACjG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC;IACvG,CAAC;IACD,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM;YAAE,OAAO,eAAe,CAAC;QAC1F,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QACnH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QACxC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACjC,OAAO,EAAE,CAAC;YACV,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,sBAAsB,CAAC;IAChC,CAAC;AACH,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type CheckupConfig } from './config.js';
|
|
|
3
3
|
import { type CiChoice, type InitPlan } from './init.js';
|
|
4
4
|
import { type RemoteRepository } from './remote.js';
|
|
5
5
|
import type { Report } from './types.js';
|
|
6
|
+
import { type HistoryReport } from './history.js';
|
|
6
7
|
/** Parsed and validated arguments of a checkup run, before the config file is applied. */
|
|
7
8
|
export interface CliArguments {
|
|
8
9
|
/** The local root; for a remote run, the repository argument until `main` has cloned it. */
|
|
@@ -20,6 +21,9 @@ export interface CliArguments {
|
|
|
20
21
|
plan: boolean;
|
|
21
22
|
/** `--project`: one project of the workspace, by name. */
|
|
22
23
|
project?: string;
|
|
24
|
+
/** `--only` and `--skip`, comma-separated check ids; undefined when not given, so the config can supply them. */
|
|
25
|
+
only?: string[];
|
|
26
|
+
skip?: string[];
|
|
23
27
|
/** `--client`, `--prepared-by`, `--notes`: the client report. */
|
|
24
28
|
client?: {
|
|
25
29
|
name: string;
|
|
@@ -55,6 +59,8 @@ export interface RunSettings {
|
|
|
55
59
|
offline: boolean;
|
|
56
60
|
plan: boolean;
|
|
57
61
|
ignore?: string[];
|
|
62
|
+
only?: string[];
|
|
63
|
+
skip?: string[];
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
66
|
* Parses `argv` (without the node and script entries) of a checkup run.
|
|
@@ -105,3 +111,27 @@ export declare function analysisIgnores(root: string, settings: Pick<RunSettings
|
|
|
105
111
|
* @throws When a measure went up and `force` is not set, listing each one.
|
|
106
112
|
*/
|
|
107
113
|
export declare function writeBaseline(args: BaselineArguments): Promise<string>;
|
|
114
|
+
/** Parsed arguments of `ng-checkup url`. */
|
|
115
|
+
export interface UrlArguments {
|
|
116
|
+
address: string;
|
|
117
|
+
json: boolean;
|
|
118
|
+
offline: boolean;
|
|
119
|
+
color: boolean;
|
|
120
|
+
}
|
|
121
|
+
/** Parses the arguments after `url`: one address, `--json`, `--offline`, `--no-color`. */
|
|
122
|
+
export declare function parseUrlArguments(argv: string[], environment?: NodeJS.ProcessEnv, isTty?: boolean): UrlArguments | 'help';
|
|
123
|
+
/** Parsed arguments of `ng-checkup history`. */
|
|
124
|
+
export interface HistoryArguments {
|
|
125
|
+
root: string;
|
|
126
|
+
/** Months back; one point per month, plus the working tree. */
|
|
127
|
+
points: number;
|
|
128
|
+
/** Resolved against the working directory; .html, .md or .json. Empty when none. */
|
|
129
|
+
reports: string[];
|
|
130
|
+
json: boolean;
|
|
131
|
+
color: boolean;
|
|
132
|
+
config?: string;
|
|
133
|
+
}
|
|
134
|
+
/** Parses the arguments after `history`. Throws a user-facing `Error` on anything invalid. */
|
|
135
|
+
export declare function parseHistoryArguments(argv: string[], environment?: NodeJS.ProcessEnv, isTty?: boolean): HistoryArguments | 'help';
|
|
136
|
+
/** Renders a history in the format its file extension names. */
|
|
137
|
+
export declare function renderHistoryForFile(report: HistoryReport, path: string): string;
|