archprint 0.13.1 → 0.14.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 (93) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +17 -8
  3. package/dist/cli/adopted-rules.d.ts +10 -1
  4. package/dist/cli/adopted-rules.d.ts.map +1 -1
  5. package/dist/cli/adopted-rules.js +63 -0
  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 +11 -3
  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 +78 -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 +46 -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 +70 -23
  19. package/dist/cli/generate.js.map +1 -1
  20. package/dist/cli/recommend.d.ts.map +1 -1
  21. package/dist/cli/recommend.js +12 -7
  22. package/dist/cli/recommend.js.map +1 -1
  23. package/dist/detector/cycle-detector.d.ts +5 -0
  24. package/dist/detector/cycle-detector.d.ts.map +1 -1
  25. package/dist/detector/cycle-detector.js +20 -0
  26. package/dist/detector/cycle-detector.js.map +1 -1
  27. package/dist/detector/dependency-internals-detector.d.ts.map +1 -1
  28. package/dist/detector/dependency-internals-detector.js +6 -20
  29. package/dist/detector/dependency-internals-detector.js.map +1 -1
  30. package/dist/detector/entry-purity-detector.d.ts +7 -0
  31. package/dist/detector/entry-purity-detector.d.ts.map +1 -1
  32. package/dist/detector/entry-purity-detector.js +21 -10
  33. package/dist/detector/entry-purity-detector.js.map +1 -1
  34. package/dist/detector/phantom-dependency-detector.d.ts +7 -0
  35. package/dist/detector/phantom-dependency-detector.d.ts.map +1 -1
  36. package/dist/detector/phantom-dependency-detector.js +18 -39
  37. package/dist/detector/phantom-dependency-detector.js.map +1 -1
  38. package/dist/detector/server-client-detector.d.ts +5 -0
  39. package/dist/detector/server-client-detector.d.ts.map +1 -1
  40. package/dist/detector/server-client-detector.js +8 -3
  41. package/dist/detector/server-client-detector.js.map +1 -1
  42. package/dist/generator/depcruise-exempt.d.ts +4 -0
  43. package/dist/generator/depcruise-exempt.d.ts.map +1 -1
  44. package/dist/generator/depcruise-exempt.js +7 -0
  45. package/dist/generator/depcruise-exempt.js.map +1 -1
  46. package/dist/generator/dependency-internals-emitters.d.ts +2 -1
  47. package/dist/generator/dependency-internals-emitters.d.ts.map +1 -1
  48. package/dist/generator/dependency-internals-emitters.js +7 -3
  49. package/dist/generator/dependency-internals-emitters.js.map +1 -1
  50. package/dist/generator/entry-purity-emitters.d.ts +7 -3
  51. package/dist/generator/entry-purity-emitters.d.ts.map +1 -1
  52. package/dist/generator/entry-purity-emitters.js +19 -5
  53. package/dist/generator/entry-purity-emitters.js.map +1 -1
  54. package/dist/generator/eslint-app-emitter.d.ts +59 -0
  55. package/dist/generator/eslint-app-emitter.d.ts.map +1 -0
  56. package/dist/generator/eslint-app-emitter.js +275 -0
  57. package/dist/generator/eslint-app-emitter.js.map +1 -0
  58. package/dist/generator/eslint-preset-emitter.d.ts +2 -2
  59. package/dist/generator/eslint-preset-emitter.d.ts.map +1 -1
  60. package/dist/generator/eslint-preset-emitter.js +6 -6
  61. package/dist/generator/eslint-preset-emitter.js.map +1 -1
  62. package/dist/generator/layer-emitters.d.ts.map +1 -1
  63. package/dist/generator/layer-emitters.js +2 -6
  64. package/dist/generator/layer-emitters.js.map +1 -1
  65. package/dist/generator/phantom-dependency-emitters.d.ts +2 -1
  66. package/dist/generator/phantom-dependency-emitters.d.ts.map +1 -1
  67. package/dist/generator/phantom-dependency-emitters.js +7 -3
  68. package/dist/generator/phantom-dependency-emitters.js.map +1 -1
  69. package/dist/generator/server-client-emitters.d.ts +10 -3
  70. package/dist/generator/server-client-emitters.d.ts.map +1 -1
  71. package/dist/generator/server-client-emitters.js +23 -4
  72. package/dist/generator/server-client-emitters.js.map +1 -1
  73. package/dist/scanner/directives.d.ts +6 -0
  74. package/dist/scanner/directives.d.ts.map +1 -0
  75. package/dist/scanner/directives.js +35 -0
  76. package/dist/scanner/directives.js.map +1 -0
  77. package/dist/scanner/package-name.d.ts +10 -0
  78. package/dist/scanner/package-name.d.ts.map +1 -0
  79. package/dist/scanner/package-name.js +37 -0
  80. package/dist/scanner/package-name.js.map +1 -0
  81. package/dist/scanner/resolve-import.d.ts +5 -0
  82. package/dist/scanner/resolve-import.d.ts.map +1 -1
  83. package/dist/scanner/resolve-import.js +22 -19
  84. package/dist/scanner/resolve-import.js.map +1 -1
  85. package/dist/scanner/role-classifier.d.ts +11 -2
  86. package/dist/scanner/role-classifier.d.ts.map +1 -1
  87. package/dist/scanner/role-classifier.js +13 -39
  88. package/dist/scanner/role-classifier.js.map +1 -1
  89. package/package.json +1 -1
  90. package/dist/generator/eslint-layer-emitter.d.ts +0 -29
  91. package/dist/generator/eslint-layer-emitter.d.ts.map +0 -1
  92. package/dist/generator/eslint-layer-emitter.js +0 -121
  93. package/dist/generator/eslint-layer-emitter.js.map +0 -1
