archprint 0.13.0 → 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 +20 -0
  2. package/README.md +45 -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
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The npm package an import specifier names, or null for a relative path, a Node builtin, a tsconfig alias, a
3
+ * `#` subpath import, or a `scheme:` specifier. Self-contained: the generated ESLint rules embed it.
4
+ */
5
+ export function packageName(specifier, aliasPrefixes, builtins) {
6
+ if (specifier.startsWith('.') || specifier.startsWith('#'))
7
+ return null;
8
+ if (/^[a-z][a-z0-9+.-]*:/i.test(specifier))
9
+ return null;
10
+ if (builtins.has(specifier) || builtins.has(specifier.split('/')[0]))
11
+ return null;
12
+ if (aliasPrefixes.some((prefix) => specifier === prefix || specifier.startsWith(`${prefix}/`))) {
13
+ return null;
14
+ }
15
+ const parts = specifier.split('/');
16
+ return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
17
+ }
18
+ /** The package names a package.json text declares in any dependency field. Self-contained: see packageName. */
19
+ export function declaredPackages(packageJsonText) {
20
+ let json;
21
+ try {
22
+ json = JSON.parse(packageJsonText);
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ return ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].flatMap((field) => {
28
+ const deps = json[field];
29
+ return deps !== null && typeof deps === 'object' ? Object.keys(deps) : [];
30
+ });
31
+ }
32
+ /** Whether an import of package `name` reaches into its build or implementation folders. Self-contained. */
33
+ export function isPackageInternal(specifier, name) {
34
+ const subpath = specifier.slice(name.length + 1).split('/')[0] ?? '';
35
+ return ['src', 'internal', 'internals'].includes(subpath);
36
+ }
37
+ //# sourceMappingURL=package-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-name.js","sourceRoot":"","sources":["../../src/scanner/package-name.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,SAAiB,EACjB,aAAgC,EAChC,QAA6B;IAE7B,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,sBAAsB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IACnF,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;AAC7E,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,gBAAgB,CAAC,eAAuB;IACtD,IAAI,IAA6B,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAA4B,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,CAAC,CAAC,OAAO,CAC5F,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,CAAC,CACF,CAAC;AACJ,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,IAAY;IAC/D,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC"}
@@ -2,5 +2,10 @@ export interface AliasEntry {
2
2
  prefix: string;
3
3
  dir: string;
4
4
  }
5
+ /**
6
+ * The files an import of `base` (a path without resolution) can mean, in the order TypeScript-style resolution
7
+ * tries them. Self-contained: the generated ESLint rules embed it.
8
+ */
9
+ export declare function firstPartyCandidates(base: string): string[];
5
10
  export declare function resolveFirstPartyImport(specifier: string, fromAbsPath: string, aliases: readonly AliasEntry[]): string | null;
6
11
  //# sourceMappingURL=resolve-import.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-import.d.ts","sourceRoot":"","sources":["../../src/scanner/resolve-import.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AA2BD,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B,MAAM,GAAG,IAAI,CAcf"}
