archprint 0.10.5 → 0.12.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.
- package/CHANGELOG.md +16 -0
- package/README.md +20 -11
- package/dist/cli/adopted-rules.d.ts +9 -10
- package/dist/cli/adopted-rules.d.ts.map +1 -1
- package/dist/cli/adopted-rules.js +26 -22
- package/dist/cli/adopted-rules.js.map +1 -1
- package/dist/cli/allow.d.ts +12 -0
- package/dist/cli/allow.d.ts.map +1 -0
- package/dist/cli/allow.js +58 -0
- package/dist/cli/allow.js.map +1 -0
- package/dist/cli/allowed-evidence.d.ts +11 -0
- package/dist/cli/allowed-evidence.d.ts.map +1 -0
- package/dist/cli/allowed-evidence.js +116 -0
- package/dist/cli/allowed-evidence.js.map +1 -0
- package/dist/cli/allowed-exceptions.d.ts +19 -0
- package/dist/cli/allowed-exceptions.d.ts.map +1 -0
- package/dist/cli/allowed-exceptions.js +84 -0
- package/dist/cli/allowed-exceptions.js.map +1 -0
- package/dist/cli/archprint-config.d.ts +14 -0
- package/dist/cli/archprint-config.d.ts.map +1 -1
- package/dist/cli/archprint-config.js +25 -3
- package/dist/cli/archprint-config.js.map +1 -1
- package/dist/cli/check.d.ts +3 -0
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +91 -30
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/generate.d.ts +3 -1
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +21 -7
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/layout.d.ts +2 -1
- package/dist/cli/layout.d.ts.map +1 -1
- package/dist/cli/layout.js +22 -5
- package/dist/cli/layout.js.map +1 -1
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +37 -4
- package/dist/cli/program.js.map +1 -1
- package/dist/generator/eslint-scope.d.ts +11 -1
- package/dist/generator/eslint-scope.d.ts.map +1 -1
- package/dist/generator/eslint-scope.js +47 -16
- package/dist/generator/eslint-scope.js.map +1 -1
- package/dist/generator/owned-paths.d.ts +2 -0
- package/dist/generator/owned-paths.d.ts.map +1 -1
- package/dist/generator/owned-paths.js +8 -0
- package/dist/generator/owned-paths.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# archprint
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `.archprint/` is back to two files for a single linter: `config.json` and `eslint.mjs`. The adopted rules and the exceptions you allow with `archprint allow` now live in `config.json` (its `rules` and `allowed` lists) instead of `rules.json` and `allow.json`. Setups from 0.9.0 to 0.11.x keep working: `check` still reads `rules.json` and `allow.json`, including on a base branch that has them, and the next `archprint generate` moves both into `config.json` and deletes them. `check` also warns when a pull request removes the rules from `config.json`.
|
|
8
|
+
|
|
9
|
+
## 0.11.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- New `archprint allow <rule> <file> --reason "..."` records a justified exception to an adopted rule in `.archprint/allow.json`. `archprint check` (and the GitHub Action and the MCP `archprint_check` tool) no longer counts an allowed exception as a new violation, lists every exception added in the pull request with its reason, and points out exceptions the code no longer needs. The next `archprint generate` stops ESLint flagging the allowed file for that rule. When `init` or `generate` re-infers the rules, allowed exceptions are left out of each rule's evidence, so allowing one justified break never drops the rule it breaks (and an allowance never promotes a rule that was not adopted). An exception without a reason is rejected, and `allow` refuses a rule that does not report the file. `--remove` takes an exception back out, and `eject` removes the list.
|
|
14
|
+
|
|
15
|
+
ESLint also gets more precise for the rules that share `no-restricted-imports` (import style, test isolation): a file excepted for one of them is still checked by the others. Before, an exception for one of them exempted the file from all of them.
|
|
16
|
+
|
|
17
|
+
File exceptions in the generated ESLint config, including the ones grandfathered at adoption, now also match when your ESLint config sits at the root of a monorepo: each one is written with the app's path in front as well as relative to the app, so it applies to that app only.
|
|
18
|
+
|
|
3
19
|
## 0.10.5
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -268,11 +268,10 @@ a tool you do not have. `--emit <eslint|dependency-cruiser|all>` forces the form
|
|
|
268
268
|
mechanical boundaries (public-API deep-import, test-isolation); the review-held ones (layer, role-layering,
|
|
269
269
|
feature-slice, app-isolation, entry-purity, dependency-internals, phantom deps) are added only with
|
|
270
270
|
`--include-structural`, after you review them.
|
|
271
|
-
- **`.archprint/
|
|
272
|
-
recorded at adoption and the resolution mode it was generated in
|
|
273
|
-
re-infers a rule
|
|
274
|
-
|
|
275
|
-
adopting, plus the list of managed outputs `eject` removes.
|
|
271
|
+
- **`.archprint/config.json`**: the exact definition of each mechanical rule you adopted, with the evidence
|
|
272
|
+
recorded at adoption and the resolution mode it was generated in (`archprint check` reads it, so a check never
|
|
273
|
+
re-infers a rule), the exceptions you allowed with a reason, what is enforced, followed but only reported, held
|
|
274
|
+
for review, and worth adopting, and the list of managed outputs `eject` removes.
|
|
276
275
|
- **A managed section in your `README.md`** summarizing what is enforced now, followed but only reported, held for
|
|
277
276
|
review, and worth adopting (written by `init`, or `generate --readme`), plus a managed `.prettierignore` entry so the generated files stay
|
|
278
277
|
out of your formatter.
|
|
@@ -340,11 +339,17 @@ jobs:
|
|
|
340
339
|
- **Warning by default.** Add `--fail-on new` (or the Action's `fail-on: new`) to fail the job on a new
|
|
341
340
|
violation, then mark the job a required status check in your branch protection so a pull request that adds one
|
|
342
341
|
can't merge.
|
|
343
|
-
- **Only adopted rules.** `check` reads `.archprint/
|
|
344
|
-
the mechanical rules recorded there, never structural ones. Rules adopted in the same pull request are listed
|
|
342
|
+
- **Only adopted rules.** `check` reads the adopted rules in `.archprint/config.json`, written by `init` and
|
|
343
|
+
`generate`, and checks only the mechanical rules recorded there, never structural ones. Rules adopted in the same pull request are listed
|
|
345
344
|
but never counted against it.
|
|
346
|
-
- **
|
|
347
|
-
`
|
|
345
|
+
- **A justified exception needs a reason.** When a file has a real reason to break a rule, record it:
|
|
346
|
+
`npx archprint allow AP-001 app/api/health/route.ts --reason "Health check queries the database directly"`. It
|
|
347
|
+
goes in the `allowed` list of `.archprint/config.json`, `check` stops counting it and lists it with its reason in the pull request, and
|
|
348
|
+
the next `archprint generate` stops ESLint flagging it. An entry without a reason is rejected, and an entry the
|
|
349
|
+
code no longer needs is pointed out. Rules enforced through dependency-cruiser (public API) are not covered on
|
|
350
|
+
that side yet.
|
|
351
|
+
- **Removing rules is never silent.** If a pull request deletes `.archprint/config.json`, or the rules in it,
|
|
352
|
+
that the base branch has, `check` warns and lists every rule that stops being checked. It does not
|
|
348
353
|
fail the job, because dropping a rule can be a deliberate team decision. To make that decision need a reviewer,
|
|
349
354
|
add a [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)
|
|
350
355
|
entry and turn on "Require review from Code Owners" in branch protection:
|
|
@@ -354,8 +359,9 @@ jobs:
|
|
|
354
359
|
/.github/workflows/ @your-team
|
|
355
360
|
```
|
|
356
361
|
|
|
357
|
-
- **Upgrading from 0.8.x or earlier
|
|
358
|
-
posts a notice that it did not run and exits 0.
|
|
362
|
+
- **Upgrading:** from 0.8.x or earlier, run `archprint generate` once to record the adopted rules. Until you do,
|
|
363
|
+
`check` posts a notice that it did not run and exits 0. Setups from 0.9.0 to 0.11.x keep working as they are:
|
|
364
|
+
`check` still reads their `rules.json` and `allow.json`, and the next `generate` moves both into `config.json`.
|
|
359
365
|
- **Other CI systems:** `--format json` gives version-keyed output, and the exit code is the contract: `0` ok, `1`
|
|
360
366
|
new violations with `--fail-on new`, `2` the CI setup is wrong (for example a shallow clone without the base
|
|
361
367
|
commit). Check out the full history (`fetch-depth: 0` or your CI's equivalent).
|
|
@@ -473,6 +479,9 @@ reachability) and knip (dead code); rather than compete, it writes the rules it
|
|
|
473
479
|
- **`archprint check [path]`**: reports the violations of your adopted rules that a change introduces, compared
|
|
474
480
|
with `--base <branch or commit>`. Warning only unless `--fail-on new`. `--format text|json|github`,
|
|
475
481
|
`--out <dir>`. See [Use in CI](#use-in-ci).
|
|
482
|
+
- **`archprint allow <rule> <file> --reason "..."`**: accepts one adopted rule being broken in one file, with a
|
|
483
|
+
reason, recorded in `.archprint/config.json`. `check` stops counting it and lists it in the pull request;
|
|
484
|
+
`generate` stops ESLint flagging it. `--remove` takes it back out.
|
|
476
485
|
- **`archprint wire`**: references the generated rules from the enforcement tools your repo uses (flat eslint
|
|
477
486
|
config, `.dependency-cruiser.json`) through a managed, reversible reference. `--out <dir>`, `--dry-run`.
|
|
478
487
|
- **`archprint eject`**: removes Archprint's generated files, its config, the managed README section, and any
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Role } from '../scanner/role-classifier.js';
|
|
2
2
|
import type { ScannedPattern, ScanResult } from './scan.js';
|
|
3
|
-
|
|
4
|
-
export declare const
|
|
3
|
+
/** Where 0.9.0 to 0.11.x kept the adopted rules, before they moved into config.json. */
|
|
4
|
+
export declare const LEGACY_RULES_FILE = "rules.json";
|
|
5
5
|
export type ResolutionMode = 'deep' | 'fast';
|
|
6
6
|
export interface AdoptionEvidence {
|
|
7
7
|
conforming: number;
|
|
@@ -30,21 +30,20 @@ export interface SingleFamilyRule extends RuleBase {
|
|
|
30
30
|
family: 'test-isolation' | 'console-isolation' | 'import-style';
|
|
31
31
|
}
|
|
32
32
|
export type AdoptedRule = ForbiddenImportRule | PublicApiRule | SingleFamilyRule;
|
|
33
|
-
export interface AdoptedRules {
|
|
34
|
-
format: typeof RULES_FORMAT;
|
|
35
|
-
archprintVersion: string;
|
|
36
|
-
rules: AdoptedRule[];
|
|
37
|
-
}
|
|
38
33
|
export declare function forbiddenImportRule(pattern: ScannedPattern, mode: ResolutionMode): AdoptedRule;
|
|
39
34
|
export declare function testIsolationRule(scan: ScanResult): AdoptedRule;
|
|
40
35
|
export declare function consoleIsolationRule(scan: ScanResult): AdoptedRule;
|
|
41
36
|
export declare function importStyleRule(scan: ScanResult): AdoptedRule;
|
|
42
37
|
export declare function publicApiRules(scan: ScanResult): AdoptedRule[];
|
|
43
|
-
export declare
|
|
38
|
+
export declare const byRuleId: (a: AdoptedRule, b: AdoptedRule) => number;
|
|
44
39
|
export declare class InvalidRulesError extends Error {
|
|
45
40
|
}
|
|
46
|
-
|
|
47
|
-
export declare function
|
|
41
|
+
/** The rules of a legacy rules.json. */
|
|
42
|
+
export declare function parseLegacyRules(text: string, file: string): AdoptedRule[];
|
|
43
|
+
/** The rules section of config.json text, or null when it has none. */
|
|
44
|
+
export declare function parseConfigRules(text: string, file: string): AdoptedRule[] | null;
|
|
45
|
+
/** The adopted rules: config.json's rules section, else a legacy rules.json, else null. */
|
|
46
|
+
export declare function readAdoptedRules(outDir: string): AdoptedRule[] | null;
|
|
48
47
|
export declare function ruleDefinitionKey(rule: AdoptedRule): string;
|
|
49
48
|
export {};
|
|
50
49
|
//# sourceMappingURL=adopted-rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopted-rules.d.ts","sourceRoot":"","sources":["../../src/cli/adopted-rules.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopted-rules.d.ts","sourceRoot":"","sources":["../../src/cli/adopted-rules.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAI1D,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5D,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAG9C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ;IACnD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAiB,SAAQ,QAAQ;IAChD,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,cAAc,CAAC;CACjE;AAED,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAQjF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,WAAW,CAW9F;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CAS/D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CASlE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CAS7D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,EAAE,CAW9D;AAED,eAAO,MAAM,QAAQ,GAAI,GAAG,WAAW,EAAE,GAAG,WAAW,KAAG,MAAkC,CAAC;AAE7F,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAkE/C,wCAAwC;AACxC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAQ1E;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,IAAI,CAGjF;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,IAAI,CAKrE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAc3D"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import { writeOwnedFile } from '../generator/owned-paths.js';
|
|
4
3
|
import { publicApiRuleName } from '../generator/public-api-emitters.js';
|
|
4
|
+
import { configPath, configSection, readConfigSection } from './archprint-config.js';
|
|
5
5
|
import { FAMILY_STATEMENTS } from './rule-statements.js';
|
|
6
|
-
|
|
7
|
-
export const
|
|
6
|
+
/** Where 0.9.0 to 0.11.x kept the adopted rules, before they moved into config.json. */
|
|
7
|
+
export const LEGACY_RULES_FILE = 'rules.json';
|
|
8
|
+
const LEGACY_RULES_FORMAT = 1;
|
|
8
9
|
const evidenceOf = (gate, total, violating) => ({
|
|
9
10
|
conforming: total - violating,
|
|
10
11
|
total,
|
|
@@ -64,13 +65,7 @@ export function publicApiRules(scan) {
|
|
|
64
65
|
evidence: evidenceOf(group.gate, group.consumerCount, group.deepImporterCount),
|
|
65
66
|
}));
|
|
66
67
|
}
|
|
67
|
-
export
|
|
68
|
-
const file = path.join(outDir, RULES_FILE);
|
|
69
|
-
const sorted = [...rules].sort((a, b) => a.id.localeCompare(b.id));
|
|
70
|
-
const content = { format: RULES_FORMAT, archprintVersion, rules: sorted };
|
|
71
|
-
writeOwnedFile(outDir, file, `${JSON.stringify(content, null, 2)}\n`);
|
|
72
|
-
return file;
|
|
73
|
-
}
|
|
68
|
+
export const byRuleId = (a, b) => a.id.localeCompare(b.id);
|
|
74
69
|
export class InvalidRulesError extends Error {
|
|
75
70
|
}
|
|
76
71
|
const FAMILIES = new Set([
|
|
@@ -121,11 +116,14 @@ function validateRule(rule, file) {
|
|
|
121
116
|
}
|
|
122
117
|
return rule;
|
|
123
118
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
function parseRuleList(rules, file) {
|
|
120
|
+
if (!Array.isArray(rules)) {
|
|
121
|
+
throw new InvalidRulesError(`${file} was written by a different archprint version. Re-run archprint generate.`);
|
|
122
|
+
}
|
|
123
|
+
return rules.map((rule) => validateRule(rule, file));
|
|
127
124
|
}
|
|
128
|
-
|
|
125
|
+
/** The rules of a legacy rules.json. */
|
|
126
|
+
export function parseLegacyRules(text, file) {
|
|
129
127
|
let parsed;
|
|
130
128
|
try {
|
|
131
129
|
parsed = JSON.parse(text);
|
|
@@ -133,14 +131,20 @@ export function parseAdoptedRules(text, file) {
|
|
|
133
131
|
catch {
|
|
134
132
|
throw new InvalidRulesError(`${file} is not valid JSON. Re-run archprint generate.`);
|
|
135
133
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
return parseRuleList(parsed.format === LEGACY_RULES_FORMAT ? parsed.rules : undefined, file);
|
|
135
|
+
}
|
|
136
|
+
/** The rules section of config.json text, or null when it has none. */
|
|
137
|
+
export function parseConfigRules(text, file) {
|
|
138
|
+
const rules = configSection(text, 'rules');
|
|
139
|
+
return rules === undefined ? null : parseRuleList(rules, file);
|
|
140
|
+
}
|
|
141
|
+
/** The adopted rules: config.json's rules section, else a legacy rules.json, else null. */
|
|
142
|
+
export function readAdoptedRules(outDir) {
|
|
143
|
+
const rules = readConfigSection(outDir, 'rules');
|
|
144
|
+
if (rules !== undefined)
|
|
145
|
+
return parseRuleList(rules, configPath(outDir));
|
|
146
|
+
const legacy = path.join(outDir, LEGACY_RULES_FILE);
|
|
147
|
+
return existsSync(legacy) ? parseLegacyRules(readFileSync(legacy, 'utf8'), legacy) : null;
|
|
144
148
|
}
|
|
145
149
|
export function ruleDefinitionKey(rule) {
|
|
146
150
|
const common = [rule.family, rule.id, rule.mode];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopted-rules.js","sourceRoot":"","sources":["../../src/cli/adopted-rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"adopted-rules.js","sourceRoot":"","sources":["../../src/cli/adopted-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class AllowError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export interface AllowOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
out: string;
|
|
6
|
+
rule: string;
|
|
7
|
+
file: string;
|
|
8
|
+
reason?: string;
|
|
9
|
+
remove?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function runAllow(options: AllowOptions): string;
|
|
12
|
+
//# sourceMappingURL=allow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../src/cli/allow.ts"],"names":[],"mappings":"AAaA,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAID,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAiDtD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { assertRealDirectory, ownedPath } from '../generator/owned-paths.js';
|
|
4
|
+
import { readAdoptedRules } from './adopted-rules.js';
|
|
5
|
+
import { allowKey, readAllowed, writeAllowed, } from './allowed-exceptions.js';
|
|
6
|
+
import { readConfig } from './archprint-config.js';
|
|
7
|
+
import { evaluateRules } from './check-evaluate.js';
|
|
8
|
+
export class AllowError extends Error {
|
|
9
|
+
}
|
|
10
|
+
const toPosix = (value) => value.split(path.sep).join('/');
|
|
11
|
+
export function runAllow(options) {
|
|
12
|
+
const outDir = path.resolve(options.cwd, options.out);
|
|
13
|
+
assertRealDirectory(outDir, options.cwd);
|
|
14
|
+
const config = readConfig(outDir);
|
|
15
|
+
const adopted = readAdoptedRules(outDir);
|
|
16
|
+
if (config === null || adopted === null) {
|
|
17
|
+
throw new AllowError('No adopted rules here. Run archprint init (or generate) first.');
|
|
18
|
+
}
|
|
19
|
+
const rule = adopted.find((candidate) => candidate.id === options.rule);
|
|
20
|
+
if (rule === undefined) {
|
|
21
|
+
const known = adopted.map((candidate) => candidate.id).join(', ');
|
|
22
|
+
throw new AllowError(`No adopted rule "${options.rule}". Adopted rules: ${known || 'none'}.`);
|
|
23
|
+
}
|
|
24
|
+
const appDir = path.resolve(options.cwd, config.app);
|
|
25
|
+
const inside = [path.resolve(options.cwd, options.file), path.resolve(appDir, options.file)]
|
|
26
|
+
.map((candidate) => ownedPath(appDir, candidate))
|
|
27
|
+
.filter((candidate) => candidate !== null);
|
|
28
|
+
const target = inside.find((candidate) => existsSync(candidate)) ?? inside[0];
|
|
29
|
+
if (target === undefined) {
|
|
30
|
+
throw new AllowError(`${options.file} is not a file inside the app (${config.app}).`);
|
|
31
|
+
}
|
|
32
|
+
const file = toPosix(path.relative(appDir, target));
|
|
33
|
+
const entries = readAllowed(outDir);
|
|
34
|
+
const key = allowKey(rule.id, file);
|
|
35
|
+
const others = entries.filter((entry) => allowKey(entry.rule, entry.file) !== key);
|
|
36
|
+
if (options.remove) {
|
|
37
|
+
if (others.length === entries.length) {
|
|
38
|
+
throw new AllowError(`There is no allowed exception for ${rule.id} in ${file}.`);
|
|
39
|
+
}
|
|
40
|
+
writeAllowed(outDir, others);
|
|
41
|
+
return `Removed the exception for ${rule.id} in ${file}. Run archprint generate to update the ESLint rules.`;
|
|
42
|
+
}
|
|
43
|
+
const reason = options.reason?.trim() ?? '';
|
|
44
|
+
if (reason === '') {
|
|
45
|
+
throw new AllowError('Give the reason this file is an exception: --reason "..."');
|
|
46
|
+
}
|
|
47
|
+
const reported = evaluateRules(appDir, [rule]).some((finding) => finding.file === file);
|
|
48
|
+
if (!reported) {
|
|
49
|
+
throw new AllowError(`${rule.id} reports nothing in ${file}, so there is nothing to allow.`);
|
|
50
|
+
}
|
|
51
|
+
const entry = { rule: rule.id, file, reason };
|
|
52
|
+
writeAllowed(outDir, [...others, entry]);
|
|
53
|
+
const lint = rule.family === 'public-api'
|
|
54
|
+
? ' dependency-cruiser does not read this list, so add the exception there too if you use it.'
|
|
55
|
+
: ' Run archprint generate to update the ESLint rules.';
|
|
56
|
+
return `Allowed ${rule.id} in ${file}. archprint check accepts it now.${lint}`;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=allow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow.js","sourceRoot":"","sources":["../../src/cli/allow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ScanResult } from './scan.js';
|
|
2
|
+
type AllowedByRule = ReadonlyMap<string, readonly string[]>;
|
|
3
|
+
/** Allowed files of the rules already adopted: an allowance can keep an adopted rule, never promote another. */
|
|
4
|
+
export declare function adoptedAllowances(outDir: string): AllowedByRule;
|
|
5
|
+
/**
|
|
6
|
+
* The scan with each rule's allowed exceptions left out of its evidence: they are reviewed exceptions, so they
|
|
7
|
+
* count neither for nor against the rule when it is re-inferred.
|
|
8
|
+
*/
|
|
9
|
+
export declare function excludeAllowed(scan: ScanResult, allowed: AllowedByRule): ScanResult;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=allowed-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowed-evidence.d.ts","sourceRoot":"","sources":["../../src/cli/allowed-evidence.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,KAAK,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AA0B5D,gHAAgH;AAChH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAU/D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,UAAU,CAgGnF"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { evaluateGate } from '../detector/confidence-gate.js';
|
|
2
|
+
import { publicApiRuleName } from '../generator/public-api-emitters.js';
|
|
3
|
+
import { readAdoptedRules } from './adopted-rules.js';
|
|
4
|
+
import { allowedFilesByRule, readAllowed } from './allowed-exceptions.js';
|
|
5
|
+
function exclude(violations, allowedFiles) {
|
|
6
|
+
const allowed = new Set(allowedFiles);
|
|
7
|
+
const kept = violations.filter((violation) => !allowed.has(violation.file));
|
|
8
|
+
const distinct = (list) => new Set(list.map((item) => item.file)).size;
|
|
9
|
+
return { kept, removedFiles: distinct(violations) - distinct(kept), keptFiles: distinct(kept) };
|
|
10
|
+
}
|
|
11
|
+
const regate = (population, violating, roleConfidence = 1, applicable = true) => evaluateGate({
|
|
12
|
+
roleFileCount: population,
|
|
13
|
+
violatingFileCount: violating,
|
|
14
|
+
roleConfidence,
|
|
15
|
+
applicable,
|
|
16
|
+
});
|
|
17
|
+
/** Allowed files of the rules already adopted: an allowance can keep an adopted rule, never promote another. */
|
|
18
|
+
export function adoptedAllowances(outDir) {
|
|
19
|
+
let adopted;
|
|
20
|
+
try {
|
|
21
|
+
adopted = new Set((readAdoptedRules(outDir) ?? []).map((rule) => rule.id));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
adopted = new Set();
|
|
25
|
+
}
|
|
26
|
+
return new Map([...allowedFilesByRule(readAllowed(outDir))].filter(([rule]) => adopted.has(rule)));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The scan with each rule's allowed exceptions left out of its evidence: they are reviewed exceptions, so they
|
|
30
|
+
* count neither for nor against the rule when it is re-inferred.
|
|
31
|
+
*/
|
|
32
|
+
export function excludeAllowed(scan, allowed) {
|
|
33
|
+
if (allowed.size === 0)
|
|
34
|
+
return scan;
|
|
35
|
+
const allowedFor = (rule) => allowed.get(rule) ?? [];
|
|
36
|
+
const patterns = scan.patterns.map((pattern) => {
|
|
37
|
+
const { kept, removedFiles, keptFiles } = exclude(pattern.result.violations, allowedFor(pattern.config.id));
|
|
38
|
+
if (removedFiles === 0)
|
|
39
|
+
return pattern;
|
|
40
|
+
const { stats } = pattern.result;
|
|
41
|
+
const roleFileCount = stats.roleFileCount - removedFiles;
|
|
42
|
+
const gate = regate(roleFileCount, keptFiles, stats.roleConfidence);
|
|
43
|
+
const infraExceptions = pattern.result.infraExceptions.filter((file) => kept.some((v) => v.file === file));
|
|
44
|
+
return {
|
|
45
|
+
...pattern,
|
|
46
|
+
result: {
|
|
47
|
+
...pattern.result,
|
|
48
|
+
violations: kept,
|
|
49
|
+
gate,
|
|
50
|
+
stats: {
|
|
51
|
+
...stats,
|
|
52
|
+
roleFileCount,
|
|
53
|
+
conformingFileCount: roleFileCount - keptFiles,
|
|
54
|
+
violatingFileCount: keptFiles,
|
|
55
|
+
ratio: gate.observedConformance,
|
|
56
|
+
},
|
|
57
|
+
infraExceptions,
|
|
58
|
+
infraCaution: keptFiles > 0 && infraExceptions.length === keptFiles,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
const consoleRule = exclude(scan.consoleIsolation.violations, allowedFor('console-isolation'));
|
|
63
|
+
const libraryFileCount = scan.consoleIsolation.libraryFileCount - consoleRule.removedFiles;
|
|
64
|
+
const deep = exclude(scan.deepRelative.violations, allowedFor('import-style'));
|
|
65
|
+
const relativeImporterCount = scan.deepRelative.relativeImporterCount - deep.removedFiles;
|
|
66
|
+
const tests = exclude(scan.testIsolation.violations, allowedFor('test-isolation'));
|
|
67
|
+
const productionFileCount = scan.testIsolation.productionFileCount - tests.removedFiles;
|
|
68
|
+
return {
|
|
69
|
+
...scan,
|
|
70
|
+
patterns,
|
|
71
|
+
consoleIsolation: consoleRule.removedFiles === 0
|
|
72
|
+
? scan.consoleIsolation
|
|
73
|
+
: {
|
|
74
|
+
...scan.consoleIsolation,
|
|
75
|
+
libraryFileCount,
|
|
76
|
+
offenderCount: consoleRule.keptFiles,
|
|
77
|
+
violations: consoleRule.kept,
|
|
78
|
+
gate: regate(libraryFileCount, consoleRule.keptFiles),
|
|
79
|
+
},
|
|
80
|
+
deepRelative: deep.removedFiles === 0
|
|
81
|
+
? scan.deepRelative
|
|
82
|
+
: {
|
|
83
|
+
...scan.deepRelative,
|
|
84
|
+
relativeImporterCount,
|
|
85
|
+
offenderCount: deep.keptFiles,
|
|
86
|
+
violations: deep.kept,
|
|
87
|
+
gate: regate(relativeImporterCount, deep.keptFiles),
|
|
88
|
+
},
|
|
89
|
+
testIsolation: tests.removedFiles === 0
|
|
90
|
+
? scan.testIsolation
|
|
91
|
+
: {
|
|
92
|
+
...scan.testIsolation,
|
|
93
|
+
productionFileCount,
|
|
94
|
+
offenderCount: tests.keptFiles,
|
|
95
|
+
violations: tests.kept,
|
|
96
|
+
gate: regate(productionFileCount, tests.keptFiles, 1, scan.testIsolation.testFileCount > 0),
|
|
97
|
+
},
|
|
98
|
+
publicApi: {
|
|
99
|
+
...scan.publicApi,
|
|
100
|
+
groups: scan.publicApi.groups.map((group) => {
|
|
101
|
+
const api = exclude(group.violations, allowedFor(publicApiRuleName(group.dir)));
|
|
102
|
+
if (api.removedFiles === 0)
|
|
103
|
+
return group;
|
|
104
|
+
const consumerCount = group.consumerCount - api.removedFiles;
|
|
105
|
+
return {
|
|
106
|
+
...group,
|
|
107
|
+
consumerCount,
|
|
108
|
+
deepImporterCount: api.keptFiles,
|
|
109
|
+
violations: api.kept,
|
|
110
|
+
gate: regate(consumerCount, api.keptFiles),
|
|
111
|
+
};
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=allowed-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowed-evidence.js","sourceRoot":"","sources":["../../src/cli/allowed-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Where 0.11.x kept the allowed exceptions, before they moved into config.json. */
|
|
2
|
+
export declare const LEGACY_ALLOW_FILE = "allow.json";
|
|
3
|
+
export interface AllowedException {
|
|
4
|
+
rule: string;
|
|
5
|
+
file: string;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class InvalidAllowError extends Error {
|
|
9
|
+
}
|
|
10
|
+
export declare const allowKey: (rule: string, file: string) => string;
|
|
11
|
+
/** The entries of a legacy allow.json. */
|
|
12
|
+
export declare function parseLegacyAllowed(text: string, source: string): AllowedException[];
|
|
13
|
+
/** The allowed exceptions: config.json's allowed section, else a legacy allow.json, else none. */
|
|
14
|
+
export declare function readAllowed(outDir: string): AllowedException[];
|
|
15
|
+
export declare const sortAllowed: (entries: readonly AllowedException[]) => AllowedException[];
|
|
16
|
+
/** Records the entries in config.json, which then supersedes any legacy allow.json. */
|
|
17
|
+
export declare function writeAllowed(outDir: string, entries: readonly AllowedException[]): string;
|
|
18
|
+
export declare function allowedFilesByRule(entries: readonly AllowedException[]): ReadonlyMap<string, readonly string[]>;
|
|
19
|
+
//# sourceMappingURL=allowed-exceptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowed-exceptions.d.ts","sourceRoot":"","sources":["../../src/cli/allowed-exceptions.ts"],"names":[],"mappings":"AAKA,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAK9C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAAgC,CAAC;AA8CvF,0CAA0C;AAC1C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAWnF;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAK9D;AAKD,eAAO,MAAM,WAAW,GAAI,SAAS,SAAS,gBAAgB,EAAE,KAAG,gBAAgB,EAChD,CAAC;AAEpC,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAOzF;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,gBAAgB,EAAE,GACnC,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAKxC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { removeOwnedFile } from '../generator/owned-paths.js';
|
|
4
|
+
import { configPath, readConfig, readConfigSection, updateConfig } from './archprint-config.js';
|
|
5
|
+
/** Where 0.11.x kept the allowed exceptions, before they moved into config.json. */
|
|
6
|
+
export const LEGACY_ALLOW_FILE = 'allow.json';
|
|
7
|
+
const LEGACY_ALLOW_FORMAT = 1;
|
|
8
|
+
const MAX_REASON = 500;
|
|
9
|
+
export class InvalidAllowError extends Error {
|
|
10
|
+
}
|
|
11
|
+
export const allowKey = (rule, file) => `${rule}\u0000${file}`;
|
|
12
|
+
const isRelativePosixPath = (file) => file !== '' &&
|
|
13
|
+
!file.includes('\\') &&
|
|
14
|
+
!/[*?[\]{}!]/.test(file) &&
|
|
15
|
+
!path.posix.isAbsolute(file) &&
|
|
16
|
+
!file.split('/').some((segment) => segment === '..' || segment === '.' || segment === '');
|
|
17
|
+
function validateEntry(entry, source) {
|
|
18
|
+
const { rule, file, reason } = (entry ?? {});
|
|
19
|
+
if (typeof rule !== 'string' || rule.trim() === '') {
|
|
20
|
+
throw new InvalidAllowError(`${source}: every entry needs the rule id it allows.`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof file !== 'string' || !isRelativePosixPath(file)) {
|
|
23
|
+
throw new InvalidAllowError(`${source}: "${String(file)}" must be a file path relative to the app, using forward slashes.`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof reason !== 'string' || reason.trim() === '') {
|
|
26
|
+
throw new InvalidAllowError(`${source}: ${rule} in ${file} needs a reason.`);
|
|
27
|
+
}
|
|
28
|
+
if (reason.length > MAX_REASON) {
|
|
29
|
+
throw new InvalidAllowError(`${source}: the reason for ${rule} in ${file} is over ${MAX_REASON} characters.`);
|
|
30
|
+
}
|
|
31
|
+
return { rule, file, reason: reason.trim() };
|
|
32
|
+
}
|
|
33
|
+
function parseAllowedList(entries, source) {
|
|
34
|
+
if (!Array.isArray(entries)) {
|
|
35
|
+
throw new InvalidAllowError(`${source} was written by a different archprint version.`);
|
|
36
|
+
}
|
|
37
|
+
const parsed = entries.map((entry) => validateEntry(entry, source));
|
|
38
|
+
const keys = new Set();
|
|
39
|
+
for (const entry of parsed) {
|
|
40
|
+
const key = allowKey(entry.rule, entry.file);
|
|
41
|
+
if (keys.has(key)) {
|
|
42
|
+
throw new InvalidAllowError(`${source} lists ${entry.rule} in ${entry.file} twice.`);
|
|
43
|
+
}
|
|
44
|
+
keys.add(key);
|
|
45
|
+
}
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
/** The entries of a legacy allow.json. */
|
|
49
|
+
export function parseLegacyAllowed(text, source) {
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(text);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new InvalidAllowError(`${source} is not valid JSON.`);
|
|
56
|
+
}
|
|
57
|
+
return parseAllowedList(parsed.format === LEGACY_ALLOW_FORMAT ? parsed.entries : undefined, source);
|
|
58
|
+
}
|
|
59
|
+
/** The allowed exceptions: config.json's allowed section, else a legacy allow.json, else none. */
|
|
60
|
+
export function readAllowed(outDir) {
|
|
61
|
+
const entries = readConfigSection(outDir, 'allowed');
|
|
62
|
+
if (entries !== undefined)
|
|
63
|
+
return parseAllowedList(entries, configPath(outDir));
|
|
64
|
+
const legacy = path.join(outDir, LEGACY_ALLOW_FILE);
|
|
65
|
+
return existsSync(legacy) ? parseLegacyAllowed(readFileSync(legacy, 'utf8'), legacy) : [];
|
|
66
|
+
}
|
|
67
|
+
const byRuleThenFile = (a, b) => a.rule === b.rule ? (a.file < b.file ? -1 : a.file > b.file ? 1 : 0) : a.rule < b.rule ? -1 : 1;
|
|
68
|
+
export const sortAllowed = (entries) => [...entries].sort(byRuleThenFile);
|
|
69
|
+
/** Records the entries in config.json, which then supersedes any legacy allow.json. */
|
|
70
|
+
export function writeAllowed(outDir, entries) {
|
|
71
|
+
if (readConfig(outDir) === null) {
|
|
72
|
+
throw new InvalidAllowError(`${configPath(outDir)} is missing.`);
|
|
73
|
+
}
|
|
74
|
+
const file = updateConfig(outDir, { allowed: sortAllowed(entries) });
|
|
75
|
+
removeOwnedFile(outDir, path.join(outDir, LEGACY_ALLOW_FILE));
|
|
76
|
+
return file;
|
|
77
|
+
}
|
|
78
|
+
export function allowedFilesByRule(entries) {
|
|
79
|
+
const byRule = new Map();
|
|
80
|
+
for (const entry of entries)
|
|
81
|
+
byRule.set(entry.rule, [...(byRule.get(entry.rule) ?? []), entry.file]);
|
|
82
|
+
return byRule;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=allowed-exceptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowed-exceptions.js","sourceRoot":"","sources":["../../src/cli/allowed-exceptions.ts"],"names":[],"mappings":"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"}
|