@@ -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,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"}
@@ -0,0 +1,59 @@
1
+ import type { LayerBoundary } from '../detector/layer-detector.js';
2
+ import { type LayerAlias } from '../detector/layer-paths.js';
3
+ import { type ServerClientAnalysis } from '../detector/server-client-detector.js';
4
+ import type { DependencyInternalsAnalysis } from '../detector/dependency-internals-detector.js';
5
+ import { type EntryPurityAnalysis } from '../detector/entry-purity-detector.js';
6
+ import type { PhantomDependencyAnalysis } from '../detector/phantom-dependency-detector.js';
7
+ /** A rule that applies to the app's files, with the files it exempts (known and allowed exceptions). */
8
+ export interface AppRule {
9
+ name: string;
10
+ message: string;
11
+ ignore: string[];
12
+ }
13
+ export interface LayerRuleSpec extends AppRule {
14
+ from: string;
15
+ to: string;
16
+ }
17
+ export interface PhantomRuleSpec extends AppRule {
18
+ /** The package.json files read for declared dependencies when ESLint loads the config. */
19
+ packageJsons: string[];
20
+ workspacePackages: string[];
21
+ }
22
+ export interface ServerClientRuleSpec extends AppRule {
23
+ serverModules: string[];
24
+ }
25
+ export interface EntryRuleSpec extends AppRule {
26
+ entryRoles: string[];
27
+ /** The `importer -> entry` imports that existed when the rule was generated. */
28
+ exemptImports: string[];
29
+ }
30
+ /** The app the rules enforce: where it is, its import aliases, and the gitignored paths the scan left out. */
31
+ export interface AppScope {
32
+ appDir: string;
33
+ aliases: LayerAlias[];
34
+ ignored: string[];
35
+ }
36
+ export interface EslintAppRules extends AppScope {
37
+ layers: LayerRuleSpec[];
38
+ phantom: PhantomRuleSpec | null;
39
+ serverClient: ServerClientRuleSpec | null;
40
+ internals: AppRule | null;
41
+ entry: EntryRuleSpec | null;
42
+ }
43
+ type AllowedFor = (ruleId: string) => readonly string[];
44
+ export declare const layerRuleSpecs: (boundaries: readonly LayerBoundary[], allowedFor: AllowedFor) => LayerRuleSpec[];
45
+ export declare const phantomRuleSpec: (analysis: PhantomDependencyAnalysis, packageJsons: readonly string[], workspacePackages: readonly string[], allowedFor: AllowedFor) => PhantomRuleSpec;
46
+ export declare const serverClientRuleSpec: (analysis: ServerClientAnalysis, allowedFor: AllowedFor) => ServerClientRuleSpec;
47
+ export declare const internalsRuleSpec: (analysis: DependencyInternalsAnalysis, allowedFor: AllowedFor) => AppRule;
48
+ export declare const entryRuleSpec: (analysis: EntryPurityAnalysis, allowedFor: AllowedFor) => EntryRuleSpec;
49
+ export declare const hasAppRules: (rules: EslintAppRules) => boolean;
50
+ export interface RenderedAppRules {
51
+ imports: string;
52
+ source: string;
53
+ rules: string;
54
+ config: string;
55
+ }
56
+ /** The pieces of the generated ESLint file that enforce app-scoped rules, located relative to that file. */
57
+ export declare function renderEslintAppRules(rules: EslintAppRules, outDir: string): RenderedAppRules;
58
+ export {};
59
+ //# sourceMappingURL=eslint-app-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint-app-emitter.d.ts","sourceRoot":"","sources":["../../src/generator/eslint-app-emitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAChG,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC7F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAa5F,wGAAwG;AACxG,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAgB,SAAQ,OAAO;IAC9C,0FAA0F;IAC1F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAqB,SAAQ,OAAO;IACnD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gFAAgF;IAChF,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8GAA8G;AAC9G,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC9C,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7B;AAED,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAC;AAKxD,eAAO,MAAM,cAAc,GACzB,YAAY,SAAS,aAAa,EAAE,EACpC,YAAY,UAAU,KACrB,aAAa,EAcZ,CAAC;AAEL,eAAO,MAAM,eAAe,GAC1B,UAAU,yBAAyB,EACnC,cAAc,SAAS,MAAM,EAAE,EAC/B,mBAAmB,SAAS,MAAM,EAAE,EACpC,YAAY,UAAU,KACrB,eAUD,CAAC;AAEH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,oBAAoB,EAC9B,YAAY,UAAU,KACrB,oBASD,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,2BAA2B,EACrC,YAAY,UAAU,KACrB,OASD,CAAC;AAIH,eAAO,MAAM,aAAa,GACxB,UAAU,mBAAmB,EAC7B,YAAY,UAAU,KACrB,aAOD,CAAC;AAEH,eAAO,MAAM,WAAW,GAAI,OAAO,cAAc,KAAG,OAE6C,CAAC;AA6JlG,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4GAA4G;AAC5G,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAwE5F"}