@lucasacoutinho/archer 0.2.1 → 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/README.md +144 -387
- package/dist/analyzer/Analyzer.d.ts +17 -0
- package/dist/analyzer/Analyzer.d.ts.map +1 -0
- package/dist/analyzer/Analyzer.js +15 -0
- package/dist/analyzer/Analyzer.js.map +1 -0
- package/dist/analyzer/bindings.d.ts +26 -0
- package/dist/analyzer/bindings.d.ts.map +1 -0
- package/dist/analyzer/bindings.js +61 -0
- package/dist/analyzer/bindings.js.map +1 -0
- package/dist/analyzer/errors.d.ts +34 -0
- package/dist/analyzer/errors.d.ts.map +1 -0
- package/dist/analyzer/errors.js +20 -0
- package/dist/analyzer/errors.js.map +1 -0
- package/dist/analyzer/graph-file.d.ts +13 -0
- package/dist/analyzer/graph-file.d.ts.map +1 -0
- package/dist/analyzer/graph-file.js +22 -0
- package/dist/analyzer/graph-file.js.map +1 -0
- package/dist/analyzer/paths.d.ts +12 -0
- package/dist/analyzer/paths.d.ts.map +1 -0
- package/dist/analyzer/paths.js +26 -0
- package/dist/analyzer/paths.js.map +1 -0
- package/dist/analyzer/server-exit.d.ts +19 -0
- package/dist/analyzer/server-exit.d.ts.map +1 -0
- package/dist/analyzer/server-exit.js +70 -0
- package/dist/analyzer/server-exit.js.map +1 -0
- package/dist/analyzer/source.d.ts +29 -0
- package/dist/analyzer/source.d.ts.map +1 -0
- package/dist/analyzer/source.js +58 -0
- package/dist/analyzer/source.js.map +1 -0
- package/dist/analyzer/syntax.d.ts +29 -0
- package/dist/analyzer/syntax.d.ts.map +1 -0
- package/dist/analyzer/syntax.js +226 -0
- package/dist/analyzer/syntax.js.map +1 -0
- package/dist/analyzer/targets.d.ts +26 -0
- package/dist/analyzer/targets.d.ts.map +1 -0
- package/dist/analyzer/targets.js +84 -0
- package/dist/analyzer/targets.js.map +1 -0
- package/dist/analyzer/typescript.d.ts +5 -0
- package/dist/analyzer/typescript.d.ts.map +1 -0
- package/dist/analyzer/typescript.js +221 -0
- package/dist/analyzer/typescript.js.map +1 -0
- package/dist/analyzer/workspace.d.ts +24 -0
- package/dist/analyzer/workspace.d.ts.map +1 -0
- package/dist/analyzer/workspace.js +9 -0
- package/dist/analyzer/workspace.js.map +1 -0
- package/dist/cli/explain.d.ts +3 -0
- package/dist/cli/explain.d.ts.map +1 -0
- package/dist/cli/explain.js +21 -0
- package/dist/cli/explain.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +159 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/config/input.d.ts +59 -0
- package/dist/config/input.d.ts.map +1 -0
- package/dist/config/input.js +115 -0
- package/dist/config/input.js.map +1 -0
- package/dist/config/rules.d.ts +94 -0
- package/dist/config/rules.d.ts.map +1 -0
- package/dist/config/rules.js +130 -0
- package/dist/config/rules.js.map +1 -0
- package/dist/core/baseline.d.ts +16 -0
- package/dist/core/baseline.d.ts.map +1 -0
- package/dist/core/baseline.js +25 -0
- package/dist/core/baseline.js.map +1 -0
- package/dist/core/check.d.ts +16 -0
- package/dist/core/check.d.ts.map +1 -0
- package/dist/core/check.js +271 -0
- package/dist/core/check.js.map +1 -0
- package/dist/core/config.d.ts +72 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +78 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/fingerprint.d.ts +54 -0
- package/dist/core/fingerprint.d.ts.map +1 -0
- package/dist/core/fingerprint.js +182 -0
- package/dist/core/fingerprint.js.map +1 -0
- package/dist/core/keys.d.ts +10 -0
- package/dist/core/keys.d.ts.map +1 -0
- package/dist/core/keys.js +16 -0
- package/dist/core/keys.js.map +1 -0
- package/dist/core/lower.d.ts +13 -0
- package/dist/core/lower.d.ts.map +1 -0
- package/dist/core/lower.js +82 -0
- package/dist/core/lower.js.map +1 -0
- package/dist/core/model.d.ts +155 -0
- package/dist/core/model.d.ts.map +1 -0
- package/dist/core/model.js +116 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/pattern.d.ts +13 -0
- package/dist/core/pattern.d.ts.map +1 -0
- package/dist/core/pattern.js +29 -0
- package/dist/core/pattern.js.map +1 -0
- package/dist/core/reach.d.ts +10 -0
- package/dist/core/reach.d.ts.map +1 -0
- package/dist/core/reach.js +81 -0
- package/dist/core/reach.js.map +1 -0
- package/dist/core/rule.d.ts +200 -0
- package/dist/core/rule.d.ts.map +1 -0
- package/dist/core/rule.js +162 -0
- package/dist/core/rule.js.map +1 -0
- package/dist/core/selector.d.ts +7 -0
- package/dist/core/selector.d.ts.map +1 -0
- package/dist/core/selector.js +27 -0
- package/dist/core/selector.js.map +1 -0
- package/dist/core/structural.d.ts +10 -0
- package/dist/core/structural.d.ts.map +1 -0
- package/dist/core/structural.js +24 -0
- package/dist/core/structural.js.map +1 -0
- package/dist/core/text.d.ts +26 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +144 -0
- package/dist/core/text.js.map +1 -0
- package/dist/effect.d.ts +19 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +21 -0
- package/dist/effect.js.map +1 -0
- package/dist/index.d.ts +24 -44
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -52
- package/dist/index.js.map +1 -0
- package/dist/report/agent.d.ts +3 -0
- package/dist/report/agent.d.ts.map +1 -0
- package/dist/report/agent.js +35 -0
- package/dist/report/agent.js.map +1 -0
- package/dist/report/dot.d.ts +4 -0
- package/dist/report/dot.d.ts.map +1 -0
- package/dist/report/dot.js +15 -0
- package/dist/report/dot.js.map +1 -0
- package/dist/report/github.d.ts +3 -0
- package/dist/report/github.d.ts.map +1 -0
- package/dist/report/github.js +18 -0
- package/dist/report/github.js.map +1 -0
- package/dist/report/json.d.ts +3 -0
- package/dist/report/json.d.ts.map +1 -0
- package/dist/report/json.js +4 -0
- package/dist/report/json.js.map +1 -0
- package/dist/report/message.d.ts +20 -0
- package/dist/report/message.d.ts.map +1 -0
- package/dist/report/message.js +73 -0
- package/dist/report/message.js.map +1 -0
- package/dist/report/pretty.d.ts +8 -0
- package/dist/report/pretty.d.ts.map +1 -0
- package/dist/report/pretty.js +89 -0
- package/dist/report/pretty.js.map +1 -0
- package/dist/report/render.d.ts +9 -0
- package/dist/report/render.d.ts.map +1 -0
- package/dist/report/render.js +10 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/report.d.ts +44 -0
- package/dist/report/report.d.ts.map +1 -0
- package/dist/report/report.js +49 -0
- package/dist/report/report.js.map +1 -0
- package/dist/report/sarif.d.ts +3 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +68 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/run.d.ts +49 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +87 -0
- package/dist/run.js.map +1 -0
- package/dist/shell/baseline-file.d.ts +20 -0
- package/dist/shell/baseline-file.d.ts.map +1 -0
- package/dist/shell/baseline-file.js +46 -0
- package/dist/shell/baseline-file.js.map +1 -0
- package/dist/shell/load-config.d.ts +19 -0
- package/dist/shell/load-config.d.ts.map +1 -0
- package/dist/shell/load-config.js +39 -0
- package/dist/shell/load-config.js.map +1 -0
- package/package.json +29 -177
- package/skills/archer/SKILL.md +127 -45
- package/skills/archer/agents/openai.yaml +2 -2
- package/skills/archer/references/rules.md +267 -54
- package/dist/bun/plugin.cjs +0 -85
- package/dist/bun/plugin.d.cts +0 -7
- package/dist/bun/plugin.d.ts +0 -7
- package/dist/bun/plugin.js +0 -11
- package/dist/bun.cjs +0 -2238
- package/dist/bun.d.cts +0 -13
- package/dist/bun.d.ts +0 -13
- package/dist/bun.js +0 -28
- package/dist/chunk-2AKMBLHG.js +0 -2165
- package/dist/chunk-67YEOTXP.js +0 -25
- package/dist/chunk-IDVLHNL2.js +0 -46
- package/dist/chunk-ZZ6TR4PO.js +0 -201
- package/dist/cli.js +0 -2643
- package/dist/index.cjs +0 -2484
- package/dist/index.d.cts +0 -45
- package/dist/jest/plugin.cjs +0 -85
- package/dist/jest/plugin.d.cts +0 -7
- package/dist/jest/plugin.d.ts +0 -7
- package/dist/jest/plugin.js +0 -11
- package/dist/jest.cjs +0 -2237
- package/dist/jest.d.cts +0 -13
- package/dist/jest.d.ts +0 -13
- package/dist/jest.js +0 -27
- package/dist/matchers-Cqnbsyxe.d.cts +0 -11
- package/dist/matchers-Cqnbsyxe.d.ts +0 -11
- package/dist/node.cjs +0 -2237
- package/dist/node.d.cts +0 -13
- package/dist/node.d.ts +0 -13
- package/dist/node.js +0 -27
- package/dist/reporters-BbAXMpuW.d.cts +0 -6
- package/dist/reporters-xh-HQneS.d.ts +0 -6
- package/dist/suite-BwnPDCvU.d.cts +0 -337
- package/dist/suite-BwnPDCvU.d.ts +0 -337
- package/dist/vitest/plugin.cjs +0 -85
- package/dist/vitest/plugin.d.cts +0 -7
- package/dist/vitest/plugin.d.ts +0 -7
- package/dist/vitest/plugin.js +0 -11
- package/dist/vitest.cjs +0 -2531
- package/dist/vitest.d.cts +0 -37
- package/dist/vitest.d.ts +0 -37
- package/dist/vitest.js +0 -139
package/package.json
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucasacoutinho/archer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Architecture tests for TypeScript and JavaScript projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
|
7
7
|
"architecture-testing",
|
|
8
|
-
"archunit",
|
|
9
8
|
"boundaries",
|
|
10
|
-
"bun",
|
|
11
9
|
"dependency-rules",
|
|
12
|
-
"
|
|
13
|
-
"jest",
|
|
14
|
-
"monorepo",
|
|
15
|
-
"typescript",
|
|
16
|
-
"vitest"
|
|
10
|
+
"typescript"
|
|
17
11
|
],
|
|
18
12
|
"homepage": "https://github.com/lucasacoutinho/archer#readme",
|
|
19
13
|
"bugs": {
|
|
@@ -25,194 +19,52 @@
|
|
|
25
19
|
"type": "git",
|
|
26
20
|
"url": "git+https://github.com/lucasacoutinho/archer.git"
|
|
27
21
|
},
|
|
28
|
-
"
|
|
29
|
-
"archer": "dist/cli.js"
|
|
30
|
-
},
|
|
22
|
+
"type": "module",
|
|
31
23
|
"files": [
|
|
32
24
|
"dist",
|
|
33
25
|
"skills",
|
|
34
|
-
"README.md",
|
|
35
26
|
"LICENSE",
|
|
36
27
|
"NOTICE"
|
|
37
28
|
],
|
|
38
|
-
"type": "module",
|
|
39
|
-
"sideEffects": [
|
|
40
|
-
"./dist/bun/plugin.*",
|
|
41
|
-
"./dist/jest/plugin.*",
|
|
42
|
-
"./dist/vitest/plugin.*"
|
|
43
|
-
],
|
|
44
|
-
"main": "./dist/index.cjs",
|
|
45
|
-
"module": "./dist/index.js",
|
|
46
|
-
"types": "./dist/index.d.ts",
|
|
47
|
-
"typesVersions": {
|
|
48
|
-
"*": {
|
|
49
|
-
"vitest": [
|
|
50
|
-
"dist/vitest.d.ts"
|
|
51
|
-
],
|
|
52
|
-
"vitest/plugin": [
|
|
53
|
-
"dist/vitest/plugin.d.ts"
|
|
54
|
-
],
|
|
55
|
-
"jest": [
|
|
56
|
-
"dist/jest.d.ts"
|
|
57
|
-
],
|
|
58
|
-
"jest/plugin": [
|
|
59
|
-
"dist/jest/plugin.d.ts"
|
|
60
|
-
],
|
|
61
|
-
"bun": [
|
|
62
|
-
"dist/bun.d.ts"
|
|
63
|
-
],
|
|
64
|
-
"bun/plugin": [
|
|
65
|
-
"dist/bun/plugin.d.ts"
|
|
66
|
-
],
|
|
67
|
-
"node": [
|
|
68
|
-
"dist/node.d.ts"
|
|
69
|
-
],
|
|
70
|
-
"*": [
|
|
71
|
-
"dist/index.d.ts"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
29
|
"exports": {
|
|
76
30
|
".": {
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
"default": "./dist/index.js"
|
|
80
|
-
},
|
|
81
|
-
"require": {
|
|
82
|
-
"types": "./dist/index.d.cts",
|
|
83
|
-
"default": "./dist/index.cjs"
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
"./vitest": {
|
|
87
|
-
"import": {
|
|
88
|
-
"types": "./dist/vitest.d.ts",
|
|
89
|
-
"default": "./dist/vitest.js"
|
|
90
|
-
},
|
|
91
|
-
"require": {
|
|
92
|
-
"types": "./dist/vitest.d.cts",
|
|
93
|
-
"default": "./dist/vitest.cjs"
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
"./vitest/plugin": {
|
|
97
|
-
"import": {
|
|
98
|
-
"types": "./dist/vitest/plugin.d.ts",
|
|
99
|
-
"default": "./dist/vitest/plugin.js"
|
|
100
|
-
},
|
|
101
|
-
"require": {
|
|
102
|
-
"types": "./dist/vitest/plugin.d.cts",
|
|
103
|
-
"default": "./dist/vitest/plugin.cjs"
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
"./jest": {
|
|
107
|
-
"import": {
|
|
108
|
-
"types": "./dist/jest.d.ts",
|
|
109
|
-
"default": "./dist/jest.js"
|
|
110
|
-
},
|
|
111
|
-
"require": {
|
|
112
|
-
"types": "./dist/jest.d.cts",
|
|
113
|
-
"default": "./dist/jest.cjs"
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
"./jest/plugin": {
|
|
117
|
-
"import": {
|
|
118
|
-
"types": "./dist/jest/plugin.d.ts",
|
|
119
|
-
"default": "./dist/jest/plugin.js"
|
|
120
|
-
},
|
|
121
|
-
"require": {
|
|
122
|
-
"types": "./dist/jest/plugin.d.cts",
|
|
123
|
-
"default": "./dist/jest/plugin.cjs"
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"./bun": {
|
|
127
|
-
"import": {
|
|
128
|
-
"types": "./dist/bun.d.ts",
|
|
129
|
-
"default": "./dist/bun.js"
|
|
130
|
-
},
|
|
131
|
-
"require": {
|
|
132
|
-
"types": "./dist/bun.d.cts",
|
|
133
|
-
"default": "./dist/bun.cjs"
|
|
134
|
-
}
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
135
33
|
},
|
|
136
|
-
"./
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
"default": "./dist/bun/plugin.js"
|
|
140
|
-
},
|
|
141
|
-
"require": {
|
|
142
|
-
"types": "./dist/bun/plugin.d.cts",
|
|
143
|
-
"default": "./dist/bun/plugin.cjs"
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
"./node": {
|
|
147
|
-
"import": {
|
|
148
|
-
"types": "./dist/node.d.ts",
|
|
149
|
-
"default": "./dist/node.js"
|
|
150
|
-
},
|
|
151
|
-
"require": {
|
|
152
|
-
"types": "./dist/node.d.cts",
|
|
153
|
-
"default": "./dist/node.cjs"
|
|
154
|
-
}
|
|
34
|
+
"./effect": {
|
|
35
|
+
"types": "./dist/effect.d.ts",
|
|
36
|
+
"default": "./dist/effect.js"
|
|
155
37
|
}
|
|
156
38
|
},
|
|
39
|
+
"bin": {
|
|
40
|
+
"archer": "dist/cli/main.js"
|
|
41
|
+
},
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.18.0"
|
|
45
|
+
},
|
|
157
46
|
"publishConfig": {
|
|
158
47
|
"access": "public"
|
|
159
48
|
},
|
|
160
49
|
"scripts": {
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"fmt": "oxfmt --write .",
|
|
165
|
-
"fmt:check": "oxfmt --check .",
|
|
166
|
-
"lint": "oxlint",
|
|
167
|
-
"lint:fix": "oxlint --fix",
|
|
168
|
-
"prepublishOnly": "npm run check",
|
|
169
|
-
"release:check": "npm run check && npm run check:package && npm audit --omit=dev && npm pack --dry-run",
|
|
50
|
+
"prepare": "effect-tsgo patch --typescript --no-oxlint",
|
|
51
|
+
"build": "tsc -p tsconfig.build.json",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json && sh scripts/check-declarations.sh",
|
|
170
53
|
"test": "vitest run",
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"test:jest": "jest --runInBand --runTestsByPath test/runners/jest-adapter.test.cjs",
|
|
176
|
-
"test:node": "node --test test/runners/node-adapter.test.mjs",
|
|
177
|
-
"test:tia": "vitest run --config vitest.tia.config.ts",
|
|
178
|
-
"test:tia:verify": "node config/run-tiacore.mjs verify --output .tia/verification.json -- npm run test:tia",
|
|
179
|
-
"typecheck": "tsc --noEmit"
|
|
54
|
+
"smoke:bun": "bun scripts/bun-smoke.mjs",
|
|
55
|
+
"lean": "sh scripts/lean-audit.sh",
|
|
56
|
+
"check": "npm run lean && npm run typecheck && npm test && npm run build && npm run dogfood",
|
|
57
|
+
"dogfood": "node dist/cli/main.js check"
|
|
180
58
|
},
|
|
181
59
|
"dependencies": {
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"picomatch": "^4.0.3",
|
|
186
|
-
"tinyglobby": "^0.2.15",
|
|
187
|
-
"typescript": "^5.9.0"
|
|
60
|
+
"@effect/platform-node": "4.0.6",
|
|
61
|
+
"effect": "4.0.6",
|
|
62
|
+
"typescript": "7.0.2"
|
|
188
63
|
},
|
|
189
64
|
"devDependencies": {
|
|
190
|
-
"@
|
|
191
|
-
"@
|
|
192
|
-
"@types/
|
|
193
|
-
"
|
|
194
|
-
"@types/picomatch": "^4.0.2",
|
|
195
|
-
"jest": "^30.4.2",
|
|
196
|
-
"oxfmt": "^0.61.0",
|
|
197
|
-
"oxlint": "^1.76.0",
|
|
198
|
-
"publint": "^0.3.12",
|
|
199
|
-
"tsup": "^8.5.0",
|
|
200
|
-
"typescript": "^5.9.0",
|
|
201
|
-
"vitest": "^4.0.0"
|
|
202
|
-
},
|
|
203
|
-
"peerDependencies": {
|
|
204
|
-
"@jest/globals": ">=29.0.0 <31.0.0",
|
|
205
|
-
"vitest": ">=3.0.0 <5.0.0"
|
|
206
|
-
},
|
|
207
|
-
"peerDependenciesMeta": {
|
|
208
|
-
"@jest/globals": {
|
|
209
|
-
"optional": true
|
|
210
|
-
},
|
|
211
|
-
"vitest": {
|
|
212
|
-
"optional": true
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
"engines": {
|
|
216
|
-
"node": ">=20.0.0"
|
|
65
|
+
"@effect/tsgo": "0.56.1",
|
|
66
|
+
"@effect/vitest": "4.0.6",
|
|
67
|
+
"@types/node": "22.20.5",
|
|
68
|
+
"vitest": "5.0.5"
|
|
217
69
|
}
|
|
218
70
|
}
|
package/skills/archer/SKILL.md
CHANGED
|
@@ -1,81 +1,163 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: archer
|
|
3
|
-
description:
|
|
3
|
+
description: Set up, run, and fix architecture rules in TypeScript and JavaScript repositories with Archer (@lucasacoutinho/archer). Covers archer.config.ts (layers, stack, allow, publicApis, r.* rules), `archer check -r agent`, repairing violations in source without weakening the policy, baselines for adopting Archer on existing debt, and the one-line architecture test. Use when the user wants to enforce layers, module boundaries, dependency direction, import cycles, or public entrypoints, when the repo has an archer.config.* file, or when `archer check` or an architecture test reports findings. Do not use for ESLint import rules or other dependency linters.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Archer
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Archer turns architecture decisions into rules that fail the build. A rule is plain data in `archer.config.ts`. `archer check` analyzes the project with TypeScript and reports every violation with its import site.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The rules are the user's policy. When a rule fails, change the source.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
2. Find existing `archer.config.*` and architecture tests before adding another entrypoint.
|
|
14
|
-
3. Identify real dependency directions from imports. Do not infer layers from folder names alone when the code says otherwise.
|
|
15
|
-
4. Read the installed Archer README or declarations when its version may differ from this skill.
|
|
12
|
+
## Ground rules
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
- Fix findings in source. Do not delete or loosen a rule, lower a severity, widen `allow`, add a `{ not: ... }` exclusion, set `localOnly`, narrow `kinds`, set `allowComputed`, or record a baseline to get a green check.
|
|
15
|
+
- Make any of those changes only when the user asks for it. If a finding looks wrong or the rule looks outdated, say so and propose the change.
|
|
16
|
+
- Never claim a boundary holds when Archer could not prove it. Say what it could not see (see "Computed imports").
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
## Inspect first
|
|
19
|
+
|
|
20
|
+
1. Look for `archer.config.{ts,mts,js,mjs}`, `.archer-baseline`, and an existing architecture test. Update what exists instead of adding a second entrypoint.
|
|
21
|
+
2. Read `package.json` and the tsconfig files. Archer analyzes only the files that the tsconfigs in `projects` list (default `tsconfig.json`). A file outside them is invisible to every rule, so list each package's tsconfig in a monorepo.
|
|
22
|
+
3. Read real imports before choosing layers. Folder names suggest layers, but the imports show which directions the code already follows. Ask the user when the intended direction is not clear.
|
|
23
|
+
4. If the installed version may differ from this skill, read `node_modules/@lucasacoutinho/archer/dist/index.d.ts`.
|
|
24
|
+
|
|
25
|
+
## Set up
|
|
26
|
+
|
|
27
|
+
Install `@lucasacoutinho/archer` as a dev dependency with the repo's package manager. It needs Node 22.18 or later. Run `npx archer init` (or the package manager's local-exec equivalent) to write a starter `archer.config.ts`, then edit it to match the repo:
|
|
20
28
|
|
|
21
29
|
```ts
|
|
22
|
-
import { defineConfig } from
|
|
30
|
+
import { defineConfig, presets, r } from "@lucasacoutinho/archer"
|
|
23
31
|
|
|
24
32
|
export default defineConfig({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.because('Domain code must remain independent of adapters.')
|
|
31
|
-
|
|
32
|
-
archer.expect('src/**').toBeFreeOfCycles().as('dependencies/no-cycles')
|
|
33
|
+
layers: {
|
|
34
|
+
web: "src/web",
|
|
35
|
+
application: "src/application",
|
|
36
|
+
domain: "src/domain",
|
|
37
|
+
infrastructure: "src/infrastructure",
|
|
33
38
|
},
|
|
39
|
+
stack: ["web", "application", "domain"],
|
|
40
|
+
allow: { infrastructure: ["application", "domain"] },
|
|
41
|
+
publicApis: { domain: "src/domain/index.ts" },
|
|
42
|
+
rules: [
|
|
43
|
+
...presets.recommended,
|
|
44
|
+
r.forbid("domain/pure", "layer:domain", ["npm:*", "node:*"], {
|
|
45
|
+
reason: "The domain stays free of frameworks and I/O.",
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
34
48
|
})
|
|
35
49
|
```
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
- `layers` names a selector per layer. Rules refer to it as `layer:name`.
|
|
52
|
+
- `stack` lists layers from top to bottom. Each layer may import the layers listed after it.
|
|
53
|
+
- `allow` adds layers a layer may import beyond its position in the stack.
|
|
54
|
+
- `publicApis` names, per layer, the entry files that other layers must import through.
|
|
55
|
+
- `rules` takes `r.*` constructors. Every rule needs an id that is unique and stays stable, because findings and baselines key on it.
|
|
56
|
+
- `projects`, `root`, `baseline`, and `failOnWarnings` are described in [references/rules.md](references/rules.md), together with every `r.*` constructor and the selector grammar. Open it when writing or reading a rule.
|
|
38
57
|
|
|
39
|
-
Use `
|
|
58
|
+
Layer rules constrain only direct imports of local files that belong to another declared layer. They ignore packages, built-ins, files outside every layer, and computed imports. Use `r.forbid` or `r.onlyDependOn` with `npm:*` or `node:*` for those. A layer named in neither `stack` nor `allow` has no limits of its own, but constrained layers may not import it unless `allow` says so, so put shared code at the bottom of `stack`.
|
|
40
59
|
|
|
41
|
-
|
|
42
|
-
import { arch } from '@lucasacoutinho/archer/vitest'
|
|
60
|
+
The starter config names `src/app`, which may not exist. A rule whose subject matches no file passes, and so does the generated `layer/<name>` rule of an empty layer. Both are listed in `emptySubjects` in the summary line. Read that list after every run, and fix a non-empty one unless the code is meant to appear later.
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
A dependency rule whose target names files by a glob but selects none is listed in `emptyObjects`. That is almost always a mistyped path. A target that names only packages is never listed, so for package targets run the new rule against a minimal case (see "Test a rule without the filesystem" in the reference).
|
|
63
|
+
|
|
64
|
+
If the first run shows many violations, check the layer order and selectors before you blame the code.
|
|
65
|
+
|
|
66
|
+
## Run
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
npx archer check -r agent
|
|
45
70
|
```
|
|
46
71
|
|
|
47
|
-
|
|
72
|
+
The command exits 0 when the verdict is `pass` and 1 when it is `fail`. It exits 2 when it cannot check, including on an unknown flag or value. The output has one `finding` line per violation and a `summary` line last:
|
|
48
73
|
|
|
49
|
-
|
|
74
|
+
```text
|
|
75
|
+
finding id="layer/domain|/src/domain/order.ts|dependency|static|../infrastructure/db.ts|file|/src/infrastructure/db.ts" severity=error rule="layer/domain" subject="src/domain/order.ts" at="src/domain/order.ts:1:25" message="imports src/infrastructure/db.ts ('../infrastructure/db.ts')"
|
|
76
|
+
summary verdict=fail errors=1 warnings=0 infos=0 suppressed=0 stale=0 files=2 dependencies=1 emptySubjects=[] emptyObjects=[]
|
|
77
|
+
```
|
|
50
78
|
|
|
51
|
-
|
|
79
|
+
- `id` is the fingerprint: rule, file, and what was found, with no line numbers or wording. It survives moving lines but changes when the specifier or target changes, so a finding is fixed when its `id` no longer appears. A file that repeats one import produces several lines with the same `id`.
|
|
80
|
+
- `severity`: `error` fails the check, `warning` fails it only with `failOnWarnings: true`, `info` never does.
|
|
81
|
+
- `rule` is a rule id from the config, or a generated `layer/<name>` or `public-api/<layer>`.
|
|
82
|
+
- `subject` is the file that breaks the rule.
|
|
83
|
+
- `at` is `file:line:column` of the import, call, or declaration. Findings about a whole file (missing dependency, export, file name, line count) have no `at`.
|
|
84
|
+
- `message` says what Archer found. `reason` is the rule's stated reason, when it has one. Read it before you choose a fix.
|
|
85
|
+
- In the summary, `suppressed` counts findings the baseline hides, `stale` counts baseline entries that no finding matches, `emptySubjects` lists rule ids that select no file, and `emptyObjects` lists dependency rules whose file-glob target selects nothing.
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
- Give durable rules an ID with `.as()` and record the reason with `.because()`.
|
|
55
|
-
- Use `.localDependencies()` for layer allow-lists so npm and platform modules do not need repetition.
|
|
56
|
-
- Use dependency-kind modifiers when type-only, runtime, or dynamic coupling has different meaning.
|
|
57
|
-
- Treat `dynamic:unknown` as unproven. Do not allow it just to make a check pass.
|
|
58
|
-
- Avoid broad ignores. Narrow an exception to the smallest selector and explain it.
|
|
87
|
+
Other flags:
|
|
59
88
|
|
|
60
|
-
|
|
89
|
+
- `-c <file>` picks a config.
|
|
90
|
+
- `-o <file>` writes the report to a file.
|
|
91
|
+
- `-w` re-runs on change.
|
|
92
|
+
- `--ignore-baseline` reports baselined findings too.
|
|
93
|
+
- `--graph <file>` checks a graph saved with `archer graph -o <file>`. The other reporters are `pretty` (the default), `json`, `sarif`, `github`, and `dot`.
|
|
61
94
|
|
|
62
|
-
|
|
95
|
+
Exit code 2 means Archer could not check at all, and the message says why. Typical causes:
|
|
63
96
|
|
|
64
|
-
|
|
97
|
+
- **Invalid configuration, with a list of issues.** A field has the wrong shape. A selector does not parse (`[a-z]`, `a//b`, unbalanced braces, a bad `npm:` name). A layer is named like an integer such as `"0"`. A `layers` or `publicApis` selector uses `layer:`.
|
|
98
|
+
- **Invalid configuration, with one reason.** A rule, `stack`, `allow`, or `publicApis` refers to an unknown layer. Two rules share an id, and a generated `layer/<name>` counts. Or the config produces no rules at all.
|
|
99
|
+
- **No configuration file.** No `archer.config.*` is in the current directory. Pass `-c`.
|
|
100
|
+
- **No tsconfig, or one that does not parse.** A tsconfig in `projects` is missing or invalid. Check `projects` and `root`.
|
|
65
101
|
|
|
66
|
-
|
|
67
|
-
|
|
102
|
+
## Fix findings
|
|
103
|
+
|
|
104
|
+
Read the summary line first. Then take findings by rule, open the file at `at`, read `reason`, and change the code. After each batch, re-run `archer check -r agent`, then the repo's typecheck and tests.
|
|
105
|
+
|
|
106
|
+
| Finding | Usual fix |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `layer/*`, `r.forbid`, `r.onlyDependOn`: "imports X" | Move the code into a layer that may own it. Or invert the dependency: put an interface in the lower layer, implement it in the higher one, and pass the implementation in. Deleting the import is a fix only when the code does not need it. |
|
|
109
|
+
| `public-api/*` | Import through the layer's entry file. If the symbol belongs to the public surface, export it from that entry file. |
|
|
110
|
+
| `archer/acyclic`: "is part of a dependency cycle: a.ts → b.ts → a.ts" | Move what both sides need into a third file that neither imports back. Every file in a cycle gets its own finding, so one fix clears several. |
|
|
111
|
+
| Transitive rule: "depends on x.ts through a.ts → b.ts → x.ts" | Cut the chain at the link that crosses the boundary. `at` is the first import in the chain, and the wrong link may be further down. |
|
|
112
|
+
| "does not satisfy the rule's requirement" | A `require`-style rule found nothing. Add the dependency, dependent, or call it asks for. |
|
|
113
|
+
| Declarations, exports, calls, file names, line counts | Change the declaration, narrow the exports, replace the call, rename the file, or split it. |
|
|
114
|
+
| Custom rule | The message is whatever the rule's check wrote. Read the check in the config. |
|
|
115
|
+
|
|
116
|
+
A fix that moves a file changes which layer it belongs to, so check that the new location is the one the layer selectors expect.
|
|
117
|
+
|
|
118
|
+
### Computed imports
|
|
119
|
+
|
|
120
|
+
An `import(expr)` or `require(expr)` with a non-literal specifier has no static target, so Archer fails closed. `r.forbid` and `r.onlyDependOn` report it whatever the rule's object is, because it might load anything. `r.require` is never satisfied by one. `archer/resolvable`, which `presets.recommended` includes, reports it too. Layer rules skip it, because they follow local files only.
|
|
121
|
+
|
|
122
|
+
Replace the computed specifier with literals, so every target is analyzed and checked against the layers:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const locales = {
|
|
126
|
+
en: () => import("./locales/en.ts"),
|
|
127
|
+
pt: () => import("./locales/pt.ts"),
|
|
128
|
+
}
|
|
68
129
|
```
|
|
69
130
|
|
|
70
|
-
|
|
131
|
+
If the dynamic load is deliberate, such as a plugin loader, tell the user. The usual policy for that is a narrow `r.resolvable(..., { allowComputed: true, reason })` on that one file, plus excluding the file from the subjects of the other rules that flag it. Apply that only after the user agrees, and name the file as unverified in your report.
|
|
132
|
+
|
|
133
|
+
## Baselines
|
|
134
|
+
|
|
135
|
+
A baseline is for adopting Archer in a codebase that already has violations. Use it only when the user agrees to accept the current debt.
|
|
136
|
+
|
|
137
|
+
1. Run `npx archer check -r agent`, fix what is cheap, and confirm that every remaining finding is debt the user accepts.
|
|
138
|
+
2. Run `npx archer baseline record`. It replaces `.archer-baseline` (the `baseline` field sets the path) with the fingerprints of all current findings. Commit the file.
|
|
139
|
+
3. From then on, the baselined findings are hidden and counted in `suppressed`. A new error still fails the check.
|
|
140
|
+
4. As the debt gets fixed, `stale` grows. Run `npx archer baseline prune` to drop entries that no finding matches. It never adds an entry.
|
|
141
|
+
|
|
142
|
+
One entry hides every finding with that `id`, so a second copy of an already baselined import in the same file is hidden too. Do not run `record` to clear a failing check, and do not edit the baseline by hand. Renaming a rule id orphans that rule's entries, and its old findings come back.
|
|
143
|
+
|
|
144
|
+
## Add the test
|
|
145
|
+
|
|
146
|
+
Add one test next to the repo's other tests, written for its runner:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { assertArchitecture, dispose } from "@lucasacoutinho/archer"
|
|
150
|
+
import { afterAll, test } from "vitest"
|
|
151
|
+
|
|
152
|
+
afterAll(dispose)
|
|
153
|
+
|
|
154
|
+
test("architecture", () => assertArchitecture(), 60_000)
|
|
155
|
+
```
|
|
71
156
|
|
|
72
|
-
|
|
73
|
-
2. Fix the source dependency or declaration that violates the stated rule.
|
|
74
|
-
3. Re-run the narrow Archer command, then the repository's normal checks.
|
|
75
|
-
4. Do not weaken a rule, add an ignore, or update the baseline unless the user explicitly approves that policy change.
|
|
157
|
+
`assertArchitecture` runs the whole check and rejects with an `ArchitectureError` when the verdict is `fail`. The error message is the pretty report, and `error.report` holds the data. It takes `{ config, cwd }`: `config` is a file path or an inline config, and `cwd` defaults to the process directory, so pass it when the runner starts elsewhere. Analysis can take a while on a large repo, so raise the runner's timeout. `dispose` stops the TypeScript server that the first run started, as Archer's own test does.
|
|
76
158
|
|
|
77
|
-
|
|
159
|
+
Add `archer check` to the repo's CI or a `package.json` script when the user wants the check outside the test run.
|
|
78
160
|
|
|
79
|
-
## Report
|
|
161
|
+
## Report
|
|
80
162
|
|
|
81
|
-
|
|
163
|
+
List the findings you fixed and the ones that remain, by `rule` and `id`. Report policy changes (rules, baseline, config) separately from source fixes, and say whether the user asked for them. Name anything Archer could not see: computed imports, files outside the tsconfigs, rules in `emptySubjects` or `emptyObjects`. End with the final `summary` line.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: 'Archer architecture tests'
|
|
3
|
-
short_description: 'Add and enforce TypeScript architecture rules'
|
|
4
|
-
default_prompt: 'Use $archer to
|
|
3
|
+
short_description: 'Add and enforce TypeScript and JavaScript architecture rules'
|
|
4
|
+
default_prompt: 'Use $archer to set up Archer in this repository, run the architecture check, and fix any findings in source without weakening the rules.'
|