archprint 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +21 -12
  3. package/dist/cli/adopted-rules.d.ts +30 -2
  4. package/dist/cli/adopted-rules.d.ts.map +1 -1
  5. package/dist/cli/adopted-rules.js +158 -2
  6. package/dist/cli/adopted-rules.js.map +1 -1
  7. package/dist/cli/allow.d.ts.map +1 -1
  8. package/dist/cli/allow.js +21 -4
  9. package/dist/cli/allow.js.map +1 -1
  10. package/dist/cli/allowed-evidence.d.ts.map +1 -1
  11. package/dist/cli/allowed-evidence.js +183 -1
  12. package/dist/cli/allowed-evidence.js.map +1 -1
  13. package/dist/cli/check-evaluate.d.ts.map +1 -1
  14. package/dist/cli/check-evaluate.js +124 -0
  15. package/dist/cli/check-evaluate.js.map +1 -1
  16. package/dist/cli/generate.d.ts +4 -4
  17. package/dist/cli/generate.d.ts.map +1 -1
  18. package/dist/cli/generate.js +115 -42
  19. package/dist/cli/generate.js.map +1 -1
  20. package/dist/cli/layout.d.ts +5 -2
  21. package/dist/cli/layout.d.ts.map +1 -1
  22. package/dist/cli/layout.js +43 -10
  23. package/dist/cli/layout.js.map +1 -1
  24. package/dist/cli/migrate.d.ts.map +1 -1
  25. package/dist/cli/migrate.js +1 -2
  26. package/dist/cli/migrate.js.map +1 -1
  27. package/dist/cli/program.d.ts.map +1 -1
  28. package/dist/cli/program.js +10 -6
  29. package/dist/cli/program.js.map +1 -1
  30. package/dist/cli/recommend.d.ts +3 -0
  31. package/dist/cli/recommend.d.ts.map +1 -1
  32. package/dist/cli/recommend.js +28 -8
  33. package/dist/cli/recommend.js.map +1 -1
  34. package/dist/cli/report.js +1 -1
  35. package/dist/cli/report.js.map +1 -1
  36. package/dist/detector/cycle-detector.d.ts +5 -0
  37. package/dist/detector/cycle-detector.d.ts.map +1 -1
  38. package/dist/detector/cycle-detector.js +20 -0
  39. package/dist/detector/cycle-detector.js.map +1 -1
  40. package/dist/detector/dependency-internals-detector.d.ts.map +1 -1
  41. package/dist/detector/dependency-internals-detector.js +6 -20
  42. package/dist/detector/dependency-internals-detector.js.map +1 -1
  43. package/dist/detector/entry-purity-detector.d.ts +7 -0
  44. package/dist/detector/entry-purity-detector.d.ts.map +1 -1
  45. package/dist/detector/entry-purity-detector.js +21 -10
  46. package/dist/detector/entry-purity-detector.js.map +1 -1
  47. package/dist/detector/phantom-dependency-detector.d.ts +7 -0
  48. package/dist/detector/phantom-dependency-detector.d.ts.map +1 -1
  49. package/dist/detector/phantom-dependency-detector.js +18 -39
  50. package/dist/detector/phantom-dependency-detector.js.map +1 -1
  51. package/dist/detector/server-client-detector.d.ts +5 -0
  52. package/dist/detector/server-client-detector.d.ts.map +1 -1
  53. package/dist/detector/server-client-detector.js +8 -3
  54. package/dist/detector/server-client-detector.js.map +1 -1
  55. package/dist/generator/app-isolation-emitters.d.ts +1 -0
  56. package/dist/generator/app-isolation-emitters.d.ts.map +1 -1
  57. package/dist/generator/app-isolation-emitters.js +2 -1
  58. package/dist/generator/app-isolation-emitters.js.map +1 -1
  59. package/dist/generator/depcruise-exempt.d.ts +4 -0
  60. package/dist/generator/depcruise-exempt.d.ts.map +1 -1
  61. package/dist/generator/depcruise-exempt.js +7 -0
  62. package/dist/generator/depcruise-exempt.js.map +1 -1
  63. package/dist/generator/dependency-internals-emitters.d.ts +2 -1
  64. package/dist/generator/dependency-internals-emitters.d.ts.map +1 -1
  65. package/dist/generator/dependency-internals-emitters.js +7 -3
  66. package/dist/generator/dependency-internals-emitters.js.map +1 -1
  67. package/dist/generator/entry-purity-emitters.d.ts +7 -3
  68. package/dist/generator/entry-purity-emitters.d.ts.map +1 -1
  69. package/dist/generator/entry-purity-emitters.js +19 -5
  70. package/dist/generator/entry-purity-emitters.js.map +1 -1
  71. package/dist/generator/env-access-emitters.d.ts +1 -0
  72. package/dist/generator/env-access-emitters.d.ts.map +1 -1
  73. package/dist/generator/env-access-emitters.js +1 -0
  74. package/dist/generator/env-access-emitters.js.map +1 -1
  75. package/dist/generator/eslint-app-emitter.d.ts +59 -0
  76. package/dist/generator/eslint-app-emitter.d.ts.map +1 -0
  77. package/dist/generator/eslint-app-emitter.js +275 -0
  78. package/dist/generator/eslint-app-emitter.js.map +1 -0
  79. package/dist/generator/eslint-preset-emitter.d.ts +2 -2
  80. package/dist/generator/eslint-preset-emitter.d.ts.map +1 -1
  81. package/dist/generator/eslint-preset-emitter.js +6 -6
  82. package/dist/generator/eslint-preset-emitter.js.map +1 -1
  83. package/dist/generator/feature-slice-emitters.d.ts +1 -0
  84. package/dist/generator/feature-slice-emitters.d.ts.map +1 -1
  85. package/dist/generator/feature-slice-emitters.js +2 -1
  86. package/dist/generator/feature-slice-emitters.js.map +1 -1
  87. package/dist/generator/layer-emitters.d.ts.map +1 -1
  88. package/dist/generator/layer-emitters.js +2 -6
  89. package/dist/generator/layer-emitters.js.map +1 -1
  90. package/dist/generator/phantom-dependency-emitters.d.ts +2 -1
  91. package/dist/generator/phantom-dependency-emitters.d.ts.map +1 -1
  92. package/dist/generator/phantom-dependency-emitters.js +7 -3
  93. package/dist/generator/phantom-dependency-emitters.js.map +1 -1
  94. package/dist/generator/role-layering-emitters.d.ts +2 -0
  95. package/dist/generator/role-layering-emitters.d.ts.map +1 -1
  96. package/dist/generator/role-layering-emitters.js +2 -1
  97. package/dist/generator/role-layering-emitters.js.map +1 -1
  98. package/dist/generator/server-client-emitters.d.ts +10 -3
  99. package/dist/generator/server-client-emitters.d.ts.map +1 -1
  100. package/dist/generator/server-client-emitters.js +23 -4
  101. package/dist/generator/server-client-emitters.js.map +1 -1
  102. package/dist/generator/stories-isolation-emitters.d.ts +1 -0
  103. package/dist/generator/stories-isolation-emitters.d.ts.map +1 -1
  104. package/dist/generator/stories-isolation-emitters.js +2 -1
  105. package/dist/generator/stories-isolation-emitters.js.map +1 -1
  106. package/dist/generator/ui-data-isolation-emitters.d.ts +1 -0
  107. package/dist/generator/ui-data-isolation-emitters.d.ts.map +1 -1
  108. package/dist/generator/ui-data-isolation-emitters.js +2 -1
  109. package/dist/generator/ui-data-isolation-emitters.js.map +1 -1
  110. package/dist/generator/workspace-package-emitters.d.ts +1 -0
  111. package/dist/generator/workspace-package-emitters.d.ts.map +1 -1
  112. package/dist/generator/workspace-package-emitters.js +1 -0
  113. package/dist/generator/workspace-package-emitters.js.map +1 -1
  114. package/dist/scanner/directives.d.ts +6 -0
  115. package/dist/scanner/directives.d.ts.map +1 -0
  116. package/dist/scanner/directives.js +35 -0
  117. package/dist/scanner/directives.js.map +1 -0
  118. package/dist/scanner/package-name.d.ts +10 -0
  119. package/dist/scanner/package-name.d.ts.map +1 -0
  120. package/dist/scanner/package-name.js +37 -0
  121. package/dist/scanner/package-name.js.map +1 -0
  122. package/dist/scanner/resolve-import.d.ts +5 -0
  123. package/dist/scanner/resolve-import.d.ts.map +1 -1
  124. package/dist/scanner/resolve-import.js +22 -19
  125. package/dist/scanner/resolve-import.js.map +1 -1
  126. package/dist/scanner/role-classifier.d.ts +11 -2
  127. package/dist/scanner/role-classifier.d.ts.map +1 -1
  128. package/dist/scanner/role-classifier.js +13 -39
  129. package/dist/scanner/role-classifier.js.map +1 -1
  130. package/package.json +2 -2
  131. package/dist/generator/eslint-layer-emitter.d.ts +0 -29
  132. package/dist/generator/eslint-layer-emitter.d.ts.map +0 -1
  133. package/dist/generator/eslint-layer-emitter.js +0 -121
  134. package/dist/generator/eslint-layer-emitter.js.map +0 -1
