@makinzm/mille 0.0.9 → 0.0.11
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 +233 -123
- package/index.js +10 -0
- package/mille.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,59 +1,51 @@
|
|
|
1
1
|
# mille
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Like a mille crêpe — your architecture, one clean layer at a time.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```
|
|
6
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ presentation
|
|
7
|
+
· · · · · · · · · · · · · · · · · · (deps only flow inward)
|
|
8
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ infrastructure
|
|
9
|
+
· · · · · · · · · · · · · · · · · ·
|
|
10
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ usecase
|
|
11
|
+
· · · · · · · · · · · · · · · · · ·
|
|
12
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ domain
|
|
13
|
+
```
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
`mille` is a static analysis CLI that enforces **dependency rules for layered architectures** — Clean Architecture, Onion Architecture, Hexagonal Architecture, and more.
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
One TOML config. Rust-powered. CI-ready. Supports multiple languages from a single config file.
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|---|---|
|
|
13
|
-
| Internal layer dependency check (`dependency_mode`) | ✅ |
|
|
14
|
-
| External library dependency check (`external_mode`) | ✅ |
|
|
15
|
-
| DI entrypoint method call check (`allow_call_patterns`) | ✅ |
|
|
16
|
-
| Rust support | ✅ |
|
|
17
|
-
| Go support | ✅ |
|
|
18
|
-
| Python support | ✅ |
|
|
19
|
-
| TypeScript / JavaScript support | ✅ |
|
|
19
|
+
## What it checks
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
| Check | Rust | Go | TypeScript | JavaScript | Python |
|
|
22
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
23
|
+
| Layer dependency rules (`dependency_mode`) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
24
|
+
| External library rules (`external_mode`) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
25
|
+
| DI method call rules (`allow_call_patterns`) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
cargo install mille
|
|
27
|
-
```
|
|
27
|
+
## Install
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### cargo
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
|
|
33
|
-
uv add --dev mille
|
|
34
|
-
uv run mille check
|
|
35
|
-
|
|
36
|
-
# pip
|
|
37
|
-
pip install mille
|
|
38
|
-
mille check
|
|
32
|
+
cargo install mille
|
|
39
33
|
```
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### npm (Node.js users)
|
|
35
|
+
### npm
|
|
44
36
|
|
|
45
37
|
```sh
|
|
46
38
|
npm install -g @makinzm/mille
|
|
47
39
|
mille check
|
|
48
40
|
```
|
|
49
41
|
|
|
50
|
-
Or
|
|
42
|
+
Or without installing globally:
|
|
51
43
|
|
|
52
44
|
```sh
|
|
53
45
|
npx @makinzm/mille check
|
|
54
46
|
```
|
|
55
47
|
|
|
56
|
-
Requires Node.js ≥ 18.
|
|
48
|
+
Requires Node.js ≥ 18. Bundles `mille.wasm` — no native compilation needed.
|
|
57
49
|
|
|
58
50
|
### go install
|
|
59
51
|
|
|
@@ -61,11 +53,23 @@ Requires Node.js ≥ 18. The npm package bundles `mille.wasm` (the compiled Rust
|
|
|
61
53
|
go install github.com/makinzm/mille/packages/go/mille@latest
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
|
|
56
|
+
Embeds `mille.wasm` via [wazero](https://wazero.io/) — fully self-contained binary.
|
|
65
57
|
|
|
66
|
-
###
|
|
58
|
+
### pip / uv
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
```sh
|
|
61
|
+
# uv (recommended)
|
|
62
|
+
uv add --dev mille
|
|
63
|
+
uv run mille check
|
|
64
|
+
|
|
65
|
+
# pip
|
|
66
|
+
pip install mille
|
|
67
|
+
mille check
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Binary download
|
|
71
|
+
|
|
72
|
+
Pre-built binaries are on [GitHub Releases](https://github.com/makinzm/mille/releases):
|
|
69
73
|
|
|
70
74
|
| Platform | Archive |
|
|
71
75
|
|---|---|
|
|
@@ -75,19 +79,56 @@ Pre-built binaries for each platform are available on [GitHub Releases](https://
|
|
|
75
79
|
| macOS arm64 | `mille-<version>-aarch64-apple-darwin.tar.gz` |
|
|
76
80
|
| Windows x86_64 | `mille-<version>-x86_64-pc-windows-msvc.zip` |
|
|
77
81
|
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### 1. Generate `mille.toml` with `mille init`
|
|
85
|
+
|
|
78
86
|
```sh
|
|
79
|
-
|
|
80
|
-
curl -L https://github.com/makinzm/mille/releases/latest/download/mille-<version>-x86_64-unknown-linux-gnu.tar.gz | tar xz
|
|
81
|
-
./mille check
|
|
87
|
+
mille init
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
`mille init` analyzes actual import statements in your source files to infer layer structure and dependencies — no predetermined naming conventions needed. It prints the inferred dependency graph before writing the config:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Detected languages: rust
|
|
94
|
+
Scanning imports...
|
|
95
|
+
Using layer depth: 2
|
|
96
|
+
|
|
97
|
+
Inferred layer structure:
|
|
98
|
+
domain ← (no internal dependencies)
|
|
99
|
+
usecase → domain
|
|
100
|
+
external: anyhow
|
|
101
|
+
infrastructure → domain
|
|
102
|
+
external: serde, tokio
|
|
103
|
+
|
|
104
|
+
Generated 'mille.toml'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Flag | Default | Description |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `--output <path>` | `mille.toml` | Write config to a custom path |
|
|
110
|
+
| `--force` | false | Overwrite an existing file without prompting |
|
|
111
|
+
| `--depth <N>` | auto | Layer detection depth from project root |
|
|
112
|
+
|
|
113
|
+
**`--depth` and auto-detection**: `mille init` automatically finds the right layer depth by trying depths 1–6, skipping common source-layout roots (`src`, `lib`, `app`, etc.), and selecting the first depth that yields 2–8 candidate layers. For a project with `src/domain/entity`, `src/domain/repository`, `src/usecase/` — depth 2 is chosen, rolling `entity` and `repository` up into `domain`. Use `--depth N` to override when auto-detection picks the wrong level.
|
|
114
|
+
|
|
115
|
+
The generated config includes `allow` (inferred internal dependencies) and `external_allow` (detected external packages) per layer. After generating, review the config and run `mille check` to see results.
|
|
116
|
+
|
|
117
|
+
**Naming in monorepos**: When multiple sub-projects contain a directory with the same name (e.g. `crawler/src/domain` and `server/src/domain`), `mille init` gives each its own layer with a distinguishing prefix (`crawler_domain`, `server_domain`). Merging is left to you.
|
|
118
|
+
|
|
119
|
+
**Excluded paths**: `mille check` automatically skips `.venv`, `venv`, `node_modules`, `target`, `dist`, `build`, and similar build/dependency directories, so generated `paths` patterns like `apps/**` are safe to use.
|
|
85
120
|
|
|
86
|
-
|
|
121
|
+
**Python submodule imports**: `external_allow = ["matplotlib"]` correctly allows both `import matplotlib` and `import matplotlib.pyplot`.
|
|
122
|
+
|
|
123
|
+
**Go projects**: `mille init` reads `go.mod` and generates `[resolve.go] module_name` automatically — internal module imports are classified correctly during `mille check`. External packages appear in `external_allow` with their full import paths (e.g. `"github.com/cilium/ebpf"`, `"fmt"`, `"net/http"`).
|
|
124
|
+
|
|
125
|
+
**TypeScript/JavaScript subpath imports**: `external_allow = ["vitest"]` correctly allows both `import "vitest"` and `import "vitest/config"`. Scoped packages (`@scope/name/sub`) are matched by `"@scope/name"`.
|
|
126
|
+
|
|
127
|
+
### 2. (Or) Create `mille.toml` manually
|
|
87
128
|
|
|
88
129
|
Place `mille.toml` in your project root:
|
|
89
130
|
|
|
90
|
-
**Rust
|
|
131
|
+
**Rust:**
|
|
91
132
|
|
|
92
133
|
```toml
|
|
93
134
|
[project]
|
|
@@ -132,17 +173,16 @@ external_allow = ["clap"]
|
|
|
132
173
|
allow_methods = ["new", "build", "create", "init", "setup"]
|
|
133
174
|
```
|
|
134
175
|
|
|
135
|
-
**
|
|
176
|
+
**TypeScript / JavaScript:**
|
|
136
177
|
|
|
137
178
|
```toml
|
|
138
179
|
[project]
|
|
139
|
-
name = "my-
|
|
180
|
+
name = "my-ts-app"
|
|
140
181
|
root = "."
|
|
141
|
-
languages = ["
|
|
182
|
+
languages = ["typescript"]
|
|
142
183
|
|
|
143
|
-
[resolve.
|
|
144
|
-
|
|
145
|
-
package_names = ["domain", "usecase", "infrastructure"]
|
|
184
|
+
[resolve.typescript]
|
|
185
|
+
tsconfig = "./tsconfig.json"
|
|
146
186
|
|
|
147
187
|
[[layers]]
|
|
148
188
|
name = "domain"
|
|
@@ -157,8 +197,8 @@ name = "usecase"
|
|
|
157
197
|
paths = ["usecase/**"]
|
|
158
198
|
dependency_mode = "opt-in"
|
|
159
199
|
allow = ["domain"]
|
|
160
|
-
external_mode = "opt-
|
|
161
|
-
|
|
200
|
+
external_mode = "opt-in"
|
|
201
|
+
external_allow = ["zod"]
|
|
162
202
|
|
|
163
203
|
[[layers]]
|
|
164
204
|
name = "infrastructure"
|
|
@@ -169,92 +209,151 @@ external_mode = "opt-out"
|
|
|
169
209
|
external_deny = []
|
|
170
210
|
```
|
|
171
211
|
|
|
172
|
-
|
|
212
|
+
> Use `languages = ["javascript"]` for plain `.js` / `.jsx` projects (no `[resolve.typescript]` needed).
|
|
213
|
+
|
|
214
|
+
**Go:**
|
|
173
215
|
|
|
174
216
|
```toml
|
|
175
217
|
[project]
|
|
176
|
-
name = "my-
|
|
218
|
+
name = "my-go-app"
|
|
177
219
|
root = "."
|
|
178
|
-
languages = ["
|
|
220
|
+
languages = ["go"]
|
|
179
221
|
|
|
180
|
-
[resolve.
|
|
181
|
-
|
|
222
|
+
[resolve.go]
|
|
223
|
+
module_name = "github.com/myorg/my-go-app"
|
|
182
224
|
|
|
183
225
|
[[layers]]
|
|
184
226
|
name = "domain"
|
|
185
227
|
paths = ["domain/**"]
|
|
186
228
|
dependency_mode = "opt-in"
|
|
187
229
|
allow = []
|
|
188
|
-
external_mode = "opt-out"
|
|
189
|
-
external_deny = []
|
|
190
230
|
|
|
191
231
|
[[layers]]
|
|
192
232
|
name = "usecase"
|
|
193
233
|
paths = ["usecase/**"]
|
|
194
234
|
dependency_mode = "opt-in"
|
|
195
235
|
allow = ["domain"]
|
|
196
|
-
external_mode = "opt-in"
|
|
197
|
-
external_allow = ["zod"]
|
|
198
236
|
|
|
199
237
|
[[layers]]
|
|
200
238
|
name = "infrastructure"
|
|
201
239
|
paths = ["infrastructure/**"]
|
|
202
240
|
dependency_mode = "opt-out"
|
|
203
241
|
deny = []
|
|
204
|
-
external_mode = "opt-out"
|
|
205
|
-
external_deny = []
|
|
206
|
-
```
|
|
207
242
|
|
|
208
|
-
|
|
243
|
+
[[layers]]
|
|
244
|
+
name = "cmd"
|
|
245
|
+
paths = ["cmd/**"]
|
|
246
|
+
dependency_mode = "opt-in"
|
|
247
|
+
allow = ["domain", "usecase", "infrastructure"]
|
|
248
|
+
```
|
|
209
249
|
|
|
210
|
-
**
|
|
250
|
+
**Python:**
|
|
211
251
|
|
|
212
252
|
```toml
|
|
213
253
|
[project]
|
|
214
|
-
name = "my-
|
|
254
|
+
name = "my-python-app"
|
|
215
255
|
root = "."
|
|
216
|
-
languages = ["
|
|
256
|
+
languages = ["python"]
|
|
217
257
|
|
|
218
|
-
[resolve.
|
|
219
|
-
|
|
258
|
+
[resolve.python]
|
|
259
|
+
src_root = "."
|
|
260
|
+
package_names = ["domain", "usecase", "infrastructure"]
|
|
220
261
|
|
|
221
262
|
[[layers]]
|
|
222
263
|
name = "domain"
|
|
223
264
|
paths = ["domain/**"]
|
|
224
265
|
dependency_mode = "opt-in"
|
|
225
266
|
allow = []
|
|
267
|
+
external_mode = "opt-out"
|
|
268
|
+
external_deny = []
|
|
226
269
|
|
|
227
270
|
[[layers]]
|
|
228
271
|
name = "usecase"
|
|
229
272
|
paths = ["usecase/**"]
|
|
230
273
|
dependency_mode = "opt-in"
|
|
231
274
|
allow = ["domain"]
|
|
275
|
+
external_mode = "opt-out"
|
|
276
|
+
external_deny = []
|
|
232
277
|
|
|
233
278
|
[[layers]]
|
|
234
279
|
name = "infrastructure"
|
|
235
280
|
paths = ["infrastructure/**"]
|
|
236
281
|
dependency_mode = "opt-out"
|
|
237
282
|
deny = []
|
|
283
|
+
external_mode = "opt-out"
|
|
284
|
+
external_deny = []
|
|
285
|
+
```
|
|
238
286
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
287
|
+
### 2. Visualize with `mille analyze`
|
|
288
|
+
|
|
289
|
+
Before enforcing rules, you can inspect the actual dependency graph:
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
mille analyze # human-readable terminal output (default)
|
|
293
|
+
mille analyze --format json # machine-readable JSON graph
|
|
294
|
+
mille analyze --format dot # Graphviz DOT (pipe to: dot -Tsvg -o graph.svg)
|
|
295
|
+
mille analyze --format svg # self-contained SVG image (open in a browser)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Example SVG output (dark theme, green edges):
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
mille analyze --format svg > graph.svg && open graph.svg
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
`mille analyze` always exits `0` — it only visualizes, never enforces rules.
|
|
305
|
+
|
|
306
|
+
### 3. Inspect external dependencies with `mille report external`
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
mille report external # human-readable table (default)
|
|
310
|
+
mille report external --format json # machine-readable JSON
|
|
311
|
+
mille report external --output report.json --format json # write to file
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Shows which external packages each layer actually imports — useful for auditing `external_allow` lists or documenting your dependency footprint.
|
|
315
|
+
|
|
316
|
+
Example output:
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
External Dependencies by Layer
|
|
320
|
+
|
|
321
|
+
domain (none)
|
|
322
|
+
usecase (none)
|
|
323
|
+
infrastructure database/sql
|
|
324
|
+
cmd fmt, os
|
|
244
325
|
```
|
|
245
326
|
|
|
246
|
-
|
|
327
|
+
`mille report external` always exits `0` — it only reports, never enforces rules.
|
|
328
|
+
|
|
329
|
+
### 4. Run `mille check`
|
|
247
330
|
|
|
248
331
|
```sh
|
|
249
332
|
mille check
|
|
250
333
|
```
|
|
251
334
|
|
|
335
|
+
Output formats:
|
|
336
|
+
|
|
337
|
+
```sh
|
|
338
|
+
mille check # human-readable terminal output (default)
|
|
339
|
+
mille check --format github-actions # GitHub Actions annotations (::error file=...)
|
|
340
|
+
mille check --format json # machine-readable JSON
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Fail threshold:
|
|
344
|
+
|
|
345
|
+
```sh
|
|
346
|
+
mille check # exit 1 on error-severity violations only (default)
|
|
347
|
+
mille check --fail-on warning # exit 1 on any violation (error or warning)
|
|
348
|
+
mille check --fail-on error # explicit default — same as no flag
|
|
349
|
+
```
|
|
350
|
+
|
|
252
351
|
Exit codes:
|
|
253
352
|
|
|
254
353
|
| Code | Meaning |
|
|
255
354
|
|---|---|
|
|
256
|
-
| `0` | No violations |
|
|
257
|
-
| `1` | One or more
|
|
355
|
+
| `0` | No violations (or only warnings without `--fail-on warning`) |
|
|
356
|
+
| `1` | One or more violations at the configured fail threshold |
|
|
258
357
|
| `3` | Configuration file error |
|
|
259
358
|
|
|
260
359
|
## Configuration Reference
|
|
@@ -265,100 +364,111 @@ Exit codes:
|
|
|
265
364
|
|---|---|
|
|
266
365
|
| `name` | Project name |
|
|
267
366
|
| `root` | Root directory for analysis |
|
|
268
|
-
| `languages` |
|
|
367
|
+
| `languages` | Languages to check: `"rust"`, `"go"`, `"typescript"`, `"javascript"`, `"python"` |
|
|
269
368
|
|
|
270
369
|
### `[[layers]]`
|
|
271
370
|
|
|
272
371
|
| Key | Description |
|
|
273
372
|
|---|---|
|
|
274
373
|
| `name` | Layer name |
|
|
275
|
-
| `paths` | Glob patterns for files
|
|
374
|
+
| `paths` | Glob patterns for files in this layer |
|
|
276
375
|
| `dependency_mode` | `"opt-in"` (deny all except `allow`) or `"opt-out"` (allow all except `deny`) |
|
|
277
|
-
| `allow` |
|
|
278
|
-
| `deny` |
|
|
376
|
+
| `allow` | Allowed layers (when `dependency_mode = "opt-in"`) |
|
|
377
|
+
| `deny` | Forbidden layers (when `dependency_mode = "opt-out"`) |
|
|
279
378
|
| `external_mode` | `"opt-in"` or `"opt-out"` for external library usage |
|
|
280
|
-
| `external_allow` |
|
|
281
|
-
| `external_deny` |
|
|
379
|
+
| `external_allow` | Allowed external packages (when `external_mode = "opt-in"`) |
|
|
380
|
+
| `external_deny` | Forbidden external packages (when `external_mode = "opt-out"`) |
|
|
282
381
|
|
|
283
382
|
### `[[layers.allow_call_patterns]]`
|
|
284
383
|
|
|
285
|
-
Restricts which methods may be called on a given layer's types. Only valid on the `main` layer.
|
|
384
|
+
Restricts which methods may be called on a given layer's types. Only valid on the `main` layer (or equivalent DI entrypoint).
|
|
286
385
|
|
|
287
386
|
| Key | Description |
|
|
288
387
|
|---|---|
|
|
289
388
|
| `callee_layer` | The layer whose methods are being restricted |
|
|
290
389
|
| `allow_methods` | List of method names that are permitted |
|
|
291
390
|
|
|
391
|
+
### `[ignore]`
|
|
392
|
+
|
|
393
|
+
Exclude files from the architecture check entirely, or suppress violations for test/mock files.
|
|
394
|
+
|
|
395
|
+
| Key | Description |
|
|
396
|
+
|---|---|
|
|
397
|
+
| `paths` | Glob patterns — matching files are excluded from collection and not counted in layer stats |
|
|
398
|
+
| `test_patterns` | Glob patterns — matching files are still counted in layer stats but their imports are not violation-checked |
|
|
399
|
+
|
|
400
|
+
```toml
|
|
401
|
+
[ignore]
|
|
402
|
+
paths = ["**/mock/**", "**/generated/**", "**/testdata/**"]
|
|
403
|
+
test_patterns = ["**/*_test.go", "**/*.spec.ts", "**/*.test.ts"]
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**When to use `paths` vs `test_patterns`:**
|
|
407
|
+
|
|
408
|
+
- `paths`: Files that should not be analyzed at all (generated code, vendor directories, mocks)
|
|
409
|
+
- `test_patterns`: Test files that intentionally import across layers (e.g., integration tests that import both domain and infrastructure)
|
|
410
|
+
|
|
411
|
+
### `[severity]`
|
|
412
|
+
|
|
413
|
+
Control the severity level of each violation type. Violations can be `"error"`, `"warning"`, or `"info"`.
|
|
414
|
+
|
|
415
|
+
| Key | Default | Description |
|
|
416
|
+
|---|---|---|
|
|
417
|
+
| `dependency_violation` | `"error"` | Layer dependency rule violated |
|
|
418
|
+
| `external_violation` | `"error"` | External library rule violated |
|
|
419
|
+
| `call_pattern_violation` | `"error"` | DI entrypoint method call rule violated |
|
|
420
|
+
| `unknown_import` | `"warning"` | Import that could not be classified |
|
|
421
|
+
|
|
422
|
+
```toml
|
|
423
|
+
[severity]
|
|
424
|
+
dependency_violation = "warning" # treat as warning for gradual adoption
|
|
425
|
+
external_violation = "error"
|
|
426
|
+
call_pattern_violation = "error"
|
|
427
|
+
unknown_import = "warning"
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Use `--fail-on warning` to exit 1 even for warnings when integrating into CI gradually.
|
|
431
|
+
|
|
292
432
|
### `[resolve.typescript]`
|
|
293
433
|
|
|
294
434
|
| Key | Description |
|
|
295
435
|
|---|---|
|
|
296
|
-
| `tsconfig` | Path to `tsconfig.json`.
|
|
436
|
+
| `tsconfig` | Path to `tsconfig.json`. mille reads `compilerOptions.paths` and resolves path aliases (e.g. `@/*`) as internal imports. |
|
|
297
437
|
|
|
298
438
|
**How TypeScript / JavaScript imports are classified:**
|
|
299
439
|
|
|
300
440
|
| Import | Classification |
|
|
301
441
|
|---|---|
|
|
302
|
-
| `import X from "./module"`
|
|
303
|
-
| `import X from "../module"`
|
|
442
|
+
| `import X from "./module"` | Internal |
|
|
443
|
+
| `import X from "../module"` | Internal |
|
|
304
444
|
| `import X from "@/module"` (path alias in `tsconfig.json`) | Internal |
|
|
305
|
-
| `import X from "react"`
|
|
306
|
-
| `import fs from "node:fs"`
|
|
307
|
-
|
|
308
|
-
For relative imports, mille resolves the path from the importing file and matches it against layer glob patterns. For example, `import { User } from "../domain/user"` in `usecase/user_usecase.ts` resolves to `domain/user`, matching the layer glob `domain/**`.
|
|
309
|
-
|
|
310
|
-
For path aliases, mille expands the alias using `compilerOptions.paths` and treats the result as an internal import. For example, with `"@/*": ["./src/*"]`, `import { User } from "@/domain/user"` resolves to `src/domain/user`.
|
|
445
|
+
| `import X from "react"` | External |
|
|
446
|
+
| `import fs from "node:fs"` | External |
|
|
311
447
|
|
|
312
448
|
### `[resolve.go]`
|
|
313
449
|
|
|
314
450
|
| Key | Description |
|
|
315
451
|
|---|---|
|
|
316
|
-
| `module_name` | Go module name (matches
|
|
452
|
+
| `module_name` | Go module name (matches `go.mod`). `mille init` generates this automatically from `go.mod`. |
|
|
317
453
|
|
|
318
454
|
### `[resolve.python]`
|
|
319
455
|
|
|
320
456
|
| Key | Description |
|
|
321
457
|
|---|---|
|
|
322
458
|
| `src_root` | Root directory of the Python source tree (relative to `mille.toml`) |
|
|
323
|
-
| `package_names` |
|
|
459
|
+
| `package_names` | Your package names — imports starting with these are classified as internal. e.g. `["domain", "usecase"]` |
|
|
324
460
|
|
|
325
461
|
**How Python imports are classified:**
|
|
326
462
|
|
|
327
463
|
| Import | Classification |
|
|
328
464
|
|---|---|
|
|
329
465
|
| `from .sibling import X` (relative) | Internal |
|
|
330
|
-
| `import domain.entity` (matches
|
|
331
|
-
| `import os`, `import sqlalchemy`
|
|
332
|
-
|
|
333
|
-
## Python API
|
|
334
|
-
|
|
335
|
-
In addition to the CLI, the Python package exposes a programmatic API:
|
|
336
|
-
|
|
337
|
-
```python
|
|
338
|
-
import mille
|
|
339
|
-
|
|
340
|
-
# Run architecture check and get a result object
|
|
341
|
-
result = mille.check("path/to/mille.toml") # defaults to "mille.toml"
|
|
342
|
-
|
|
343
|
-
print(f"violations: {len(result.violations)}")
|
|
344
|
-
for v in result.violations:
|
|
345
|
-
print(f" {v.file}:{v.line} {v.from_layer} -> {v.to_layer} ({v.import_path})")
|
|
346
|
-
|
|
347
|
-
for stat in result.layer_stats:
|
|
348
|
-
print(f" {stat.name}: {stat.file_count} file(s), {stat.violation_count} violation(s)")
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
**Types exposed:**
|
|
352
|
-
|
|
353
|
-
| Class | Attributes |
|
|
354
|
-
|---|---|
|
|
355
|
-
| `CheckResult` | `violations: list[Violation]`, `layer_stats: list[LayerStat]` |
|
|
356
|
-
| `Violation` | `file`, `line`, `from_layer`, `to_layer`, `import_path`, `kind` |
|
|
357
|
-
| `LayerStat` | `name`, `file_count`, `violation_count` |
|
|
466
|
+
| `import domain.entity` (matches `package_names`) | Internal |
|
|
467
|
+
| `import os`, `import sqlalchemy` | External |
|
|
358
468
|
|
|
359
469
|
## How it Works
|
|
360
470
|
|
|
361
|
-
mille uses [tree-sitter](https://tree-sitter.github.io/) for AST-based import extraction — no regex heuristics.
|
|
471
|
+
mille uses [tree-sitter](https://tree-sitter.github.io/) for AST-based import extraction — no regex heuristics.
|
|
362
472
|
|
|
363
473
|
```
|
|
364
474
|
mille.toml
|
|
@@ -366,7 +476,7 @@ mille.toml
|
|
|
366
476
|
▼
|
|
367
477
|
Layer definitions
|
|
368
478
|
│
|
|
369
|
-
Source files (*.rs, *.go, *.py, ...)
|
|
479
|
+
Source files (*.rs, *.go, *.py, *.ts, *.js, ...)
|
|
370
480
|
│ tree-sitter parse
|
|
371
481
|
▼
|
|
372
482
|
RawImport list
|
package/index.js
CHANGED
|
@@ -8,6 +8,16 @@ process.on('warning', (w) => {
|
|
|
8
8
|
process.stderr.write(w.stack + '\n');
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
// NOTE: Intercept --version/-V before forwarding to WASM so that the npm
|
|
12
|
+
// package version (updated by `npm version X.Y.Z` at release time) is shown,
|
|
13
|
+
// rather than the version baked into mille.wasm at WASM-build time.
|
|
14
|
+
const userArgs = process.argv.slice(2);
|
|
15
|
+
if (userArgs.includes('--version') || userArgs.includes('-V')) {
|
|
16
|
+
const { version } = require('./package.json');
|
|
17
|
+
process.stdout.write(`mille ${version}\n`);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
const { WASI } = require('node:wasi');
|
|
12
22
|
const { readFileSync } = require('node:fs');
|
|
13
23
|
const { join } = require('node:path');
|
package/mille.wasm
CHANGED
|
Binary file
|