archprint 0.4.1 → 0.6.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 +71 -0
- package/README.md +74 -51
- package/dist/cli/adoption-readme.d.ts +7 -0
- package/dist/cli/adoption-readme.d.ts.map +1 -0
- package/dist/cli/adoption-readme.js +76 -0
- package/dist/cli/adoption-readme.js.map +1 -0
- package/dist/cli/archprint-config.d.ts +33 -0
- package/dist/cli/archprint-config.d.ts.map +1 -0
- package/dist/cli/archprint-config.js +83 -0
- package/dist/cli/archprint-config.js.map +1 -0
- package/dist/cli/generate.d.ts +44 -37
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +167 -258
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/ignore-file.d.ts +8 -0
- package/dist/cli/ignore-file.d.ts.map +1 -0
- package/dist/cli/ignore-file.js +37 -0
- package/dist/cli/ignore-file.js.map +1 -0
- package/dist/cli/layout.d.ts +24 -0
- package/dist/cli/layout.d.ts.map +1 -0
- package/dist/cli/layout.js +74 -0
- package/dist/cli/layout.js.map +1 -0
- package/dist/cli/migrate.d.ts +27 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +177 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli/outputs-manifest.d.ts +0 -6
- package/dist/cli/outputs-manifest.d.ts.map +1 -1
- package/dist/cli/outputs-manifest.js +2 -29
- package/dist/cli/outputs-manifest.js.map +1 -1
- package/dist/cli/program.d.ts +1 -0
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +237 -51
- package/dist/cli/program.js.map +1 -1
- package/dist/cli/recommend.d.ts +5 -1
- package/dist/cli/recommend.d.ts.map +1 -1
- package/dist/cli/recommend.js +42 -2
- package/dist/cli/recommend.js.map +1 -1
- package/dist/cli/report.d.ts +4 -2
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +31 -5
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/wiring.d.ts +2 -13
- package/dist/cli/wiring.d.ts.map +1 -1
- package/dist/cli/wiring.js +63 -110
- package/dist/cli/wiring.js.map +1 -1
- package/dist/detector/family-maturity.d.ts.map +1 -1
- package/dist/detector/family-maturity.js +0 -2
- package/dist/detector/family-maturity.js.map +1 -1
- package/dist/generator/console-isolation-emitters.d.ts +3 -0
- package/dist/generator/console-isolation-emitters.d.ts.map +1 -1
- package/dist/generator/console-isolation-emitters.js +5 -1
- package/dist/generator/console-isolation-emitters.js.map +1 -1
- package/dist/generator/deep-relative-emitters.d.ts +4 -0
- package/dist/generator/deep-relative-emitters.d.ts.map +1 -1
- package/dist/generator/deep-relative-emitters.js +4 -0
- package/dist/generator/deep-relative-emitters.js.map +1 -1
- package/dist/generator/depcruise-exempt.d.ts +5 -0
- package/dist/generator/depcruise-exempt.d.ts.map +1 -0
- package/dist/generator/depcruise-exempt.js +8 -0
- package/dist/generator/depcruise-exempt.js.map +1 -0
- package/dist/generator/dependency-internals-emitters.d.ts +1 -1
- package/dist/generator/dependency-internals-emitters.d.ts.map +1 -1
- package/dist/generator/dependency-internals-emitters.js +5 -2
- package/dist/generator/dependency-internals-emitters.js.map +1 -1
- package/dist/generator/env-access-emitters.d.ts +1 -1
- package/dist/generator/env-access-emitters.d.ts.map +1 -1
- package/dist/generator/env-access-emitters.js +3 -1
- package/dist/generator/env-access-emitters.js.map +1 -1
- package/dist/generator/eslint-preset-emitter.d.ts +4 -1
- package/dist/generator/eslint-preset-emitter.d.ts.map +1 -1
- package/dist/generator/eslint-preset-emitter.js +6 -4
- package/dist/generator/eslint-preset-emitter.js.map +1 -1
- package/dist/generator/eslint-scope.d.ts +10 -0
- package/dist/generator/eslint-scope.d.ts.map +1 -0
- package/dist/generator/eslint-scope.js +37 -0
- package/dist/generator/eslint-scope.js.map +1 -0
- package/dist/generator/phantom-dependency-emitters.d.ts +1 -1
- package/dist/generator/phantom-dependency-emitters.d.ts.map +1 -1
- package/dist/generator/phantom-dependency-emitters.js +4 -1
- package/dist/generator/phantom-dependency-emitters.js.map +1 -1
- package/dist/generator/public-api-emitters.d.ts +1 -1
- package/dist/generator/public-api-emitters.d.ts.map +1 -1
- package/dist/generator/public-api-emitters.js +2 -1
- package/dist/generator/public-api-emitters.js.map +1 -1
- package/dist/generator/test-isolation-emitters.d.ts +3 -1
- package/dist/generator/test-isolation-emitters.d.ts.map +1 -1
- package/dist/generator/test-isolation-emitters.js +26 -2
- package/dist/generator/test-isolation-emitters.js.map +1 -1
- package/dist/generator/workspace-package-emitters.d.ts +1 -1
- package/dist/generator/workspace-package-emitters.d.ts.map +1 -1
- package/dist/generator/workspace-package-emitters.js +2 -0
- package/dist/generator/workspace-package-emitters.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/scanner/enforcers.d.ts +8 -0
- package/dist/scanner/enforcers.d.ts.map +1 -0
- package/dist/scanner/enforcers.js +74 -0
- package/dist/scanner/enforcers.js.map +1 -0
- package/package.json +8 -8
- package/dist/cli/init.d.ts +0 -21
- package/dist/cli/init.d.ts.map +0 -1
- package/dist/cli/init.js +0 -19
- package/dist/cli/init.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# archprint
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Archprint now writes a minimal `.archprint/` directory instead of a folder of per-family files. `generate` and `init` emit one self-contained rules file per linter you actually use (`.archprint/eslint.mjs` and/or `.archprint/dependency-cruiser.json`) plus a `.archprint/config.json` that records what is enforced and what Archprint manages. The plain-language summary that used to be `ADOPTION.md` is now a managed section in your `README.md`, and a managed `.prettierignore` entry keeps the generated files out of your formatter.
|
|
8
|
+
|
|
9
|
+
- `--expand` writes the granular artifacts (per-family configs, per-rule cards and fixtures, the eslint-plugin, ts-arch tests, and the layer graph) inside `.archprint/` for anyone who wants to inspect or vendor individual rules.
|
|
10
|
+
- New `archprint migrate` (alias `upgrade`) moves an older `archprint-rules/` setup to the new layout and rewrites the reference in your linter config in place, writing the new files before removing the old ones so the config never points at a deleted file. `generate` and `init` stop and point you to it if they detect the old layout.
|
|
11
|
+
- `wire` imports the single rules file with an ESM-safe relative specifier, and `eject` removes the files, the config, the managed README section, and every wired reference.
|
|
12
|
+
|
|
13
|
+
## 0.5.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- archprint now generates rules for the enforcers your repo actually uses. It detects whether you have
|
|
18
|
+
ESLint or dependency-cruiser and emits each rule for a tool you already run, instead of writing
|
|
19
|
+
dependency-cruiser configs you have no way to enforce. On an ESLint-only repo it expresses test
|
|
20
|
+
isolation as an ESLint `no-restricted-imports` rule; dependency-cruiser configs are only written when
|
|
21
|
+
dependency-cruiser is present. `archprint recommend` now names, per rule, the installed tool that will
|
|
22
|
+
enforce it (or what you would need to install).
|
|
23
|
+
- `archprint generate --readme` writes an `ADOPTION.md` in the output directory summarizing what is
|
|
24
|
+
enforced now, held for review, and worth adopting, each with the tool that enforces it. It is tracked
|
|
25
|
+
in the manifest, so `archprint eject` removes it too.
|
|
26
|
+
- `archprint generate` gains two output controls: `--emit <eslint|dependency-cruiser|all>` forces the
|
|
27
|
+
output format regardless of the tooling archprint detects, and `--no-graph` skips the layer dependency
|
|
28
|
+
graph (Mermaid and Graphviz).
|
|
29
|
+
- `archprint generate` gains finer output control: `--only <family>` emits a single rule family,
|
|
30
|
+
`--rules <ids>` emits only the named forbidden-import rule ids, and `--check` runs the generated
|
|
31
|
+
ESLint rules against your repo and reports whether they pass, so you can confirm before wiring.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Generated rules are now green-by-construction: a rule archprint says you "already follow" passes clean
|
|
36
|
+
when you wire it, instead of flagging code that was there all along. Emitted rules are scoped to exactly
|
|
37
|
+
the files the detector measured (test files and cli/config files are excluded, matching what the analysis
|
|
38
|
+
skips) and exempt the specific exceptions the confidence gate accepted. This covers the console,
|
|
39
|
+
environment-access, deep-relative-import, workspace-package, test-isolation, phantom-dependency, and
|
|
40
|
+
dependency-internals rules across both ESLint and dependency-cruiser. The ESLint import rules
|
|
41
|
+
(deep-relative, workspace, test) are also emitted as a single merged block so they no longer override one
|
|
42
|
+
another in flat config.
|
|
43
|
+
- More fixes so an "enforce now" rule cannot flag code it was mined from:
|
|
44
|
+
|
|
45
|
+
- The dependency-cruiser test-isolation, phantom-deps, no-internals, and public-API deep-import rules now
|
|
46
|
+
exclude test files (`__tests__`, `e2e`, `cypress`, `.test`/`.spec`/`e2e` suffixes), matching what the
|
|
47
|
+
analysis excludes.
|
|
48
|
+
- The public-API deep-import rule also exempts the tolerated deep importers the confidence gate already
|
|
49
|
+
accepted, so an "enforce now" rule cannot flag code it was mined from.
|
|
50
|
+
- no-internals matches only the internals directories the analysis measures (`src`, `internal`,
|
|
51
|
+
`internals`), not a package's normal `dist`/`lib` entry.
|
|
52
|
+
- no-internals and phantom-deps are now held for review (emitted with `--include-structural`) rather than
|
|
53
|
+
auto-enforced, because their rules match resolved paths or dependency types and can flag a package whose
|
|
54
|
+
public entry resolves through `src/`, or a workspace package with no local `package.json` entry.
|
|
55
|
+
- Phantom (undeclared) dependency checking is emitted only for dependency-cruiser; the ESLint form is
|
|
56
|
+
dropped because it flags test files and first-party path aliases the analysis excludes.
|
|
57
|
+
- `wire` only splices into a recognized flat-config array or factory (`defineConfig`, `tseslint.config`);
|
|
58
|
+
an unrecognized call such as `export default loadConfig()` bails instead of reporting a false success.
|
|
59
|
+
- `generate --check` reports files it could not parse instead of calling them clean.
|
|
60
|
+
|
|
61
|
+
- - `wire` parses your ESLint config with the TypeScript AST, so it reliably edits the common
|
|
62
|
+
`const config = [...]; export default config;` shape and is not fooled by `export default` appearing
|
|
63
|
+
inside a string or comment.
|
|
64
|
+
- archprint no longer writes rule files for a tool your repo does not use; pass `--emit all` to force
|
|
65
|
+
every format.
|
|
66
|
+
- The wired ESLint config and the shareable preset ignore archprint's own generated files, so linting
|
|
67
|
+
your repo never flags them.
|
|
68
|
+
- `archprint generate --check` reports only violations of the rules archprint generated.
|
|
69
|
+
- `archprint wire` now edits the common config shape where the ESLint flat config is bound to a name
|
|
70
|
+
and exported by reference (`const config = [...]; export default config;`, the default for Next.js and
|
|
71
|
+
many TypeScript projects) instead of falling back to a manual edit. It resolves the exported name to
|
|
72
|
+
its declaration, including a `defineConfig([...])` wrapper, and `archprint eject` reverses it as before.
|
|
73
|
+
|
|
3
74
|
## 0.4.1
|
|
4
75
|
|
|
5
76
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -17,25 +17,27 @@ Your `CLAUDE.md` is guidance. Your lint rules are enforcement. Archprint closes
|
|
|
17
17
|
enforcement from patterns your codebase already demonstrates, so you adopt rules you can trust instead of
|
|
18
18
|
authoring them by hand.
|
|
19
19
|
|
|
20
|
-
Validated at scale:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
Validated at scale: `scan` and `recommend` ran across all 92,861 real public TypeScript repositories with zero
|
|
21
|
+
crashes, and the full `init`/`wire`/`eject` round-trip ran clean on a 2,000-repo stratified sample. A companion
|
|
22
|
+
benchmark, [AgentRuleBench](https://github.com/Tommkruix/agentrulebench), measures the guidance-vs-enforcement
|
|
23
|
+
question directly (a pre-registered, honest null result on the boundary it tested).
|
|
24
24
|
|
|
25
25
|
**What auto-enforces vs. what you review.** Archprint is honest about which of its inferences it will stand
|
|
26
26
|
behind unattended. An adversarial correctness audit (three rounds over four real repositories) found that the
|
|
27
27
|
_mechanical_ families, ones grounded in unambiguous signals (no cycles, production must not import tests, no
|
|
28
|
-
`console` in library code, no undeclared dependencies, deep-relative import style, public-API barrels,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
>
|
|
37
|
-
>
|
|
38
|
-
> structural families
|
|
28
|
+
`console` in library code, no undeclared dependencies, deep-relative import style, public-API barrels, and the
|
|
29
|
+
DB/UI-in-server-entry rule), had zero false positives every round. So those auto-generate as enforcement. The
|
|
30
|
+
families held for human review by default, emitted only with `--include-structural`, are the ones that infer a
|
|
31
|
+
"layer" or "role" from paths, which can be wrong (layer and role boundaries, UI/data separation, entry purity,
|
|
32
|
+
server/client, feature-slice and app isolation), plus dependency hygiene, whose enforcement can over-flag.
|
|
33
|
+
Nothing that could be wrong is written as enforcement without you opting in.
|
|
34
|
+
|
|
35
|
+
> Status: published on npm and safe to run on your real repo. Every rule is review-gated by default,
|
|
36
|
+
> reversible in one command (`archprint eject`), and deterministic, and a rule archprint marks
|
|
37
|
+
> "enforce now" is checked to pass on your code before it says so. `scan` and `recommend` are stable;
|
|
38
|
+
> the structural families stay review-only while they are hardened. Versioning is still 0.x, so the CLI
|
|
39
|
+
> surface and rule format can refine between minor versions (the 0.6.0 release moved output to the
|
|
40
|
+
> `.archprint/` layout; run `archprint migrate` to upgrade), but the analysis is not experimental.
|
|
39
41
|
|
|
40
42
|
## What makes it different
|
|
41
43
|
|
|
@@ -74,15 +76,18 @@ package such as `apps/web`; a monorepo root is fine too, Archprint discovers the
|
|
|
74
76
|
|
|
75
77
|
```bash
|
|
76
78
|
# One-shot setup: detect the stack, enforce the rules your code already follows,
|
|
77
|
-
# and record what to adopt next in archprint.json
|
|
79
|
+
# and record what to adopt next in .archprint/config.json
|
|
78
80
|
archprint init apps/web
|
|
79
81
|
|
|
80
82
|
# See the rules your repo already follows, with the evidence
|
|
81
83
|
archprint scan apps/web
|
|
82
84
|
|
|
83
|
-
# Write the auto-trusted (mechanical) rules to
|
|
85
|
+
# Write the auto-trusted (mechanical) rules to .archprint/, only for the linters your repo uses.
|
|
84
86
|
# Structural-inference rules are held for review; add --include-structural to emit them too.
|
|
85
|
-
archprint generate apps/web
|
|
87
|
+
archprint generate apps/web
|
|
88
|
+
|
|
89
|
+
# Confirm the generated rules pass on your repo before wiring
|
|
90
|
+
archprint generate apps/web --check
|
|
86
91
|
|
|
87
92
|
# Inspect the gate evidence behind one rule
|
|
88
93
|
archprint explain AP-002 apps/web
|
|
@@ -98,9 +103,12 @@ archprint wire
|
|
|
98
103
|
|
|
99
104
|
# Remove archprint's files and any wired references (clean uninstall)
|
|
100
105
|
archprint eject
|
|
106
|
+
|
|
107
|
+
# Upgrading from 0.5.x? move an older archprint-rules/ setup to the .archprint layout
|
|
108
|
+
archprint migrate
|
|
101
109
|
```
|
|
102
110
|
|
|
103
|
-
Re-running `generate` (or `init`) refreshes the files in
|
|
111
|
+
Re-running `generate` (or `init`) refreshes the files in `.archprint/` and removes any rule the
|
|
104
112
|
evidence no longer supports, so the output never drifts from the current codebase. `wire` detects the
|
|
105
113
|
enforcement tools your repo already uses (a flat eslint config, a `.dependency-cruiser.json`) and inserts a
|
|
106
114
|
single managed reference into each, one that survives those regenerations; `eject` removes archprint's files
|
|
@@ -162,8 +170,8 @@ regardless of framework.
|
|
|
162
170
|
| Forbidden imports (marker based) | A role (route handler, server entry) must not import a target (the DB client, the UI layer) | Auto |
|
|
163
171
|
| Circular dependencies | The module graph should stay acyclic (gated on how cycle free it already is) | Auto |
|
|
164
172
|
| Test isolation | Production (non-test) code must not import test or spec files | Auto |
|
|
165
|
-
| Dependency hygiene | Import third-party packages by their public entry, not a dependency's `src`/`internal` internals |
|
|
166
|
-
| Dependency declaration | Every imported third-party package must be declared in `package.json` (no phantom/transitive deps) |
|
|
173
|
+
| Dependency hygiene | Import third-party packages by their public entry, not a dependency's `src`/`internal` internals | Review |
|
|
174
|
+
| Dependency declaration | Every imported third-party package must be declared in `package.json` (no phantom/transitive deps) | Review |
|
|
167
175
|
| Import style | Prefer workspace aliases over deep relative imports (`../../../`) | Auto |
|
|
168
176
|
| Console isolation | Library (non-CLI) code must not call `console.*` | Auto |
|
|
169
177
|
| Public API (barrel) boundaries | Files outside a feature or package must import it through its `index` barrel, not deep import its internals | Auto |
|
|
@@ -202,22 +210,32 @@ zero rules.
|
|
|
202
210
|
|
|
203
211
|
## Output formats
|
|
204
212
|
|
|
205
|
-
`archprint generate` writes
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
-
|
|
211
|
-
import-style boundaries
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
213
|
+
`archprint generate` writes a minimal `.archprint/` directory: one self-contained rules file per linter your
|
|
214
|
+
repo actually uses, plus a `config.json` that records what is enforced and what Archprint manages. It detects
|
|
215
|
+
ESLint and dependency-cruiser and emits each rule for a tool you already run, so you are not left with config
|
|
216
|
+
for a tool you do not have. `--emit <eslint|dependency-cruiser|all>` forces the format. The default output:
|
|
217
|
+
|
|
218
|
+
- **`.archprint/eslint.mjs`**: one self-contained ESLint flat-config file that inlines every inferred ESLint
|
|
219
|
+
rule (marker-based forbidden imports, `no-restricted-imports` import-style boundaries, console isolation) and
|
|
220
|
+
needs only eslint, so you can commit it, publish it, or hand it to another repo and adopt it in one line
|
|
221
|
+
(`import archprint from './.archprint/eslint.mjs'`). It self-ignores `**/.archprint/**`.
|
|
222
|
+
- **`.archprint/dependency-cruiser.json`** (when dependency-cruiser is present): one `forbidden` ruleset with
|
|
223
|
+
the mechanical boundaries (public-API deep-import, test-isolation); the review-held ones (layer, role-layering,
|
|
224
|
+
feature-slice, app-isolation, entry-purity, dependency-internals, phantom deps) are added only with
|
|
225
|
+
`--include-structural`, after you review them.
|
|
226
|
+
- **`.archprint/config.json`**: the system file, what is enforced / held / worth adopting, and the managed
|
|
227
|
+
outputs list `eject` uses.
|
|
228
|
+
- **A managed README section** summarizing what is enforced now, held for review, and worth adopting (written
|
|
229
|
+
by `init`, or `generate --readme`), plus a managed `.prettierignore` entry so the generated files stay out of
|
|
230
|
+
your formatter.
|
|
231
|
+
|
|
232
|
+
`--expand` additionally writes the granular artifacts inside `.archprint/`: the per-family ESLint and
|
|
233
|
+
dependency-cruiser JSON, per-rule cards (`.md`) with passing and failing fixtures, the
|
|
234
|
+
eslint-plugin-boundaries element-types config, ts-arch tests, and the Mermaid and Graphviz DOT layer graph.
|
|
235
|
+
|
|
236
|
+
`generate --check` runs the generated ESLint rules against your repo and reports whether they pass, so you
|
|
237
|
+
can confirm before wiring. Upgrading from 0.5.x? `archprint migrate` moves an older `archprint-rules/` setup to
|
|
238
|
+
this layout and rewires your configs in place.
|
|
221
239
|
|
|
222
240
|
## How it compares
|
|
223
241
|
|
|
@@ -241,15 +259,16 @@ orphans, reachability) and knip (dead code); rather than compete, it emits into
|
|
|
241
259
|
|
|
242
260
|
## Commands
|
|
243
261
|
|
|
244
|
-
| Command | What it does
|
|
245
|
-
| ------------------------------- |
|
|
246
|
-
| `archprint init [path]` | Zero-config setup: detect the stack, enforce the rules the code already follows, and write
|
|
247
|
-
| `archprint scan [path]` | Report the rules the repo already follows, with evidence. `--deep` resolves through barrels and aliases.
|
|
248
|
-
| `archprint generate [path]` | Write the auto-trusted mechanical rules
|
|
249
|
-
| `archprint
|
|
250
|
-
| `archprint
|
|
251
|
-
| `archprint
|
|
252
|
-
| `archprint
|
|
262
|
+
| Command | What it does |
|
|
263
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
264
|
+
| `archprint init [path]` | Zero-config setup: detect the stack, enforce the rules the code already follows, and write `.archprint/` plus a managed README section. `--expand`, `--include-structural`, `--out <dir>`, `--fast`, `--force`. |
|
|
265
|
+
| `archprint scan [path]` | Report the rules the repo already follows, with evidence. `--deep` resolves through barrels and aliases. |
|
|
266
|
+
| `archprint generate [path]` | Write the auto-trusted mechanical rules to `.archprint/` for the linters your repo uses; structural rules held for review. `--emit <eslint\|dependency-cruiser\|all>` forces the format, `--only <family>` and `--rules <ids>` narrow the output, `--check` runs the generated rules against your repo, `--readme` adds the README section, `--expand` also writes the per-family files/cards/fixtures/graph, `--rule <id>` emits one reviewed rule. Also `--include-structural`, `--no-graph`, `--out <dir>`, `--fast`. |
|
|
267
|
+
| `archprint migrate` (`upgrade`) | Move an older `archprint-rules/` setup to the `.archprint/` layout and rewire your configs in place. `--dry-run`. |
|
|
268
|
+
| `archprint explain <id> [path]` | Show the gate breakdown for one rule, with a codeframe per exception plus how-to-fix, when-not-to-use, and how-to-enforce. |
|
|
269
|
+
| `archprint recommend [path]` | Recommend a rule set from the repo's evidence and detected stack (works on a fresh repo too); names the installed tool that will enforce each rule. |
|
|
270
|
+
| `archprint wire` | Reference the generated rules from the enforcement tools your repo uses (flat eslint config, `.dependency-cruiser.json`) via a managed, reversible reference. `--out <dir>`, `--dry-run`. |
|
|
271
|
+
| `archprint eject` | Remove archprint's generated files, its config, the managed README section, and any wired references. `--out <dir>`, `--dry-run`. |
|
|
253
272
|
|
|
254
273
|
## Documentation
|
|
255
274
|
|
|
@@ -272,11 +291,15 @@ Same repo plus same version produces the same output. Analysis is pure and sorte
|
|
|
272
291
|
|
|
273
292
|
## Status and roadmap
|
|
274
293
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
294
|
+
Archprint is safe to adopt today: every rule is review-gated and reversible via `archprint eject`, the analysis
|
|
295
|
+
is deterministic, and `scan`/`recommend` are battle-tested at census scale. The engine (twenty detectors, the
|
|
296
|
+
confidence gate, and emitters for a self-contained ESLint file, dependency-cruiser, ts-arch, and the layer
|
|
297
|
+
graph) is in place and tested, and an adversarial correctness audit (three rounds, four real repositories) drove
|
|
298
|
+
the false-positive rate on auto-generated rules to zero for the mechanical families, which is why those
|
|
299
|
+
auto-enforce while the structural-inference families are held for review. Versioning is still 0.x, so the CLI
|
|
300
|
+
surface and rule format can refine between minor versions, that is a maturing surface, not experimental
|
|
301
|
+
analysis; the 0.6.0 release moved the output to the compact `.archprint/` layout, and `archprint migrate`
|
|
302
|
+
upgrades an older setup in place.
|
|
280
303
|
|
|
281
304
|
Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families,
|
|
282
305
|
with a self-consistency check at generate time, an `init` scaffolder for fresh repos, and framework coverage
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ReadmeResult {
|
|
2
|
+
status: 'created' | 'updated' | 'skipped';
|
|
3
|
+
reason?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function injectAdoptionSection(readmePath: string, body: string): ReadmeResult;
|
|
6
|
+
export declare function stripAdoptionSection(readmePath: string, createdByArchprint: boolean): ReadmeResult;
|
|
7
|
+
//# sourceMappingURL=adoption-readme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adoption-readme.d.ts","sourceRoot":"","sources":["../../src/cli/adoption-readme.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsBD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CA6BpF;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,OAAO,GAC1B,YAAY,CAwBd"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
const START = '<!-- archprint:start -->';
|
|
3
|
+
const END = '<!-- archprint:end -->';
|
|
4
|
+
function detectLayout(content) {
|
|
5
|
+
const bom = content.startsWith('') ? '' : '';
|
|
6
|
+
const eol = /\r\n/.test(content) ? '\r\n' : '\n';
|
|
7
|
+
return { eol, bom };
|
|
8
|
+
}
|
|
9
|
+
function block(body, eol) {
|
|
10
|
+
const lines = [
|
|
11
|
+
'## Architecture rules (managed by archprint, do not edit between the markers)',
|
|
12
|
+
'',
|
|
13
|
+
body.trim(),
|
|
14
|
+
];
|
|
15
|
+
return [START, ...lines, END].join(eol);
|
|
16
|
+
}
|
|
17
|
+
export function injectAdoptionSection(readmePath, body) {
|
|
18
|
+
try {
|
|
19
|
+
if (!existsSync(readmePath)) {
|
|
20
|
+
writeFileSync(readmePath, `${block(body, '\n')}\n`);
|
|
21
|
+
return { status: 'created' };
|
|
22
|
+
}
|
|
23
|
+
const raw = readFileSync(readmePath, 'utf8');
|
|
24
|
+
const { eol, bom } = detectLayout(raw);
|
|
25
|
+
const content = bom ? raw.slice(bom.length) : raw;
|
|
26
|
+
const start = content.indexOf(START);
|
|
27
|
+
const end = content.indexOf(END);
|
|
28
|
+
if ((start !== -1) !== (end !== -1) || (start !== -1 && end < start)) {
|
|
29
|
+
return { status: 'skipped', reason: 'unpaired archprint markers in README; left untouched' };
|
|
30
|
+
}
|
|
31
|
+
const normalized = content.replace(/\r\n/g, '\n');
|
|
32
|
+
const fresh = block(body, '\n');
|
|
33
|
+
let next;
|
|
34
|
+
if (start !== -1) {
|
|
35
|
+
const s = normalized.indexOf(START);
|
|
36
|
+
const e = normalized.indexOf(END) + END.length;
|
|
37
|
+
next = normalized.slice(0, s) + fresh + normalized.slice(e);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
next = `${normalized.replace(/\n+$/, '')}\n\n${fresh}\n`;
|
|
41
|
+
}
|
|
42
|
+
writeFileSync(readmePath, bom + (eol === '\r\n' ? next.replace(/\n/g, '\r\n') : next));
|
|
43
|
+
return { status: 'updated' };
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return { status: 'skipped', reason: `could not write README (${error.message})` };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function stripAdoptionSection(readmePath, createdByArchprint) {
|
|
50
|
+
try {
|
|
51
|
+
if (!existsSync(readmePath))
|
|
52
|
+
return { status: 'skipped', reason: 'no README' };
|
|
53
|
+
const raw = readFileSync(readmePath, 'utf8');
|
|
54
|
+
const { eol, bom } = detectLayout(raw);
|
|
55
|
+
const content = (bom ? raw.slice(bom.length) : raw).replace(/\r\n/g, '\n');
|
|
56
|
+
const start = content.indexOf(START);
|
|
57
|
+
const end = content.indexOf(END);
|
|
58
|
+
if (start === -1 || end === -1 || end < start) {
|
|
59
|
+
return { status: 'skipped', reason: 'no archprint section' };
|
|
60
|
+
}
|
|
61
|
+
const head = content.slice(0, start).replace(/\n+$/, '');
|
|
62
|
+
const tail = content.slice(end + END.length).replace(/^\n+/, '');
|
|
63
|
+
const without = head === '' ? tail : tail === '' ? head : `${head}\n\n${tail}`;
|
|
64
|
+
if (createdByArchprint && without.trim() === '') {
|
|
65
|
+
rmSync(readmePath, { force: true });
|
|
66
|
+
return { status: 'updated', reason: 'removed archprint-created README' };
|
|
67
|
+
}
|
|
68
|
+
const out = without.trim() === '' ? '' : `${without.replace(/\n+$/, '')}\n`;
|
|
69
|
+
writeFileSync(readmePath, bom + (eol === '\r\n' ? out.replace(/\n/g, '\r\n') : out));
|
|
70
|
+
return { status: 'updated' };
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return { status: 'skipped', reason: `could not update README (${error.message})` };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=adoption-readme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adoption-readme.js","sourceRoot":"","sources":["../../src/cli/adoption-readme.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Recommendation, Recommendations } from './recommend.js';
|
|
2
|
+
export declare const CONFIG_FILE = "config.json";
|
|
3
|
+
export interface ManagedOutputs {
|
|
4
|
+
files: string[];
|
|
5
|
+
readme: boolean;
|
|
6
|
+
readmeCreated: boolean;
|
|
7
|
+
prettierignore: boolean;
|
|
8
|
+
prettierignoreCreated: boolean;
|
|
9
|
+
npmignore: boolean;
|
|
10
|
+
npmignoreCreated: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ArchprintConfig {
|
|
13
|
+
archprintVersion: string;
|
|
14
|
+
app: string;
|
|
15
|
+
stack: string[];
|
|
16
|
+
rulesDir: string;
|
|
17
|
+
enforced: Recommendation[];
|
|
18
|
+
review: Recommendation[];
|
|
19
|
+
adopt: Recommendation[];
|
|
20
|
+
evidence: {
|
|
21
|
+
apps: number;
|
|
22
|
+
asOf: string;
|
|
23
|
+
};
|
|
24
|
+
managed: ManagedOutputs;
|
|
25
|
+
}
|
|
26
|
+
export declare function buildConfig(recommendations: Recommendations, version: string, location: {
|
|
27
|
+
app: string;
|
|
28
|
+
rulesDir: string;
|
|
29
|
+
}, managed: ManagedOutputs): ArchprintConfig;
|
|
30
|
+
export declare function writeConfig(outDir: string, config: ArchprintConfig): string;
|
|
31
|
+
export declare function recordManagedFiles(outDir: string, cwd: string, version: string, absoluteFiles: readonly string[]): void;
|
|
32
|
+
export declare function readConfig(outDir: string): ArchprintConfig | null;
|
|
33
|
+
//# sourceMappingURL=archprint-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archprint-config.d.ts","sourceRoot":"","sources":["../../src/cli/archprint-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,OAAO,EAAE,cAAc,CAAC;CACzB;AAgBD,wBAAgB,WAAW,CACzB,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC3C,OAAO,EAAE,cAAc,GACtB,eAAe,CAYjB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CAK3E;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,IAAI,CAmBN;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwBjE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
export const CONFIG_FILE = 'config.json';
|
|
4
|
+
const EMPTY_MANAGED = {
|
|
5
|
+
files: [],
|
|
6
|
+
readme: false,
|
|
7
|
+
readmeCreated: false,
|
|
8
|
+
prettierignore: false,
|
|
9
|
+
prettierignoreCreated: false,
|
|
10
|
+
npmignore: false,
|
|
11
|
+
npmignoreCreated: false,
|
|
12
|
+
};
|
|
13
|
+
function configPath(outDir) {
|
|
14
|
+
return path.join(outDir, CONFIG_FILE);
|
|
15
|
+
}
|
|
16
|
+
export function buildConfig(recommendations, version, location, managed) {
|
|
17
|
+
return {
|
|
18
|
+
archprintVersion: version,
|
|
19
|
+
app: location.app,
|
|
20
|
+
stack: recommendations.stack,
|
|
21
|
+
rulesDir: location.rulesDir,
|
|
22
|
+
enforced: recommendations.enforceNow,
|
|
23
|
+
review: recommendations.review,
|
|
24
|
+
adopt: recommendations.adopt,
|
|
25
|
+
evidence: recommendations.evidence,
|
|
26
|
+
managed,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function writeConfig(outDir, config) {
|
|
30
|
+
mkdirSync(outDir, { recursive: true });
|
|
31
|
+
const file = configPath(outDir);
|
|
32
|
+
writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`);
|
|
33
|
+
return file;
|
|
34
|
+
}
|
|
35
|
+
export function recordManagedFiles(outDir, cwd, version, absoluteFiles) {
|
|
36
|
+
const relative = absoluteFiles.map((file) => path.relative(cwd, file));
|
|
37
|
+
const existing = readConfig(outDir);
|
|
38
|
+
if (existing) {
|
|
39
|
+
const files = [...new Set([...existing.managed.files, ...relative])];
|
|
40
|
+
writeConfig(outDir, { ...existing, managed: { ...existing.managed, files } });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
writeConfig(outDir, {
|
|
44
|
+
archprintVersion: version,
|
|
45
|
+
app: '.',
|
|
46
|
+
stack: [],
|
|
47
|
+
rulesDir: path.relative(cwd, outDir) || outDir,
|
|
48
|
+
enforced: [],
|
|
49
|
+
review: [],
|
|
50
|
+
adopt: [],
|
|
51
|
+
evidence: { apps: 0, asOf: '' },
|
|
52
|
+
managed: { ...EMPTY_MANAGED, files: relative },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function readConfig(outDir) {
|
|
56
|
+
const file = configPath(outDir);
|
|
57
|
+
if (!existsSync(file))
|
|
58
|
+
return null;
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
61
|
+
if (typeof parsed.archprintVersion !== 'string')
|
|
62
|
+
return null;
|
|
63
|
+
return {
|
|
64
|
+
archprintVersion: parsed.archprintVersion,
|
|
65
|
+
app: parsed.app ?? '.',
|
|
66
|
+
stack: parsed.stack ?? [],
|
|
67
|
+
rulesDir: parsed.rulesDir ?? '.archprint',
|
|
68
|
+
enforced: parsed.enforced ?? [],
|
|
69
|
+
review: parsed.review ?? [],
|
|
70
|
+
adopt: parsed.adopt ?? [],
|
|
71
|
+
evidence: parsed.evidence ?? { apps: 0, asOf: '' },
|
|
72
|
+
managed: {
|
|
73
|
+
...EMPTY_MANAGED,
|
|
74
|
+
...(parsed.managed ?? {}),
|
|
75
|
+
files: Array.isArray(parsed.managed?.files) ? parsed.managed.files : [],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=archprint-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archprint-config.js","sourceRoot":"","sources":["../../src/cli/archprint-config.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/generate.d.ts
CHANGED
|
@@ -1,41 +1,48 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InstalledEnforcers } from '../scanner/enforcers.js';
|
|
2
|
+
import { type BoundaryRule } from '../generator/tsarch-emitter.js';
|
|
3
|
+
import { type ForbiddenImportSpec } from '../generator/eslint-plugin-emitter.js';
|
|
2
4
|
import type { ScannedPattern, ScanResult } from './scan.js';
|
|
3
|
-
export declare
|
|
4
|
-
export declare
|
|
5
|
-
export declare
|
|
6
|
-
export declare
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export declare function writeTestIsolationConfig(scan: ScanResult, outDir: string): string[];
|
|
10
|
-
export declare function writeRoleLayeringConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
|
|
11
|
-
export declare function writeDeepRelativeConfig(scan: ScanResult, outDir: string): string[];
|
|
12
|
-
export declare function writeConsoleIsolationConfig(scan: ScanResult, outDir: string): string[];
|
|
13
|
-
export declare function writeServerClientConfig(scan: ScanResult, outDir: string): string[];
|
|
14
|
-
export declare function writeUiDataConfig(scan: ScanResult, outDir: string): string[];
|
|
15
|
-
export declare function writeStoriesIsolationConfig(scan: ScanResult, outDir: string): string[];
|
|
16
|
-
export declare function writeWorkspacePackageConfig(scan: ScanResult, outDir: string): string[];
|
|
17
|
-
export declare function writeEnvAccessConfig(scan: ScanResult, outDir: string): string[];
|
|
18
|
-
export declare function writePhantomDependencyConfig(scan: ScanResult, outDir: string): string[];
|
|
19
|
-
export declare function writeEntryPurityConfig(scan: ScanResult, outDir: string): string[];
|
|
20
|
-
export declare function writeDependencyInternalsConfig(scan: ScanResult, outDir: string): string[];
|
|
21
|
-
export declare function writeEslintPreset(scan: ScanResult, outDir: string, options?: {
|
|
22
|
-
structural?: boolean;
|
|
23
|
-
}): string[];
|
|
24
|
-
export declare function writeEslintPlugin(scan: ScanResult, outDir: string): string[];
|
|
25
|
-
export declare function writeTsArchTests(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
|
|
26
|
-
export declare function writeGraph(scan: ScanResult, outDir: string): string[];
|
|
27
|
-
export interface WrittenConfig {
|
|
28
|
-
files: string[];
|
|
29
|
-
label: string | null;
|
|
5
|
+
export declare const ESLINT_FILE = "eslint.mjs";
|
|
6
|
+
export declare const DEPCRUISE_FILE = "dependency-cruiser.json";
|
|
7
|
+
export declare const ARCHPRINT_DIR = ".archprint";
|
|
8
|
+
export declare const FAMILY_NAMES: readonly ["forbidden-imports", "layer", "role-layering", "public-api", "feature-slice", "app-isolation", "test-isolation", "dependency-hygiene", "entry-purity", "phantom-deps", "import-style", "console", "env-access", "workspace-package", "stories-isolation", "ui-data", "server-client"];
|
|
9
|
+
interface DependencyCruiserRule {
|
|
10
|
+
name: string;
|
|
30
11
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
interface EslintFamilyBlock {
|
|
13
|
+
family: string;
|
|
14
|
+
block: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface DependencyCruiserFamily {
|
|
17
|
+
family: string;
|
|
18
|
+
forbidden: DependencyCruiserRule[];
|
|
19
|
+
}
|
|
20
|
+
export interface CollectedEnforcement {
|
|
21
|
+
eslintSpecs: ForbiddenImportSpec[];
|
|
22
|
+
eslintBlocks: EslintFamilyBlock[];
|
|
23
|
+
depcruise: DependencyCruiserFamily[];
|
|
24
|
+
boundaries: unknown | null;
|
|
25
|
+
tsArchRules: BoundaryRule[];
|
|
26
|
+
hasGraph: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface CollectOptions {
|
|
35
29
|
structural?: boolean;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
enforcers: InstalledEnforcers;
|
|
31
|
+
only?: string;
|
|
32
|
+
ruleIds?: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function collectEnforcement(scan: ScanResult, options: CollectOptions): CollectedEnforcement;
|
|
35
|
+
export declare function writeArchprintEslint(collected: CollectedEnforcement, outDir: string): string | null;
|
|
36
|
+
export declare function writeArchprintDepcruise(collected: CollectedEnforcement, outDir: string): string | null;
|
|
37
|
+
export interface EmitResult {
|
|
38
|
+
eslint: string | null;
|
|
39
|
+
depcruise: string | null;
|
|
40
|
+
expanded: string[];
|
|
41
|
+
}
|
|
42
|
+
export declare function emitLayout(scan: ScanResult, outDir: string, options: CollectOptions & {
|
|
43
|
+
expand?: boolean;
|
|
44
|
+
graph?: boolean;
|
|
45
|
+
}): EmitResult;
|
|
46
|
+
export declare function emitOne(pattern: ScannedPattern, appDir: string, outDir: string): string;
|
|
47
|
+
export {};
|
|
41
48
|
//# sourceMappingURL=generate.d.ts.map
|