@@ -1,5 +1,8 @@
1
1
  import { type ImportGraph } from '../scanner/import-graph.js';
2
+ import { type Role } from '../scanner/role-classifier.js';
2
3
  import { type GateResult } from './confidence-gate.js';
4
+ /** Files the framework loads itself: pages, layouts, route and API handlers. */
5
+ export declare const ENTRY_ROLES: readonly Role[];
3
6
  export interface EntryImportViolation {
4
7
  file: string;
5
8
  importer: string;
@@ -7,10 +10,14 @@ export interface EntryImportViolation {
7
10
  export interface EntryPurityAnalysis {
8
11
  appDir: string;
9
12
  entryCount: number;
13
+ /** Every import of a framework entry from a file that is not one. */
14
+ imports: EntryImportViolation[];
10
15
  offenderCount: number;
11
16
  gate: GateResult;
12
17
  violations: EntryImportViolation[];
13
18
  }
19
+ /** One violation per imported entry: its first importer in path order. */
20
+ export declare function importedEntries(imports: readonly EntryImportViolation[]): EntryImportViolation[];
14
21
  export interface EntryPurityOptions {
15
22
  graph?: ImportGraph;
16
23
  resolve?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"entry-purity-detector.d.ts","sourceRoot":"","sources":["../../src/detector/entry-purity-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIrE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,mBAAmB,CA8BrB"}
1
+ {"version":3,"file":"entry-purity-detector.d.ts","sourceRoot":"","sources":["../../src/detector/entry-purity-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE,gFAAgF;AAChF,eAAO,MAAM,WAAW,EAAE,SAAS,IAAI,EAAoD,CAAC;AAE5F,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,oBAAoB,EAAE,GAAG,oBAAoB,EAAE,CAShG;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,mBAAmB,CA6BrB"}
@@ -1,25 +1,36 @@
1
1
  import { buildImportGraph } from '../scanner/import-graph.js';
2
2
  import {} from '../scanner/role-classifier.js';
3
3
  import { evaluateGate } from './confidence-gate.js';
4
- const ENTRY_ROLES = new Set(['ROUTE_ENTRY', 'ROUTE_HANDLER', 'API_HANDLER']);
4
+ /** Files the framework loads itself: pages, layouts, route and API handlers. */
5
+ export const ENTRY_ROLES = ['ROUTE_ENTRY', 'ROUTE_HANDLER', 'API_HANDLER'];
6
+ /** One violation per imported entry: its first importer in path order. */
7
+ export function importedEntries(imports) {
8
+ const firstImporter = new Map();
9
+ for (const { file, importer } of imports) {
10
+ const known = firstImporter.get(file);
11
+ if (known === undefined || importer < known)
12
+ firstImporter.set(file, importer);
13
+ }
14
+ return [...firstImporter]
15
+ .map(([file, importer]) => ({ file, importer }))
16
+ .sort((a, b) => a.file.localeCompare(b.file));
17
+ }
5
18
  export function detectEntryPurity(appDir, options = {}) {
6
19
  const { root, files, adjacency } = options.graph ?? buildImportGraph(appDir, { resolve: options.resolve ?? false });
7
- const entries = new Set(files.filter((file) => ENTRY_ROLES.has(file.role)).map((file) => file.relativePath));
8
- const importerOf = new Map();
20
+ const entries = new Set(files.filter((file) => ENTRY_ROLES.includes(file.role)).map((file) => file.relativePath));
21
+ const imports = [];
9
22
  for (const [file, targets] of adjacency) {
10
23
  if (entries.has(file))
11
24
  continue;
12
- for (const target of targets) {
13
- if (entries.has(target) && !importerOf.has(target))
14
- importerOf.set(target, file);
15
- }
25
+ for (const target of targets)
26
+ if (entries.has(target))
27
+ imports.push({ file: target, importer: file });
16
28
  }
17
- const violations = [...importerOf.entries()]
18
- .map(([file, importer]) => ({ file, importer }))
19
- .sort((a, b) => a.file.localeCompare(b.file));
29
+ const violations = importedEntries(imports);
20
30
  return {
21
31
  appDir: root,
22
32
  entryCount: entries.size,
33
+ imports: imports.sort((a, b) => a.file.localeCompare(b.file) || a.importer.localeCompare(b.importer)),
23
34
  offenderCount: violations.length,
24
35
  gate: evaluateGate({
25
36
  roleFileCount: entries.size,
@@ -1 +1 @@
1
- {"version":3,"file":"entry-purity-detector.js","sourceRoot":"","sources":["../../src/detector/entry-purity-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAoB,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAa,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAErE,MAAM,WAAW,GAAsB,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC;AAoBhG,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,UAA8B,EAAE;IAEhC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAC9B,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;IAEnF,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CACpF,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,SAAS,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAChC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAA2B,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;SACjE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;SAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhD,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,OAAO,CAAC,IAAI;QACxB,aAAa,EAAE,UAAU,CAAC,MAAM;QAChC,IAAI,EAAE,YAAY,CAAC;YACjB,aAAa,EAAE,OAAO,CAAC,IAAI;YAC3B,kBAAkB,EAAE,UAAU,CAAC,MAAM;YACrC,cAAc,EAAE,CAAC;SAClB,CAAC;QACF,UAAU;KACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"entry-purity-detector.js","sourceRoot":"","sources":["../../src/detector/entry-purity-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAoB,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAa,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAErE,gFAAgF;AAChF,MAAM,CAAC,MAAM,WAAW,GAAoB,CAAC,aAAa,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;AAiB5F,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,OAAwC;IACtE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,KAAK,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,OAAO,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,SAAS,IAAI,QAAQ,GAAG,KAAK;YAAE,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,CAAC,GAAG,aAAa,CAAC;SACtB,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;SAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAClD,CAAC;AAOD,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,UAA8B,EAAE;IAEhC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAC9B,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;IAEnF,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CACzF,CAAC;IACF,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,SAAS,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAChC,KAAK,MAAM,MAAM,IAAI,OAAO;YAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAE5C,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,OAAO,CAAC,IAAI;QACxB,OAAO,EAAE,OAAO,CAAC,IAAI,CACnB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAC/E;QACD,aAAa,EAAE,UAAU,CAAC,MAAM;QAChC,IAAI,EAAE,YAAY,CAAC;YACjB,aAAa,EAAE,OAAO,CAAC,IAAI;YAC3B,kBAAkB,EAAE,UAAU,CAAC,MAAM;YACrC,cAAc,EAAE,CAAC;SAClB,CAAC;QACF,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -1,4 +1,8 @@
1
1
  import { type GateResult } from './confidence-gate.js';
2
+ /** The names of the workspace's packages, which count as declared. */
3
+ export declare const workspacePackageNames: (appDir: string) => string[];
4
+ /** The package.json files whose dependencies count as declared: the app's, and the workspace root's. */
5
+ export declare function declaringPackageJsons(appDir: string): string[];
2
6
  export interface PhantomDependencyViolation {
3
7
  file: string;
4
8
  specifier: string;
@@ -7,6 +11,9 @@ export interface PhantomDependencyViolation {
7
11
  export interface PhantomDependencyAnalysis {
8
12
  appDir: string;
9
13
  externalImporterCount: number;
14
+ /** The package.json files and workspace packages that count as declared. */
15
+ declaringPackageJsons: string[];
16
+ workspacePackages: string[];
10
17
  offenderCount: number;
11
18
  gate: GateResult;
12
19
  violations: PhantomDependencyViolation[];
@@ -1 +1 @@
1
- {"version":3,"file":"phantom-dependency-detector.d.ts","sourceRoot":"","sources":["../../src/detector/phantom-dependency-detector.ts"],"names":[],"mappings":"AAWA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAiCrE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,0BAA0B,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,wBAA6B,GACrC,yBAAyB,CAmD3B"}
1
+ {"version":3,"file":"phantom-dependency-detector.d.ts","sourceRoot":"","sources":["../../src/detector/phantom-dependency-detector.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIrE,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,KAAG,MAAM,EAC0B,CAAC;AAExF,wGAAwG;AACxG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM9D;AAKD,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,0BAA0B,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,wBAA6B,GACrC,yBAAyB,CAsD3B"}
@@ -2,54 +2,31 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { builtinModules } from 'node:module';
3
3
  import * as path from 'node:path';
4
4
  import { createImportAnalyzer, walkRepo, } from '../scanner/file-walker.js';
5
+ import { declaredPackages, packageName } from '../scanner/package-name.js';
5
6
  import { buildWorkspaceMap } from '../scanner/workspace-resolver.js';
6
7
  import { buildWorkspacePackageMap, findWorkspaceRoot } from '../scanner/workspace-packages.js';
7
8
  import { evaluateGate } from './confidence-gate.js';
8
9
  const BUILTINS = new Set(builtinModules);
9
- const DEP_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
10
- function packageName(specifier, aliasPrefixes) {
11
- if (specifier.startsWith('.'))
12
- return null;
13
- const bare = specifier.startsWith('node:') ? specifier.slice(5) : specifier;
14
- if (specifier.startsWith('node:') || BUILTINS.has(bare) || BUILTINS.has(bare.split('/')[0])) {
15
- return null;
16
- }
17
- for (const prefix of aliasPrefixes) {
18
- if (specifier === prefix || specifier.startsWith(`${prefix}/`))
19
- return null;
20
- }
21
- const parts = specifier.split('/');
22
- return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
23
- }
24
- function declaredIn(packageJsonPath, into) {
25
- if (!existsSync(packageJsonPath))
26
- return;
27
- try {
28
- const json = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
29
- for (const field of DEP_FIELDS) {
30
- const deps = json[field];
31
- if (deps && typeof deps === 'object') {
32
- for (const name of Object.keys(deps))
33
- into.add(name);
34
- }
35
- }
36
- }
37
- catch {
38
- /* v8 ignore next -- unreadable/invalid package.json is treated as declaring nothing */
39
- }
10
+ /** The names of the workspace's packages, which count as declared. */
11
+ export const workspacePackageNames = (appDir) => Object.keys(buildWorkspacePackageMap(findWorkspaceRoot(path.resolve(appDir)))).sort();
12
+ /** The package.json files whose dependencies count as declared: the app's, and the workspace root's. */
13
+ export function declaringPackageJsons(appDir) {
14
+ const root = path.resolve(appDir);
15
+ const workspaceRoot = findWorkspaceRoot(root);
16
+ return [root, ...(workspaceRoot === root ? [] : [workspaceRoot])].map((dir) => path.join(dir, 'package.json'));
40
17
  }
18
+ const readIfPresent = (file) => existsSync(file) ? readFileSync(file, 'utf-8') : '';
41
19
  export function detectPhantomDependencies(appDir, options = {}) {
42
20
  const root = path.resolve(appDir);
43
21
  const resolve = options.resolve ?? false;
44
22
  const files = walkRepo(root).filter((file) => file.role !== 'TEST');
45
23
  const aliasPrefixes = Object.keys(buildWorkspaceMap(root)).map((key) => key.replace(/\/?\*$/, ''));
46
- const declared = new Set();
47
- declaredIn(path.join(root, 'package.json'), declared);
48
- const workspaceRoot = findWorkspaceRoot(root);
49
- if (workspaceRoot !== root)
50
- declaredIn(path.join(workspaceRoot, 'package.json'), declared);
51
- for (const name of Object.keys(buildWorkspacePackageMap(workspaceRoot)))
52
- declared.add(name);
24
+ const packageJsons = declaringPackageJsons(root);
25
+ const workspacePackages = workspacePackageNames(root);
26
+ const declared = new Set([
27
+ ...packageJsons.flatMap((file) => declaredPackages(readIfPresent(file))),
28
+ ...workspacePackages,
29
+ ]);
53
30
  const analyze = createImportAnalyzer(root, { resolve });
54
31
  const externalImporters = new Set();
55
32
  const offenders = new Set();
@@ -66,7 +43,7 @@ export function detectPhantomDependencies(appDir, options = {}) {
66
43
  for (const imp of imports) {
67
44
  if (!imp.hasValueBinding)
68
45
  continue;
69
- const name = packageName(imp.specifier, aliasPrefixes);
46
+ const name = packageName(imp.specifier, aliasPrefixes, BUILTINS);
70
47
  if (name === null)
71
48
  continue;
72
49
  externalImporters.add(file.relativePath);
@@ -79,6 +56,8 @@ export function detectPhantomDependencies(appDir, options = {}) {
79
56
  return {
80
57
  appDir: root,
81
58
  externalImporterCount: externalImporters.size,
59
+ declaringPackageJsons: packageJsons,
60
+ workspacePackages,
82
61
  offenderCount: offenders.size,
83
62
  gate: evaluateGate({
84
63
  roleFileCount: externalImporters.size,
@@ -1 +1 @@
1
- {"version":3,"file":"phantom-dependency-detector.js","sourceRoot":"","sources":["../../src/detector/phantom-dependency-detector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"phantom-dependency-detector.js","sourceRoot":"","sources":["../../src/detector/phantom-dependency-detector.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { type ImportGraph } from '../scanner/import-graph.js';
2
2
  import { type GateResult } from './confidence-gate.js';
3
+ /** The package that marks a module as server-only in React Server Components (Next.js). */
4
+ export declare const SERVER_ONLY_PACKAGE = "server-only";
3
5
  export interface ServerClientViolation {
4
6
  file: string;
5
7
  target: string;
@@ -7,6 +9,9 @@ export interface ServerClientViolation {
7
9
  export interface ServerClientAnalysis {
8
10
  appDir: string;
9
11
  clientCount: number;
12
+ /** The "use client" files and the modules importing "server-only", relative to the app. */
13
+ clients: string[];
14
+ serverModules: string[];
10
15
  offenderCount: number;
11
16
  gate: GateResult;
12
17
  violations: ServerClientViolation[];
@@ -1 +1 @@
1
- {"version":3,"file":"server-client-detector.d.ts","sourceRoot":"","sources":["../../src/detector/server-client-detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIrE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,mBAAwB,GAChC,oBAAoB,CA+CtB"}
1
+ {"version":3,"file":"server-client-detector.d.ts","sourceRoot":"","sources":["../../src/detector/server-client-detector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE,2FAA2F;AAC3F,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,mBAAwB,GAChC,oBAAoB,CAmDtB"}
@@ -1,8 +1,10 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { importLocations } from '../scanner/file-walker.js';
2
3
  import { buildImportGraph } from '../scanner/import-graph.js';
3
4
  import { hasUseClientDirective } from '../scanner/role-classifier.js';
4
5
  import { evaluateGate } from './confidence-gate.js';
5
- const SERVER_ONLY = /(^|[^.\w])['"]server-only['"]/;
6
+ /** The package that marks a module as server-only in React Server Components (Next.js). */
7
+ export const SERVER_ONLY_PACKAGE = 'server-only';
6
8
  export function detectServerClientBoundary(appDir, options = {}) {
7
9
  const { root, files, adjacency } = options.graph ?? buildImportGraph(appDir, { resolve: options.resolve ?? false });
8
10
  const clients = new Set();
@@ -18,15 +20,16 @@ export function detectServerClientBoundary(appDir, options = {}) {
18
20
  }
19
21
  if (hasUseClientDirective(text))
20
22
  clients.add(file.relativePath);
21
- if (SERVER_ONLY.test(text))
23
+ if (importLocations(file.absolutePath).some((imp) => imp.specifier === SERVER_ONLY_PACKAGE)) {
22
24
  serverModules.add(file.relativePath);
25
+ }
23
26
  }
24
27
  const offenders = new Set();
25
28
  const violations = [];
26
29
  for (const file of clients) {
27
30
  if (serverModules.has(file)) {
28
31
  offenders.add(file);
29
- violations.push({ file, target: 'server-only' });
32
+ violations.push({ file, target: SERVER_ONLY_PACKAGE });
30
33
  }
31
34
  for (const target of adjacency.get(file) ?? []) {
32
35
  if (serverModules.has(target)) {
@@ -38,6 +41,8 @@ export function detectServerClientBoundary(appDir, options = {}) {
38
41
  return {
39
42
  appDir: root,
40
43
  clientCount: clients.size,
44
+ clients: [...clients].sort(),
45
+ serverModules: [...serverModules].sort(),
41
46
  offenderCount: offenders.size,
42
47
  gate: evaluateGate({
43
48
  roleFileCount: clients.size,
@@ -1 +1 @@
1
- {"version":3,"file":"server-client-detector.js","sourceRoot":"","sources":["../../src/detector/server-client-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAoB,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAErE,MAAM,WAAW,GAAG,+BAA+B,CAAC;AAoBpD,MAAM,UAAU,0BAA0B,CACxC,MAAc,EACd,UAA+B,EAAE;IAEjC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAC9B,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;IAEnF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,+FAA+F;YAC/F,SAAS;QACX,CAAC;QACD,IAAI,qBAAqB,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChE,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/C,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,OAAO,CAAC,IAAI;QACzB,aAAa,EAAE,SAAS,CAAC,IAAI;QAC7B,IAAI,EAAE,YAAY,CAAC;YACjB,aAAa,EAAE,OAAO,CAAC,IAAI;YAC3B,kBAAkB,EAAE,SAAS,CAAC,IAAI;YAClC,cAAc,EAAE,CAAC;YACjB,UAAU,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC;SACnC,CAAC;QACF,UAAU,EAAE,UAAU,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAC3E;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"server-client-detector.js","sourceRoot":"","sources":["../../src/detector/server-client-detector.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { GenerationStatus } from '../detector/confidence-gate.js';
2
2
  import type { AppIsolationGroup } from '../detector/app-isolation-detector.js';
3
+ export declare const appIsolationRuleName: (container: string) => string;
3
4
  export interface CrossAppRule {
4
5
  name: string;
5
6
  comment: string;
@@ -1 +1 @@
1
- {"version":3,"file":"app-isolation-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/app-isolation-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAU/E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,cAAc,CAchB"}
1
+ {"version":3,"file":"app-isolation-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/app-isolation-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAO/E,eAAO,MAAM,oBAAoB,GAAI,WAAW,MAAM,KAAG,MACtB,CAAC;AAKpC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,cAAc,CAchB"}
@@ -1,5 +1,6 @@
1
1
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
2
  const slug = (container) => container.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '');
3
+ export const appIsolationRuleName = (container) => `no-cross-app-${slug(container)}`;
3
4
  const confidencePct = (group) => `${(group.gate.conditions.confidence.value * 100).toFixed(0)}%`;
4
5
  export function toDependencyCruiserAppIsolation(groups, include = ['AUTO']) {
5
6
  const forbidden = groups
@@ -7,7 +8,7 @@ export function toDependencyCruiserAppIsolation(groups, include = ['AUTO']) {
7
8
  .map((group) => {
8
9
  const container = escapeRegExp(group.container);
9
10
  return {
10
- name: `no-cross-app-${slug(group.container)}`,
11
+ name: appIsolationRuleName(group.container),
11
12
  comment: `Archprint inferred app isolation: the ${group.appCount} apps under "${group.container}" do not import each other (${group.appFileCount - group.crossImporterCount}/${group.appFileCount} app files conform); cross-app imports are forbidden (confidence ${confidencePct(group)}).`,
12
13
  severity: 'error',
13
14
  from: { path: `^${container}/([^/]+)/` },
@@ -1 +1 @@
1
- {"version":3,"file":"app-isolation-emitters.js","sourceRoot":"","sources":["../../src/generator/app-isolation-emitters.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,IAAI,GAAG,CAAC,SAAiB,EAAU,EAAE,CACzC,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAEjE,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAU,EAAE,CACzD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAclE,MAAM,UAAU,+BAA+B,CAC7C,MAAoC,EACpC,UAAuC,CAAC,MAAM,CAAC;IAE/C,MAAM,SAAS,GAAG,MAAM;SACrB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,gBAAgB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE;YAC7C,OAAO,EAAE,yCAAyC,KAAK,CAAC,QAAQ,gBAAgB,KAAK,CAAC,SAAS,+BAA+B,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,YAAY,oEAAoE,aAAa,CAAC,KAAK,CAAC,IAAI;YAC7R,QAAQ,EAAE,OAAgB;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE;YACxC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE,OAAO,EAAE,IAAI,SAAS,MAAM,EAAE;SACrE,CAAC;IACJ,CAAC,CAAC,CAAC;IACL,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"app-isolation-emitters.js","sourceRoot":"","sources":["../../src/generator/app-isolation-emitters.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,IAAI,GAAG,CAAC,SAAiB,EAAU,EAAE,CACzC,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,SAAiB,EAAU,EAAE,CAChE,gBAAgB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;AAEpC,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAU,EAAE,CACzD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAclE,MAAM,UAAU,+BAA+B,CAC7C,MAAoC,EACpC,UAAuC,CAAC,MAAM,CAAC;IAE/C,MAAM,SAAS,GAAG,MAAM;SACrB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,oBAAoB,CAAC,KAAK,CAAC,SAAS,CAAC;YAC3C,OAAO,EAAE,yCAAyC,KAAK,CAAC,QAAQ,gBAAgB,KAAK,CAAC,SAAS,+BAA+B,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,YAAY,oEAAoE,aAAa,CAAC,KAAK,CAAC,IAAI;YAC7R,QAAQ,EAAE,OAAgB;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE;YACxC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE,OAAO,EAAE,IAAI,SAAS,MAAM,EAAE;SACrE,CAAC;IACJ,CAAC,CAAC,CAAC;IACL,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -1,3 +1,7 @@
1
+ /** The regex prefix for paths under the app, as dependency-cruiser reports them from where archprint ran. */
2
+ export declare const appPathPrefix: (appPath: string) => string;
3
+ /** A regex matching exactly these app-relative files. */
4
+ export declare const exactAppFiles: (files: readonly string[], appPath: string) => string;
1
5
  export declare const TEST_ROLE_REGEX = "(\\.(test|spec|e2e-spec|e2e)\\.(ts|tsx)$)|((^|/)(__tests__|__mocks__|e2e|cypress|playwright|test|tests)/)";
2
6
  export declare function exemptDepcruiseFrom(base: string | readonly string[], violations: readonly {
3
7
  file: string;
@@ -1 +1 @@
1
- {"version":3,"file":"depcruise-exempt.d.ts","sourceRoot":"","sources":["../../src/generator/depcruise-exempt.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,8GACiF,CAAC;AAE9G,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAChC,UAAU,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACtC,MAAM,GAAG,MAAM,EAAE,CAOnB"}
1
+ {"version":3,"file":"depcruise-exempt.d.ts","sourceRoot":"","sources":["../../src/generator/depcruise-exempt.ts"],"names":[],"mappings":"AAEA,6GAA6G;AAC7G,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,MAG/C,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,aAAa,GAAI,OAAO,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,KAAG,MACH,CAAC;AAExE,eAAO,MAAM,eAAe,8GACiF,CAAC;AAE9G,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAChC,UAAU,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACtC,MAAM,GAAG,MAAM,EAAE,CAOnB"}
@@ -1,4 +1,11 @@
1
1
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
+ /** The regex prefix for paths under the app, as dependency-cruiser reports them from where archprint ran. */
3
+ export const appPathPrefix = (appPath) => {
4
+ const normalized = appPath.replace(/\\/g, '/').replace(/\/+$/, '');
5
+ return normalized === '' || normalized === '.' ? '' : `${escapeRegExp(normalized)}/`;
6
+ };
7
+ /** A regex matching exactly these app-relative files. */
8
+ export const exactAppFiles = (files, appPath) => `^${appPathPrefix(appPath)}(?:${files.map(escapeRegExp).join('|')})$`;
2
9
  export const TEST_ROLE_REGEX = '(\\.(test|spec|e2e-spec|e2e)\\.(ts|tsx)$)|((^|/)(__tests__|__mocks__|e2e|cypress|playwright|test|tests)/)';
3
10
  export function exemptDepcruiseFrom(base, violations) {
4
11
  const bases = typeof base === 'string' ? [base] : [...base];
@@ -1 +1 @@
1
- {"version":3,"file":"depcruise-exempt.js","sourceRoot":"","sources":["../../src/generator/depcruise-exempt.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,CAAC,MAAM,eAAe,GAC1B,2GAA2G,CAAC;AAE9G,MAAM,UAAU,mBAAmB,CACjC,IAAgC,EAChC,UAAuC;IAEvC,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG;QACZ,GAAG,KAAK;QACR,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;KAC1E,CAAC;IACF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"depcruise-exempt.js","sourceRoot":"","sources":["../../src/generator/depcruise-exempt.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,6GAA6G;AAC7G,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IACvD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnE,OAAO,UAAU,KAAK,EAAE,IAAI,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC;AACvF,CAAC,CAAC;AAEF,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAE,OAAe,EAAU,EAAE,CACjF,IAAI,aAAa,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AAExE,MAAM,CAAC,MAAM,eAAe,GAC1B,2GAA2G,CAAC;AAE9G,MAAM,UAAU,mBAAmB,CACjC,IAAgC,EAChC,UAAuC;IAEvC,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG;QACZ,GAAG,KAAK;QACR,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;KAC1E,CAAC;IACF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAChD,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { DependencyInternalsAnalysis } from '../detector/dependency-internals-detector.js';
2
+ export declare const DEPENDENCY_INTERNALS_RULE = "no-dependency-internals";
2
3
  export interface NoInternalsRule {
3
4
  name: string;
4
5
  comment: string;
@@ -13,5 +14,5 @@ export interface NoInternalsRule {
13
14
  export interface NoInternalsConfig {
14
15
  forbidden: NoInternalsRule[];
15
16
  }
16
- export declare function toDependencyCruiserDependencyInternals(analysis: DependencyInternalsAnalysis): NoInternalsConfig;
17
+ export declare function toDependencyCruiserDependencyInternals(analysis: DependencyInternalsAnalysis, allowedFor: (ruleId: string) => readonly string[]): NoInternalsConfig;
17
18
  //# sourceMappingURL=dependency-internals-emitters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-internals-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/dependency-internals-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAKhG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,2BAA2B,GACpC,iBAAiB,CAmBnB"}
1
+ {"version":3,"file":"dependency-internals-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/dependency-internals-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAGhG,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AAInE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,2BAA2B,EACrC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,GAChD,iBAAiB,CAyBnB"}
@@ -1,6 +1,7 @@
1
1
  import { exemptDepcruiseFrom, TEST_ROLE_REGEX } from './depcruise-exempt.js';
2
+ export const DEPENDENCY_INTERNALS_RULE = 'no-dependency-internals';
2
3
  const INTERNAL_PATH = 'node_modules/(?:@[^/]+/)?[^/]+/(?:src|internal|internals)/';
3
- export function toDependencyCruiserDependencyInternals(analysis) {
4
+ export function toDependencyCruiserDependencyInternals(analysis, allowedFor) {
4
5
  if (analysis.externalImporterCount === 0 || analysis.gate.status !== 'AUTO') {
5
6
  return { forbidden: [] };
6
7
  }
@@ -9,11 +10,14 @@ export function toDependencyCruiserDependencyInternals(analysis) {
9
10
  return {
10
11
  forbidden: [
11
12
  {
12
- name: 'no-dependency-internals',
13
+ name: DEPENDENCY_INTERNALS_RULE,
13
14
  comment: `Archprint inferred dependency hygiene: ${conform}/${analysis.externalImporterCount} files import third-party packages only by their public entry; reaching into a package's build/impl directory is forbidden (confidence ${floor}).`,
14
15
  severity: 'error',
15
16
  from: {
16
- pathNot: exemptDepcruiseFrom([TEST_ROLE_REGEX, 'node_modules'], analysis.violations),
17
+ pathNot: exemptDepcruiseFrom([TEST_ROLE_REGEX, 'node_modules'], [
18
+ ...analysis.violations,
19
+ ...allowedFor(DEPENDENCY_INTERNALS_RULE).map((file) => ({ file })),
20
+ ]),
17
21
  },
18
22
  to: { path: INTERNAL_PATH },
19
23
  },
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-internals-emitters.js","sourceRoot":"","sources":["../../src/generator/dependency-internals-emitters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,aAAa,GAAG,4DAA4D,CAAC;AAcnF,MAAM,UAAU,sCAAsC,CACpD,QAAqC;IAErC,IAAI,QAAQ,CAAC,qBAAqB,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5E,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC;IACxE,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,0CAA0C,OAAO,IAAI,QAAQ,CAAC,qBAAqB,0IAA0I,KAAK,IAAI;gBAC/O,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE;oBACJ,OAAO,EAAE,mBAAmB,CAAC,CAAC,eAAe,EAAE,cAAc,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC;iBACrF;gBACD,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;aAC5B;SACF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"dependency-internals-emitters.js","sourceRoot":"","sources":["../../src/generator/dependency-internals-emitters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,CAAC,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAEnE,MAAM,aAAa,GAAG,4DAA4D,CAAC;AAcnF,MAAM,UAAU,sCAAsC,CACpD,QAAqC,EACrC,UAAiD;IAEjD,IAAI,QAAQ,CAAC,qBAAqB,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5E,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC;IACxE,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,0CAA0C,OAAO,IAAI,QAAQ,CAAC,qBAAqB,0IAA0I,KAAK,IAAI;gBAC/O,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE;oBACJ,OAAO,EAAE,mBAAmB,CAC1B,CAAC,eAAe,EAAE,cAAc,CAAC,EACjC;wBACE,GAAG,QAAQ,CAAC,UAAU;wBACtB,GAAG,UAAU,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACnE,CACF;iBACF;gBACD,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;aAC5B;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -1,17 +1,21 @@
1
- import type { EntryPurityAnalysis } from '../detector/entry-purity-detector.js';
1
+ import { type EntryPurityAnalysis } from '../detector/entry-purity-detector.js';
2
+ export declare const ENTRY_PURITY_RULE = "no-import-framework-entry";
2
3
  export interface EntryPurityRule {
3
4
  name: string;
4
5
  comment: string;
5
6
  severity: 'error' | 'warn' | 'info';
6
7
  from: {
7
- pathNot: string;
8
+ pathNot: string | string[];
8
9
  };
9
10
  to: {
10
11
  path: string;
12
+ pathNot?: string | string[];
13
+ dependencyTypesNot: string[];
11
14
  };
12
15
  }
13
16
  export interface EntryPurityConfig {
14
17
  forbidden: EntryPurityRule[];
15
18
  }
16
- export declare function toDependencyCruiserEntryPurity(analysis: EntryPurityAnalysis): EntryPurityConfig;
19
+ /** Dependency-cruiser cannot exempt a single import, so an entry already imported is exempt as a target. */
20
+ export declare function toDependencyCruiserEntryPurity(analysis: EntryPurityAnalysis, allowedFor: (ruleId: string) => readonly string[]): EntryPurityConfig;
17
21
  //# sourceMappingURL=entry-purity-emitters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-purity-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/entry-purity-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAahF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,iBAAiB,CAe/F"}
1
+ {"version":3,"file":"entry-purity-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/entry-purity-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAI7F,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAS7D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAAC,kBAAkB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACjF;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,4GAA4G;AAC5G,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,mBAAmB,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,GAChD,iBAAiB,CA6BnB"}
@@ -1,22 +1,36 @@
1
+ import { ENTRY_ROLES } from '../detector/entry-purity-detector.js';
1
2
  import { ROLE_PATTERNS } from '../scanner/role-classifier.js';
2
- const ENTRY_ROLES = ['ROUTE_ENTRY', 'ROUTE_HANDLER', 'API_HANDLER'];
3
+ import { exemptDepcruiseFrom, TEST_ROLE_REGEX } from './depcruise-exempt.js';
4
+ export const ENTRY_PURITY_RULE = 'no-import-framework-entry';
3
5
  const entryPath = () => {
4
6
  const sources = ENTRY_ROLES.flatMap((role) => (ROLE_PATTERNS.get(role) ?? []).map((pattern) => pattern.source));
5
7
  return `(${sources.join('|')})`;
6
8
  };
7
- export function toDependencyCruiserEntryPurity(analysis) {
9
+ /** Dependency-cruiser cannot exempt a single import, so an entry already imported is exempt as a target. */
10
+ export function toDependencyCruiserEntryPurity(analysis, allowedFor) {
8
11
  if (analysis.entryCount === 0 || analysis.gate.status !== 'AUTO')
9
12
  return { forbidden: [] };
10
13
  const conform = analysis.entryCount - analysis.offenderCount;
11
14
  const floor = `${(analysis.gate.conditions.confidence.value * 100).toFixed(0)}%`;
15
+ const importedEntries = [...new Set(analysis.imports.map((entry) => entry.file))].map((file) => ({
16
+ file,
17
+ }));
12
18
  return {
13
19
  forbidden: [
14
20
  {
15
- name: 'no-import-framework-entry',
21
+ name: ENTRY_PURITY_RULE,
16
22
  comment: `Archprint inferred entry purity: ${conform}/${analysis.entryCount} framework entries (pages, routes) are loaded by the framework and imported by nothing; importing a framework entry from other code is forbidden (confidence ${floor}).`,
17
23
  severity: 'error',
18
- from: { pathNot: 'node_modules' },
19
- to: { path: entryPath() },
24
+ from: {
25
+ pathNot: exemptDepcruiseFrom(['node_modules', TEST_ROLE_REGEX, entryPath()], allowedFor(ENTRY_PURITY_RULE).map((file) => ({ file }))),
26
+ },
27
+ to: {
28
+ path: entryPath(),
29
+ ...(importedEntries.length === 0
30
+ ? {}
31
+ : { pathNot: exemptDepcruiseFrom([], importedEntries) }),
32
+ dependencyTypesNot: ['type-only'],
33
+ },
20
34
  },
21
35
  ],
22
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"entry-purity-emitters.js","sourceRoot":"","sources":["../../src/generator/entry-purity-emitters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAG9D,MAAM,WAAW,GAAoB,CAAC,aAAa,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;AAErF,MAAM,SAAS,GAAG,GAAW,EAAE;IAC7B,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3C,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CACjE,CAAC;IACF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClC,CAAC,CAAC;AAcF,MAAM,UAAU,8BAA8B,CAAC,QAA6B;IAC1E,IAAI,QAAQ,CAAC,UAAU,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,oCAAoC,OAAO,IAAI,QAAQ,CAAC,UAAU,gKAAgK,KAAK,IAAI;gBACpP,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE;gBACjC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aAC1B;SACF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"entry-purity-emitters.js","sourceRoot":"","sources":["../../src/generator/entry-purity-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA4B,MAAM,sCAAsC,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAE7D,MAAM,SAAS,GAAG,GAAW,EAAE;IAC7B,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3C,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CACjE,CAAC;IACF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClC,CAAC,CAAC;AAcF,4GAA4G;AAC5G,MAAM,UAAU,8BAA8B,CAC5C,QAA6B,EAC7B,UAAiD;IAEjD,IAAI,QAAQ,CAAC,UAAU,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/F,IAAI;KACL,CAAC,CAAC,CAAC;IACJ,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,oCAAoC,OAAO,IAAI,QAAQ,CAAC,UAAU,gKAAgK,KAAK,IAAI;gBACpP,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE;oBACJ,OAAO,EAAE,mBAAmB,CAC1B,CAAC,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,EAC9C,UAAU,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CACxD;iBACF;gBACD,EAAE,EAAE;oBACF,IAAI,EAAE,SAAS,EAAE;oBACjB,GAAG,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC;wBAC9B,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,CAAC;oBAC1D,kBAAkB,EAAE,CAAC,WAAW,CAAC;iBAClC;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { EnvAccessAnalysis } from '../detector/env-access-detector.js';
2
2
  import { type EslintFlatConfigBlock } from './console-isolation-emitters.js';
3
+ export declare const ENV_ACCESS_RULE = "env-access";
3
4
  export declare function toEslintEnvAccess(analysis: EnvAccessAnalysis): EslintFlatConfigBlock | null;
4
5
  //# sourceMappingURL=env-access-emitters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env-access-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/env-access-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAGlG,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,qBAAqB,GAAG,IAAI,CAgB3F"}
1
+ {"version":3,"file":"env-access-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/env-access-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAGlG,eAAO,MAAM,eAAe,eAAe,CAAC;AAE5C,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,qBAAqB,GAAG,IAAI,CAgB3F"}
@@ -1,5 +1,6 @@
1
1
  import { withMinedExemptions } from './console-isolation-emitters.js';
2
2
  import { CONFIG_GLOBS, TEST_GLOBS } from './eslint-scope.js';
3
+ export const ENV_ACCESS_RULE = 'env-access';
3
4
  export function toEslintEnvAccess(analysis) {
4
5
  if (analysis.subjectFileCount === 0 || analysis.gate.status !== 'AUTO')
5
6
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"env-access-emitters.js","sourceRoot":"","sources":["../../src/generator/env-access-emitters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAA8B,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,UAAU,iBAAiB,CAAC,QAA2B;IAC3D,IAAI,QAAQ,CAAC,gBAAgB,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACpF,OAAO;QACL,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,OAAO,EAAE,mBAAmB,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC;QACnF,KAAK,EAAE;YACL,0BAA0B,EAAE;gBAC1B,OAAO;gBACP;oBACE,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,KAAK;oBACf,OAAO,EAAE,0DAA0D;iBACpE;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"env-access-emitters.js","sourceRoot":"","sources":["../../src/generator/env-access-emitters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAA8B,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,QAA2B;IAC3D,IAAI,QAAQ,CAAC,gBAAgB,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACpF,OAAO;QACL,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,OAAO,EAAE,mBAAmB,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC;QACnF,KAAK,EAAE;YACL,0BAA0B,EAAE;gBAC1B,OAAO;gBACP;oBACE,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,KAAK;oBACf,OAAO,EAAE,0DAA0D;iBACpE;aACF;SACF;KACF,CAAC;AACJ,CAAC"}