1
+ {"version":3,"file":"resolve-import.d.ts","sourceRoot":"","sources":["../../src/scanner/resolve-import.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB3D;AAkBD,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B,MAAM,GAAG,IAAI,CAOf"}
@@ -1,13 +1,25 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import * as path from 'node:path';
3
- const JS_EXT_TO_TS = {
4
- '.js': ['.ts', '.tsx'],
5
- '.jsx': ['.tsx'],
6
- '.mjs': ['.ts', '.tsx'],
7
- '.cjs': ['.ts', '.tsx'],
8
- };
9
- const BARE_CANDIDATES = ['', '.ts', '.tsx', '.vue', '.svelte', '/index.ts', '/index.tsx'];
10
- const FIRST_PARTY_TARGET = /\.(ts|tsx|vue|svelte)$/;
3
+ /**
4
+ * The files an import of `base` (a path without resolution) can mean, in the order TypeScript-style resolution
5
+ * tries them. Self-contained: the generated ESLint rules embed it.
6
+ */
7
+ export function firstPartyCandidates(base) {
8
+ const jsToTs = {
9
+ '.js': ['.ts', '.tsx'],
10
+ '.jsx': ['.tsx'],
11
+ '.mjs': ['.ts', '.tsx'],
12
+ '.cjs': ['.ts', '.tsx'],
13
+ };
14
+ const name = base.slice(Math.max(base.lastIndexOf('/'), base.lastIndexOf('\\')) + 1);
15
+ const dot = name.lastIndexOf('.');
16
+ const extension = dot > 0 ? name.slice(dot) : '';
17
+ const candidates = (jsToTs[extension] ?? []).map((tsExtension) => base.slice(0, -extension.length) + tsExtension);
18
+ for (const suffix of ['', '.ts', '.tsx', '.vue', '.svelte', '/index.ts', '/index.tsx']) {
19
+ candidates.push(base + suffix);
20
+ }
21
+ return candidates.filter((candidate) => /\.(ts|tsx|vue|svelte)$/.test(candidate));
22
+ }
11
23
  function specifierBase(specifier, fromAbsPath, aliases) {
12
24
  if (specifier.startsWith('.')) {
13
25
  return path.resolve(path.dirname(fromAbsPath), specifier);
@@ -23,17 +35,8 @@ export function resolveFirstPartyImport(specifier, fromAbsPath, aliases) {
23
35
  const base = specifierBase(specifier, fromAbsPath, aliases);
24
36
  if (base === null)
25
37
  return null;
26
- const candidates = [];
27
- const tsExtensions = JS_EXT_TO_TS[path.extname(base)];
28
- if (tsExtensions !== undefined) {
29
- const stem = base.slice(0, -path.extname(base).length);
30
- for (const tsExt of tsExtensions)
31
- candidates.push(stem + tsExt);
32
- }
33
- for (const suffix of BARE_CANDIDATES)
34
- candidates.push(base + suffix);
35
- for (const candidate of candidates) {
36
- if (FIRST_PARTY_TARGET.test(candidate) && existsSync(candidate))
38
+ for (const candidate of firstPartyCandidates(base)) {
39
+ if (existsSync(candidate))
37
40
  return candidate;
38
41
  }
39
42
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-import.js","sourceRoot":"","sources":["../../src/scanner/resolve-import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC,MAAM,YAAY,GAAsC;IACtD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AACF,MAAM,eAAe,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAC1F,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAEpD,SAAS,aAAa,CACpB,SAAiB,EACjB,WAAmB,EACnB,OAA8B;IAE9B,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC;QACtC,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,SAAiB,EACjB,WAAmB,EACnB,OAA8B;IAE9B,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;QACvD,KAAK,MAAM,KAAK,IAAI,YAAY;YAAE,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IAClE,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,eAAe;QAAE,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACrE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IACpF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"resolve-import.js","sourceRoot":"","sources":["../../src/scanner/resolve-import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,MAAM,GAAsC;QAChD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,CAAC,MAAM,CAAC;QAChB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACrF,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAC9C,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,WAAW,CAChE,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC;QACvF,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,aAAa,CACpB,SAAiB,EACjB,WAAmB,EACnB,OAA8B;IAE9B,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC;QACtC,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,SAAiB,EACjB,WAAmB,EACnB,OAA8B;IAE9B,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,KAAK,MAAM,SAAS,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -6,8 +6,17 @@ export interface RoleClassification {
6
6
  }
7
7
  export declare const ROLE_PATTERNS: ReadonlyMap<Role, readonly RegExp[]>;
8
8
  export declare const REQUEST_ENTRY_ROLES: readonly Role[];
9
+ /** The index of the first rule whose pattern matches the path, or -1. Self-contained: the ESLint rules embed it. */
10
+ export declare function firstMatchingRule(relativePath: string, rules: readonly {
11
+ test: RegExp;
12
+ }[]): number;
13
+ /** The role rules in the order classifyFile tries them, as regex sources. */
14
+ export declare const ROLE_RULE_SOURCES: readonly {
15
+ role: Role;
16
+ source: string;
17
+ }[];
9
18
  export declare function classifyFile(relativePath: string): RoleClassification;
10
- export declare function hasUseServerDirective(sourceHead: string): boolean;
11
- export declare function hasUseClientDirective(sourceHead: string): boolean;
19
+ export declare const hasUseServerDirective: (sourceHead: string) => boolean;
20
+ export declare const hasUseClientDirective: (sourceHead: string) => boolean;
12
21
  export declare function classifyFileWithDirective(relativePath: string, hasServerDirective: boolean): RoleClassification;
13
22
  //# sourceMappingURL=role-classifier.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"role-classifier.d.ts","sourceRoot":"","sources":["../../src/scanner/role-classifier.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GACZ,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,eAAe,GACf,aAAa,GACb,eAAe,GACf,aAAa,GACb,aAAa,GACb,aAAa,GACb,WAAW,GACX,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,MAAM,GACN,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAoHD,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAO9D,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,SAAS,IAAI,EAM9C,CAAC;AAEF,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAQrE;AA2BD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,OAAO,GAC1B,kBAAkB,CAOpB"}
1
+ {"version":3,"file":"role-classifier.d.ts","sourceRoot":"","sources":["../../src/scanner/role-classifier.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,IAAI,GACZ,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,eAAe,GACf,aAAa,GACb,eAAe,GACf,aAAa,GACb,aAAa,GACb,aAAa,GACb,WAAW,GACX,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,MAAM,GACN,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAoHD,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAO9D,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,SAAS,IAAI,EAM9C,CAAC;AAEF,oHAAoH;AACpH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACjC,MAAM,CAGR;AAED,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,EAAE,SAAS;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAEtE,CAAC;AAEF,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAKrE;AAED,eAAO,MAAM,qBAAqB,GAAI,YAAY,MAAM,KAAG,OACZ,CAAC;AAEhD,eAAO,MAAM,qBAAqB,GAAI,YAAY,MAAM,KAAG,OACZ,CAAC;AAEhD,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,OAAO,GAC1B,kBAAkB,CAOpB"}
@@ -1,3 +1,4 @@
1
+ import { hasLeadingDirective } from './directives.js';
1
2
  const ROLE_RULES = [
2
3
  {
3
4
  role: 'TEST',
@@ -116,48 +117,21 @@ export const REQUEST_ENTRY_ROLES = [
116
117
  'API_HANDLER',
117
118
  'TRPC_ROUTER',
118
119
  ];
119
- export function classifyFile(relativePath) {
120
+ /** The index of the first rule whose pattern matches the path, or -1. Self-contained: the ESLint rules embed it. */
121
+ export function firstMatchingRule(relativePath, rules) {
120
122
  const normalizedPath = relativePath.replace(/\\/g, '/');
121
- for (const rule of ROLE_RULES) {
122
- if (rule.test.test(normalizedPath)) {
123
- return { role: rule.role, confidence: rule.confidence, matchedRule: rule.id };
124
- }
125
- }
126
- return { role: 'UNKNOWN', confidence: 0, matchedRule: null };
127
- }
128
- const USE_SERVER_LITERAL = /^["']use server["']/;
129
- const USE_CLIENT_LITERAL = /^["']use client["']/;
130
- const isWhitespace = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === '\f' || c === '\v';
131
- function hasLeadingDirective(sourceHead, literal) {
132
- let i = 0;
133
- const n = sourceHead.length;
134
- for (;;) {
135
- while (i < n && isWhitespace(sourceHead[i]))
136
- i++;
137
- if (sourceHead.startsWith('//', i)) {
138
- const newline = sourceHead.indexOf('\n', i);
139
- if (newline === -1)
140
- return false;
141
- i = newline + 1;
142
- }
143
- else if (sourceHead.startsWith('/*', i)) {
144
- const end = sourceHead.indexOf('*/', i + 2);
145
- if (end === -1)
146
- return false;
147
- i = end + 2;
148
- }
149
- else {
150
- break;
151
- }
152
- }
153
- return literal.test(sourceHead.slice(i));
154
- }
155
- export function hasUseServerDirective(sourceHead) {
156
- return hasLeadingDirective(sourceHead, USE_SERVER_LITERAL);
123
+ return rules.findIndex((rule) => rule.test.test(normalizedPath));
157
124
  }
158
- export function hasUseClientDirective(sourceHead) {
159
- return hasLeadingDirective(sourceHead, USE_CLIENT_LITERAL);
125
+ /** The role rules in the order classifyFile tries them, as regex sources. */
126
+ export const ROLE_RULE_SOURCES = ROLE_RULES.map((rule) => ({ role: rule.role, source: rule.test.source }));
127
+ export function classifyFile(relativePath) {
128
+ const rule = ROLE_RULES[firstMatchingRule(relativePath, ROLE_RULES)];
129
+ return rule === undefined
130
+ ? { role: 'UNKNOWN', confidence: 0, matchedRule: null }
131
+ : { role: rule.role, confidence: rule.confidence, matchedRule: rule.id };
160
132
  }
133
+ export const hasUseServerDirective = (sourceHead) => hasLeadingDirective(sourceHead, 'use server');
134
+ export const hasUseClientDirective = (sourceHead) => hasLeadingDirective(sourceHead, 'use client');
161
135
  export function classifyFileWithDirective(relativePath, hasServerDirective) {
162
136
  const base = classifyFile(relativePath);
163
137
  const isTsx = /\.tsx$/.test(relativePath.replace(/\\/g, '/'));
@@ -1 +1 @@
1
- {"version":3,"file":"role-classifier.js","sourceRoot":"","sources":["../../src/scanner/role-classifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"role-classifier.js","sourceRoot":"","sources":["../../src/scanner/role-classifier.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archprint",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "mcpName": "io.github.Tommkruix/archprint",
5
5
  "description": "Mine the architecture rules your TypeScript repo already enforces, with the evidence attached.",
6
6
  "keywords": [
@@ -1,29 +0,0 @@
1
- import type { LayerBoundary } from '../detector/layer-detector.js';
2
- import { type LayerAlias } from '../detector/layer-paths.js';
3
- export interface EslintLayerRule {
4
- name: string;
5
- from: string;
6
- to: string;
7
- message: string;
8
- ignore: string[];
9
- }
10
- /** The app the rules enforce: where it is, its import aliases, and the gitignored paths the scan left out. */
11
- export interface LayerApp {
12
- appDir: string;
13
- aliases: LayerAlias[];
14
- ignored: string[];
15
- }
16
- export interface EslintLayers extends LayerApp {
17
- rules: EslintLayerRule[];
18
- }
19
- /** The given layer boundaries as ESLint rules, each exempting its known and allowed exceptions. */
20
- export declare function buildEslintLayers(boundaries: readonly LayerBoundary[], app: LayerApp, allowedFor: (ruleId: string) => readonly string[]): EslintLayers | null;
21
- export interface RenderedEslintLayers {
22
- imports: string;
23
- source: string;
24
- rules: string;
25
- config: string;
26
- }
27
- /** The pieces of the generated ESLint file that enforce layer boundaries, located relative to that file. */
28
- export declare function renderEslintLayers(layers: EslintLayers, outDir: string): RenderedEslintLayers;
29
- //# sourceMappingURL=eslint-layer-emitter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"eslint-layer-emitter.d.ts","sourceRoot":"","sources":["../../src/generator/eslint-layer-emitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,4BAA4B,CAAC;AAM7F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;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,YAAa,SAAQ,QAAQ;IAC5C,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,mGAAmG;AACnG,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,GAAG,EAAE,QAAQ,EACb,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,GAChD,YAAY,GAAG,IAAI,CAgBrB;AAkED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4GAA4G;AAC5G,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,oBAAoB,CA+B7F"}
@@ -1,121 +0,0 @@
1
- import * as path from 'node:path';
2
- import { layerRootOf, resolveImportPath } from '../detector/layer-paths.js';
3
- import { ROLE_PATTERNS } from '../scanner/role-classifier.js';
4
- import { isSkippedFolder, isSourceFileName } from '../scanner/source-paths.js';
5
- import { LINTED_FILES } from './eslint-plugin-emitter.js';
6
- import { layerRuleName } from './layer-emitters.js';
7
- /** The given layer boundaries as ESLint rules, each exempting its known and allowed exceptions. */
8
- export function buildEslintLayers(boundaries, app, allowedFor) {
9
- const rules = boundaries.map((boundary) => {
10
- const name = layerRuleName(boundary);
11
- const exceptions = [
12
- ...boundary.violations.map((violation) => violation.file),
13
- ...allowedFor(name),
14
- ];
15
- return {
16
- name,
17
- from: boundary.from,
18
- to: boundary.to,
19
- message: boundary.description,
20
- ignore: [...new Set(exceptions)].sort(),
21
- };
22
- });
23
- return rules.length === 0 ? null : { ...app, rules };
24
- }
25
- const LAYER_RULE_FUNCTIONS = `function isValueImport(node) {
26
- return (
27
- node.importKind !== 'type' &&
28
- (node.specifiers.length === 0 ||
29
- node.specifiers.some((specifier) => specifier.type !== 'ImportSpecifier' || specifier.importKind !== 'type'))
30
- );
31
- }
32
-
33
- function realPathOf(filename) {
34
- try {
35
- return realpathSync(filename);
36
- } catch {
37
- try {
38
- return join(realpathSync(dirname(filename)), basename(filename));
39
- } catch {
40
- return filename;
41
- }
42
- }
43
- }
44
-
45
- function isScanned(file) {
46
- const segments = file.split('/');
47
- return (
48
- isSourceFileName(segments.pop()) &&
49
- !segments.some(isSkippedFolder) &&
50
- !LAYERS.ignored.some((path) => file === path || file.startsWith(path + '/'))
51
- );
52
- }
53
-
54
- function appRelativePath(filename) {
55
- const relativePath = relative(LAYER_APP_DIR, realPathOf(filename));
56
- return relativePath.startsWith('..') || isAbsolute(relativePath)
57
- ? null
58
- : relativePath.split(sep).join('/');
59
- }
60
-
61
- function makeLayerRule(spec) {
62
- return {
63
- meta: { type: 'problem', schema: [], messages: { forbidden: spec.message } },
64
- create(context) {
65
- const file = appRelativePath(context.physicalFilename);
66
- if (file === null || !isScanned(file)) return {};
67
- if (LAYER_TESTS.some((test) => test.test(file)) || spec.ignore.includes(file)) return {};
68
- if (layerRootOf(file)?.layer !== spec.from) return {};
69
- const check = (node, specifier) => {
70
- const target = resolveImportPath(file, specifier, LAYERS.aliases);
71
- if (target !== null && layerRootOf(target)?.layer === spec.to) {
72
- context.report({ node, messageId: 'forbidden' });
73
- }
74
- };
75
- return {
76
- ImportDeclaration(node) {
77
- if (node.parent.type === 'Program' && isValueImport(node)) check(node, node.source.value);
78
- },
79
- ImportExpression(node) {
80
- if (node.source.type === 'Literal' && typeof node.source.value === 'string') {
81
- check(node, node.source.value);
82
- }
83
- },
84
- };
85
- },
86
- };
87
- }`;
88
- /** The pieces of the generated ESLint file that enforce layer boundaries, located relative to that file. */
89
- export function renderEslintLayers(layers, outDir) {
90
- const data = {
91
- app: path.relative(outDir, layers.appDir).split(path.sep).join('/') || '.',
92
- aliases: layers.aliases,
93
- ignored: layers.ignored,
94
- tests: (ROLE_PATTERNS.get('TEST') ?? []).map((pattern) => pattern.source),
95
- rules: layers.rules,
96
- };
97
- return {
98
- imports: [
99
- "import { realpathSync } from 'node:fs';",
100
- "import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';",
101
- "import { fileURLToPath } from 'node:url';",
102
- ].join('\n'),
103
- source: [
104
- `const LAYERS = ${JSON.stringify(data, null, 2)};`,
105
- 'const LAYER_APP_DIR = realPathOf(resolve(dirname(fileURLToPath(import.meta.url)), LAYERS.app));',
106
- 'const LAYER_TESTS = LAYERS.tests.map((source) => new RegExp(source));',
107
- layerRootOf.toString(),
108
- resolveImportPath.toString(),
109
- isSkippedFolder.toString(),
110
- isSourceFileName.toString(),
111
- LAYER_RULE_FUNCTIONS,
112
- ].join('\n\n'),
113
- rules: 'LAYERS.rules.map((spec) => [spec.name, makeLayerRule(spec)])',
114
- config: `{
115
- files: ${LINTED_FILES},
116
- plugins: { archprint: plugin },
117
- rules: Object.fromEntries(LAYERS.rules.map((spec) => ['archprint/' + spec.name, 'error'])),
118
- }`,
119
- };
120
- }
121
- //# sourceMappingURL=eslint-layer-emitter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"eslint-layer-emitter.js","sourceRoot":"","sources":["../../src/generator/eslint-layer-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAmB,WAAW,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAqBpD,mGAAmG;AACnG,MAAM,UAAU,iBAAiB,CAC/B,UAAoC,EACpC,GAAa,EACb,UAAiD;IAEjD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG;YACjB,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;YACzD,GAAG,UAAU,CAAC,IAAI,CAAC;SACpB,CAAC;QACF,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,OAAO,EAAE,QAAQ,CAAC,WAAW;YAC7B,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE;SACxC,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC;AACvD,CAAC;AAED,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D3B,CAAC;AASH,4GAA4G;AAC5G,MAAM,UAAU,kBAAkB,CAAC,MAAoB,EAAE,MAAc;IACrE,MAAM,IAAI,GAAG;QACX,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG;QAC1E,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;QACzE,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;IACF,OAAO;QACL,OAAO,EAAE;YACP,yCAAyC;YACzC,0FAA0F;YAC1F,2CAA2C;SAC5C,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,MAAM,EAAE;YACN,kBAAkB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG;YAClD,iGAAiG;YACjG,uEAAuE;YACvE,WAAW,CAAC,QAAQ,EAAE;YACtB,iBAAiB,CAAC,QAAQ,EAAE;YAC5B,eAAe,CAAC,QAAQ,EAAE;YAC1B,gBAAgB,CAAC,QAAQ,EAAE;YAC3B,oBAAoB;SACrB,CAAC,IAAI,CAAC,MAAM,CAAC;QACd,KAAK,EAAE,8DAA8D;QACrE,MAAM,EAAE;WACD,YAAY;;;EAGrB;KACC,CAAC;AACJ,CAAC"}