@rsdk/depdoc.cli 6.0.0-next.43 → 6.0.0-next.45

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 (40) hide show
  1. package/DEPDOC_MODEL.md +42 -6
  2. package/__tests__/config-validation.test.ts +30 -0
  3. package/__tests__/engine.test.ts +110 -2
  4. package/__tests__/fixtures/imports/plain-root-file.ts +3 -0
  5. package/__tests__/fixtures/imports/sidebars.ts +5 -0
  6. package/__tests__/fixtures/imports/vite.config.ts +3 -0
  7. package/__tests__/imports.test.ts +42 -1
  8. package/__tests__/peer-requirements.test.ts +158 -0
  9. package/dist/bin/depdoc.js +18 -8
  10. package/dist/bin/depdoc.js.map +1 -1
  11. package/dist/collectors/workspaces.d.ts +2 -2
  12. package/dist/collectors/workspaces.js +12 -4
  13. package/dist/collectors/workspaces.js.map +1 -1
  14. package/dist/lib/imports.d.ts +4 -2
  15. package/dist/lib/imports.js +40 -3
  16. package/dist/lib/imports.js.map +1 -1
  17. package/dist/lib/peer-requirements.d.ts +36 -0
  18. package/dist/lib/peer-requirements.js +85 -0
  19. package/dist/lib/peer-requirements.js.map +1 -0
  20. package/dist/model/config-validation.d.ts +1 -0
  21. package/dist/model/config-validation.js +29 -0
  22. package/dist/model/config-validation.js.map +1 -1
  23. package/dist/model/diagnostics.js +4 -1
  24. package/dist/model/diagnostics.js.map +1 -1
  25. package/dist/model/placement.js +12 -1
  26. package/dist/model/placement.js.map +1 -1
  27. package/dist/model/types.d.ts +6 -0
  28. package/dist/model/types.js.map +1 -1
  29. package/dist/runner.js +1 -1
  30. package/dist/runner.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/bin/depdoc.ts +36 -8
  33. package/src/collectors/workspaces.ts +34 -4
  34. package/src/lib/imports.ts +57 -2
  35. package/src/lib/peer-requirements.ts +117 -0
  36. package/src/model/config-validation.ts +45 -2
  37. package/src/model/diagnostics.ts +6 -1
  38. package/src/model/placement.ts +19 -1
  39. package/src/model/types.ts +14 -0
  40. package/src/runner.ts +1 -0
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * Config validation for rule shapes that the model cannot interpret safely.
3
3
  */
4
- import type { DependencyModelConfig, DependencyRule } from './types';
4
+ import type {
5
+ DependencyModelConfig,
6
+ DependencyRule,
7
+ ToolingFileRule,
8
+ } from './types';
5
9
 
6
- function getRuleMatches(rule: DependencyRule): string[] {
10
+ function getRuleMatches(rule: DependencyRule | ToolingFileRule): string[] {
7
11
  return Array.isArray(rule.match) ? rule.match : [rule.match];
8
12
  }
9
13
 
@@ -47,12 +51,51 @@ export function validateIgnoredImports(ignoredImports: unknown): string[] {
47
51
  );
48
52
  }
49
53
 
54
+ function isStringOrStringArray(value: unknown): boolean {
55
+ return (
56
+ typeof value === 'string' ||
57
+ (Array.isArray(value) && value.every((item) => typeof item === 'string'))
58
+ );
59
+ }
60
+
61
+ export function validateToolingFiles(toolingFiles: unknown): string[] {
62
+ if (toolingFiles === undefined) return [];
63
+ if (!Array.isArray(toolingFiles)) {
64
+ return ['toolingFiles must be an array of { match, workspace? } rules'];
65
+ }
66
+
67
+ return toolingFiles.flatMap((item, index) => {
68
+ const label = `toolingFiles[${index}]`;
69
+ if (typeof item !== 'object' || item === null) {
70
+ return [`${label} must be an object with a match field`];
71
+ }
72
+
73
+ const rule = item as Record<string, unknown>;
74
+ const errors: string[] = [];
75
+
76
+ if (!isStringOrStringArray(rule.match)) {
77
+ errors.push(`${label}.match must be a string or an array of strings`);
78
+ }
79
+ if (
80
+ rule.workspace !== undefined &&
81
+ !isStringOrStringArray(rule.workspace)
82
+ ) {
83
+ errors.push(
84
+ `${label}.workspace must be a string or an array of strings`,
85
+ );
86
+ }
87
+
88
+ return errors;
89
+ });
90
+ }
91
+
50
92
  export function assertValidDependencyModelConfig(
51
93
  config: DependencyModelConfig,
52
94
  ): void {
53
95
  const errors = [
54
96
  ...validateDependencyRules(config.rules ?? []),
55
97
  ...validateIgnoredImports(config.ignoredImports),
98
+ ...validateToolingFiles(config.toolingFiles),
56
99
  ];
57
100
 
58
101
  if (errors.length === 0) return;
@@ -258,7 +258,12 @@ export function validateBasicShape(
258
258
  });
