@rsdk/yarn.constraints 6.0.0-next.4 → 6.0.0-next.41

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