archprint 0.2.0 → 0.3.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 +34 -405
- package/README.md +40 -20
- package/dist/cli/generate.d.ts +4 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +38 -0
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/recommend.d.ts.map +1 -1
- package/dist/cli/recommend.js +2 -0
- package/dist/cli/recommend.js.map +1 -1
- package/dist/cli/wiring.d.ts.map +1 -1
- package/dist/cli/wiring.js +0 -3
- package/dist/cli/wiring.js.map +1 -1
- package/dist/generator/eslint-plugin-emitter.d.ts +2 -0
- package/dist/generator/eslint-plugin-emitter.d.ts.map +1 -1
- package/dist/generator/eslint-plugin-emitter.js +13 -13
- package/dist/generator/eslint-plugin-emitter.js.map +1 -1
- package/dist/generator/eslint-preset-emitter.d.ts +3 -0
- package/dist/generator/eslint-preset-emitter.d.ts.map +1 -0
- package/dist/generator/eslint-preset-emitter.js +17 -0
- package/dist/generator/eslint-preset-emitter.js.map +1 -0
- package/dist/generator/tsarch-emitter.d.ts +12 -0
- package/dist/generator/tsarch-emitter.d.ts.map +1 -0
- package/dist/generator/tsarch-emitter.js +29 -0
- package/dist/generator/tsarch-emitter.js.map +1 -0
- package/dist/generator/ui-data-isolation-emitters.d.ts.map +1 -1
- package/dist/generator/ui-data-isolation-emitters.js +4 -1
- package/dist/generator/ui-data-isolation-emitters.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/scanner/file-walker.d.ts.map +1 -1
- package/dist/scanner/file-walker.js +23 -13
- package/dist/scanner/file-walker.js.map +1 -1
- package/dist/scanner/ignore-filter.d.ts.map +1 -1
- package/dist/scanner/ignore-filter.js +0 -3
- package/dist/scanner/ignore-filter.js.map +1 -1
- package/dist/scanner/resolve-import.d.ts.map +1 -1
- package/dist/scanner/resolve-import.js +3 -4
- package/dist/scanner/resolve-import.js.map +1 -1
- package/dist/scanner/role-classifier.d.ts.map +1 -1
- package/dist/scanner/role-classifier.js +4 -0
- package/dist/scanner/role-classifier.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,412 +1,41 @@
|
|
|
1
1
|
# archprint
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.3.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
a SUGGEST instead of vanishing, while a role we are more-unsure-than-not about is still rejected. Consequently
|
|
20
|
-
ui-data isolation, whose COMPONENT role is a 0.5-confidence catch-all, can no longer auto-enforce at the gate
|
|
21
|
-
level (not merely by family tiering); it is review-only by construction. The four vacuous-guard detectors
|
|
22
|
-
(ui-data, env-access, server-client, test-isolation) were migrated to the new `applicable` flag with no change
|
|
23
|
-
to their deterministic-role outcomes.
|
|
24
|
-
- fccc369: `archprint scan` now discovers app directories automatically. Pointed at a repo root, including a monorepo,
|
|
25
|
-
it finds each app or package that has its own tsconfig.json and enough of its own source, and scans each one,
|
|
26
|
-
rather than requiring you to point at a single app directory. Sub-packages below the size threshold are
|
|
27
|
-
skipped; single-app repos are unchanged.
|
|
28
|
-
- 4a36220: Add monorepo app isolation, a new rule family: sibling apps under an `apps`/`services` container must not
|
|
29
|
-
import each other directly (they should communicate through shared packages). `detectAppIsolation` gates
|
|
30
|
-
"apps under `<container>` must not import each other" with the Wilson floor, and `archprint generate` writes
|
|
31
|
-
`dependency-cruiser.app-isolation.archprint.json`, a `$1`-back-reference cross-app rule. The sibling-isolation
|
|
32
|
-
traversal and gating are now factored into a shared `detectSiblingIsolation` core used by both the
|
|
33
|
-
feature-slice and app-isolation detectors.
|
|
34
|
-
- 864fff5: Add the CLI: `archprint scan | generate | explain | approve` (commander), wiring the full pipeline so a
|
|
35
|
-
repo is scanned, its markers inferred, patterns gated, and rule artifacts emitted end to end.
|
|
36
|
-
|
|
37
|
-
`scan` and `explain` are specifier-level by default (about a second on a few-thousand-file repo), with
|
|
38
|
-
`--deep` to resolve imports through barrels and aliases. `generate` and `approve` resolve the graph by
|
|
39
|
-
default (the commitment point should not mint an AUTO rule the full graph would reject), with a `--fast`
|
|
40
|
-
opt-out that warns to confirm with a deep pass before enforcing.
|
|
41
|
-
|
|
42
|
-
Internally, `detectForbiddenImports` runs several patterns in one shared pass, and `createImportAnalyzer`
|
|
43
|
-
takes `{ resolve: false }` to skip module resolution and the type checker. The fast path matches only at
|
|
44
|
-
the import-specifier level, so barrel/alias-hidden imports are not caught in that mode (disclosed in the
|
|
45
|
-
fast-scan report footer); the emitted rule enforces at the specifier level regardless, which its rule card
|
|
46
|
-
documents.
|
|
47
|
-
|
|
48
|
-
- 6f27c1a: Add circular-dependency detection (`detectCycles`). Archprint builds the first-party import graph for an app
|
|
49
|
-
and finds every strongly connected component (Tarjan), reports the cycles, and gates a "no circular
|
|
50
|
-
dependencies" rule by how cycle-free the repo already is. The graph is built fast (specifier-level) by
|
|
51
|
-
default, with a deep type-resolved mode available; both agree on the cycles. A cycle-free repo yields an AUTO
|
|
52
|
-
no-cycles rule; a cyclic one surfaces the cycles for review.
|
|
53
|
-
- 25040e3: Add import-style detection: prefer workspace aliases over deep relative imports (`../../../` and deeper).
|
|
54
|
-
`detectDeepRelativeImports` gates the rule with the Wilson floor over files that use relative imports, and
|
|
55
|
-
`archprint generate` writes `eslint.deep-relative.archprint.json`, an ESLint `no-restricted-imports` config,
|
|
56
|
-
Archprint's first ESLint-core output format.
|
|
57
|
-
- 258777b: Add dependency hygiene, a new rule family covering external packages (the first detector that is not
|
|
58
|
-
first-party only): a file should import a third-party package by its public entry or a documented subpath, not
|
|
59
|
-
by reaching into its build/impl directories (`dist`, `src`, `lib`, `esm`, `cjs`, `build`, `out`, `internal`).
|
|
60
|
-
`detectDependencyInternals` counts files importing external packages and those reaching into internals, gates
|
|
61
|
-
with the Wilson floor, and `archprint generate` writes `dependency-cruiser.dependency-internals.archprint.json`.
|
|
62
|
-
- 8440ef3: Add the pattern detector and confidence gate: `detectForbiddenImport` / `detectNoDbInRequestEntry`
|
|
63
|
-
infer a "role A must not import target B" boundary from the resolved import graph, and `evaluateGate`
|
|
64
|
-
decides AUTO / SUGGEST / REJECT against four conditions (ratio >= 90%, evidence >= 20 files,
|
|
65
|
-
exceptions <= 3, role confidence >= 80%). Markers match the import specifier and first-party leaves
|
|
66
|
-
only (never a dependency's internal `node_modules` folders). The file walker now classifies `.ts`
|
|
67
|
-
modules with a top-level `"use server"` directive as server actions.
|
|
68
|
-
- 5d4af81: The import analyzer now captures dynamic imports (`import('...')`) as value edges, resolving the target in
|
|
69
|
-
deep mode (barrel-aware, like a namespace import) and by specifier in fast mode. Layer / dependency-direction
|
|
70
|
-
and cycle detection now see dependencies that flow through dynamic imports; on real codebases this surfaces
|
|
71
|
-
cycles and edges a static-only scan would miss.
|
|
72
|
-
- 629468e: `archprint generate` now emits the inferred layer / dependency-direction boundaries as a dependency-cruiser
|
|
73
|
-
config (`dependency-cruiser.archprint.json`), one `forbidden` rule per AUTO boundary. This is the first of the
|
|
74
|
-
ecosystem output formats: the rules Archprint infers can be enforced directly by dependency-cruiser in CI,
|
|
75
|
-
rather than being hand-written.
|
|
76
|
-
- 5726159: `archprint generate` now also emits the inferred layer boundaries as an eslint-plugin-boundaries config
|
|
77
|
-
(`eslint-boundaries.archprint.json`): each layer becomes an element type, and each layer disallows the layers
|
|
78
|
-
it must not import. Alongside the dependency-cruiser output, the inferred rules can be enforced by whichever
|
|
79
|
-
tool a TypeScript team already uses.
|
|
80
|
-
- e92d273: Add entry-point purity, a new rule family: framework file-convention entries (pages, routes, layouts, API
|
|
81
|
-
handlers) are loaded by the framework and should not be imported by other first-party code. `detectEntryPurity`
|
|
82
|
-
counts entries with a non-zero first-party in-degree and gates the rule with the Wilson floor; `archprint
|
|
83
|
-
generate` writes `dependency-cruiser.entry-purity.archprint.json`.
|
|
84
|
-
- 667de54: Make `archprint explain` actionable (roadmap B2). Each explanation now shows, below the gate breakdown, a
|
|
85
|
-
codeframe for every exception (the offending import line with its line number), a "How to fix" line, a "When
|
|
86
|
-
not to use this" caveat, and a "How to enforce" line that names the exact next command for the rule's gate
|
|
87
|
-
status. Per-rule guidance lives in a single `rule-guidance` source and the codeframe reader is a small,
|
|
88
|
-
self-contained module.
|
|
89
|
-
- b5598e4: Introduce family-maturity tiering so only audit-trusted rules auto-enforce. The Phase A1 adversarial audit
|
|
90
|
-
found that every false-AUTO came from the structural-inference families (layer, role-layering, entry-purity,
|
|
91
|
-
ui/data, server/client, feature-slice, app-isolation, stories, plus env-access and workspace-package-api),
|
|
92
|
-
while the mechanical families had zero false-AUTO across 148 audited rules. `archprint generate` now emits only
|
|
93
|
-
the mechanical families as AUTO by default and holds the structural families for review (pass
|
|
94
|
-
`--include-structural` to emit them anyway); `archprint recommend` correspondingly lists structural AUTO under
|
|
95
|
-
"review and adopt" rather than "enforce now". This makes the tool honest and shippable today: nothing whose
|
|
96
|
-
inferred layer/role can be wrong is ever silently written as enforcement, while the structural families are
|
|
97
|
-
hardened toward AUTO over time.
|
|
98
|
-
- 87ad55d: Add feature-slice isolation, a new rule family. A container directory (`features`, `modules`, `slices`,
|
|
99
|
-
`domains`) holds sibling slices that should not import one another; `detectFeatureSliceIsolation` counts each
|
|
100
|
-
slice file that imports a different sibling slice and runs it through the Wilson gate, so "slices under
|
|
101
|
-
`<container>` must not import each other" becomes enforceable (AUTO), provisional (SUGGEST), or unsupported.
|
|
102
|
-
Surfaced in the scan report, and `archprint generate` writes
|
|
103
|
-
`dependency-cruiser.feature-slice.archprint.json`, a cross-slice `forbidden` rule that captures the source
|
|
104
|
-
slice and forbids the others with a `$1` back-reference.
|
|
105
|
-
- 84b9da9: Broaden framework role coverage in the classifier so the existing rules apply beyond Next.js and NestJS:
|
|
106
|
-
SvelteKit (`+server.ts` endpoints, `+page.server.ts`/`+layout.server.ts` server loads, `hooks.server.ts`,
|
|
107
|
-
and `+page.ts`/`+layout.ts` universal loads), Nuxt Nitro handlers (`server/api|routes|middleware|plugins`),
|
|
108
|
-
and Remix / React Router file-based routes (`app/routes/**`, `root`, `entry.server`/`entry.client`).
|
|
109
|
-
|
|
110
|
-
`ROLE_PATTERNS` now maps each role to all of its path patterns rather than one, and the rule generator embeds
|
|
111
|
-
every pattern per role. Previously a role matched by more than one rule would have collapsed to a single
|
|
112
|
-
pattern in generated rules; with the new multi-framework rules this would have dropped variants.
|
|
113
|
-
|
|
114
|
-
- bbae98a: Grandfather known exceptions in the generated AP- eslint rules (roadmap B3). An AUTO rule is inferred because the
|
|
115
|
-
code already follows it apart from at most a few exception files; the generated plugin rule now skips exactly
|
|
116
|
-
those files, so wiring the rule and running the linter is green on the current codebase (no red wall on day one)
|
|
117
|
-
while any new violation elsewhere is still caught, the standard ratchet. The exceptions remain visible in `scan`
|
|
118
|
-
and `explain`; only enforcement grandfathers them. Proven end-to-end: the exception file passes and the same
|
|
119
|
-
violation in a new file is flagged by the real eslint engine.
|
|
120
|
-
- 293be92: Emit the inferred layer dependency graph as Mermaid and Graphviz DOT (the visualization formats
|
|
121
|
-
dependency-cruiser and madge produce). `archprint generate` now also writes `layer-graph.archprint.mmd` and
|
|
122
|
-
`layer-graph.archprint.dot` whenever layers are present. Each interacting layer pair renders as a weighted
|
|
123
|
-
directed edge: the dominant dependency is a solid arrow, and a leak that runs against an inferred boundary is
|
|
124
|
-
dotted (Mermaid) or dashed red (DOT), so a reader sees the architecture and where it is violated.
|
|
125
|
-
- 0671bf9: Add `archprint init` (roadmap B1): a single zero-config onboarding command. It detects the repo's stack, scans
|
|
126
|
-
the import graph, writes enforcement configs for the rules the code already follows (mechanical families by
|
|
127
|
-
default, `--include-structural` to add the review-tier families), and records an `archprint.json` manifest with
|
|
128
|
-
the three census-backed recommendation tiers (enforce now / review / adopt) so the setup is reproducible and
|
|
129
|
-
inspectable. It refuses to overwrite an existing manifest without `--force`, runs the self-consistency guardrail
|
|
130
|
-
before writing, and prints a friendly summary with next steps. The writer orchestration shared with `generate`
|
|
131
|
-
was extracted into a single `writeEnforcementConfigs` helper so the mechanical/structural partition lives in one
|
|
132
|
-
place.
|
|
133
|
-
- f4943d8: Add machine-readable output (roadmap B4). `scan --json` emits a stable, serializable summary (per app: file and
|
|
134
|
-
alias counts, plus each non-rejected rule with its family, status, observed conformance, confidence floor,
|
|
135
|
-
observations, and violating-file count), and `recommend --json` emits the three recommendation tiers as JSON.
|
|
136
|
-
Both are keyed by `archprintVersion` for forward compatibility, making archprint scriptable in CI. Exit codes are
|
|
137
|
-
the documented contract: 0 on success, 1 on error or refusal. SARIF is intentionally left to the tools archprint
|
|
138
|
-
emits into (eslint and dependency-cruiser already produce it) rather than reimplemented here.
|
|
139
|
-
- fccc369: Add layer / dependency-direction inference (`detectLayerBoundaries`). Archprint now infers a repo's layers
|
|
140
|
-
from its directory structure and, for each interacting layer pair, the observed dependency direction, then
|
|
141
|
-
flags the minority ("upward") direction as a candidate forbidden boundary through the same Wilson confidence
|
|
142
|
-
gate used for the other rules. The scan is fast (specifier-level) by default, with a deep type-resolved mode
|
|
143
|
-
available; both agree on the enforced (AUTO) boundaries. This is the first of the broader rule families that
|
|
144
|
-
bring Archprint level with hand-written architecture-conformance tools, with the difference that the rules
|
|
145
|
-
are inferred from the real import graph and evidence-gated rather than hand-written.
|
|
146
|
-
- 0f41d6f: Add the generated-output lifecycle (roadmap B6, part 1). Archprint now records everything it writes into an
|
|
147
|
-
`.archprint-outputs.json` manifest in the output directory, so it owns a precise, safe list of its own files.
|
|
148
|
-
`generate` and `init` now clean their previous outputs before writing fresh ones, so a rule the evidence no
|
|
149
|
-
longer supports stops being enforced instead of lingering as a stale file (upholds the conservative-bias
|
|
150
|
-
principle). A new `archprint eject` command removes archprint's generated files and its config manifests cleanly
|
|
151
|
-
(`--dry-run` to preview), giving a clean uninstall. The clean step only ever touches paths archprint recorded
|
|
152
|
-
as its own, and refuses to delete anything outside the output directory.
|
|
153
|
-
- 38789b8: Make the flagship forbidden-import rules (AP-) loadable and enforceable (roadmap B7). `generate`/`init` now emit
|
|
154
|
-
a self-contained local eslint plugin (`eslint-plugin.archprint.mjs`) that implements each AUTO forbidden-import
|
|
155
|
-
rule faithfully to the detector (matches the import specifier, skips type-only imports, and only applies to files
|
|
156
|
-
whose path matches the rule's role). The eslint aggregator loads the plugin so a single wired reference activates
|
|
157
|
-
the rules, and they are removed cleanly on `eject`. An end-to-end test runs the real eslint engine and confirms a
|
|
158
|
-
server-entry route importing the UI layer is flagged while a clean route passes. Also fixes a wiring bug where a
|
|
159
|
-
single-line `export default []` config had its array closer swallowed by the inserted managed comment.
|
|
160
|
-
- 1faa4aa: Infer forbidden-import markers per repo instead of using one repo's hardcoded conventions.
|
|
161
|
-
`inferUiLayerMarkers` derives the UI-layer specifier from where a repo's components cluster,
|
|
162
|
-
disambiguated by import fan-in so a shared component library beats a feature directory.
|
|
163
|
-
`inferDbClientMarkers` combines a curated list of known db libraries with first-party wrappers
|
|
164
|
-
discovered by client instantiation (unambiguous ORM constructors, or generic pool/driver
|
|
165
|
-
constructors paired with a known-library import), and emits a leaf-path marker per wrapper so
|
|
166
|
-
barrel re-exports are caught via the detector's barrel resolution. `detectUiLayerInServerEntry` and
|
|
167
|
-
`detectDbClientInRequestEntry` use them, and `DEFAULT_DB_MARKERS` drops its repo-specific first-party
|
|
168
|
-
entries.
|
|
169
|
-
|
|
170
|
-
This reduces hardcoding of first-party conventions; it does not eliminate hardcoding: the known-db
|
|
171
|
-
library list, the ORM constructor set, and the structural-segment exclusions are curated framework/ORM
|
|
172
|
-
vocabulary by design. Inference is heuristic: an exotic ORM whose constructor is not listed is missed,
|
|
173
|
-
and UI fan-in only helps when the shared layer is actually imported, so a fully colocated app whose
|
|
174
|
-
routes never import the shared library can still pick a feature directory.
|
|
175
|
-
|
|
176
|
-
- 0fbca1c: Merge `approve` into `generate --rule <id>`. Emitting a single reviewed rule (including a SUGGEST rule) was a
|
|
177
|
-
special case of generate, and having two commands that both write rule artifacts was the one genuinely ambiguous
|
|
178
|
-
pair in the CLI. Now `archprint generate --rule AP-001` does what `archprint approve AP-001` did; the standalone
|
|
179
|
-
`approve` command is removed. Naming the rule is the explicit consent, so the AUTO-is-automatic /
|
|
180
|
-
SUGGEST-needs-review distinction is preserved. This drops the command surface from 8 to 7. Done pre-1.0, before
|
|
181
|
-
the surface is public.
|
|
182
|
-
- 2e7d4bc: Add orphan-module detection: `detectOrphans` finds first-party source files that no other file imports and
|
|
183
|
-
that are not framework or tooling entry points (routes, pages, layouts, middleware, config). These dead-code
|
|
184
|
-
candidates are surfaced in the scan report as an informational SUGGEST (never enforced), since dynamic or
|
|
185
|
-
string-based loading can make a live file look unreferenced. The cycle and orphan detectors now share a
|
|
186
|
-
single `buildImportGraph` builder so the first-party value-import graph is constructed one way.
|
|
187
|
-
- 5b57f3c: Add phantom-dependency detection: every imported third-party package should be declared in package.json rather
|
|
188
|
-
than relied on transitively. `detectPhantomDependencies` collects the declared dependencies (the app's
|
|
189
|
-
package.json, the workspace root's, and all workspace package names) and flags imports of anything else, gated
|
|
190
|
-
by the Wilson floor. `archprint generate` writes `dependency-cruiser.phantom-deps.archprint.json` keyed on
|
|
191
|
-
dependency-cruiser's `npm-no-pkg` / `npm-unknown` dependency types.
|
|
192
|
-
- e94ac18: Add public-API (barrel) boundary detection. A directory holding an `index.ts`/`index.tsx` exposes a public
|
|
193
|
-
API; `detectPublicApiBoundaries` classifies every external consumer against the nearest enclosing barrel (an
|
|
194
|
-
import of the barrel conforms, an import of any other file in the group is a deep-import violation) and runs
|
|
195
|
-
the count through the Wilson gate, so "files outside `<dir>` must import it through its barrel" becomes
|
|
196
|
-
enforceable (AUTO), provisional (SUGGEST), or unsupported. Surfaced in the scan report. Uses the fast import
|
|
197
|
-
graph deliberately: deep resolution would resolve through the barrel and erase the barrel-vs-deep signal.
|
|
198
|
-
- 7d6a325: Generate an enforceable rule from public-API boundaries. `archprint generate` now writes
|
|
199
|
-
`dependency-cruiser.public-api.archprint.json`: one `forbidden` deep-import rule per AUTO barrel group, where
|
|
200
|
-
a module outside `<dir>` may not import any file inside it except the barrel. Completes the public-API rule
|
|
201
|
-
family (detect plus emit), alongside the existing dependency-cruiser and eslint-plugin-boundaries layer
|
|
202
|
-
outputs.
|
|
203
|
-
- d25caf9: Back `archprint recommend` with real adoption evidence. The command now ships a catalog of per-family
|
|
204
|
-
AUTO-adoption rates, overall and per detected stack, mined from a 92,861-repo census, and attaches to every
|
|
205
|
-
recommendation the share of comparable repos that already enforce that rule. The "adopt from day one" tier is
|
|
206
|
-
now decided by that census evidence (a family is recommended when a meaningful share of comparable repos
|
|
207
|
-
already enforce it) instead of hand-picked stack flags, so the guidance reflects what the TypeScript ecosystem
|
|
208
|
-
actually does.
|
|
209
|
-
- dcc2b66: Add `archprint recommend`, which sorts every rule family into three tiers from the repo's
|
|
210
|
-
evidence and detected stack: enforce now (already followed), review and adopt (thin evidence),
|
|
211
|
-
and adopt from day one (suits the stack but not yet evidenced). The last tier gives fresh repos a
|
|
212
|
-
stack-aware baseline to enforce from day one, where there is little code to infer from.
|
|
213
|
-
- 803839d: Add role-based layering, a new rule family that uses the classifier's semantic roles rather than directory
|
|
214
|
-
names, so it catches the classic backend tier hierarchy even when the tiers are named suffixes rather than
|
|
215
|
-
folders (NestJS-style). `detectRoleLayering` counts value edges between roles (CONTROLLER, SERVICE, REPOSITORY,
|
|
216
|
-
DATA_ACCESS, DB_MODULE), takes the minority direction of each interacting pair as the candidate boundary, and
|
|
217
|
-
gates it with the Wilson floor (using the classifier's confidence as roleConfidence). `archprint generate`
|
|
218
|
-
writes `dependency-cruiser.role-layering.archprint.json`.
|
|
219
|
-
- ded3544: Add the rule generator: from an AUTO-gated detected pattern, emit four artifacts (ESLint rule `.ts`,
|
|
220
|
-
rule card `.md` with the evidence attached, plus passing and failing fixtures). The emitted rule is
|
|
221
|
-
self-contained plain ESLint (no `@typescript-eslint` runtime dependency) and enforces the detector's
|
|
222
|
-
direct-import semantics. Validated by RuleTester and an independent scratch-project run.
|
|
223
|
-
- 2d372bd: Add the scanner core: a workspace tsconfig alias resolver, a convention-based role classifier, a
|
|
224
|
-
barrel resolver, and a symbol-level, type-aware, workspace-package-aware import analyzer
|
|
225
|
-
(`analyzeImports`). This is the deterministic substrate the rule generator builds on.
|
|
226
|
-
- 0751a34: Fix two scanning bugs found by running archprint on itself. (1) Respect `.gitignore`: the walker and app-dir
|
|
227
|
-
discovery now skip git-ignored paths (via the standard `ignore` matcher) in addition to the always-noise
|
|
228
|
-
directories, so a scan no longer descends into vendored repos or other large ignored trees and hang. (2) Resolve
|
|
229
|
-
ESM `.js` specifiers to their `.ts` source: `import './x.js'` where the file is `x.ts` (the NodeNext/ESM
|
|
230
|
-
convention, which archprint's own code uses) now resolves, so first-party edges are found on ESM TypeScript
|
|
231
|
-
projects instead of every file looking like an orphan. The three duplicated import resolvers were unified into
|
|
232
|
-
one `resolveFirstPartyImport`. Verified against the five-repo regression corpus (unchanged) and by dogfooding
|
|
233
|
-
this repo (false orphans dropped from 66 to 1, the real CLI entry).
|
|
234
|
-
- 6e758fa: Add the server/client boundary rule family (the twentieth detector): a Next.js `"use client"` module must not
|
|
235
|
-
import a `server-only` module. `detectServerClientBoundary` finds `"use client"` modules (via a ReDoS-safe
|
|
236
|
-
leading-directive scan) and modules importing the `server-only` package, then flags client modules that reach a
|
|
237
|
-
server-only module, gated by the Wilson floor. `archprint generate` writes
|
|
238
|
-
`dependency-cruiser.server-client.archprint.json`. The directive scanner is generalized to expose both
|
|
239
|
-
`hasUseServerDirective` and `hasUseClientDirective`.
|
|
240
|
-
- 949d583: Add stories isolation: Storybook `.stories` files are loaded by Storybook and should not be imported by other
|
|
241
|
-
code. `detectStoriesIsolation` flags stories with a non-story importer (over the shared import graph), gated by
|
|
242
|
-
the Wilson floor, and `archprint generate` writes `dependency-cruiser.stories-isolation.archprint.json`.
|
|
243
|
-
- 3408ba6: Add test isolation, a new rule family: production (non-test) code must not import test or spec files.
|
|
244
|
-
`detectTestIsolation` builds the import graph with test files kept as nodes (via a new `includeTests` option on
|
|
245
|
-
`buildImportGraph`), counts production files that import a test file, and runs the count through the Wilson
|
|
246
|
-
gate. Surfaced in the scan report (only when the app has test files), and `archprint generate` writes
|
|
247
|
-
`dependency-cruiser.test-isolation.archprint.json`, a `not-to-test` `forbidden` rule.
|
|
248
|
-
- d28f41a: Add transitive layer reachability. `computeLayerReachability` condenses the first-party import graph by
|
|
249
|
-
strongly-connected component into a DAG and computes, for every layer, the set of layers a file in it can
|
|
250
|
-
reach through a chain of value imports. The scan now flags an AUTO layer boundary that a plain import rule
|
|
251
|
-
would pass but that still leaks through an intermediary layer (`from` reaches `to` transitively), pointing at
|
|
252
|
-
the stronger dependency-cruiser `reachable` form. The cycle, orphan, and reachability passes now share one
|
|
253
|
-
prebuilt import graph and one strongly-connected-components routine, so a full scan builds the graph once.
|
|
254
|
-
- ea29d9c: Add UI/data separation: reusable UI components (COMPONENT role) should reach the data layer through services,
|
|
255
|
-
not import the DB/data layer (`DB_MODULE` / `DATA_ACCESS`) directly. `detectUiDataIsolation` gates it with the
|
|
256
|
-
Wilson floor over the component population, and `archprint generate` writes
|
|
257
|
-
`dependency-cruiser.ui-data.archprint.json`.
|
|
258
|
-
- 628c96e: Add two AST usage-based rule families over a new shared usage-scanner: console isolation (library / non-CLI
|
|
259
|
-
code must not call `console.*`) and env access (read `process.env` only in the config/env layer). Both are
|
|
260
|
-
Wilson-gated and emit scoped ESLint flat-config blocks (`no-console`, `no-restricted-properties`) via
|
|
261
|
-
`archprint generate`. This is Archprint's first analysis beyond the import graph.
|
|
262
|
-
- 756c800: Gate rules on a statistical confidence bound instead of a fixed file-count threshold.
|
|
263
|
-
|
|
264
|
-
The confidence gate previously required `ratio >= 90%` AND `evidence >= 20` files as two separate checks.
|
|
265
|
-
These are now fused into one principled test: the Wilson score 95% lower bound on the true conformance rate
|
|
266
|
-
must be at least 90%. This accounts for sample size and observed rate together, a pattern followed in 5/5
|
|
267
|
-
files is not evidence of a real rule (low bound), while 40/40 or 216/217 is. A pattern that looks like a
|
|
268
|
-
rule (observed >= 80%) but lacks the evidence to be confident is now surfaced as a provisional `SUGGEST`
|
|
269
|
-
rather than a silent `REJECT`, so a thin or simple codebase is not locked out. The `exceptions <= 3` and
|
|
270
|
-
`roleConfidence >= 0.80` guards are unchanged. `GATE_THRESHOLDS` now exposes `{ confidence, exceptions,
|
|
271
|
-
roleConfidence }`.
|
|
272
|
-
|
|
273
|
-
- d39460b: `wire` now edits the common `export default tseslint.config(...)` and `export default defineConfig([...])`
|
|
274
|
-
flat-config shapes, not just a bare `export default [` array (dogfood finding). It inserts the managed spread as
|
|
275
|
-
the first config, keeping the file's formatting, and `eject` restores it exactly. Config shapes it still cannot
|
|
276
|
-
parse fall back to the printed snippet as before. Proven end-to-end: wiring archprint's own `tseslint.config()`
|
|
277
|
-
eslint config and running the real eslint engine fires the generated `no-console` rule.
|
|
278
|
-
- 1ddb697: Add `archprint wire` to reference the generated eslint rules from your flat eslint config (roadmap B6, part 2).
|
|
279
|
-
`generate`/`init` now also emit an aggregator (`eslint.archprint.mjs`) that globs archprint's eslint rule blocks,
|
|
280
|
-
so the reference is one stable line that survives regeneration (new rules are picked up, dropped ones disappear,
|
|
281
|
-
without re-wiring). `wire` inserts a MANAGED, reversible block (a marked import + one spread) into a flat
|
|
282
|
-
`eslint.config.{js,mjs,cjs}` when it can detect the array-form export, is idempotent, supports `--dry-run`, and
|
|
283
|
-
prints the exact snippet to paste for any other shape. `eject` now also removes that managed reference, restoring
|
|
284
|
-
the config exactly. Scope note: this wires the eslint built-in-rule blocks; the layer rules (eslint-plugin-
|
|
285
|
-
boundaries), the AP- custom rules, and the dependency-cruiser configs still target their own tools.
|
|
286
|
-
- 23fba83: Generalize `wire`/`eject` across the enforcement tools a repo uses, not just eslint. Wiring is now a tool
|
|
287
|
-
registry: `wire` detects each supported tool's config and inserts a managed, reversible reference into every one
|
|
288
|
-
it finds, and `eject` removes them all. Dependency-cruiser is now supported: `generate`/`init` emit an aggregated
|
|
289
|
-
`dependency-cruiser.all.archprint.json` (merging the individual forbidden-rule sets, refreshed on every
|
|
290
|
-
regeneration), and `wire` adds a managed `extends` to a `.dependency-cruiser.json`, preserving the rest of the
|
|
291
|
-
config and removing exactly that entry on eject. A tool config that cannot be edited safely (a JS
|
|
292
|
-
dependency-cruiser config) gets the exact snippet printed instead. Adding a future tool is now a single registry
|
|
293
|
-
entry.
|
|
294
|
-
- e4c7307: Add workspace-package public-API detection: in a monorepo, a workspace package should be imported by its name
|
|
295
|
-
(which resolves to its entry), not by a deep path into its source. `detectWorkspacePackageApi` matches import
|
|
296
|
-
specifiers against the workspace package names, gates deep imports with the Wilson floor, and `archprint
|
|
297
|
-
generate` writes `eslint.workspace-package.archprint.json` (a `no-restricted-imports` rule over the package
|
|
298
|
-
names).
|
|
299
|
-
|
|
300
|
-
### Patch Changes
|
|
301
|
-
|
|
302
|
-
- 6188b91: Fix `detectLayerBoundaries` and `detectCycles` returning empty or incorrect results when called with a
|
|
303
|
-
relative `appDir`. They now normalize the directory to an absolute path, so import edges resolve consistently
|
|
304
|
-
whether an absolute or relative directory is passed. (The CLI already passed absolute paths and was
|
|
305
|
-
unaffected.)
|
|
306
|
-
- bd70f41: Identify UI components by rendered JSX, not the `.tsx` extension.
|
|
7
|
+
- Add Angular support: Archprint now detects Angular in your stack and recognizes Angular components, so the
|
|
8
|
+
component-aware rules (such as UI/data separation) apply to Angular projects.
|
|
9
|
+
- Generate a shareable ESLint preset. `archprint generate` now writes a single, self-contained
|
|
10
|
+
`eslint-preset.archprint.mjs` that inlines the inferred rules and needs only eslint, so you can commit it,
|
|
11
|
+
publish it, or hand it to another repository and adopt the rules in one line:
|
|
12
|
+
`import archprint from './eslint-preset.archprint.mjs'`.
|
|
13
|
+
- Scan Vue and Svelte single-file components. Archprint now reads the `<script>` block of `.vue` and `.svelte`
|
|
14
|
+
files, treats them as UI components, and follows their imports, so the component-aware rules (such as UI/data
|
|
15
|
+
separation) apply to Vue and Svelte projects.
|
|
16
|
+
- Emit architecture boundary tests for ts-arch. Alongside the ESLint and dependency-cruiser outputs, Archprint
|
|
17
|
+
now writes a ts-arch test file for the inferred first-party boundaries (layer, role, and UI/data), so you can
|
|
18
|
+
run them inside your existing Vitest or Jest suite.
|
|
307
19
|
|
|
308
|
-
|
|
309
|
-
`React.createElement`/`cloneElement` call), matching how react-docgen and eslint-plugin-react define a
|
|
310
|
-
component, instead of trusting the `.tsx` file extension. A non-rendering `.tsx` file (types, constants,
|
|
311
|
-
re-exports) no longer inflates a directory's component count, and a `.ts` file that renders via
|
|
312
|
-
`createElement` is correctly counted. Detection uses a syntactic parse only (no type checker), so it stays
|
|
313
|
-
fast. No marker change on inbox-zero, dub, formbricks, or cal.com.
|
|
314
|
-
|
|
315
|
-
- 96fb249: Detector accuracy fixes:
|
|
316
|
-
|
|
317
|
-
- Type-only imports (`import type { X } from '...'` and inline `import { type X } from '...'`) are no longer
|
|
318
|
-
counted as a runtime dependency on a forbidden target. They are erased at compile time, so a request entry
|
|
319
|
-
that imports a marker only as a type is no longer a false violation, including in fast (specifier-only)
|
|
320
|
-
mode. Implemented as a syntactic `hasValueBinding` signal on every analyzed import.
|
|
321
|
-
- Database-client marker inference now recognizes first-party files that re-export a known database library
|
|
322
|
-
(`export * from '@prisma/client'`, for example `@/lib/db` or a workspace-scoped
|
|
323
|
-
`@scope/core/prisma-client`), not only files that instantiate a client. This closes a false-AUTO gap where
|
|
324
|
-
direct database access through a re-export surface was invisible.
|
|
325
|
-
|
|
326
|
-
- 4d2a0fc: Speed up fast-mode scanning about 2.6x (roadmap C1/C2). Fast mode now extracts imports with the raw TypeScript
|
|
327
|
-
parser instead of ts-morph's heavier wrapper layer (same AST fidelity, no regex parsing), and caches each file's
|
|
328
|
-
parse by path + mtime + size so the many detectors that scan the same files reuse one parse instead of
|
|
329
|
-
re-parsing. On inbox-zero (2,232 files) a fast scan drops from ~13.5s to ~5.2s. Output is unchanged: the
|
|
330
|
-
per-family gate distribution is identical across the five-repo regression corpus, and all tests pass. Deep mode
|
|
331
|
-
(full barrel/alias resolution via ts-morph) is unaffected.
|
|
332
|
-
- fcd8010: Fix four more Phase A1 re-audit findings in the structural families: server-client is REJECTed as vacuous when
|
|
333
|
-
no `server-only` module exists anywhere; `.e2e-spec` files and `__tests__`/`test`/`e2e`/`cypress`/`playwright`
|
|
334
|
-
directories are classified as tests (so test scaffolding no longer forms bogus layers); generated code
|
|
335
|
-
(`generated/**`) is neutral so a component importing generated enums is not a false data-layer violation; and
|
|
336
|
-
an entry re-exporting another entry (idiomatic route aliasing) no longer breaks entry-purity.
|
|
337
|
-
- f70c105: Fix four correctness bugs the Phase A1 audit found in the structural detectors, all of which produced
|
|
338
|
-
false "enforceable" rules:
|
|
339
|
-
|
|
340
|
-
- **Workspace self-reference imports** (`@scope/pkg/sub` where that is the app's own package name, an npm
|
|
341
|
-
workspaces self-reference, not a tsconfig alias) are now resolved as first-party. They were silently
|
|
342
|
-
dropped, undercounting violations (a false-AUTO source) and leaving a live enforcement gap.
|
|
343
|
-
- **Vacuous rules** no longer reach AUTO: UI/data-separation when the app has no data layer, and env-access
|
|
344
|
-
when nothing reads `process.env` at all, govern nothing, so they can no longer be "enforceable".
|
|
345
|
-
- **Data-access classification** now recognizes a flat client file named after a known ORM
|
|
346
|
-
(`utils/prisma.ts`, `lib/drizzle.ts`), not only files under a `db/`/`database/`/`prisma/` directory, so
|
|
347
|
-
UI-imports-the-database violations are detected instead of a false "clean".
|
|
348
|
-
- **Dependency-hygiene** no longer flags a package's published `dist/lib/esm/cjs/build/out` subpaths as
|
|
349
|
-
internal reaches (they are commonly the public entry point, e.g. `react-syntax-highlighter/dist/esm/...`);
|
|
350
|
-
it flags only the unambiguous `/src/` and `/internal(s)/` reaches.
|
|
351
|
-
|
|
352
|
-
- 9ab170d: Resolve bare `baseUrl`-relative imports as first-party. With an explicit tsconfig `baseUrl`, TypeScript
|
|
353
|
-
resolves a bare specifier like `import x from "app/foo"` or `"test/fixtures/x"` against baseUrl before
|
|
354
|
-
node_modules, but the resolver treated these as external and dropped them, undercounting real violations (a
|
|
355
|
-
false-AUTO source) and misreporting them as phantom dependencies. Each top-level directory under an explicit
|
|
356
|
-
baseUrl is now a first-party prefix. Surfaced by the Phase A1 re-audit on three monorepos.
|
|
357
|
-
- 596df8f: Fix the env-access detector's confidence gate, which could never reach AUTO. It gated on the count of files
|
|
358
|
-
that read `process.env`, but a repo that centralizes env access reads it in only a handful of config files, so
|
|
359
|
-
the population was always far below the Wilson floor. It now gates on the non-config files the rule governs
|
|
360
|
-
(the population), with violations being non-config files that read `process.env` directly, mirroring the
|
|
361
|
-
console-isolation detector. A full 92,861-repo census surfaced the bug: env-access was AUTO on 0% of apps.
|
|
362
|
-
- 1266ebd: Fix the layer detector treating file-router route directories as architectural layers. `layerOfPath` skipped
|
|
363
|
-
`app`/`pages` and promoted the first route segment beneath them to a "layer", so a file deep in
|
|
364
|
-
`app/(group)/sub.domain/(group)/<segment>/` became layer `<segment>`, and same-named route directories across
|
|
365
|
-
disjoint route groups collapsed into one fake, non-cohesive layer (e.g. a "programs" layer spanning nine
|
|
366
|
-
unrelated `app/**/programs` directories). This produced many false "enforceable" rules on Next.js app-router
|
|
367
|
-
repos. The router tree is now a single layer (`app`/`pages`), so only genuine top-level source directories
|
|
368
|
-
become layers. Surfaced by the Phase A1 false-AUTO audit; on the audited repos this removed 131 of 222 layer
|
|
369
|
-
AUTO rules, nearly all of them false positives, while preserving the real "shared layer must not import the
|
|
370
|
-
router" boundaries.
|
|
371
|
-
- 3740ff8: Make the test-isolation gate REJECT when an app has no test files at all, instead of vacuously passing as
|
|
372
|
-
AUTO. `generate`/`recommend` already suppressed this case at the surface, but the detector's own gate now
|
|
373
|
-
reports it honestly, so every consumer of the gate status is consistent. Surfaced by the Phase A1 round-3 audit.
|
|
374
|
-
- 3cc1b6b: Make the scan report label consistent with what actually ships. The structural-inference families (layer,
|
|
375
|
-
role-layering, entry-purity, ui/data, server/client, feature-slice, app-isolation, env-access,
|
|
376
|
-
workspace-package, stories) now render as "(review before enforcing)" instead of "(enforceable)", matching the
|
|
377
|
-
fact that `generate` holds them for review by default; the mechanical families keep "(enforceable)".
|
|
378
|
-
- 96e3ae4: Stop test files diluting UI-layer inference, and select the UI layer by coverage.
|
|
379
|
-
|
|
380
|
-
Colocated test files were counted as counter-evidence against a component directory, sinking a real UI
|
|
381
|
-
layer below the confidence gate (a false negative on repos that colocate tests next to components).
|
|
382
|
-
Inference now excludes tests and stories from the file set, and selects the UI layer by component coverage
|
|
383
|
-
(where components concentrate) instead of import fan-in, which mis-selected heavily-imported primitive
|
|
384
|
-
sub-libraries such as `components/ui`. Validated with no marker change on inbox-zero, dub, formbricks, and
|
|
385
|
-
cal.com.
|
|
386
|
-
|
|
387
|
-
- 0f5617d: Fix catastrophic backtracking (ReDoS) in the "use server" directive check.
|
|
388
|
-
|
|
389
|
-
`hasUseServerDirective` used a regex with nested quantifiers over overlapping whitespace/comments, which
|
|
390
|
-
backtracks exponentially: a file whose first bytes contain ~20+ comment tokens took ~6s, and more hung the
|
|
391
|
-
scan indefinitely (a real hang on `archprint scan` for repos with heavily-commented or generated leading
|
|
392
|
-
files). It now scans the head linearly (skipping whitespace, line comments, and block comments) with only a
|
|
393
|
-
fixed-literal final check, so the worst case is linear. Behavior is unchanged for real inputs; a
|
|
394
|
-
comment-heavy head that took seconds now takes microseconds.
|
|
395
|
-
|
|
396
|
-
- f2d0572: Classify Next.js App Router entry files as route entries, not reusable components.
|
|
397
|
-
|
|
398
|
-
`page.tsx`, `layout.tsx`, `template.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `default.tsx`, and
|
|
399
|
-
`global-error.tsx` under `app/` render UI but are framework entry points, not part of the shared UI layer.
|
|
400
|
-
Classifying them as a distinct `ROUTE_ENTRY` role keeps them out of the component count during UI-layer
|
|
401
|
-
inference, so a page-heavy feature area is no longer mistaken for the UI layer. No marker change on
|
|
402
|
-
inbox-zero, dub, formbricks, or cal.com.
|
|
403
|
-
|
|
404
|
-
- d706e63: `archprint scan` now surfaces circular-dependency detection: it lists the import cycles it finds, or, when
|
|
405
|
-
the repo is cycle-free, reports that the "no circular dependencies" rule is enforceable. Cycle detection runs
|
|
406
|
-
in fast mode (the structural graph is faithful without the type checker), so it does not slow a `--deep` scan.
|
|
407
|
-
- d94fa88: Classify `.tsx` route handlers and `pages/api` files as request entries. Route handlers can legitimately use
|
|
408
|
-
JSX (for example `next/og` `ImageResponse` routes), and matching only `.ts` misclassified those as UI
|
|
409
|
-
components, hiding request-entry boundary violations in them.
|
|
20
|
+
## 0.2.0
|
|
410
21
|
|
|
411
|
-
|
|
412
|
-
|
|
22
|
+
First public release. Archprint mines the architecture rules your repository already follows from its real
|
|
23
|
+
import graph, gates each on statistical evidence, and emits them into the tools you already use.
|
|
24
|
+
|
|
25
|
+
### Highlights
|
|
26
|
+
|
|
27
|
+
- **Evidence-gated rule inference across 20 families**, including forbidden imports (DB client / UI in a server
|
|
28
|
+
entry), import cycles, test isolation, console isolation, import style, public-API barrels, dependency hygiene
|
|
29
|
+
and declaration, layer and role boundaries, UI/data separation, entry purity, server/client boundaries,
|
|
30
|
+
feature-slice and app isolation, env access, workspace-package API, and stories isolation. Mechanical families
|
|
31
|
+
auto-enforce; inferred structural families are held for review.
|
|
32
|
+
- **CLI**: `init` (zero-config setup), `scan` (the rules your code already follows, with the evidence),
|
|
33
|
+
`recommend` (adoption tiers backed by a census of tens of thousands of public repositories, works on a fresh
|
|
34
|
+
repo too), `explain` (per-rule evidence, codeframes, and how-to-fix), `generate` (write the rule configs), and
|
|
35
|
+
`wire` / `eject` (reference the generated rules from your config, reversibly).
|
|
36
|
+
- **Emits into your stack**: ESLint, including a generated plugin for the forbidden-import rules, and
|
|
37
|
+
dependency-cruiser, wired in with a single managed reference that survives regeneration.
|
|
38
|
+
- **Clean lifecycle**: re-running refreshes the output and drops any rule the evidence no longer supports; the
|
|
39
|
+
few known exceptions are grandfathered so adoption is green on day one.
|
|
40
|
+
- **Framework awareness**: Next.js, Nest, SvelteKit, Nuxt, Remix, and React / Vue / Svelte stacks.
|
|
41
|
+
- **Machine-readable output** (`scan --json`, `recommend --json`) and stable exit codes for CI.
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Archprint
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/archprint)
|
|
4
|
+
[](https://github.com/Tommkruix/archprint/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/archprint)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://tommkruix.github.io/archprint/)
|
|
8
|
+
|
|
3
9
|
**Mine the architecture rules your repo already enforces, with the evidence attached.**
|
|
4
10
|
|
|
5
11
|
Archprint scans a TypeScript repository's real import graph, finds the architectural boundaries the code
|
|
@@ -22,9 +28,9 @@ from paths, which can be wrong, so Archprint holds them for human review by defa
|
|
|
22
28
|
enforcing them. Nothing whose inferred layer or role could be wrong is written as enforcement without you
|
|
23
29
|
opting in.
|
|
24
30
|
|
|
25
|
-
> Status:
|
|
26
|
-
>
|
|
27
|
-
>
|
|
31
|
+
> Status: published on npm, pre-stable (0.x may break between minor versions). Production-ready today: the
|
|
32
|
+
> insight commands (`scan`, `recommend`) and the auto-enforcement of the mechanical families above. The
|
|
33
|
+
> structural families are review-only while they are hardened.
|
|
28
34
|
|
|
29
35
|
## What makes it different
|
|
30
36
|
|
|
@@ -36,7 +42,17 @@ your stack rather than replacing it.
|
|
|
36
42
|
|
|
37
43
|
## Install
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
```bash
|
|
46
|
+
npm install --save-dev archprint
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then run it (or use `npx archprint …` without installing):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx archprint scan .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or build from source:
|
|
40
56
|
|
|
41
57
|
```bash
|
|
42
58
|
git clone https://github.com/Tommkruix/archprint
|
|
@@ -46,13 +62,6 @@ npm run build
|
|
|
46
62
|
node dist/cli.js scan <path-to-your-app>
|
|
47
63
|
```
|
|
48
64
|
|
|
49
|
-
Once published, it will install as a normal dev dependency:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
npm install --save-dev archprint
|
|
53
|
-
npx archprint scan .
|
|
54
|
-
```
|
|
55
|
-
|
|
56
65
|
Requires Node >= 20. Point Archprint at an app directory that has a `tsconfig.json` (for a monorepo, a
|
|
57
66
|
package such as `apps/web`; a monorepo root is fine too, Archprint discovers the app directories).
|
|
58
67
|
|
|
@@ -111,7 +120,7 @@ A real scan of [inbox-zero](https://github.com/elie222/inbox-zero) (`apps/web`,
|
|
|
111
120
|
trimmed:
|
|
112
121
|
|
|
113
122
|
```
|
|
114
|
-
Archprint v0.
|
|
123
|
+
Archprint v0.2.0
|
|
115
124
|
Scanned 2,232 TypeScript files
|
|
116
125
|
Workspace aliases: 18 resolved
|
|
117
126
|
|
|
@@ -138,6 +147,11 @@ Ships as: **Auto** = auto-generated as enforcement (mechanical families, 0 false
|
|
|
138
147
|
correctness audit). **Review** = held for human review by default; emit with `--include-structural` (the
|
|
139
148
|
inferred layer/role can be wrong, so it is not enforced silently). **Report** = surfaced only, never enforced.
|
|
140
149
|
|
|
150
|
+
Framework aware: Archprint recognizes the stack (Next.js, Nest, SvelteKit, Nuxt, Remix) and classifies UI
|
|
151
|
+
components across React (`.tsx`), Angular (`.component.ts`, `.directive.ts`), and Vue and Svelte single-file
|
|
152
|
+
components (it reads the `<script>` block of `.vue`/`.svelte` files), so the component-aware rules apply
|
|
153
|
+
regardless of framework.
|
|
154
|
+
|
|
141
155
|
| Detector | Rule it can infer | Ships as |
|
|
142
156
|
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------- |
|
|
143
157
|
| Forbidden imports (marker based) | A role (route handler, server entry) must not import a target (the DB client, the UI layer) | Auto |
|
|
@@ -192,6 +206,11 @@ zero rules.
|
|
|
192
206
|
import-style boundaries
|
|
193
207
|
- **ESLint rule files** for marker based patterns: a rule card (`.md`), the rule (`.ts`), and a passing and a
|
|
194
208
|
failing fixture
|
|
209
|
+
- **A shareable ESLint preset**: one self-contained `eslint-preset.archprint.mjs` that inlines the inferred
|
|
210
|
+
rules and needs only eslint, so you can commit it, publish it, or hand it to another repo and adopt the rules
|
|
211
|
+
in one line
|
|
212
|
+
- **ts-arch tests** for the first-party boundaries (layer, role, UI/data), so the inferred architecture can run
|
|
213
|
+
inside your existing Vitest or Jest suite
|
|
195
214
|
- **Mermaid** and **Graphviz DOT** of the layer dependency graph, so the inferred architecture is visible and
|
|
196
215
|
its violations are marked
|
|
197
216
|
|
|
@@ -248,15 +267,16 @@ Same repo plus same version produces the same output. Analysis is pure and sorte
|
|
|
248
267
|
|
|
249
268
|
## Status and roadmap
|
|
250
269
|
|
|
251
|
-
`0.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
270
|
+
Pre-stable (`0.x`). The engine (twenty detectors, the confidence gate, and emitters for ESLint, a shareable
|
|
271
|
+
preset, dependency-cruiser, ts-arch, and the layer graph) is in place and tested, and an adversarial
|
|
272
|
+
correctness audit (three rounds, four real repositories) drove the false-positive rate on auto-generated rules
|
|
273
|
+
to zero for the mechanical families, which is why those auto-enforce while the structural-inference families
|
|
274
|
+
are held for review.
|
|
255
275
|
|
|
256
|
-
Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
276
|
+
Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families,
|
|
277
|
+
with a self-consistency check at generate time, an `init` scaffolder for fresh repos, and framework coverage
|
|
278
|
+
across React, Angular, Vue, and Svelte. Still ahead: hardening the structural families toward auto-enforcement
|
|
279
|
+
(a real per-file role-confidence measure, layer-cohesion, role-classifier ordering).
|
|
260
280
|
|
|
261
281
|
## Contributing
|
|
262
282
|
|
package/dist/cli/generate.d.ts
CHANGED
|
@@ -18,7 +18,11 @@ export declare function writeEnvAccessConfig(scan: ScanResult, outDir: string):
|
|
|
18
18
|
export declare function writePhantomDependencyConfig(scan: ScanResult, outDir: string): string[];
|
|
19
19
|
export declare function writeEntryPurityConfig(scan: ScanResult, outDir: string): string[];
|
|
20
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[];
|
|
21
24
|
export declare function writeEslintPlugin(scan: ScanResult, outDir: string): string[];
|
|
25
|
+
export declare function writeTsArchTests(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
|
|
22
26
|
export declare function writeGraph(scan: ScanResult, outDir: string): string[];
|
|
23
27
|
export interface WrittenConfig {
|
|
24
28
|
files: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
|