@codefast/cli 0.12.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 +65 -0
- package/README.md +120 -16
- package/dist/arrange/domain/ast/translator.js +42 -28
- package/dist/arrange/simplify/process-file.d.ts +1 -1
- package/dist/audit/assertions/cli-result.d.ts +13 -0
- package/dist/audit/assertions/cli-result.js +22 -0
- package/dist/audit/assertions/cli-schema.d.ts +18 -0
- package/dist/audit/assertions/cli-schema.js +12 -0
- package/dist/audit/assertions/domain/double-assertion.d.ts +20 -0
- package/dist/audit/assertions/domain/double-assertion.js +122 -0
- package/dist/audit/assertions/output.d.ts +7 -0
- package/dist/audit/assertions/output.js +21 -0
- package/dist/audit/assertions/prepare.d.ts +16 -0
- package/dist/audit/assertions/prepare.js +12 -0
- package/dist/audit/assertions/run.d.ts +14 -0
- package/dist/audit/assertions/run.js +40 -0
- package/dist/audit/command.js +46 -1
- package/dist/audit/comments/domain/comment-content.d.ts +12 -0
- package/dist/audit/comments/domain/comment-content.js +17 -4
- package/dist/audit/display-names/domain/display-names.js +1 -9
- package/dist/audit/domain/types.d.ts +86 -0
- package/dist/audit/imports/domain/import-policy.js +4 -18
- package/dist/audit/layers/cli-result.d.ts +13 -0
- package/dist/audit/layers/cli-result.js +22 -0
- 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/layers/output.js +21 -0
- 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/audit/publish/cli-result.d.ts +1 -1
- package/dist/audit/publish/cli-result.js +6 -3
- package/dist/audit/publish/domain/stylesheet-sources.d.ts +22 -0
- package/dist/audit/publish/domain/stylesheet-sources.js +64 -0
- package/dist/audit/publish/output.js +12 -3
- package/dist/audit/publish/run.d.ts +2 -1
- package/dist/audit/publish/run.js +30 -1
- package/dist/audit/publish/shipped-files.d.ts +21 -0
- package/dist/audit/publish/shipped-files.js +36 -0
- package/dist/core/config/schema.d.ts +13 -0
- package/dist/core/config/schema.js +14 -0
- package/dist/core/filesystem/filesystem.d.ts +3 -4
- package/dist/core/filesystem/node.js +1 -7
- package/dist/core/oxc-node.d.ts +32 -0
- package/dist/core/oxc-node.js +25 -0
- package/dist/core/source-position.d.ts +15 -0
- package/dist/core/source-position.js +26 -0
- package/dist/mirror/dist-filesystem-node.js +4 -14
- package/dist/pack-slim/run.js +1 -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/domain/version-summary.d.ts +5 -2
- 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 +43 -13
- package/package.json +4 -4
- package/dist/mirror/domain/dirent-guard.d.ts +0 -10
- package/dist/mirror/domain/dirent-guard.js +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
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
|
+
|
|
32
|
+
## 0.13.0
|
|
33
|
+
|
|
34
|
+
### Minor Changes
|
|
35
|
+
|
|
36
|
+
- [#951](https://github.com/codefastlabs/codefast/pull/951) Add `codefast audit assertions`, which reports every double type assertion through `unknown` or `any` —
|
|
37
|
+
`x as unknown as T`, `(x as unknown) as T`, `<T><unknown>x` and the `any` spellings — in `.ts`/`.tsx` files, tests
|
|
38
|
+
included. Where the erasure is the point, keep one with `// codefast-allow-double-assertion: <reason>` on its line or
|
|
39
|
+
the line above; a directive with no reason, or one that keeps no assertion, is reported too. Exceptions can also go in
|
|
40
|
+
`audit.assertions.allowlist`.
|
|
41
|
+
|
|
42
|
+
`Filesystem.readdir` is replaced by `readdirEntries(path, { recursive })`, which always returns directory entries: every
|
|
43
|
+
caller asked for them, and the old `string[] | DirectoryEntry[]` union forced each one to cast or guard.
|
|
44
|
+
|
|
45
|
+
- [#894](https://github.com/codefastlabs/codefast/pull/894) `codefast audit constants` reports every upper-case `const` bound to a number in a library's sources whose comment names
|
|
46
|
+
none of the three kinds a number may be — a constant of the machine, a value the contract fixes, or one derived from
|
|
47
|
+
bind-time data. Sentinel values (`0`, `1`, `-1`) are exempt; `audit.constants.target` and `audit.constants.allowlist` in
|
|
48
|
+
`codefast.config` scope and except it.
|
|
49
|
+
|
|
50
|
+
- [#980](https://github.com/codefastlabs/codefast/pull/980) `audit publish` also reports a shipped stylesheet whose Tailwind `@source` paths reach none of the files the slimmed
|
|
51
|
+
tarball ships, naming any `files` entry missing on disk so an unbuilt `dist` reads as such. A workspace resolves those
|
|
52
|
+
paths against `src`, so only the published layout used to show the failure.
|
|
53
|
+
|
|
54
|
+
- [#976](https://github.com/codefastlabs/codefast/pull/976) `engines.node` is now `>=24.0.0`, up from `>=22.12.0`, and Node 22 is no longer supported. Node 24.0.0 is the first
|
|
55
|
+
release with explicit resource management built in (`using`, `await using`, `DisposableStack`, `AsyncDisposableStack`,
|
|
56
|
+
`SuppressedError`) and all of ES2025, so the packages use both as the platform ships them instead of shimming them for
|
|
57
|
+
an older line, and the CI matrix runs the unit suite on 24.0.0 itself. Move to Node 24, or stay on the current minor
|
|
58
|
+
while a deployment still runs Node 22.
|
|
59
|
+
|
|
60
|
+
### Patch Changes
|
|
61
|
+
|
|
62
|
+
- [#965](https://github.com/codefastlabs/codefast/pull/965) The optional `typescript` peer is now `>=7.0.0`. It was published as the repository's own `^7.0.2` pin, which excluded
|
|
63
|
+
TypeScript 8 and would have risen with each pin bump instead of staying at the supported floor.
|
|
64
|
+
|
|
65
|
+
- [#965](https://github.com/codefastlabs/codefast/pull/965) `README.md` now states TypeScript 7 or later as the floor for the package's types — the one compiler every `@codefast/*`
|
|
66
|
+
package is built and checked with. No code or declaration changed.
|
|
67
|
+
|
|
3
68
|
## 0.12.0
|
|
4
69
|
|
|
5
70
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -22,7 +22,9 @@ encode an opinionated house style (called out below) that you can adopt, ignore,
|
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
- **Node.js ≥
|
|
25
|
+
- **Node.js ≥ 24** (the CLI is published as ESM).
|
|
26
|
+
- **TypeScript ≥ 7** to type-check a config written with `defineConfig`, and for
|
|
27
|
+
`arrange simplify --fold-variant-classname`, the one command that loads `typescript` (an optional peer).
|
|
26
28
|
- **A project root — workspace or single package.** Commands resolve their root by walking up from the current
|
|
27
29
|
directory: the nearest `pnpm-workspace.yaml` marks a **workspace** (every package under it is in scope), and with no
|
|
28
30
|
workspace file the nearest `package.json` marks a **single package** (that one package is the whole scope). Only
|
|
@@ -108,7 +110,10 @@ codefast # Codefast monorepo developer CLI
|
|
|
108
110
|
│ ├─ rtl [target] # physical-direction Tailwind classes to make logical / rtl:-paired
|
|
109
111
|
│ ├─ links [target] # markdown links pointing at a missing path/anchor
|
|
110
112
|
│ ├─ imports [target] # banned import forms (React by-name, Zod namespace in front-end, …)
|
|
113
|
+
│ ├─ assertions [target] # double type assertions through unknown/any (x as unknown as T)
|
|
111
114
|
│ ├─ display-names [target] # token()/tag()/module names breaking the <namespace>:<Name> convention
|
|
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
|
|
112
117
|
│ └─ comments [target] # section dividers not in the one allowed form
|
|
113
118
|
│ └─ --fix # rewrite every fixable divider in place (the only audit that writes)
|
|
114
119
|
│ (each audit also takes [target] + --json)
|
|
@@ -139,14 +144,17 @@ Every command also responds to `--help`; each command's section below explains w
|
|
|
139
144
|
| `audit rtl` | Report physical-direction Tailwind classes that should be logical | no |
|
|
140
145
|
| `audit links` | Report markdown cross-references that resolve to nothing | no |
|
|
141
146
|
| `audit imports` | Enforce the import policy (React by-name, Zod namespace in front-end) | no (report only) |
|
|
147
|
+
| `audit assertions` | Report double type assertions through `unknown` / `any`, tests included | no |
|
|
142
148
|
| `audit display-names` | Enforce the `namespace:Name` display-name convention | no |
|
|
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 |
|
|
143
151
|
| `audit comments` | Check doc-comment conventions; repair section dividers | `--fix` only |
|
|
144
152
|
|
|
145
|
-
**Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`,
|
|
146
|
-
work for any pnpm workspace or single package that builds
|
|
147
|
-
style (logical Tailwind directions, named React imports, a
|
|
148
|
-
for `@codefast/di` tokens). Adopt them if they fit your
|
|
149
|
-
their scope.
|
|
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.
|
|
150
158
|
|
|
151
159
|
## `arrange`
|
|
152
160
|
|
|
@@ -244,12 +252,13 @@ apply (see the [Configuration](#configuration) example for their shape):
|
|
|
244
252
|
## `pack-slim`
|
|
245
253
|
|
|
246
254
|
Slims a published package down to what a consumer's `tsc` and Node actually read, so the npm tarball ships `dist`
|
|
247
|
-
runtime and types
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
result is **never
|
|
255
|
+
runtime and types, plus any `src` subtree an export still points into (a raw stylesheet export such as `./css/*`). Where
|
|
256
|
+
`mirror` writes the full exports — including the `source` condition — for local development, `pack-slim` removes that
|
|
257
|
+
development lane for publish: it drops the rest of `src` from `files`, every `source` condition from
|
|
258
|
+
`exports`/`imports`, every `imports` entry left pointing outside `files`, every script that is not an install or publish
|
|
259
|
+
lifecycle hook, `devDependencies`, and the `dist` source maps plus their dangling `sourceMappingURL` directives. Private
|
|
260
|
+
packages are skipped. It is meant to run on an ephemeral CI checkout right before publish, so its result is **never
|
|
261
|
+
committed**.
|
|
253
262
|
|
|
254
263
|
Because that result must never be committed, `pack-slim` refuses to write when the git working tree has uncommitted
|
|
255
264
|
tracked changes — a guard against an accidental local run landing on real work. `--dry-run` is exempt (it writes
|
|
@@ -267,9 +276,15 @@ Exits `1` when any package fails, `0` otherwise.
|
|
|
267
276
|
## `tag`
|
|
268
277
|
|
|
269
278
|
Adds `@since <version>` tags to the doc comments of exported declarations that lack one, creating the doc block when
|
|
270
|
-
there is none.
|
|
271
|
-
|
|
272
|
-
|
|
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.
|
|
273
288
|
|
|
274
289
|
```bash
|
|
275
290
|
codefast tag # auto-discover packages from cwd (or the single package)
|
|
@@ -277,7 +292,8 @@ codefast tag packages/ui/src # tag one directory or file
|
|
|
277
292
|
codefast tag --dry-run # summary only, no writes
|
|
278
293
|
```
|
|
279
294
|
|
|
280
|
-
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.
|
|
281
297
|
|
|
282
298
|
## `audit`
|
|
283
299
|
|
|
@@ -332,6 +348,85 @@ codefast audit imports --json # machine-readable summary
|
|
|
332
348
|
Configure exceptions via `audit.imports.allowlist` — each entry is the offending source text as written or
|
|
333
349
|
`repo/relative/path.tsx:<text>`.
|
|
334
350
|
|
|
351
|
+
### `audit assertions`
|
|
352
|
+
|
|
353
|
+
Reports every double type assertion through `unknown` or `any` in `.ts`/`.tsx` files, tests included —
|
|
354
|
+
`x as unknown as T`, `(x as unknown) as T`, `<T><unknown>x` and the `any` spellings. The pair tells the compiler two
|
|
355
|
+
types are unrelated and silences it anyway; no oxlint rule targets it (`typescript/no-unnecessary-type-assertion`
|
|
356
|
+
catches the redundant ones only). Make the types agree, narrow with a type guard, or — where the erasure is the point —
|
|
357
|
+
keep it with a directive that states why, on the assertion's line or the line above:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
// codefast-allow-double-assertion: the host stores each plugin's config untyped; the plugin owns its shape
|
|
361
|
+
const config = host.configFor(plugin.id) as unknown as PluginConfig;
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
A directive with no reason, or one that keeps no assertion, is itself reported, so a kept assertion cannot outlive its
|
|
365
|
+
cause.
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
codefast audit assertions # whole repo
|
|
369
|
+
codefast audit assertions packages/di # explicit target
|
|
370
|
+
codefast audit assertions --json # machine-readable summary
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Configure exceptions via `audit.assertions.allowlist` — each entry is the assertion as written or
|
|
374
|
+
`repo/relative/path.ts:<assertion>` — though the inline directive keeps the reason beside the code it excuses.
|
|
375
|
+
|
|
376
|
+
### `audit publish`
|
|
377
|
+
|
|
378
|
+
Reports what would break a consumer's install of a published package, while it can still be fixed:
|
|
379
|
+
|
|
380
|
+
- a `#/`-prefixed internal import, which Node's ESM resolver rejects on Node 24 before 24.14 while every in-repo runner
|
|
381
|
+
accepts it;
|
|
382
|
+
- an `exports`/`imports` target the slimmed manifest does not ship, found by applying the same slim as `pack-slim`;
|
|
383
|
+
- a shipped stylesheet whose Tailwind `@source` paths reach none of the files the tarball ships. A workspace still
|
|
384
|
+
resolves them against `src`, so only the published layout shows it: the consumer's Tailwind registers no class names
|
|
385
|
+
and the components render unstyled.
|
|
386
|
+
|
|
387
|
+
It reads the built output, so run it after the build; a `files` entry missing on disk, such as an unbuilt `dist`, is
|
|
388
|
+
named in the report. Private packages are skipped, and it takes no allowlist.
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
codefast audit publish # every published package in the workspace
|
|
392
|
+
codefast audit publish --json # machine-readable summary
|
|
393
|
+
```
|
|
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
|
+
|
|
335
430
|
### `audit comments`
|
|
336
431
|
|
|
337
432
|
_House style._ Checks doc-comment conventions. Section dividers not in the one allowed form are mechanical, so `--fix`
|
|
@@ -500,7 +595,13 @@ export default {
|
|
|
500
595
|
links: { allowlist: [] }, // bare link target, or `repo/relative/doc.md:target`
|
|
501
596
|
comments: { allowlist: [] }, // divider as written, or `repo/relative/path.ts:<divider>`
|
|
502
597
|
imports: { allowlist: [] }, // offending import text as written, or `repo/relative/path.tsx:<text>`
|
|
598
|
+
assertions: { allowlist: [] }, // assertion as written, or `repo/relative/path.ts:<assertion>`
|
|
503
599
|
displayNames: { allowlist: [] }, // call as written, or `repo/relative/path.ts:<call>`
|
|
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>`
|
|
604
|
+
},
|
|
504
605
|
},
|
|
505
606
|
};
|
|
506
607
|
```
|
|
@@ -551,7 +652,10 @@ pnpm run cli:audit:links # codefast audit links
|
|
|
551
652
|
pnpm run cli:audit:rtl # codefast audit rtl
|
|
552
653
|
pnpm run cli:audit:comments # codefast audit comments
|
|
553
654
|
pnpm run cli:audit:imports # codefast audit imports
|
|
655
|
+
pnpm run cli:audit:assertions # codefast audit assertions
|
|
554
656
|
pnpm run cli:audit:display-names # codefast audit display-names
|
|
657
|
+
pnpm run cli:audit:layers # codefast audit layers
|
|
658
|
+
pnpm run cli:audit:publish # codefast audit publish
|
|
555
659
|
```
|
|
556
660
|
|
|
557
661
|
`pnpm run version-packages` runs `changeset version` and then `codefast tag`, so published APIs are stamped at release,
|
|
@@ -4,9 +4,26 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { parseSync } from "oxc-parser";
|
|
6
6
|
import { DomainBinaryOperator, DomainSyntaxKind } from "#arrange/domain/ast/ast-node";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import { isOxcNode, programStatements } from "#core/oxc-node";
|
|
8
|
+
/**
|
|
9
|
+
* Stands in for a child while its parent is built first, so the child has a parent to point at; the
|
|
10
|
+
* build replaces it before the node leaves the translator.
|
|
11
|
+
*/
|
|
12
|
+
const PENDING_NODE = Object.freeze({
|
|
13
|
+
kind: DomainSyntaxKind.Unknown,
|
|
14
|
+
pos: -1,
|
|
15
|
+
end: -1,
|
|
16
|
+
parent: null,
|
|
17
|
+
children: [],
|
|
18
|
+
});
|
|
19
|
+
/** The same stand-in for a child typed as an identifier. */
|
|
20
|
+
const PENDING_IDENTIFIER = Object.freeze({
|
|
21
|
+
kind: DomainSyntaxKind.Identifier,
|
|
22
|
+
pos: -1,
|
|
23
|
+
end: -1,
|
|
24
|
+
parent: null,
|
|
25
|
+
text: "",
|
|
26
|
+
});
|
|
10
27
|
function nodeName(node) {
|
|
11
28
|
return typeof node.name === "string" ? node.name : "";
|
|
12
29
|
}
|
|
@@ -87,15 +104,13 @@ export class TypeScriptAstTranslator {
|
|
|
87
104
|
end: node.end,
|
|
88
105
|
parent,
|
|
89
106
|
importClause: undefined,
|
|
90
|
-
moduleSpecifier:
|
|
107
|
+
moduleSpecifier: PENDING_NODE,
|
|
91
108
|
};
|
|
92
109
|
if (specifiers.length > 0) {
|
|
93
110
|
self.importClause = this.buildImportClause(node, specifiers, self);
|
|
94
111
|
}
|
|
95
112
|
const source = isOxcNode(node.source) ? node.source : undefined;
|
|
96
|
-
self.moduleSpecifier = source
|
|
97
|
-
? this.translateNode(source, self)
|
|
98
|
-
: this.translateUnknown(node, self);
|
|
113
|
+
self.moduleSpecifier = source ? this.translateNode(source, self) : this.translateUnknown(node, self);
|
|
99
114
|
return self;
|
|
100
115
|
}
|
|
101
116
|
buildImportClause(declaration, specifiers, parent) {
|
|
@@ -131,7 +146,7 @@ export class TypeScriptAstTranslator {
|
|
|
131
146
|
pos: specifier.start,
|
|
132
147
|
end: specifier.end,
|
|
133
148
|
parent,
|
|
134
|
-
name:
|
|
149
|
+
name: PENDING_IDENTIFIER,
|
|
135
150
|
};
|
|
136
151
|
self.name = this.translateIdentifier(local, self);
|
|
137
152
|
return self;
|
|
@@ -158,7 +173,7 @@ export class TypeScriptAstTranslator {
|
|
|
158
173
|
end: specifier.end,
|
|
159
174
|
parent,
|
|
160
175
|
propertyName: undefined,
|
|
161
|
-
name:
|
|
176
|
+
name: PENDING_IDENTIFIER,
|
|
162
177
|
};
|
|
163
178
|
if (imported && imported.type === "Identifier" && nodeName(imported) !== nodeName(local)) {
|
|
164
179
|
self.propertyName = this.translateIdentifier(imported, self);
|
|
@@ -220,7 +235,7 @@ export class TypeScriptAstTranslator {
|
|
|
220
235
|
pos,
|
|
221
236
|
end,
|
|
222
237
|
parent,
|
|
223
|
-
expression:
|
|
238
|
+
expression: PENDING_NODE,
|
|
224
239
|
arguments: [],
|
|
225
240
|
};
|
|
226
241
|
self.expression = this.translateNode(callee, self);
|
|
@@ -238,8 +253,8 @@ export class TypeScriptAstTranslator {
|
|
|
238
253
|
pos,
|
|
239
254
|
end,
|
|
240
255
|
parent,
|
|
241
|
-
expression:
|
|
242
|
-
name:
|
|
256
|
+
expression: PENDING_NODE,
|
|
257
|
+
name: PENDING_IDENTIFIER,
|
|
243
258
|
};
|
|
244
259
|
self.expression = this.translateNode(object, self);
|
|
245
260
|
self.name = this.translateNode(property, self);
|
|
@@ -273,8 +288,8 @@ export class TypeScriptAstTranslator {
|
|
|
273
288
|
pos,
|
|
274
289
|
end,
|
|
275
290
|
parent,
|
|
276
|
-
name:
|
|
277
|
-
initializer:
|
|
291
|
+
name: PENDING_NODE,
|
|
292
|
+
initializer: PENDING_NODE,
|
|
278
293
|
};
|
|
279
294
|
self.name = this.translateNode(key, self);
|
|
280
295
|
self.initializer = this.translateNode(value, self);
|
|
@@ -305,7 +320,7 @@ export class TypeScriptAstTranslator {
|
|
|
305
320
|
pos,
|
|
306
321
|
end,
|
|
307
322
|
parent,
|
|
308
|
-
expression:
|
|
323
|
+
expression: PENDING_NODE,
|
|
309
324
|
};
|
|
310
325
|
self.expression = this.translateNode(argument, self);
|
|
311
326
|
return self;
|
|
@@ -317,7 +332,7 @@ export class TypeScriptAstTranslator {
|
|
|
317
332
|
pos,
|
|
318
333
|
end,
|
|
319
334
|
parent,
|
|
320
|
-
expression:
|
|
335
|
+
expression: PENDING_NODE,
|
|
321
336
|
};
|
|
322
337
|
self.expression = this.translateNode(expression, self);
|
|
323
338
|
return self;
|
|
@@ -329,7 +344,7 @@ export class TypeScriptAstTranslator {
|
|
|
329
344
|
pos,
|
|
330
345
|
end,
|
|
331
346
|
parent,
|
|
332
|
-
expression:
|
|
347
|
+
expression: PENDING_NODE,
|
|
333
348
|
};
|
|
334
349
|
self.expression = this.translateNode(expression, self);
|
|
335
350
|
return self;
|
|
@@ -341,7 +356,7 @@ export class TypeScriptAstTranslator {
|
|
|
341
356
|
pos,
|
|
342
357
|
end,
|
|
343
358
|
parent,
|
|
344
|
-
expression:
|
|
359
|
+
expression: PENDING_NODE,
|
|
345
360
|
};
|
|
346
361
|
self.expression = this.translateNode(expression, self);
|
|
347
362
|
return self;
|
|
@@ -353,7 +368,7 @@ export class TypeScriptAstTranslator {
|
|
|
353
368
|
pos,
|
|
354
369
|
end,
|
|
355
370
|
parent,
|
|
356
|
-
expression:
|
|
371
|
+
expression: PENDING_NODE,
|
|
357
372
|
};
|
|
358
373
|
self.expression = this.translateNode(expression, self);
|
|
359
374
|
return self;
|
|
@@ -367,9 +382,9 @@ export class TypeScriptAstTranslator {
|
|
|
367
382
|
pos,
|
|
368
383
|
end,
|
|
369
384
|
parent,
|
|
370
|
-
condition:
|
|
371
|
-
whenTrue:
|
|
372
|
-
whenFalse:
|
|
385
|
+
condition: PENDING_NODE,
|
|
386
|
+
whenTrue: PENDING_NODE,
|
|
387
|
+
whenFalse: PENDING_NODE,
|
|
373
388
|
};
|
|
374
389
|
self.condition = this.translateNode(test, self);
|
|
375
390
|
self.whenTrue = this.translateNode(consequent, self);
|
|
@@ -384,9 +399,9 @@ export class TypeScriptAstTranslator {
|
|
|
384
399
|
pos,
|
|
385
400
|
end,
|
|
386
401
|
parent,
|
|
387
|
-
left:
|
|
402
|
+
left: PENDING_NODE,
|
|
388
403
|
operator: this.mapBinaryOperator(node.operator),
|
|
389
|
-
right:
|
|
404
|
+
right: PENDING_NODE,
|
|
390
405
|
};
|
|
391
406
|
self.left = this.translateNode(left, self);
|
|
392
407
|
self.right = this.translateNode(right, self);
|
|
@@ -399,7 +414,7 @@ export class TypeScriptAstTranslator {
|
|
|
399
414
|
pos,
|
|
400
415
|
end,
|
|
401
416
|
parent,
|
|
402
|
-
expression:
|
|
417
|
+
expression: PENDING_NODE,
|
|
403
418
|
};
|
|
404
419
|
self.expression = this.translateNode(expression, self);
|
|
405
420
|
return self;
|
|
@@ -412,7 +427,7 @@ export class TypeScriptAstTranslator {
|
|
|
412
427
|
pos,
|
|
413
428
|
end,
|
|
414
429
|
parent,
|
|
415
|
-
name:
|
|
430
|
+
name: PENDING_NODE,
|
|
416
431
|
initializer: undefined,
|
|
417
432
|
};
|
|
418
433
|
self.name = this.translateNode(name, self);
|
|
@@ -442,8 +457,7 @@ export class TypeScriptAstTranslator {
|
|
|
442
457
|
parseDomainSourceFile(filePath, sourceText) {
|
|
443
458
|
// Best-effort parse to mirror `ts.createSourceFile` leniency — recoverable syntax
|
|
444
459
|
// errors still yield a usable tree, so `parseSync` errors are intentionally ignored.
|
|
445
|
-
const
|
|
446
|
-
const statements = program.body.map((statement) => this.translateNode(statement, null));
|
|
460
|
+
const statements = programStatements(parseSync(filePath, sourceText).program).map((statement) => this.translateNode(statement, null));
|
|
447
461
|
return {
|
|
448
462
|
fileName: filePath,
|
|
449
463
|
text: sourceText,
|
|
@@ -6,7 +6,7 @@ import type { Filesystem } from "#core/filesystem/filesystem";
|
|
|
6
6
|
*
|
|
7
7
|
* @since 0.3.16-canary.0
|
|
8
8
|
*/
|
|
9
|
-
export declare function processArrangeSimplifyFile(fs: Filesystem, args: {
|
|
9
|
+
export declare function processArrangeSimplifyFile(fs: Pick<Filesystem, "readFileSync" | "writeFileSync">, args: {
|
|
10
10
|
readonly filePath: string;
|
|
11
11
|
readonly write: boolean;
|
|
12
12
|
readonly probe?: VariantClassNameProbe | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AssertionAuditResult } from "#audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted type-assertion violation remains.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.13.0
|
|
6
|
+
*/
|
|
7
|
+
export declare function exitCodeForAssertionAuditResult(result: AssertionAuditResult): number;
|
|
8
|
+
/**
|
|
9
|
+
* Machine-readable type-assertion summary for `--json`.
|
|
10
|
+
*
|
|
11
|
+
* @since 0.13.0
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatAssertionAuditJsonOutput(result: AssertionAuditResult, rootDir: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CLI_EXIT_GENERAL_ERROR, CLI_EXIT_SUCCESS } from "#core/exit-codes";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted type-assertion violation remains.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.13.0
|
|
6
|
+
*/
|
|
7
|
+
export function exitCodeForAssertionAuditResult(result) {
|
|
8
|
+
return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Machine-readable type-assertion summary for `--json`.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.13.0
|
|
14
|
+
*/
|
|
15
|
+
export function formatAssertionAuditJsonOutput(result, rootDir) {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
schemaVersion: 1,
|
|
18
|
+
ok: result.violationCount === 0,
|
|
19
|
+
cwd: rootDir,
|
|
20
|
+
result,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Resolved request for a single type-assertion audit run.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.13.0
|
|
6
|
+
*/
|
|
7
|
+
export type AssertionAuditRunRequest = {
|
|
8
|
+
readonly rootDir: string;
|
|
9
|
+
readonly targetPath: string;
|
|
10
|
+
readonly allowlist?: ReadonlyArray<string> | undefined;
|
|
11
|
+
readonly json: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Zod schema for {@link AssertionAuditRunRequest}.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.13.0
|
|
17
|
+
*/
|
|
18
|
+
export declare const assertionAuditRunRequestSchema: z.ZodType<AssertionAuditRunRequest>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for {@link AssertionAuditRunRequest}.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.13.0
|
|
6
|
+
*/
|
|
7
|
+
export const assertionAuditRunRequestSchema = 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
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AssertionViolation } from "#audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* The comment that keeps one double assertion, with the reason it has to stay.
|
|
4
|
+
*
|
|
5
|
+
* @remarks It covers an assertion on its own line or the line below it, and must carry a reason
|
|
6
|
+
* after the colon; one that covers nothing is reported, so a kept assertion cannot outlive its cause.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.13.0
|
|
9
|
+
*/
|
|
10
|
+
export declare const DOUBLE_ASSERTION_DIRECTIVE = "codefast-allow-double-assertion";
|
|
11
|
+
/**
|
|
12
|
+
* Scans one TypeScript source for double assertions through `unknown` or `any`, and for directives
|
|
13
|
+
* that keep none or give no reason.
|
|
14
|
+
*
|
|
15
|
+
* @remarks `x as unknown as T`, `(x as unknown) as T` and `<T><unknown>x` are one shape: the
|
|
16
|
+
* compiler found the two types unrelated, and the pair silences it instead of reconciling them.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.13.0
|
|
19
|
+
*/
|
|
20
|
+
export declare function auditDoubleAssertionSource(filePath: string, sourceText: string): Array<AssertionViolation>;
|