259
259
  }
260
260
 
261
- if (workspace.hasSrc && !workspace.hasDist && withDts) {
261
+ if (
262
+ workspace.role === 'library' &&
263
+ workspace.hasSrc &&
264
+ !workspace.hasDist &&
265
+ withDts
266
+ ) {
262
267
  violations.push({
263
268
  code: 'dist-missing',
264
269
  workspace: getWorkspaceLabel(workspace),
@@ -27,6 +27,20 @@ function isTestFile(file: string): boolean {
27
27
  );
28
28
  }
29
29
 
30
+ // Build-tool config files (vite.config.ts, eslint.config.js, ...) import
31
+ // real packages, but those imports are tooling usage, not the workspace's
32
+ // own runtime surface — treat them like test files for placement purposes
33
+ // so they don't get forced into dependencies/peerDependencies. Matched by
34
+ // exact path against the workspace's collected tooling files (not by name
35
+ // pattern), so an app module that merely happens to be named e.g.
36
+ // src/auth.config.ts is never misclassified.
37
+ function isPrivateUsageFile(
38
+ file: string,
39
+ toolingFiles: ReadonlySet<string>,
40
+ ): boolean {
41
+ return isTestFile(file) || toolingFiles.has(file);
42
+ }
43
+
30
44
  export function isExternal(
31
45
  depIdent: string,
32
46
  workspaceNames: Set<string>,
@@ -215,9 +229,13 @@ export function addUsageDependencies(
215
229
  const workspace = expected.workspace;
216
230
  if (workspace.isRoot) continue;
217
231
 
232
+ const toolingFiles = workspace.toolingFiles ?? new Set<string>();
233
+
218
234
  for (const [depIdent, usage] of workspace.sourceUsage) {
219
235
  const publicType = workspace.dtsImports.has(depIdent);
220
- const runtime = [...usage.runtimeFiles].some((file) => !isTestFile(file));
236
+ const runtime = [...usage.runtimeFiles].some(
237
+ (file) => !isPrivateUsageFile(file, toolingFiles),
238
+ );
221
239
  const allTestOnly = [...usage.files].every(isTestFile);
222
240
 
223
241
  if (runtime || publicType) {
@@ -23,9 +23,18 @@ export interface DependencyRule {
23
23
  required?: boolean;
24
24
  }
25
25
 
26
+ // Extra root-level filename patterns (beyond the built-in *.config.* suffix
27
+ // convention) whose imports should be treated as tooling usage rather than
28
+ // the workspace's own runtime surface, e.g. Docusaurus' sidebars.ts.
29
+ export interface ToolingFileRule {
30
+ match: string | string[];
31
+ workspace?: string | string[];
32
+ }
33
+
26
34
  export interface DependencyModelConfig {
27
35
  version?: number;
28
36
  ignoredImports?: string[];
37
+ toolingFiles?: ToolingFileRule[];
29
38
  rules?: DependencyRule[];
30
39
  doctor?: DoctorConfig;
31
40
  }
@@ -95,6 +104,11 @@ export interface WorkspaceFacts {
95
104
  hasSrc: boolean;
96
105
  hasDist: boolean;
97
106
  sourceFileCount?: number;
107
+
108
+ // Absolute paths of build-tool config files (vite.config.ts, ...) collected
109
+ // for this workspace; their imports are tooling usage, not the workspace's
110
+ // own runtime surface. Optional so callers that predate this field compile.
111
+ toolingFiles?: Set<string>;
98
112
  }
99
113
 
100
114
  export interface WorkspaceContext extends WorkspaceFacts {
package/src/runner.ts CHANGED
@@ -37,6 +37,7 @@ export function runDependencyModel(
37
37
  rootDir,
38
38
  withDts,
39
39
  config.ignoredImports ?? [],
40
+ config.toolingFiles ?? [],
40
41
  );
41
42
  const packageExtensions = loadPackageExtensions(rootDir);
42
43
  const workspaceNames = new Set(