@codefast/cli 0.13.0 → 0.14.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/CHANGELOG.md +29 -0
- package/README.md +58 -39
- package/dist/audit/command.js +23 -22
- package/dist/audit/comments/domain/comment-content.d.ts +12 -0
- package/dist/audit/comments/domain/comment-content.js +17 -4
- package/dist/audit/domain/types.d.ts +32 -31
- package/dist/audit/layers/cli-result.d.ts +13 -0
- package/dist/audit/{constants → layers}/cli-result.js +6 -6
- package/dist/audit/layers/cli-schema.d.ts +29 -0
- package/dist/audit/layers/cli-schema.js +17 -0
- package/dist/audit/layers/domain/layering.d.ts +46 -0
- package/dist/audit/layers/domain/layering.js +191 -0
- package/dist/audit/layers/output.d.ts +7 -0
- package/dist/audit/{constants → layers}/output.js +5 -5
- package/dist/audit/layers/prepare.d.ts +25 -0
- package/dist/audit/layers/prepare.js +66 -0
- package/dist/audit/layers/run.d.ts +19 -0
- package/dist/audit/layers/run.js +62 -0
- package/dist/audit/prepare.d.ts +13 -1
- package/dist/audit/prepare.js +16 -1
- package/dist/core/config/schema.d.ts +12 -4
- package/dist/core/config/schema.js +13 -1
- package/dist/tag/cli-result.d.ts +3 -0
- package/dist/tag/cli-result.js +8 -2
- package/dist/tag/domain/types.d.ts +15 -0
- package/dist/tag/output.js +13 -6
- package/dist/tag/run.js +2 -0
- package/dist/tag/writer/since-writer.d.ts +5 -0
- package/dist/tag/writer/since-writer.js +41 -9
- package/package.json +1 -1
- package/dist/audit/constants/cli-result.d.ts +0 -13
- package/dist/audit/constants/cli-schema.d.ts +0 -18
- package/dist/audit/constants/cli-schema.js +0 -12
- package/dist/audit/constants/domain/constants.d.ts +0 -8
- package/dist/audit/constants/domain/constants.js +0 -66
- package/dist/audit/constants/output.d.ts +0 -7
- package/dist/audit/constants/prepare.d.ts +0 -16
- package/dist/audit/constants/prepare.js +0 -37
- package/dist/audit/constants/run.d.ts +0 -14
- package/dist/audit/constants/run.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @codefast/cli
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1007](https://github.com/codefastlabs/codefast/pull/1007) Add `codefast audit layers`, which holds a package's `src/` to the layering its architecture states.
|
|
8
|
+
`audit.layers.packages` lists each package's layers bottom to top, every entry a family directly under the root — a
|
|
9
|
+
directory, or a lone module sitting flat — and the audit reports every value import or re-export that points up the
|
|
10
|
+
list, plus every module no layer places. Type-only imports pass whichever way they point; a dynamic `import()` counts as
|
|
11
|
+
a value import. A configured name the workspace does not hold, an entry nested below the root, and a family placed twice
|
|
12
|
+
are refused before anything is scanned. `audit.layers.allowlist` takes the offending import as written, or
|
|
13
|
+
`path:<import>`.
|
|
14
|
+
|
|
15
|
+
- [#990](https://github.com/codefastlabs/codefast/pull/990) Remove `codefast audit constants`, the audit that required every upper-case numeric `const` to name its kind in the
|
|
16
|
+
comment above it.
|
|
17
|
+
|
|
18
|
+
Breaking: the `audit constants` subcommand is gone, and `audit.constants` is no longer a config key, so a
|
|
19
|
+
`codefast.config` that still sets it fails validation as an unknown key; delete that section.
|
|
20
|
+
|
|
21
|
+
- [#992](https://github.com/codefastlabs/codefast/pull/992) `codefast tag` stamps every overload signature again, each in its own doc block. Since the move to `oxc-parser` it
|
|
22
|
+
stamped only an overloaded function's implementation, the one signature a `.d.ts` drops, so a released overload reached
|
|
23
|
+
consumers with no `@since` on any signature they can see.
|
|
24
|
+
|
|
25
|
+
A declaration with no doc block and a `//` comment on the line above it now fails the run instead of getting a fresh
|
|
26
|
+
block there, where the block would stack under a note (which `audit comments` rejects) or split a directive from the
|
|
27
|
+
code it governs. The run names each such declaration by file and line, lists it under `blockedDeclarations` in `--json`,
|
|
28
|
+
leaves its file untouched, and exits `1`: once a release ships the declaration unstamped, a later run can only stamp a
|
|
29
|
+
version that did not introduce it. The `--json` `ok` field now follows the exit code, so a failed target also reports
|
|
30
|
+
`ok: false`.
|
|
31
|
+
|
|
3
32
|
## 0.13.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -111,9 +111,9 @@ codefast # Codefast monorepo developer CLI
|
|
|
111
111
|
│ ├─ links [target] # markdown links pointing at a missing path/anchor
|
|
112
112
|
│ ├─ imports [target] # banned import forms (React by-name, Zod namespace in front-end, …)
|
|
113
113
|
│ ├─ assertions [target] # double type assertions through unknown/any (x as unknown as T)
|
|
114
|
-
│ ├─ constants [target] # numeric constants whose comment names none of the three kinds
|
|
115
114
|
│ ├─ display-names [target] # token()/tag()/module names breaking the <namespace>:<Name> convention
|
|
116
115
|
│ ├─ publish [target] # what breaks a consumer's install: #/ imports, unshipped targets, @source paths
|
|
116
|
+
│ ├─ layers [target] # value imports pointing up a package's configured layers
|
|
117
117
|
│ └─ comments [target] # section dividers not in the one allowed form
|
|
118
118
|
│ └─ --fix # rewrite every fixable divider in place (the only audit that writes)
|
|
119
119
|
│ (each audit also takes [target] + --json)
|
|
@@ -145,16 +145,16 @@ Every command also responds to `--help`; each command's section below explains w
|
|
|
145
145
|
| `audit links` | Report markdown cross-references that resolve to nothing | no |
|
|
146
146
|
| `audit imports` | Enforce the import policy (React by-name, Zod namespace in front-end) | no (report only) |
|
|
147
147
|
| `audit assertions` | Report double type assertions through `unknown` / `any`, tests included | no |
|
|
148
|
-
| `audit constants` | Require every tuned numeric constant to name the kind of number it is | no |
|
|
149
148
|
| `audit display-names` | Enforce the `namespace:Name` display-name convention | no |
|
|
150
149
|
| `audit publish` | Report what would break a consumer's install of a published package | no |
|
|
150
|
+
| `audit layers` | Report value imports that point up a package's configured layers | no |
|
|
151
151
|
| `audit comments` | Check doc-comment conventions; repair section dividers | `--fix` only |
|
|
152
152
|
|
|
153
|
-
**Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`, `audit links`, `audit assertions`,
|
|
154
|
-
`audit publish` are general-purpose — they work for any pnpm workspace or single package that builds
|
|
155
|
-
other
|
|
156
|
-
comment/divider grammar, a `namespace:Name` scheme for `@codefast/di` tokens
|
|
157
|
-
|
|
153
|
+
**Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`, `audit links`, `audit assertions`,
|
|
154
|
+
`audit layers`, and `audit publish` are general-purpose — they work for any pnpm workspace or single package that builds
|
|
155
|
+
with `tsc`. The other four audits encode codefast's own house style (logical Tailwind directions, named React imports, a
|
|
156
|
+
specific comment/divider grammar, a `namespace:Name` scheme for `@codefast/di` tokens). Adopt them if they fit your
|
|
157
|
+
project; otherwise skip them, or use an allowlist to narrow their scope.
|
|
158
158
|
|
|
159
159
|
## `arrange`
|
|
160
160
|
|
|
@@ -276,9 +276,15 @@ Exits `1` when any package fails, `0` otherwise.
|
|
|
276
276
|
## `tag`
|
|
277
277
|
|
|
278
278
|
Adds `@since <version>` tags to the doc comments of exported declarations that lack one, creating the doc block when
|
|
279
|
-
there is none.
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
there is none. Overload signatures are declarations too, each stamped on its own, because the `.d.ts` keeps every
|
|
280
|
+
overload's doc block and drops the implementation's. The version comes from the nearest `package.json` above each target
|
|
281
|
+
file, and declarations that already carry `@since` are left alone. Run it at release time so published APIs carry
|
|
282
|
+
accurate version metadata — never hand-write `@since`.
|
|
283
|
+
|
|
284
|
+
A declaration with no doc block and a `//` comment on the line above it is reported instead: a block written there would
|
|
285
|
+
stack under a note, which `audit comments` rejects, or split a directive from the code it governs. Its file is left as
|
|
286
|
+
it is, so every reported line stays accurate. Write the doc block by hand before the release that ships the declaration,
|
|
287
|
+
since a later run can only stamp a version that did not introduce it.
|
|
282
288
|
|
|
283
289
|
```bash
|
|
284
290
|
codefast tag # auto-discover packages from cwd (or the single package)
|
|
@@ -286,7 +292,8 @@ codefast tag packages/ui/src # tag one directory or file
|
|
|
286
292
|
codefast tag --dry-run # summary only, no writes
|
|
287
293
|
```
|
|
288
294
|
|
|
289
|
-
Exits `1` when no target is selected, when any target fails, or when the
|
|
295
|
+
Exits `1` when no target is selected, when any target fails, when a declaration is left unstamped, or when the
|
|
296
|
+
`tag.onAfterWrite` hook fails.
|
|
290
297
|
|
|
291
298
|
## `audit`
|
|
292
299
|
|
|
@@ -385,6 +392,41 @@ codefast audit publish # every published package in the work
|
|
|
385
392
|
codefast audit publish --json # machine-readable summary
|
|
386
393
|
```
|
|
387
394
|
|
|
395
|
+
### `audit layers`
|
|
396
|
+
|
|
397
|
+
_General-purpose._ Holds a package's `src/` to the layering its architecture states. You list the layers in
|
|
398
|
+
configuration, bottom to top, each naming the families directly under the root — a directory, or a lone module sitting
|
|
399
|
+
flat (`errors.ts`) — and the audit reports every value import or re-export that points up the list, plus every module no
|
|
400
|
+
layer places. Type-only imports erase at build time and couple nothing, so they may point anywhere; a dynamic `import()`
|
|
401
|
+
counts as a value import wherever it sits.
|
|
402
|
+
|
|
403
|
+
```js
|
|
404
|
+
// codefast.config.js
|
|
405
|
+
export default {
|
|
406
|
+
audit: {
|
|
407
|
+
layers: {
|
|
408
|
+
packages: {
|
|
409
|
+
"@acme/di": {
|
|
410
|
+
root: "src", // where the layers sit, relative to the package (default)
|
|
411
|
+
layers: [["core", "errors.ts"], ["engine"], ["container"], ["index.ts"]],
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
allowlist: [],
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
codefast audit layers # every package audit.layers.packages names
|
|
422
|
+
codefast audit layers packages/di # one package, or a directory beneath its root
|
|
423
|
+
codefast audit layers --json # machine-readable summary
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
A configured package name the workspace does not hold, a layer entry nested below the root, and a family placed twice
|
|
427
|
+
are refused before anything is scanned. Configure exceptions via `audit.layers.allowlist` — each entry is the offending
|
|
428
|
+
import as written or `repo/relative/path.ts:<import>`.
|
|
429
|
+
|
|
388
430
|
### `audit comments`
|
|
389
431
|
|
|
390
432
|
_House style._ Checks doc-comment conventions. Section dividers not in the one allowed form are mechanical, so `--fix`
|
|
@@ -420,30 +462,6 @@ codefast audit display-names --json # machine-readable summary
|
|
|
420
462
|
Configure exceptions via `audit.displayNames.allowlist` — each entry is the call as written, through its closing quote
|
|
421
463
|
(or parenthesis when the name is the only argument), or `repo/relative/path.ts:<call>`.
|
|
422
464
|
|
|
423
|
-
### `audit constants`
|
|
424
|
-
|
|
425
|
-
_House style._ Holds library sources to one rule for tuned numbers: a numeric constant says which kind of number it is.
|
|
426
|
-
It flags an upper-case `const NAME = <number>` in a `.ts`/`.tsx` file under a `src` directory — tests, benchmarks, apps,
|
|
427
|
-
and examples are out of scope — unless the comment directly above it, a `/* … */` block or a run of `//` lines, names
|
|
428
|
-
one of three kinds:
|
|
429
|
-
|
|
430
|
-
- `a constant of the machine` — a width of the platform the code runs on;
|
|
431
|
-
- `a value the contract fixes` — a number the documented contract promises;
|
|
432
|
-
- `derived from bind-time data` — a figure computed from what a caller hands in.
|
|
433
|
-
|
|
434
|
-
A count that merely looks reasonable is none of them, so it is reported. `0`, `1`, and `-1` are skipped: they stand for
|
|
435
|
-
absence or identity, not for a tuned size.
|
|
436
|
-
|
|
437
|
-
```bash
|
|
438
|
-
codefast audit constants # uses audit.constants.target from config
|
|
439
|
-
codefast audit constants packages/di/src # explicit target
|
|
440
|
-
codefast audit constants --json # machine-readable summary
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
With no `[target]`, the scan root is `audit.constants.target` from the config; when neither is set the command fails.
|
|
444
|
-
Configure exceptions via `audit.constants.allowlist` — each entry is the constant's name or `repo/relative/path.ts:NAME`
|
|
445
|
-
— for a measured policy that has to stay a tuned number.
|
|
446
|
-
|
|
447
465
|
## Configuration
|
|
448
466
|
|
|
449
467
|
**You do not need a config file.** Every command has sensible defaults and works with none. Add a `codefast.config.*`
|
|
@@ -579,9 +597,10 @@ export default {
|
|
|
579
597
|
imports: { allowlist: [] }, // offending import text as written, or `repo/relative/path.tsx:<text>`
|
|
580
598
|
assertions: { allowlist: [] }, // assertion as written, or `repo/relative/path.ts:<assertion>`
|
|
581
599
|
displayNames: { allowlist: [] }, // call as written, or `repo/relative/path.ts:<call>`
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
600
|
+
layers: {
|
|
601
|
+
// per package: its layers bottom to top, each entry a directory or module file under `root` (default `src`)
|
|
602
|
+
packages: { "@acme/di": { layers: [["core", "errors.ts"], ["engine"], ["index.ts"]] } },
|
|
603
|
+
allowlist: [], // import as written, or `repo/relative/path.ts:<import>`
|
|
585
604
|
},
|
|
586
605
|
},
|
|
587
606
|
};
|
|
@@ -635,7 +654,7 @@ pnpm run cli:audit:comments # codefast audit comments
|
|
|
635
654
|
pnpm run cli:audit:imports # codefast audit imports
|
|
636
655
|
pnpm run cli:audit:assertions # codefast audit assertions
|
|
637
656
|
pnpm run cli:audit:display-names # codefast audit display-names
|
|
638
|
-
pnpm run cli:audit:
|
|
657
|
+
pnpm run cli:audit:layers # codefast audit layers
|
|
639
658
|
pnpm run cli:audit:publish # codefast audit publish
|
|
640
659
|
```
|
|
641
660
|
|
package/dist/audit/command.js
CHANGED
|
@@ -9,11 +9,6 @@ import { commentAuditRunRequestSchema } from "#audit/comments/cli-schema";
|
|
|
9
9
|
import { presentCommentAuditResult } from "#audit/comments/output";
|
|
10
10
|
import { prepareCommentAudit } from "#audit/comments/prepare";
|
|
11
11
|
import { runCommentAudit } from "#audit/comments/run";
|
|
12
|
-
import { exitCodeForConstantAuditResult, formatConstantAuditJsonOutput } from "#audit/constants/cli-result";
|
|
13
|
-
import { constantAuditRunRequestSchema } from "#audit/constants/cli-schema";
|
|
14
|
-
import { presentConstantAuditResult } from "#audit/constants/output";
|
|
15
|
-
import { prepareConstantAudit } from "#audit/constants/prepare";
|
|
16
|
-
import { runConstantAudit } from "#audit/constants/run";
|
|
17
12
|
import { exitCodeForDisplayNameAuditResult, formatDisplayNameAuditJsonOutput } from "#audit/display-names/cli-result";
|
|
18
13
|
import { displayNameAuditRunRequestSchema } from "#audit/display-names/cli-schema";
|
|
19
14
|
import { presentDisplayNameAuditResult } from "#audit/display-names/output";
|
|
@@ -24,6 +19,11 @@ import { importsAuditRunRequestSchema } from "#audit/imports/cli-schema";
|
|
|
24
19
|
import { presentImportsAuditResult } from "#audit/imports/output";
|
|
25
20
|
import { prepareImportsAudit } from "#audit/imports/prepare";
|
|
26
21
|
import { runImportsAudit } from "#audit/imports/run";
|
|
22
|
+
import { exitCodeForLayersAuditResult, formatLayersAuditJsonOutput } from "#audit/layers/cli-result";
|
|
23
|
+
import { layersAuditRunRequestSchema } from "#audit/layers/cli-schema";
|
|
24
|
+
import { presentLayersAuditResult } from "#audit/layers/output";
|
|
25
|
+
import { prepareLayersAudit } from "#audit/layers/prepare";
|
|
26
|
+
import { runLayersAudit } from "#audit/layers/run";
|
|
27
27
|
import { exitCodeForLinkAuditResult, formatLinkAuditJsonOutput } from "#audit/links/cli-result";
|
|
28
28
|
import { linkAuditRunRequestSchema } from "#audit/links/cli-schema";
|
|
29
29
|
import { presentLinkAuditResult } from "#audit/links/output";
|
|
@@ -121,22 +121,6 @@ const displayNamesCheck = {
|
|
|
121
121
|
formatJson: formatDisplayNameAuditJsonOutput,
|
|
122
122
|
exitCode: exitCodeForDisplayNameAuditResult,
|
|
123
123
|
};
|
|
124
|
-
const constantsCheck = {
|
|
125
|
-
name: "constants",
|
|
126
|
-
description: "Report numeric constants whose comment names none of the three kinds a number may be",
|
|
127
|
-
targetHelp: "Directory or file to scan (default: audit.constants.target from config)",
|
|
128
|
-
schema: constantAuditRunRequestSchema,
|
|
129
|
-
prepare: prepareConstantAudit,
|
|
130
|
-
buildRequest: baseAuditRequest,
|
|
131
|
-
run: (fs, request) => runConstantAudit(fs, {
|
|
132
|
-
rootDir: request.rootDir,
|
|
133
|
-
targetPath: request.targetPath,
|
|
134
|
-
allowlist: request.allowlist ?? [],
|
|
135
|
-
}),
|
|
136
|
-
present: presentConstantAuditResult,
|
|
137
|
-
formatJson: formatConstantAuditJsonOutput,
|
|
138
|
-
exitCode: exitCodeForConstantAuditResult,
|
|
139
|
-
};
|
|
140
124
|
const commentsCheck = {
|
|
141
125
|
name: "comments",
|
|
142
126
|
description: "Report section dividers that are not in the repo's one allowed form",
|
|
@@ -157,6 +141,23 @@ const commentsCheck = {
|
|
|
157
141
|
command.option("--fix", "Rewrite every mechanically fixable divider in place", false);
|
|
158
142
|
},
|
|
159
143
|
};
|
|
144
|
+
const layersCheck = {
|
|
145
|
+
name: "layers",
|
|
146
|
+
description: "Report value imports that point up a package's configured layers, and modules sitting in no layer",
|
|
147
|
+
targetHelp: "Directory or file to scan (default: the repo root, reaching every package audit.layers names)",
|
|
148
|
+
schema: layersAuditRunRequestSchema,
|
|
149
|
+
prepare: prepareLayersAudit,
|
|
150
|
+
buildRequest: (prelude, opts) => ({ ...baseAuditRequest(prelude, opts), packages: prelude.packages }),
|
|
151
|
+
run: (fs, request) => runLayersAudit(fs, {
|
|
152
|
+
rootDir: request.rootDir,
|
|
153
|
+
targetPath: request.targetPath,
|
|
154
|
+
allowlist: request.allowlist ?? [],
|
|
155
|
+
packages: request.packages,
|
|
156
|
+
}),
|
|
157
|
+
present: presentLayersAuditResult,
|
|
158
|
+
formatJson: formatLayersAuditJsonOutput,
|
|
159
|
+
exitCode: exitCodeForLayersAuditResult,
|
|
160
|
+
};
|
|
160
161
|
const publishCheck = {
|
|
161
162
|
name: "publish",
|
|
162
163
|
description: "Report what breaks a consumer's install: #/ imports, unshipped exports/imports targets, and stylesheet @source paths reaching nothing shipped",
|
|
@@ -200,8 +201,8 @@ export function createAuditCommand() {
|
|
|
200
201
|
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(importsCheck));
|
|
201
202
|
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(assertionsCheck));
|
|
202
203
|
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(displayNamesCheck));
|
|
203
|
-
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(constantsCheck));
|
|
204
204
|
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(commentsCheck));
|
|
205
|
+
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(layersCheck));
|
|
205
206
|
registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(publishCheck));
|
|
206
207
|
return cmd;
|
|
207
208
|
}
|
|
@@ -18,6 +18,18 @@ export interface CommentContentFinding {
|
|
|
18
18
|
readonly raw: string;
|
|
19
19
|
readonly defect: CommentContentDefectKind;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns whether a line is a `//` note: a line comment that is neither a divider nor a tooling directive.
|
|
23
|
+
*
|
|
24
|
+
* @since 0.14.0
|
|
25
|
+
*/
|
|
26
|
+
export declare function isNoteLine(line: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Returns whether a line is a `//` tooling directive, which governs the code below it.
|
|
29
|
+
*
|
|
30
|
+
* @since 0.14.0
|
|
31
|
+
*/
|
|
32
|
+
export declare function isDirectiveLine(line: string): boolean;
|
|
21
33
|
/**
|
|
22
34
|
* Scans a source file's comments for banned content, in source order.
|
|
23
35
|
*
|
|
@@ -18,6 +18,22 @@ const declarationPattern = /^[ \t]*(?:export|const|let|var|function|class|interf
|
|
|
18
18
|
// A divider or a tooling directive above a doc block is not a stacked note.
|
|
19
19
|
const dividerLinePattern = /^[ \t]*\/\/[ \t]*[-=─_*~#]{2,}/;
|
|
20
20
|
const directiveLinePattern = /^[ \t]*\/\/[ \t]*(?:oxlint-|eslint-|@ts-|prettier-)/;
|
|
21
|
+
/**
|
|
22
|
+
* Returns whether a line is a `//` note: a line comment that is neither a divider nor a tooling directive.
|
|
23
|
+
*
|
|
24
|
+
* @since 0.14.0
|
|
25
|
+
*/
|
|
26
|
+
export function isNoteLine(line) {
|
|
27
|
+
return lineCommentPattern.test(line) && !dividerLinePattern.test(line) && !isDirectiveLine(line);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns whether a line is a `//` tooling directive, which governs the code below it.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.14.0
|
|
33
|
+
*/
|
|
34
|
+
export function isDirectiveLine(line) {
|
|
35
|
+
return directiveLinePattern.test(line);
|
|
36
|
+
}
|
|
21
37
|
/**
|
|
22
38
|
* Scans a source file's comments for banned content, in source order.
|
|
23
39
|
*
|
|
@@ -37,10 +53,7 @@ export function scanCommentContent(content, language) {
|
|
|
37
53
|
// A `//` run stacked directly above a doc block reads as a second doc — it belongs inside.
|
|
38
54
|
if (language === "js" && !insideBlock && trimmed.startsWith("/**")) {
|
|
39
55
|
let runStart = index;
|
|
40
|
-
while (runStart > 0 &&
|
|
41
|
-
lineCommentPattern.test(lines[runStart - 1]) &&
|
|
42
|
-
!dividerLinePattern.test(lines[runStart - 1]) &&
|
|
43
|
-
!directiveLinePattern.test(lines[runStart - 1])) {
|
|
56
|
+
while (runStart > 0 && isNoteLine(lines[runStart - 1])) {
|
|
44
57
|
runStart--;
|
|
45
58
|
}
|
|
46
59
|
if (runStart < index) {
|
|
@@ -135,37 +135,6 @@ export type DisplayNameAuditResult = {
|
|
|
135
135
|
readonly allowlistedCount: number;
|
|
136
136
|
readonly scannedFileCount: number;
|
|
137
137
|
};
|
|
138
|
-
/**
|
|
139
|
-
* A numeric constant whose comment names none of the three kinds.
|
|
140
|
-
*
|
|
141
|
-
* @since 0.11.0
|
|
142
|
-
*/
|
|
143
|
-
export type ConstantViolation = {
|
|
144
|
-
readonly line: number;
|
|
145
|
-
/** The declaration's name and value, as `NAME = 32`. */
|
|
146
|
-
readonly raw: string;
|
|
147
|
-
readonly reason: string;
|
|
148
|
-
};
|
|
149
|
-
/**
|
|
150
|
-
* The numeric-constant violations found in one file.
|
|
151
|
-
*
|
|
152
|
-
* @since 0.11.0
|
|
153
|
-
*/
|
|
154
|
-
export type ConstantFileViolations = {
|
|
155
|
-
readonly relativePath: string;
|
|
156
|
-
readonly violations: Array<ConstantViolation>;
|
|
157
|
-
};
|
|
158
|
-
/**
|
|
159
|
-
* Outcome of one `audit constants` run.
|
|
160
|
-
*
|
|
161
|
-
* @since 0.11.0
|
|
162
|
-
*/
|
|
163
|
-
export type ConstantAuditResult = {
|
|
164
|
-
readonly files: Array<ConstantFileViolations>;
|
|
165
|
-
readonly violationCount: number;
|
|
166
|
-
readonly allowlistedCount: number;
|
|
167
|
-
readonly scannedFileCount: number;
|
|
168
|
-
};
|
|
169
138
|
/**
|
|
170
139
|
* A broken link or anchor found by the link audit.
|
|
171
140
|
*
|
|
@@ -296,4 +265,36 @@ export type PublishAuditResult = {
|
|
|
296
265
|
readonly legacyImportCount: number;
|
|
297
266
|
readonly scannedFileCount: number;
|
|
298
267
|
readonly packageCount: number;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* A module outside every configured layer, or a value import that points up the layers.
|
|
271
|
+
*
|
|
272
|
+
* @since 0.14.0
|
|
273
|
+
*/
|
|
274
|
+
export type LayerViolation = {
|
|
275
|
+
readonly line: number;
|
|
276
|
+
/** The import or re-export as written, or the module path when the module itself is unplaced. */
|
|
277
|
+
readonly raw: string;
|
|
278
|
+
readonly reason: string;
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* The layering violations found in one file.
|
|
282
|
+
*
|
|
283
|
+
* @since 0.14.0
|
|
284
|
+
*/
|
|
285
|
+
export type LayerFileViolations = {
|
|
286
|
+
readonly relativePath: string;
|
|
287
|
+
readonly violations: Array<LayerViolation>;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Outcome of one `audit layers` run.
|
|
291
|
+
*
|
|
292
|
+
* @since 0.14.0
|
|
293
|
+
*/
|
|
294
|
+
export type LayersAuditResult = {
|
|
295
|
+
readonly files: Array<LayerFileViolations>;
|
|
296
|
+
readonly violationCount: number;
|
|
297
|
+
readonly allowlistedCount: number;
|
|
298
|
+
readonly scannedFileCount: number;
|
|
299
|
+
readonly packageCount: number;
|
|
299
300
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LayersAuditResult } from "#audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted layering violation remains.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.14.0
|
|
6
|
+
*/
|
|
7
|
+
export declare function exitCodeForLayersAuditResult(result: LayersAuditResult): number;
|
|
8
|
+
/**
|
|
9
|
+
* Machine-readable layering summary for `--json`.
|
|
10
|
+
*
|
|
11
|
+
* @since 0.14.0
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatLayersAuditJsonOutput(result: LayersAuditResult, rootDir: string): string;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { CLI_EXIT_GENERAL_ERROR, CLI_EXIT_SUCCESS } from "#core/exit-codes";
|
|
2
2
|
/**
|
|
3
|
-
* Exit `1` when any non-allowlisted
|
|
3
|
+
* Exit `1` when any non-allowlisted layering violation remains.
|
|
4
4
|
*
|
|
5
|
-
* @since 0.
|
|
5
|
+
* @since 0.14.0
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
7
|
+
export function exitCodeForLayersAuditResult(result) {
|
|
8
8
|
return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Machine-readable
|
|
11
|
+
* Machine-readable layering summary for `--json`.
|
|
12
12
|
*
|
|
13
|
-
* @since 0.
|
|
13
|
+
* @since 0.14.0
|
|
14
14
|
*/
|
|
15
|
-
export function
|
|
15
|
+
export function formatLayersAuditJsonOutput(result, rootDir) {
|
|
16
16
|
return JSON.stringify({
|
|
17
17
|
schemaVersion: 1,
|
|
18
18
|
ok: result.violationCount === 0,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* One layered package as the run reads it: its name, the absolute root the layers sit under, and the layers.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.14.0
|
|
6
|
+
*/
|
|
7
|
+
export type LayersAuditPackage = {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly rootPath: string;
|
|
10
|
+
readonly layers: ReadonlyArray<ReadonlyArray<string>>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolved request for a single layering audit run.
|
|
14
|
+
*
|
|
15
|
+
* @since 0.14.0
|
|
16
|
+
*/
|
|
17
|
+
export type LayersAuditRunRequest = {
|
|
18
|
+
readonly rootDir: string;
|
|
19
|
+
readonly targetPath: string;
|
|
20
|
+
readonly allowlist?: ReadonlyArray<string> | undefined;
|
|
21
|
+
readonly json: boolean;
|
|
22
|
+
readonly packages: ReadonlyArray<LayersAuditPackage>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Zod schema for {@link LayersAuditRunRequest}.
|
|
26
|
+
*
|
|
27
|
+
* @since 0.14.0
|
|
28
|
+
*/
|
|
29
|
+
export declare const layersAuditRunRequestSchema: z.ZodType<LayersAuditRunRequest>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for {@link LayersAuditRunRequest}.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.14.0
|
|
6
|
+
*/
|
|
7
|
+
export const layersAuditRunRequestSchema = z.object({
|
|
8
|
+
rootDir: z.string().min(1),
|
|
9
|
+
targetPath: z.string().min(1),
|
|
10
|
+
allowlist: z.array(z.string()).optional(),
|
|
11
|
+
json: z.boolean(),
|
|
12
|
+
packages: z.array(z.object({
|
|
13
|
+
name: z.string().min(1),
|
|
14
|
+
rootPath: z.string().min(1),
|
|
15
|
+
layers: z.array(z.array(z.string().min(1))),
|
|
16
|
+
})),
|
|
17
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { LayerViolation } from "#audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* The key a layer entry and a module path share: the first path segment, without a module extension.
|
|
4
|
+
*
|
|
5
|
+
* @remarks `errors.ts`, `errors/` and `errors/taxonomy.ts` all key as `errors`, so a layer entry names
|
|
6
|
+
* a family directly under the root — a directory, or a lone module sitting flat.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.14.0
|
|
9
|
+
*/
|
|
10
|
+
export declare function layerKeyOf(modulePath: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Returns why a layer list breaks its contract, or `undefined` when every entry is a family under the root, placed once.
|
|
13
|
+
*
|
|
14
|
+
* @since 0.14.0
|
|
15
|
+
*/
|
|
16
|
+
export declare function invalidLayerEntry(layers: ReadonlyArray<ReadonlyArray<string>>): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Where a module sits: its layer's position from the bottom, and the entry that placed it there.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.14.0
|
|
21
|
+
*/
|
|
22
|
+
export interface LayerPlacement {
|
|
23
|
+
readonly index: number;
|
|
24
|
+
readonly entry: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A package's layers, bottom to top, answering the placement of any module path under the root.
|
|
28
|
+
*
|
|
29
|
+
* @since 0.14.0
|
|
30
|
+
*/
|
|
31
|
+
export declare class LayerMap {
|
|
32
|
+
#private;
|
|
33
|
+
constructor(layers: ReadonlyArray<ReadonlyArray<string>>);
|
|
34
|
+
/** The placement of a root-relative module path, or `undefined` when no layer names its family. */
|
|
35
|
+
placementOf(modulePath: string): LayerPlacement | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Scans one module against its package's layers and returns the violations: the module sitting in no
|
|
39
|
+
* layer, or a value import or re-export whose target sits in a higher layer, or in none.
|
|
40
|
+
*
|
|
41
|
+
* @remarks Type-only imports and re-exports erase at build time and couple nothing, so they pass
|
|
42
|
+
* whichever way they point. Dynamic `import()` counts as a value import wherever it sits.
|
|
43
|
+
*
|
|
44
|
+
* @since 0.14.0
|
|
45
|
+
*/
|
|
46
|
+
export declare function auditLayeringSource(filePath: string, modulePath: string, sourceText: string, layers: LayerMap): Array<LayerViolation>;
|