@rsdk/yarn.constraints 6.0.0-next.39 → 6.0.0-next.40
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/DEPENDENCY_MODEL.md +452 -0
- package/README.MD +24 -0
- package/__tests__/compatibility.test.ts +321 -0
- package/__tests__/engine.test.ts +1002 -0
- package/__tests__/fixtures/imports/bin.js +4 -0
- package/__tests__/fixtures/imports/export-entry.mjs +1 -0
- package/__tests__/fixtures/imports/root-entry.js +3 -0
- package/__tests__/fixtures/imports/src/common.cjs +3 -0
- package/__tests__/fixtures/imports/src/common.cts +3 -0
- package/__tests__/fixtures/imports/src/component.tsx +4 -0
- package/__tests__/fixtures/imports/src/index.ts +13 -0
- package/__tests__/fixtures/imports/src/module.mjs +3 -0
- package/__tests__/fixtures/imports/src/module.mts +3 -0
- package/__tests__/fixtures/imports/src/plain.js +3 -0
- package/__tests__/fixtures/imports/src/test-only-usage.ts +1 -0
- package/__tests__/imports.test.ts +206 -0
- package/__tests__/manifest-writer.test.ts +157 -0
- package/dist/ansi.d.ts +9 -0
- package/dist/ansi.js +24 -0
- package/dist/ansi.js.map +1 -0
- package/dist/bin/depdoc.d.ts +2 -0
- package/dist/bin/depdoc.js +157 -0
- package/dist/bin/depdoc.js.map +1 -0
- package/dist/collectors/config.d.ts +2 -0
- package/dist/collectors/config.js +25 -0
- package/dist/collectors/config.js.map +1 -0
- package/dist/collectors/external-metadata.d.ts +5 -0
- package/dist/collectors/external-metadata.js +110 -0
- package/dist/collectors/external-metadata.js.map +1 -0
- package/dist/collectors/package-extensions.d.ts +3 -0
- package/dist/collectors/package-extensions.js +43 -0
- package/dist/collectors/package-extensions.js.map +1 -0
- package/dist/collectors/type-providers.d.ts +3 -0
- package/dist/collectors/type-providers.js +46 -0
- package/dist/collectors/type-providers.js.map +1 -0
- package/dist/collectors/workspaces.d.ts +2 -0
- package/dist/collectors/workspaces.js +88 -0
- package/dist/collectors/workspaces.js.map +1 -0
- package/dist/dependency-model.d.ts +11 -0
- package/dist/dependency-model.js +18 -0
- package/dist/dependency-model.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +13 -33
- package/dist/index.js.map +1 -1
- package/dist/lib/imports.d.ts +9 -0
- package/dist/lib/imports.js +249 -0
- package/dist/lib/imports.js.map +1 -0
- package/dist/lib/package-json.d.ts +21 -0
- package/dist/lib/package-json.js +32 -0
- package/dist/lib/package-json.js.map +1 -0
- package/dist/model/diagnostics.d.ts +4 -0
- package/dist/model/diagnostics.js +273 -0
- package/dist/model/diagnostics.js.map +1 -0
- package/dist/model/engine.d.ts +5 -0
- package/dist/model/engine.js +52 -0
- package/dist/model/engine.js.map +1 -0
- package/dist/model/expected.d.ts +20 -0
- package/dist/model/expected.js +89 -0
- package/dist/model/expected.js.map +1 -0
- package/dist/model/peer-propagation.d.ts +2 -0
- package/dist/model/peer-propagation.js +124 -0
- package/dist/model/peer-propagation.js.map +1 -0
- package/dist/model/placement.d.ts +9 -0
- package/dist/model/placement.js +205 -0
- package/dist/model/placement.js.map +1 -0
- package/dist/model/rules.d.ts +14 -0
- package/dist/model/rules.js +46 -0
- package/dist/model/rules.js.map +1 -0
- package/dist/model/types.d.ts +117 -0
- package/dist/model/types.js +9 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/versions.d.ts +3 -0
- package/dist/model/versions.js +73 -0
- package/dist/model/versions.js.map +1 -0
- package/dist/reporting.d.ts +3 -0
- package/dist/reporting.js +80 -0
- package/dist/reporting.js.map +1 -0
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +70 -0
- package/dist/runner.js.map +1 -0
- package/dist/writer/manifest-writer.d.ts +2 -0
- package/dist/writer/manifest-writer.js +72 -0
- package/dist/writer/manifest-writer.js.map +1 -0
- package/eslint.config.cjs +3 -0
- package/jest.config.js +1 -0
- package/package.json +7 -3
- package/src/ansi.ts +23 -0
- package/src/bin/depdoc.ts +213 -0
- package/src/collectors/config.ts +26 -0
- package/src/collectors/external-metadata.ts +148 -0
- package/src/collectors/package-extensions.ts +52 -0
- package/src/collectors/type-providers.ts +51 -0
- package/src/collectors/workspaces.ts +99 -0
- package/src/dependency-model.ts +26 -0
- package/src/index.ts +28 -45
- package/src/lib/imports.ts +293 -0
- package/src/lib/package-json.ts +46 -0
- package/src/model/diagnostics.ts +328 -0
- package/src/model/engine.ts +120 -0
- package/src/model/expected.ts +141 -0
- package/src/model/peer-propagation.ts +199 -0
- package/src/model/placement.ts +372 -0
- package/src/model/rules.ts +73 -0
- package/src/model/types.ts +164 -0
- package/src/model/versions.ts +109 -0
- package/src/reporting.ts +117 -0
- package/src/runner.ts +102 -0
- package/src/writer/manifest-writer.ts +111 -0
- package/tsconfig.build.json +1 -0
- package/tsconfig.json +6 -1
- package/dist/constraint-schema.d.ts +0 -1
- package/dist/constraint-schema.js +0 -17
- package/dist/constraint-schema.js.map +0 -1
- package/dist/dependency-checker.d.ts +0 -8
- package/dist/dependency-checker.js +0 -40
- package/dist/dependency-checker.js.map +0 -1
- package/src/constraint-schema.ts +0 -20
- package/src/dependency-checker.ts +0 -41
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# Dependency Model
|
|
2
|
+
|
|
3
|
+
Status: draft.
|
|
4
|
+
|
|
5
|
+
This document defines the dependency ownership model for the monorepo. The goal
|
|
6
|
+
is to make dependency placement deterministic, checkable, and mostly
|
|
7
|
+
autofixable.
|
|
8
|
+
|
|
9
|
+
## Terms
|
|
10
|
+
|
|
11
|
+
| Term | Meaning |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Root | The monorepo root workspace. It is an orchestrator, not a product package. |
|
|
14
|
+
| Workspace | Any non-root workspace package. |
|
|
15
|
+
| Role | Explicit package role: `library`, `service`, or `cli`. |
|
|
16
|
+
| Local dependency | A package that is also present as a workspace in this monorepo. |
|
|
17
|
+
| External dependency | Any dependency that is not a local workspace package. |
|
|
18
|
+
| Runtime surface | Imports required by emitted JavaScript / production execution. |
|
|
19
|
+
| Public type surface | Imports that appear in emitted `dist/**/*.d.ts`. |
|
|
20
|
+
| Private dev surface | Test, build, lint, type-only, and tooling usage that does not reach runtime or public `.d.ts`. |
|
|
21
|
+
| Mirror | A `library` package has the same dependency name and range in both `peerDependencies` and `devDependencies`. |
|
|
22
|
+
|
|
23
|
+
## Invariants
|
|
24
|
+
|
|
25
|
+
### Root
|
|
26
|
+
|
|
27
|
+
| ID | Invariant | Check |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| R1 | Root is not a product package. | Root `package.json` has `private: true`. |
|
|
30
|
+
| R2 | Root may contain only `devDependencies`. | Root `dependencies` and `peerDependencies` are forbidden. |
|
|
31
|
+
| R3 | Root `devDependencies` define the shared dev/type/build environment. | Tooling, `@types/*`, test infra, build infra, release infra, and constraints infra live here. |
|
|
32
|
+
| R4 | Root dependencies do not declare dependencies for workspaces. | A workspace runtime/public dependency must be declared in that workspace manifest. |
|
|
33
|
+
|
|
34
|
+
### Workspace Roles
|
|
35
|
+
|
|
36
|
+
| ID | Invariant | Check |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| W1 | Every workspace has an explicit role. | `role` is one of `library`, `service`, `cli`. |
|
|
39
|
+
| W2 | Local workspace dependencies are runtime graph edges. | Local deps are in `dependencies` with `workspace:*`. |
|
|
40
|
+
| W3 | Dependency ownership belongs to the importing workspace. | Runtime/public imports must be represented in that workspace manifest. |
|
|
41
|
+
|
|
42
|
+
### Libraries
|
|
43
|
+
|
|
44
|
+
| ID | Invariant | Check |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| L1 | External deps required by runtime or public type surface default to `peerDependencies`. | Unless an explicit override says otherwise. |
|
|
47
|
+
| L2 | `devDependencies` exactly mirror `peerDependencies`. | Same names and same ranges. |
|
|
48
|
+
| L3 | Extra `devDependencies` are forbidden. | `devDependencies - peerDependencies = empty`. |
|
|
49
|
+
| L4 | A dependency cannot be both `dependencies` and `peerDependencies`. | A package has one runtime/public ownership mode. |
|
|
50
|
+
| L5 | External implementation deps are explicit exceptions. | They require a `section: dependencies` override. |
|
|
51
|
+
| L6 | Private dev/type/test/build-only deps are not declared in the library package. | They belong to root `devDependencies`. |
|
|
52
|
+
|
|
53
|
+
### Services and CLI Packages
|
|
54
|
+
|
|
55
|
+
| ID | Invariant | Check |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| S1 | Runtime/prod deps are declared in `dependencies`. | Includes local and external runtime deps. |
|
|
58
|
+
| S2 | `peerDependencies` are forbidden. | Services and CLIs do not push a peer contract to consumers. |
|
|
59
|
+
| S3 | `devDependencies` are forbidden. | Dev/type/test/build deps live in root. |
|
|
60
|
+
| S4 | CLI runtime deps cannot be peers. | Global install / `dlx` flows do not reliably install peer dependencies. |
|
|
61
|
+
|
|
62
|
+
### Types
|
|
63
|
+
|
|
64
|
+
| ID | Invariant | Check |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| T1 | Type-only deps that do not appear in `dist/**/*.d.ts` are private dev deps. | They belong to root `devDependencies`. |
|
|
67
|
+
| T2 | Type deps that appear in `dist/**/*.d.ts` are public contract deps. | `library`: peer + dev mirror. `service`/`cli`: dependency. |
|
|
68
|
+
| T3 | `@types/*` default to root `devDependencies`. | Until they appear in public `.d.ts`. |
|
|
69
|
+
| T4 | If public `.d.ts` requires a DefinitelyTyped provider, that provider is public contract. | The corresponding `@types/*` is promoted according to T2. |
|
|
70
|
+
|
|
71
|
+
### Peer Propagation
|
|
72
|
+
|
|
73
|
+
| ID | Invariant | Check |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| P1 | A workspace depending on a local library must provide that library's peers. | `library` consumer: peer + dev mirror. `service`/`cli`: dependencies. |
|
|
76
|
+
| P2 | Required peers of external npm deps must be provided by the current workspace. | Optional peers are not forced. |
|
|
77
|
+
| P3 | Incorrect third-party peer metadata is fixed through package manager metadata overrides. | Use `packageExtensions`, not random workspace deps. |
|
|
78
|
+
| P4 | Local workspace packages are not propagated as peer targets. | Local packages stay `dependencies: workspace:*`. |
|
|
79
|
+
|
|
80
|
+
### Versions and Overrides
|
|
81
|
+
|
|
82
|
+
| ID | Invariant | Check |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| V1 | Any external dependency that appears more than once must have a version rule. | Count root and all workspaces, all dependency sections. |
|
|
85
|
+
| V2 | Wildcard rules count as version rules. | For example `@nestjs/*`. |
|
|
86
|
+
| V3 | The constraints config does not describe the whole model. | It contains versions, overrides, root-only rules, manual required dependencies, and workspace-specific exceptions. |
|
|
87
|
+
| V4 | The last matching rule wins. | Enables broad rules plus specific overrides. |
|
|
88
|
+
| V5 | `rootOnly` means the dependency belongs only to the root dev environment. | Workspace usage is forbidden unless a more specific override exists. |
|
|
89
|
+
|
|
90
|
+
## Placement Formula
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
root:
|
|
94
|
+
only devDependencies
|
|
95
|
+
|
|
96
|
+
library:
|
|
97
|
+
dependencies = local workspace deps + explicit implementation overrides
|
|
98
|
+
peerDependencies = external runtime/public deps by default
|
|
99
|
+
devDependencies = exact mirror of peerDependencies
|
|
100
|
+
|
|
101
|
+
service/cli:
|
|
102
|
+
dependencies = runtime/prod deps
|
|
103
|
+
peerDependencies = forbidden
|
|
104
|
+
devDependencies = forbidden
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Check Algorithm
|
|
108
|
+
|
|
109
|
+
The checks should run from facts to derived manifests. A later phase should not
|
|
110
|
+
guess something that an earlier phase can derive.
|
|
111
|
+
|
|
112
|
+
### Phase 1. Load Facts
|
|
113
|
+
|
|
114
|
+
1. Read root `package.json`.
|
|
115
|
+
2. Read every workspace `package.json`.
|
|
116
|
+
3. Build the set of local workspace package names.
|
|
117
|
+
4. Read dependency rules: version rules, section overrides, `rootOnly` rules,
|
|
118
|
+
manual `required` dependencies, and workspace-specific exceptions.
|
|
119
|
+
5. Read package manager metadata overrides, such as `packageExtensions`.
|
|
120
|
+
|
|
121
|
+
### Phase 2. Validate Basic Shape
|
|
122
|
+
|
|
123
|
+
1. Root has `private: true`.
|
|
124
|
+
2. Root has no `dependencies`.
|
|
125
|
+
3. Root has no `peerDependencies`.
|
|
126
|
+
4. Every non-root workspace has a valid `role`.
|
|
127
|
+
5. `service` and `cli` workspaces have no `peerDependencies`.
|
|
128
|
+
6. `service` and `cli` workspaces have no `devDependencies`.
|
|
129
|
+
7. `library` workspaces have no dependency listed in both `dependencies` and
|
|
130
|
+
`peerDependencies`.
|
|
131
|
+
8. No workspace has the same dependency in both `dependencies` and
|
|
132
|
+
`devDependencies`.
|
|
133
|
+
|
|
134
|
+
### Phase 3. Collect Usage
|
|
135
|
+
|
|
136
|
+
1. Scan source imports for every workspace.
|
|
137
|
+
2. Classify each import as local or external.
|
|
138
|
+
3. Classify each import as runtime, type-only, test-only, or tooling-only.
|
|
139
|
+
4. Scan emitted `dist/**/*.d.ts` imports when `dist/` exists.
|
|
140
|
+
5. In `--with-dts` mode, require `dist/` for workspaces with `src/`; without
|
|
141
|
+
`--with-dts`, missing `dist/` is allowed so local source-only checks remain
|
|
142
|
+
usable before build.
|
|
143
|
+
6. Mark all dependencies found in emitted `.d.ts` as public type surface.
|
|
144
|
+
7. Collect `bin`, `exports`, `main`, and package entry metadata to identify
|
|
145
|
+
runtime/public entrypoints where needed.
|
|
146
|
+
|
|
147
|
+
### Phase 4. Derive Required Public/Runtime Deps
|
|
148
|
+
|
|
149
|
+
For each workspace:
|
|
150
|
+
|
|
151
|
+
1. Local runtime/public deps become `dependencies: workspace:*`.
|
|
152
|
+
2. For `library`, external runtime/public deps become `peerDependencies` by
|
|
153
|
+
default.
|
|
154
|
+
3. For `library`, external deps with `section: dependencies` override become
|
|
155
|
+
`dependencies`.
|
|
156
|
+
4. For `service` and `cli`, external runtime deps become `dependencies`.
|
|
157
|
+
5. Type-only deps that appear in `dist/**/*.d.ts` are treated as public deps.
|
|
158
|
+
6. Type-only/test/tooling deps that do not appear in `dist/**/*.d.ts` are moved
|
|
159
|
+
to root `devDependencies`.
|
|
160
|
+
7. Dependencies matched by a concrete `required: true` rule are treated as
|
|
161
|
+
required public/runtime dependencies even when source scanning cannot see a
|
|
162
|
+
direct import.
|
|
163
|
+
|
|
164
|
+
### Phase 5. Propagate Peers
|
|
165
|
+
|
|
166
|
+
For each workspace dependency edge:
|
|
167
|
+
|
|
168
|
+
1. If a workspace depends on a local library, read that library's
|
|
169
|
+
`peerDependencies`.
|
|
170
|
+
2. If a workspace depends on an external npm package, read that package's
|
|
171
|
+
required `peerDependencies`.
|
|
172
|
+
3. Ignore optional peers.
|
|
173
|
+
4. Ignore local workspace packages as peer targets.
|
|
174
|
+
5. For a `library` consumer, required external peers become `peerDependencies`.
|
|
175
|
+
6. For a `service` or `cli` consumer, required external peers become
|
|
176
|
+
`dependencies`.
|
|
177
|
+
7. Third-party metadata mistakes are handled by metadata overrides before this
|
|
178
|
+
phase, so this phase sees corrected peer metadata.
|
|
179
|
+
|
|
180
|
+
### Phase 6. Derive Mirrors
|
|
181
|
+
|
|
182
|
+
For each `library`:
|
|
183
|
+
|
|
184
|
+
1. `devDependencies` must be exactly equal to `peerDependencies`.
|
|
185
|
+
2. Every peer range is copied exactly into the dev mirror.
|
|
186
|
+
3. Any extra dev dependency is removed or moved to root dev dependencies,
|
|
187
|
+
depending on whether it is still needed by private dev usage.
|
|
188
|
+
|
|
189
|
+
### Phase 7. Resolve Versions
|
|
190
|
+
|
|
191
|
+
1. Count every external dependency occurrence across root and all workspaces,
|
|
192
|
+
all sections.
|
|
193
|
+
2. If an external dependency appears more than once, require a matching version
|
|
194
|
+
rule.
|
|
195
|
+
3. A wildcard rule is valid if it provides a version.
|
|
196
|
+
4. Apply the last matching rule.
|
|
197
|
+
5. Mirror ranges must stay identical.
|
|
198
|
+
6. Local workspace deps use `workspace:*`.
|
|
199
|
+
|
|
200
|
+
### Phase 8. Compare Expected vs Actual Manifests
|
|
201
|
+
|
|
202
|
+
For each manifest:
|
|
203
|
+
|
|
204
|
+
1. Report missing dependencies.
|
|
205
|
+
2. Report dependencies in the wrong section.
|
|
206
|
+
3. Report dependencies with the wrong version range.
|
|
207
|
+
4. Report forbidden dependencies.
|
|
208
|
+
5. Report stale dependencies that are not required by the derived graph.
|
|
209
|
+
6. Report missing or incorrect library peer/dev mirrors.
|
|
210
|
+
7. Report root-only violations.
|
|
211
|
+
|
|
212
|
+
### Phase 9. Autofix
|
|
213
|
+
|
|
214
|
+
Autofix should write manifests from the derived expected graph:
|
|
215
|
+
|
|
216
|
+
1. Remove forbidden root `dependencies` and `peerDependencies`.
|
|
217
|
+
2. Add/move local workspace deps to `dependencies: workspace:*`.
|
|
218
|
+
3. Add/move library external public/runtime deps to `peerDependencies`.
|
|
219
|
+
4. Rebuild library `devDependencies` from `peerDependencies`.
|
|
220
|
+
5. Add/move service and CLI runtime deps to `dependencies`.
|
|
221
|
+
6. Move private dev/type/test/tooling deps to root `devDependencies`.
|
|
222
|
+
7. Apply version rules.
|
|
223
|
+
8. Sort dependency sections consistently.
|
|
224
|
+
|
|
225
|
+
### Phase 10. Verification Order
|
|
226
|
+
|
|
227
|
+
1. Run the manifest model checker with autofix.
|
|
228
|
+
2. Run package install.
|
|
229
|
+
3. Build packages to generate `dist/**/*.d.ts`.
|
|
230
|
+
4. Run the type-surface check.
|
|
231
|
+
5. Run the manifest model checker again, because `.d.ts` leaks can change the
|
|
232
|
+
expected graph.
|
|
233
|
+
6. Run package install again if manifests changed.
|
|
234
|
+
7. Assert that package manager peer requirements have no failures.
|
|
235
|
+
8. Run the full dependency check task.
|
|
236
|
+
|
|
237
|
+
## Target Tooling Shape
|
|
238
|
+
|
|
239
|
+
The implementation should have one CLI backed by one dependency model engine.
|
|
240
|
+
Separate scripts must not own separate placement logic.
|
|
241
|
+
|
|
242
|
+
```text
|
|
243
|
+
depdoc check
|
|
244
|
+
depdoc fix
|
|
245
|
+
depdoc explain <workspace> <dependency>
|
|
246
|
+
depdoc doctor
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The CLI commands share the same pipeline:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
collect facts
|
|
253
|
+
-> derive expected dependency graph
|
|
254
|
+
-> compare actual manifests with expected manifests
|
|
255
|
+
-> print diagnostics or write fixes
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`check` and `fix` differ only in the final step. `check` reports diagnostics.
|
|
259
|
+
`fix` writes the expected manifests. Both commands include `.d.ts` facts when
|
|
260
|
+
`dist/` already exists, so a normal `fix` must not delete public type-surface
|
|
261
|
+
dependencies from a built checkout. The `--with-dts` flag only changes
|
|
262
|
+
strictness around missing build artifacts: it requires `dist/` for packages with
|
|
263
|
+
`src/`.
|
|
264
|
+
|
|
265
|
+
`explain` explains why a dependency belongs to a specific section, which rule
|
|
266
|
+
matched it, which import or peer edge required it, and whether the dependency is
|
|
267
|
+
part of runtime surface, public type surface, or private dev surface.
|
|
268
|
+
|
|
269
|
+
`doctor` runs the full compatibility pipeline:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
depdoc fix
|
|
273
|
+
yarn install
|
|
274
|
+
build packages
|
|
275
|
+
depdoc check --with-dts
|
|
276
|
+
yarn install
|
|
277
|
+
yarn explain peer-requirements
|
|
278
|
+
lint
|
|
279
|
+
typecheck
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The exact build/lint/typecheck commands are repository-specific, but the order
|
|
283
|
+
is part of the model: `.d.ts` facts are available when `dist/` exists, and
|
|
284
|
+
`--with-dts` makes missing `.d.ts` artifacts a hard failure after build.
|
|
285
|
+
|
|
286
|
+
## Implementation Layers
|
|
287
|
+
|
|
288
|
+
### Engine
|
|
289
|
+
|
|
290
|
+
The engine should be pure and deterministic.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
deriveDependencyModel({
|
|
294
|
+
rootPackage,
|
|
295
|
+
workspaces,
|
|
296
|
+
sourceImports,
|
|
297
|
+
dtsImports,
|
|
298
|
+
externalPeerMetadata,
|
|
299
|
+
rules,
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The engine returns:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
{
|
|
307
|
+
expectedManifests,
|
|
308
|
+
diagnostics,
|
|
309
|
+
fixes
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The engine must not run Yarn, read the filesystem, or execute TypeScript. It
|
|
314
|
+
only consumes facts and derives the expected dependency graph.
|
|
315
|
+
|
|
316
|
+
### Collectors
|
|
317
|
+
|
|
318
|
+
Collectors gather facts for the engine:
|
|
319
|
+
|
|
320
|
+
| Collector | Responsibility |
|
|
321
|
+
|---|---|
|
|
322
|
+
| Package JSON collector | Reads root and workspace manifests. |
|
|
323
|
+
| Workspace collector | Reads workspace names and locations. |
|
|
324
|
+
| Source import collector | Reads source imports and classifies runtime/type/test/tooling usage. |
|
|
325
|
+
| DTS import collector | Reads emitted `dist/**/*.d.ts` imports. |
|
|
326
|
+
| External peer metadata collector | Reads required/optional peers from installed packages and metadata overrides. |
|
|
327
|
+
| Rules collector | Reads version rules, section overrides, root-only rules, and workspace exceptions. |
|
|
328
|
+
|
|
329
|
+
Collectors may be integration-tested separately. The core placement model should
|
|
330
|
+
remain testable without filesystem or Yarn.
|
|
331
|
+
|
|
332
|
+
### Manifest Writer
|
|
333
|
+
|
|
334
|
+
The writer applies engine fixes:
|
|
335
|
+
|
|
336
|
+
1. Preserve unrelated manifest fields.
|
|
337
|
+
2. Rewrite dependency sections from the expected graph.
|
|
338
|
+
3. Keep peer/dev mirrors exact for libraries.
|
|
339
|
+
4. Apply normalized sorting.
|
|
340
|
+
5. Avoid touching files when generated content is identical.
|
|
341
|
+
|
|
342
|
+
## Test Strategy
|
|
343
|
+
|
|
344
|
+
Testing must prove two different things:
|
|
345
|
+
|
|
346
|
+
1. The dependency model is internally consistent.
|
|
347
|
+
2. Real tools accept manifests produced by the model.
|
|
348
|
+
|
|
349
|
+
### Test Layers
|
|
350
|
+
|
|
351
|
+
| Layer | Goal | Uses Yarn/build |
|
|
352
|
+
|---|---|---|
|
|
353
|
+
| Model unit tests | Test individual invariants and section decisions. | No |
|
|
354
|
+
| Model fixture tests | Test complete expected manifests from package/import facts. | No |
|
|
355
|
+
| Collector tests | Test import parsing, workspace discovery, package metadata parsing. | Minimal |
|
|
356
|
+
| Type-surface tests | Test behavior when `dist/**/*.d.ts` contains public type imports. | No build required if `.d.ts` fixtures are provided |
|
|
357
|
+
| Tool compatibility tests | Test generated manifests against real Yarn, TypeScript, and ESLint. | Yes |
|
|
358
|
+
| Repo smoke tests | Test the model on the current monorepo. | Yes |
|
|
359
|
+
|
|
360
|
+
### Model Fixture Format
|
|
361
|
+
|
|
362
|
+
Each fixture should provide package manifests, model facts, rules, and expected
|
|
363
|
+
output.
|
|
364
|
+
|
|
365
|
+
```text
|
|
366
|
+
fixtures/library-external-default-peer/
|
|
367
|
+
input/
|
|
368
|
+
package.json
|
|
369
|
+
packages/foo/package.json
|
|
370
|
+
facts.json
|
|
371
|
+
rules.yml
|
|
372
|
+
expected/
|
|
373
|
+
package.json
|
|
374
|
+
packages/foo/package.json
|
|
375
|
+
diagnostics.json
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Example `facts.json`:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"sourceImports": {
|
|
383
|
+
"packages/foo": [
|
|
384
|
+
{
|
|
385
|
+
"packageName": "lodash",
|
|
386
|
+
"kind": "runtime",
|
|
387
|
+
"file": "src/index.ts"
|
|
388
|
+
}
|
|
389
|
+
]
|
|
390
|
+
},
|
|
391
|
+
"dtsImports": {
|
|
392
|
+
"packages/foo": []
|
|
393
|
+
},
|
|
394
|
+
"externalPeerMetadata": {}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Required Model Fixtures
|
|
399
|
+
|
|
400
|
+
| Fixture | Proves |
|
|
401
|
+
|---|---|
|
|
402
|
+
| `root-only-devdeps` | Root rejects `dependencies` and `peerDependencies`. |
|
|
403
|
+
| `library-external-default-peer` | External runtime dep in a library becomes peer + dev mirror. |
|
|
404
|
+
| `library-implementation-override` | `section: dependencies` keeps an external library dep in `dependencies`. |
|
|
405
|
+
| `library-dev-mirror-exact` | Library `devDependencies` exactly mirror `peerDependencies`. |
|
|
406
|
+
| `library-private-type-root-dev` | Private type-only usage belongs to root `devDependencies`. |
|
|
407
|
+
| `library-dts-type-leak` | A dependency imported by public `.d.ts` becomes public contract. |
|
|
408
|
+
| `service-runtime-deps` | Service external runtime deps become `dependencies`; peers/dev are forbidden. |
|
|
409
|
+
| `cli-runtime-deps` | CLI runtime deps become `dependencies`; peers are forbidden. |
|
|
410
|
+
| `local-workspace-deps` | Local workspace deps are `dependencies: workspace:*`. |
|
|
411
|
+
| `peer-propagation-local-library` | A consumer provides peers required by a local library. |
|
|
412
|
+
| `peer-propagation-external-package` | A consumer provides required peers of an external npm dependency. |
|
|
413
|
+
| `optional-peers-ignored` | Optional peers are not added. |
|
|
414
|
+
| `repeated-dep-needs-version-rule` | External deps used more than once require a version rule. |
|
|
415
|
+
| `rule-precedence-last-wins` | The last matching rule wins. |
|
|
416
|
+
| `rootonly-violation` | `rootOnly` deps are rejected in workspaces. |
|
|
417
|
+
|
|
418
|
+
### Compatibility Tests
|
|
419
|
+
|
|
420
|
+
Compatibility tests run on small temporary monorepos. They should be few and
|
|
421
|
+
focused, because they invoke real tools.
|
|
422
|
+
|
|
423
|
+
| Test | Proves |
|
|
424
|
+
|---|---|
|
|
425
|
+
| `yarn-install-clean` | After autofix, `yarn install` has no dependency placement warnings. |
|
|
426
|
+
| `yarn-peer-requirements-clean` | `yarn explain peer-requirements` has no failed peer requirements. |
|
|
427
|
+
| `tsc-library-build` | A library with peer + dev mirror compiles. |
|
|
428
|
+
| `tsc-dts-leak` | A public `.d.ts` leak is detected when `dist/` exists and changes expected manifests. |
|
|
429
|
+
| `eslint-extraneous-compatible` | ESLint import/dependency rules do not contradict the model. |
|
|
430
|
+
| `production-focus` | Production install/focus uses only production graph and does not require root dev deps or package dev mirrors. |
|
|
431
|
+
| `cli-install-shape` | CLI runtime deps are in `dependencies`, so install/dlx-style usage has the required deps. |
|
|
432
|
+
| `service-prod-shape` | Service production graph is complete from `dependencies`. |
|
|
433
|
+
|
|
434
|
+
The production test may use `yarn workspaces focus --production` if that is the
|
|
435
|
+
chosen production mechanism. Otherwise it should assert the same invariant
|
|
436
|
+
directly: production runtime must not depend on root `devDependencies` or
|
|
437
|
+
workspace `devDependencies`.
|
|
438
|
+
|
|
439
|
+
### Repo Smoke Tests
|
|
440
|
+
|
|
441
|
+
Repo smoke tests verify that the model works on the real repository:
|
|
442
|
+
|
|
443
|
+
1. Run `depdoc fix`.
|
|
444
|
+
2. Run `yarn install`.
|
|
445
|
+
3. Build packages.
|
|
446
|
+
4. Run `depdoc check --with-dts`.
|
|
447
|
+
5. Run `yarn install` again if manifests changed.
|
|
448
|
+
6. Assert no failed peer requirements.
|
|
449
|
+
7. Run repo lint/typecheck/dependency task.
|
|
450
|
+
|
|
451
|
+
This is the final confidence layer. Most edge cases should still be covered by
|
|
452
|
+
fast model fixtures, not by expanding the smoke suite.
|
package/README.MD
CHANGED
|
@@ -12,6 +12,30 @@ some-package: 1.0.0
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
+
Dependency model CLI:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
depdoc check
|
|
19
|
+
depdoc fix
|
|
20
|
+
depdoc explain <workspace> <dependency>
|
|
21
|
+
depdoc doctor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`depdoc` is the unified dependency-model entrypoint. It derives the
|
|
25
|
+
expected manifests from workspace roles, dependency rules, source imports,
|
|
26
|
+
emitted `.d.ts` imports, peer propagation, and library peer/dev mirrors.
|
|
27
|
+
|
|
28
|
+
Dependency rules can mark indirect runtime requirements explicitly:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
rules:
|
|
32
|
+
- match: '@fastify/static'
|
|
33
|
+
workspace: '@rsdk/http.server.fastify'
|
|
34
|
+
section: dependencies
|
|
35
|
+
version: '^6.12.0'
|
|
36
|
+
required: true
|
|
37
|
+
```
|
|
38
|
+
|
|
15
39
|
Default
|
|
16
40
|
|
|
17
41
|
```js
|