@yuu1111/quality-check 0.10.0 → 1.0.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/README.ja.md +17 -13
- package/README.md +17 -14
- package/dist/cli.js +119 -12
- package/package.json +7 -2
- package/src/config.ts +130 -20
- package/src/engines.ts +80 -10
package/README.ja.md
CHANGED
|
@@ -10,7 +10,10 @@ Projectごとのscriptから個別に呼んでいたBiome、型検査、Knip、c
|
|
|
10
10
|
bun add -D @yuu1111/quality-check
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
このCLIは `comment-check`、`document-style-check`、`tsdoc-check` を、configの型がengineの公開するrule名を取り込むためpeer dependencyとして持つ
|
|
14
|
+
package managerはこのCLIと一緒に導入し、engineのbinaryは利用Projectの `node_modules/.bin` から実行時に解決する
|
|
15
|
+
|
|
16
|
+
`@yuu1111/code-style-check` を宣言するProjectではKnipが未使用依存として報告するため `knip.ts` の `ignoreDependencies` で理由付きに宣言する
|
|
14
17
|
|
|
15
18
|
## Usage
|
|
16
19
|
|
|
@@ -39,15 +42,11 @@ export default defineConfig({
|
|
|
39
42
|
},
|
|
40
43
|
config: {
|
|
41
44
|
"comment-check": {
|
|
42
|
-
enable: ["japanese-period"],
|
|
43
45
|
ignore: ["another-project"],
|
|
46
|
+
rules: { "japanese-period": "on" },
|
|
44
47
|
},
|
|
45
|
-
"document-style-check": {
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
"tsdoc-check": {
|
|
49
|
-
error: ["missing-doc"],
|
|
50
|
-
},
|
|
48
|
+
"document-style-check": { enable: true },
|
|
49
|
+
"tsdoc-check": { error: true },
|
|
51
50
|
},
|
|
52
51
|
});
|
|
53
52
|
```
|
|
@@ -88,9 +87,9 @@ engineは `biome` → `typecheck` → `knip` → `code-style-check` → `comment
|
|
|
88
87
|
| `typecheck` | `tsc --noEmit` | `args`、`projects` 設定は `tsconfig.json` が持つ |
|
|
89
88
|
| `knip` | `knip` | `args` 設定は `knip.ts` が持つ |
|
|
90
89
|
| `code-style-check` | `code-style-check --json` | `ignore`、`targets`、`args` |
|
|
91
|
-
| `comment-check` | `comment-check --json` | `ignore`、`targets`、`args`、`enable
|
|
92
|
-
| `document-style-check` | `document-style-check lint --json` | `ignore`、`targets`、`args`、`enable
|
|
93
|
-
| `tsdoc-check` | `tsdoc-check --json` | `ignore`、`targets`、`args`、`enable
|
|
90
|
+
| `comment-check` | `comment-check --json` | `ignore`、`targets`、`args`、`enable`、`rules` |
|
|
91
|
+
| `document-style-check` | `document-style-check lint --json` | `ignore`、`targets`、`args`、`enable`、`rules` |
|
|
92
|
+
| `tsdoc-check` | `tsdoc-check --json` | `ignore`、`targets`、`args`、`enable`、`error`、`rules` |
|
|
94
93
|
|
|
95
94
|
`args` はengineの既定引数の後ろへ足す
|
|
96
95
|
設定fileで表せない起動条件や、engineの引数が変わったときの逃げ道として使う
|
|
@@ -102,8 +101,13 @@ engineが受け取らない条件は渡さず、その旨をそのengineのsecti
|
|
|
102
101
|
biome: ignore skipped (biome.json holds its settings)
|
|
103
102
|
```
|
|
104
103
|
|
|
105
|
-
`enable`
|
|
106
|
-
|
|
104
|
+
`enable` と `error` はruleをまとめて選び、`rules` はruleを1つずつ選ぶ
|
|
105
|
+
`enable: true` はengineが既定で無効にしているruleを全て有効にする
|
|
106
|
+
`error: true` は `tsdoc-check` の全ruleを違反として扱い、opt-inのruleは先に有効にする 違反へ上げられるのはこのengineだけ
|
|
107
|
+
`rules` はrule名をkeyにして `off`、`on`、`error` のいずれかを渡す `on` はengineの既定のまま、`error` はそのruleだけpresetと同じ扱いにする
|
|
108
|
+
keyがrule名そのものなので導入済みengineのunionで型付けし、engineが知らない名前は起動時の失敗ではなく型errorになる
|
|
109
|
+
`error` を取れるのは `tsdoc-check` だけ、`off` を取れるのは既定で無効のruleだけで、既定で有効なruleを無効にする引数をengineは持たない
|
|
110
|
+
選んだruleは `--enable <rule>` になり、`tsdoc-check` では `--error <rule>` にもなる 他のengineはこれらのoptionを拒否する
|
|
107
111
|
|
|
108
112
|
`comment-check` はbaseline差分を無効化する未作成のpathを渡して起動する
|
|
109
113
|
新規と解消済みの判定はengineごとではなく統合CLIが1つのbaseline fileで行うため、既存の `comment-baseline.json` がある場合は `--update-baseline` で移す
|
package/README.md
CHANGED
|
@@ -11,8 +11,10 @@ The baseline diff lives here too.
|
|
|
11
11
|
bun add -D @yuu1111/quality-check
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
A
|
|
14
|
+
`comment-check`, `document-style-check`, and `tsdoc-check` are peer dependencies of this CLI, because the config types embed the rule names those engines publish.
|
|
15
|
+
A package manager installs them with this CLI, and each engine binary is resolved at run time from the project's `node_modules/.bin`.
|
|
16
|
+
|
|
17
|
+
A project that declares `@yuu1111/code-style-check` sees Knip report it as an unused dependency, so it names it in `ignoreDependencies` with the reason.
|
|
16
18
|
|
|
17
19
|
## Usage
|
|
18
20
|
|
|
@@ -41,15 +43,11 @@ export default defineConfig({
|
|
|
41
43
|
},
|
|
42
44
|
config: {
|
|
43
45
|
"comment-check": {
|
|
44
|
-
enable: ["japanese-period"],
|
|
45
46
|
ignore: ["another-project"],
|
|
47
|
+
rules: { "japanese-period": "on" },
|
|
46
48
|
},
|
|
47
|
-
"document-style-check": {
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
"tsdoc-check": {
|
|
51
|
-
error: ["missing-doc"],
|
|
52
|
-
},
|
|
49
|
+
"document-style-check": { enable: true },
|
|
50
|
+
"tsdoc-check": { error: true },
|
|
53
51
|
},
|
|
54
52
|
});
|
|
55
53
|
```
|
|
@@ -92,9 +90,9 @@ Each engine runs:
|
|
|
92
90
|
| `typecheck` | `tsc --noEmit` | `args`, `projects`; `tsconfig.json` holds the settings |
|
|
93
91
|
| `knip` | `knip` | `args`; `knip.ts` holds the settings |
|
|
94
92
|
| `code-style-check` | `code-style-check --json` | `ignore`, `targets`, `args` |
|
|
95
|
-
| `comment-check` | `comment-check --json` | `ignore`, `targets`, `args`, `enable`
|
|
96
|
-
| `document-style-check` | `document-style-check lint --json` | `ignore`, `targets`, `args`, `enable`
|
|
97
|
-
| `tsdoc-check` | `tsdoc-check --json` | `ignore`, `targets`, `args`, `enable
|
|
93
|
+
| `comment-check` | `comment-check --json` | `ignore`, `targets`, `args`, `enable`, `rules` |
|
|
94
|
+
| `document-style-check` | `document-style-check lint --json` | `ignore`, `targets`, `args`, `enable`, `rules` |
|
|
95
|
+
| `tsdoc-check` | `tsdoc-check --json` | `ignore`, `targets`, `args`, `enable`, `error`, `rules` |
|
|
98
96
|
|
|
99
97
|
`args` is appended after the engine defaults, for conditions the config cannot express and for the case where an engine changes its arguments.
|
|
100
98
|
|
|
@@ -105,8 +103,13 @@ A condition that an engine does not take is not passed on, and the section says
|
|
|
105
103
|
biome: ignore skipped (biome.json holds its settings)
|
|
106
104
|
```
|
|
107
105
|
|
|
108
|
-
`enable`
|
|
109
|
-
|
|
106
|
+
`enable` and `error` pick rules in bulk, and `rules` picks one rule at a time.
|
|
107
|
+
`enable: true` turns on every rule the engine keeps off by default.
|
|
108
|
+
`error: true` turns every rule of `tsdoc-check` into an error, enabling the opt-in ones first; it is the only condition that raises a rule.
|
|
109
|
+
A `rules` entry names one rule and gives it `off`, `on`, or `error`, where `on` keeps the engine default and `error` means the same as the `error` preset for that rule.
|
|
110
|
+
The `rules` key is the rule name itself, so the installed engine's union types it and a name that engine does not know is a type error rather than a start-up failure.
|
|
111
|
+
Only `tsdoc-check` takes `error` in `rules`, and only rules that are off by default take `off`, because no engine can turn off a rule that is on by default.
|
|
112
|
+
Each selected rule becomes `--enable <rule>` and, on `tsdoc-check`, `--error <rule>`; the other engines reject the fields.
|
|
110
113
|
|
|
111
114
|
`comment-check` runs with an unwritten baseline path so that it reports every finding.
|
|
112
115
|
The new-and-resolved diff is done by this CLI from a single baseline file, so an existing `comment-baseline.json` is moved over with `--update-baseline`.
|
package/dist/cli.js
CHANGED
|
@@ -194,6 +194,18 @@ function colorEnabled(stream, env) {
|
|
|
194
194
|
import { existsSync as existsSync2 } from "fs";
|
|
195
195
|
import { resolve } from "path";
|
|
196
196
|
import { pathToFileURL } from "url";
|
|
197
|
+
import {
|
|
198
|
+
OPT_IN_RULE_IDS as COMMENT_CHECK_OPT_IN_RULE_IDS,
|
|
199
|
+
RULE_IDS as COMMENT_CHECK_RULE_IDS
|
|
200
|
+
} from "@yuu1111/comment-check/rule-ids";
|
|
201
|
+
import {
|
|
202
|
+
OPT_IN_RULE_IDS as DOCUMENT_STYLE_CHECK_OPT_IN_RULE_IDS,
|
|
203
|
+
RULE_IDS as DOCUMENT_STYLE_CHECK_RULE_IDS
|
|
204
|
+
} from "@yuu1111/document-style-check/rule-ids";
|
|
205
|
+
import {
|
|
206
|
+
OPT_IN_RULE_IDS as TSDOC_CHECK_OPT_IN_RULE_IDS,
|
|
207
|
+
KNOWN_RULE_NAMES as TSDOC_CHECK_RULE_NAMES
|
|
208
|
+
} from "@yuu1111/tsdoc-check/rule-ids";
|
|
197
209
|
var ENGINE_NAMES = [
|
|
198
210
|
"biome",
|
|
199
211
|
"typecheck",
|
|
@@ -203,6 +215,27 @@ var ENGINE_NAMES = [
|
|
|
203
215
|
"document-style-check",
|
|
204
216
|
"tsdoc-check"
|
|
205
217
|
];
|
|
218
|
+
var RULE_VOCABULARY = {
|
|
219
|
+
biome: { all: [], optIn: [], promotes: false },
|
|
220
|
+
typecheck: { all: [], optIn: [], promotes: false },
|
|
221
|
+
knip: { all: [], optIn: [], promotes: false },
|
|
222
|
+
"code-style-check": { all: [], optIn: [], promotes: false },
|
|
223
|
+
"comment-check": {
|
|
224
|
+
all: COMMENT_CHECK_RULE_IDS,
|
|
225
|
+
optIn: COMMENT_CHECK_OPT_IN_RULE_IDS,
|
|
226
|
+
promotes: false
|
|
227
|
+
},
|
|
228
|
+
"document-style-check": {
|
|
229
|
+
all: DOCUMENT_STYLE_CHECK_RULE_IDS,
|
|
230
|
+
optIn: DOCUMENT_STYLE_CHECK_OPT_IN_RULE_IDS,
|
|
231
|
+
promotes: false
|
|
232
|
+
},
|
|
233
|
+
"tsdoc-check": {
|
|
234
|
+
all: TSDOC_CHECK_RULE_NAMES,
|
|
235
|
+
optIn: TSDOC_CHECK_OPT_IN_RULE_IDS,
|
|
236
|
+
promotes: true
|
|
237
|
+
}
|
|
238
|
+
};
|
|
206
239
|
var DEFAULT_CONFIG_FILES = [
|
|
207
240
|
"quality.config.ts",
|
|
208
241
|
"quality.config.mts",
|
|
@@ -221,9 +254,9 @@ var ENGINE_OPTION_KEYS = {
|
|
|
221
254
|
typecheck: ["args", "ignore", "projects", "targets"],
|
|
222
255
|
knip: ["args", "ignore", "targets"],
|
|
223
256
|
"code-style-check": ["args", "ignore", "targets"],
|
|
224
|
-
"comment-check": ["args", "enable", "ignore", "targets"],
|
|
225
|
-
"document-style-check": ["args", "enable", "ignore", "targets"],
|
|
226
|
-
"tsdoc-check": ["args", "enable", "error", "ignore", "targets"]
|
|
257
|
+
"comment-check": ["args", "enable", "ignore", "rules", "targets"],
|
|
258
|
+
"document-style-check": ["args", "enable", "ignore", "rules", "targets"],
|
|
259
|
+
"tsdoc-check": ["args", "enable", "error", "ignore", "rules", "targets"]
|
|
227
260
|
};
|
|
228
261
|
function readStringArray(value, field) {
|
|
229
262
|
if (value === undefined) {
|
|
@@ -234,6 +267,41 @@ function readStringArray(value, field) {
|
|
|
234
267
|
}
|
|
235
268
|
return [...value];
|
|
236
269
|
}
|
|
270
|
+
function readBoolean(value, field) {
|
|
271
|
+
if (value === undefined) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (typeof value !== "boolean") {
|
|
275
|
+
throw new Error(`${field} must be a boolean`);
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
function readRuleStates(value, field, name) {
|
|
280
|
+
if (value === undefined) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (!isJsonObject2(value)) {
|
|
284
|
+
throw new Error(`${field} must be an object`);
|
|
285
|
+
}
|
|
286
|
+
const vocabulary = RULE_VOCABULARY[name];
|
|
287
|
+
const states = {};
|
|
288
|
+
for (const [rule, state] of Object.entries(value)) {
|
|
289
|
+
if (!vocabulary.all.includes(rule)) {
|
|
290
|
+
throw new Error(`${field} has an unknown rule: ${rule}`);
|
|
291
|
+
}
|
|
292
|
+
if (state !== "off" && state !== "on" && state !== "error") {
|
|
293
|
+
throw new Error(`${field}.${rule} must be "off", "on" or "error"`);
|
|
294
|
+
}
|
|
295
|
+
if (state === "error" && !vocabulary.promotes) {
|
|
296
|
+
throw new Error(`${field}.${rule}: ${name} cannot treat a rule as an error`);
|
|
297
|
+
}
|
|
298
|
+
if (state === "off" && !vocabulary.optIn.includes(rule)) {
|
|
299
|
+
throw new Error(`${field}.${rule}: ${name} cannot turn off a rule that is on by default`);
|
|
300
|
+
}
|
|
301
|
+
states[rule] = state;
|
|
302
|
+
}
|
|
303
|
+
return states;
|
|
304
|
+
}
|
|
237
305
|
function readBaseline2(value, source) {
|
|
238
306
|
if (value === undefined || value === false) {
|
|
239
307
|
return value;
|
|
@@ -265,13 +333,17 @@ function parseEngines(value, source) {
|
|
|
265
333
|
function parseEngineExtras(value, source, name) {
|
|
266
334
|
const extras = {};
|
|
267
335
|
if (name === "comment-check" || name === "document-style-check" || name === "tsdoc-check") {
|
|
268
|
-
const enable =
|
|
336
|
+
const enable = readBoolean(value.enable, `${source}: config.${name}.enable`);
|
|
269
337
|
if (enable !== undefined) {
|
|
270
338
|
extras.enable = enable;
|
|
271
339
|
}
|
|
340
|
+
const rules = readRuleStates(value.rules, `${source}: config.${name}.rules`, name);
|
|
341
|
+
if (rules !== undefined) {
|
|
342
|
+
extras.rules = rules;
|
|
343
|
+
}
|
|
272
344
|
}
|
|
273
345
|
if (name === "tsdoc-check") {
|
|
274
|
-
const error =
|
|
346
|
+
const error = readBoolean(value.error, `${source}: config.${name}.error`);
|
|
275
347
|
if (error !== undefined) {
|
|
276
348
|
extras.error = error;
|
|
277
349
|
}
|
|
@@ -460,19 +532,54 @@ function buildTypecheckCommand(executable, context, project) {
|
|
|
460
532
|
...options.args ?? []
|
|
461
533
|
];
|
|
462
534
|
}
|
|
463
|
-
function
|
|
464
|
-
|
|
535
|
+
function applyRuleState(vocabulary, selected, rule, state) {
|
|
536
|
+
if (state === "off") {
|
|
537
|
+
selected.enable.delete(rule);
|
|
538
|
+
selected.error.delete(rule);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (state === "on") {
|
|
542
|
+
selected.error.delete(rule);
|
|
543
|
+
} else {
|
|
544
|
+
selected.error.add(rule);
|
|
545
|
+
}
|
|
546
|
+
if (vocabulary.optIn.includes(rule)) {
|
|
547
|
+
selected.enable.add(rule);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function selectRules(name, options) {
|
|
551
|
+
const selection = options ?? {};
|
|
552
|
+
const vocabulary = RULE_VOCABULARY[name];
|
|
553
|
+
const selected = { enable: new Set, error: new Set };
|
|
554
|
+
if (selection.enable === true) {
|
|
555
|
+
for (const rule of vocabulary.optIn) {
|
|
556
|
+
selected.enable.add(rule);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (selection.error === true) {
|
|
560
|
+
for (const rule of vocabulary.all) {
|
|
561
|
+
applyRuleState(vocabulary, selected, rule, "error");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
for (const [rule, state] of Object.entries(selection.rules ?? {})) {
|
|
565
|
+
applyRuleState(vocabulary, selected, rule, state);
|
|
566
|
+
}
|
|
567
|
+
return { enable: [...selected.enable], error: [...selected.error] };
|
|
465
568
|
}
|
|
466
569
|
function buildEngineCommand(name, executable, context) {
|
|
467
570
|
const options = engineConfig(context.config, name) ?? {};
|
|
468
571
|
const limits = ENGINE_LIMITS[name];
|
|
572
|
+
const selection = selectRules(name, options);
|
|
573
|
+
const enableArguments = selection.enable.flatMap((rule) => [
|
|
574
|
+
"--enable",
|
|
575
|
+
rule
|
|
576
|
+
]);
|
|
577
|
+
const errorArguments = selection.error.flatMap((rule) => ["--error", rule]);
|
|
469
578
|
const extra = options.args ?? [];
|
|
470
579
|
const ignores = limits.ignore === undefined ? [...options.ignore ?? [], ...context.overrides.ignore] : [];
|
|
471
580
|
const requested = context.overrides.targets.length > 0 ? context.overrides.targets : options.targets ?? ["."];
|
|
472
581
|
const targets = limits.targets === undefined ? requested : [];
|
|
473
582
|
const ignoreArguments = ignores.flatMap((ignore) => ["--ignore", ignore]);
|
|
474
|
-
const rules = engineConfig(context.config, "tsdoc-check")?.error ?? [];
|
|
475
|
-
const errorArguments = rules.flatMap((rule) => ["--error", rule]);
|
|
476
583
|
if (name === "biome") {
|
|
477
584
|
return [
|
|
478
585
|
executable,
|
|
@@ -494,7 +601,7 @@ function buildEngineCommand(name, executable, context) {
|
|
|
494
601
|
"--json",
|
|
495
602
|
"--baseline",
|
|
496
603
|
context.rawBaseline,
|
|
497
|
-
...enableArguments
|
|
604
|
+
...enableArguments,
|
|
498
605
|
...targets,
|
|
499
606
|
...ignoreArguments,
|
|
500
607
|
...extra
|
|
@@ -505,7 +612,7 @@ function buildEngineCommand(name, executable, context) {
|
|
|
505
612
|
executable,
|
|
506
613
|
"lint",
|
|
507
614
|
"--json",
|
|
508
|
-
...enableArguments
|
|
615
|
+
...enableArguments,
|
|
509
616
|
...targets,
|
|
510
617
|
...ignoreArguments,
|
|
511
618
|
...extra
|
|
@@ -517,7 +624,7 @@ function buildEngineCommand(name, executable, context) {
|
|
|
517
624
|
return [
|
|
518
625
|
executable,
|
|
519
626
|
"--json",
|
|
520
|
-
...enableArguments
|
|
627
|
+
...enableArguments,
|
|
521
628
|
...errorArguments,
|
|
522
629
|
...targets,
|
|
523
630
|
...ignoreArguments,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuu1111/quality-check",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Integrated quality check runner",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,12 +23,17 @@
|
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "bun build src/cli.ts --target=bun --outdir=dist"
|
|
26
|
+
"build": "bun build src/cli.ts --target=bun --outdir=dist --external @yuu1111/comment-check/rule-ids --external @yuu1111/document-style-check/rule-ids --external @yuu1111/tsdoc-check/rule-ids"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"quality",
|
|
30
30
|
"lint"
|
|
31
31
|
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@yuu1111/comment-check": ">=2.3.0",
|
|
34
|
+
"@yuu1111/document-style-check": ">=0.5.0",
|
|
35
|
+
"@yuu1111/tsdoc-check": ">=2.2.0"
|
|
36
|
+
},
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"@yuu1111/shared": "workspace:*"
|
|
34
39
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
+
import {
|
|
5
|
+
OPT_IN_RULE_IDS as COMMENT_CHECK_OPT_IN_RULE_IDS,
|
|
6
|
+
RULE_IDS as COMMENT_CHECK_RULE_IDS,
|
|
7
|
+
type OptInRuleId as CommentCheckRuleName,
|
|
8
|
+
} from "@yuu1111/comment-check/rule-ids";
|
|
9
|
+
import {
|
|
10
|
+
OPT_IN_RULE_IDS as DOCUMENT_STYLE_CHECK_OPT_IN_RULE_IDS,
|
|
11
|
+
RULE_IDS as DOCUMENT_STYLE_CHECK_RULE_IDS,
|
|
12
|
+
type OptInRuleId as DocumentStyleCheckRuleName,
|
|
13
|
+
} from "@yuu1111/document-style-check/rule-ids";
|
|
14
|
+
import {
|
|
15
|
+
OPT_IN_RULE_IDS as TSDOC_CHECK_OPT_IN_RULE_IDS,
|
|
16
|
+
KNOWN_RULE_NAMES as TSDOC_CHECK_RULE_NAMES,
|
|
17
|
+
type TsdocRule,
|
|
18
|
+
} from "@yuu1111/tsdoc-check/rule-ids";
|
|
4
19
|
|
|
5
20
|
/**
|
|
6
21
|
* 統合CLIが起動できるengineの名前 並び順が実行順になる
|
|
@@ -36,26 +51,32 @@ export interface EngineOptions {
|
|
|
36
51
|
* comment-checkへ渡す起動条件
|
|
37
52
|
*/
|
|
38
53
|
export interface CommentCheckOptions extends EngineOptions {
|
|
39
|
-
/** 既定で無効のopt-in rule
|
|
40
|
-
enable?:
|
|
54
|
+
/** 既定で無効のopt-in ruleを全て有効にする */
|
|
55
|
+
enable?: boolean;
|
|
56
|
+
/** rule名ごとの状態 offで無効 onで有効 省略したruleはengineの既定に従う */
|
|
57
|
+
rules?: Partial<Record<CommentCheckRuleName, "off" | "on">>;
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
/**
|
|
44
61
|
* document-style-checkへ渡す起動条件
|
|
45
62
|
*/
|
|
46
63
|
export interface DocumentStyleCheckOptions extends EngineOptions {
|
|
47
|
-
/** 既定で無効のopt-in rule
|
|
48
|
-
enable?:
|
|
64
|
+
/** 既定で無効のopt-in ruleを全て有効にする */
|
|
65
|
+
enable?: boolean;
|
|
66
|
+
/** rule名ごとの状態 offで無効 onで有効 省略したruleはengineの既定に従う */
|
|
67
|
+
rules?: Partial<Record<DocumentStyleCheckRuleName, "off" | "on">>;
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
/**
|
|
52
71
|
* TSDoc検査へ渡す起動条件
|
|
53
72
|
*/
|
|
54
73
|
export interface TsdocCheckOptions extends EngineOptions {
|
|
55
|
-
/** 既定で無効のopt-in rule
|
|
56
|
-
enable?:
|
|
57
|
-
/**
|
|
58
|
-
error?:
|
|
74
|
+
/** 既定で無効のopt-in ruleを全て有効にする */
|
|
75
|
+
enable?: boolean;
|
|
76
|
+
/** 全てのruleを違反として扱う opt-in ruleは有効にしてから上げる */
|
|
77
|
+
error?: boolean;
|
|
78
|
+
/** rule名ごとの状態 offで無効 onで既定のseverity errorで違反として扱う */
|
|
79
|
+
rules?: Partial<Record<TsdocRule, "off" | "on" | "error">>;
|
|
59
80
|
}
|
|
60
81
|
|
|
61
82
|
/**
|
|
@@ -79,6 +100,39 @@ export interface EngineConfigMap {
|
|
|
79
100
|
"tsdoc-check": TsdocCheckOptions;
|
|
80
101
|
}
|
|
81
102
|
|
|
103
|
+
/**
|
|
104
|
+
* rule名ごとの状態
|
|
105
|
+
*/
|
|
106
|
+
export type RuleState = "off" | "on" | "error";
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* engineが公開するrule語彙 全ruleと既定で無効のopt-in ruleを持つ
|
|
110
|
+
*/
|
|
111
|
+
export const RULE_VOCABULARY: Record<
|
|
112
|
+
EngineName,
|
|
113
|
+
{ all: readonly string[]; optIn: readonly string[]; promotes: boolean }
|
|
114
|
+
> = {
|
|
115
|
+
biome: { all: [], optIn: [], promotes: false },
|
|
116
|
+
typecheck: { all: [], optIn: [], promotes: false },
|
|
117
|
+
knip: { all: [], optIn: [], promotes: false },
|
|
118
|
+
"code-style-check": { all: [], optIn: [], promotes: false },
|
|
119
|
+
"comment-check": {
|
|
120
|
+
all: COMMENT_CHECK_RULE_IDS,
|
|
121
|
+
optIn: COMMENT_CHECK_OPT_IN_RULE_IDS,
|
|
122
|
+
promotes: false,
|
|
123
|
+
},
|
|
124
|
+
"document-style-check": {
|
|
125
|
+
all: DOCUMENT_STYLE_CHECK_RULE_IDS,
|
|
126
|
+
optIn: DOCUMENT_STYLE_CHECK_OPT_IN_RULE_IDS,
|
|
127
|
+
promotes: false,
|
|
128
|
+
},
|
|
129
|
+
"tsdoc-check": {
|
|
130
|
+
all: TSDOC_CHECK_RULE_NAMES,
|
|
131
|
+
optIn: TSDOC_CHECK_OPT_IN_RULE_IDS,
|
|
132
|
+
promotes: true,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
82
136
|
/**
|
|
83
137
|
* quality.config.tsが受け付ける統合検査の設定
|
|
84
138
|
*/
|
|
@@ -131,9 +185,9 @@ const ENGINE_OPTION_KEYS: Record<EngineName, readonly string[]> = {
|
|
|
131
185
|
typecheck: ["args", "ignore", "projects", "targets"],
|
|
132
186
|
knip: ["args", "ignore", "targets"],
|
|
133
187
|
"code-style-check": ["args", "ignore", "targets"],
|
|
134
|
-
"comment-check": ["args", "enable", "ignore", "targets"],
|
|
135
|
-
"document-style-check": ["args", "enable", "ignore", "targets"],
|
|
136
|
-
"tsdoc-check": ["args", "enable", "error", "ignore", "targets"],
|
|
188
|
+
"comment-check": ["args", "enable", "ignore", "rules", "targets"],
|
|
189
|
+
"document-style-check": ["args", "enable", "ignore", "rules", "targets"],
|
|
190
|
+
"tsdoc-check": ["args", "enable", "error", "ignore", "rules", "targets"],
|
|
137
191
|
};
|
|
138
192
|
|
|
139
193
|
function readStringArray(value: unknown, field: string): string[] | undefined {
|
|
@@ -149,6 +203,54 @@ function readStringArray(value: unknown, field: string): string[] | undefined {
|
|
|
149
203
|
return [...value] as string[];
|
|
150
204
|
}
|
|
151
205
|
|
|
206
|
+
function readBoolean(value: unknown, field: string): boolean | undefined {
|
|
207
|
+
if (value === undefined) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (typeof value !== "boolean") {
|
|
211
|
+
throw new Error(`${field} must be a boolean`);
|
|
212
|
+
}
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* rule名ごとの状態を読み取る engineが公開する語彙とengineが受け付ける状態で検証する
|
|
218
|
+
*/
|
|
219
|
+
function readRuleStates(
|
|
220
|
+
value: unknown,
|
|
221
|
+
field: string,
|
|
222
|
+
name: EngineName,
|
|
223
|
+
): Record<string, RuleState> | undefined {
|
|
224
|
+
if (value === undefined) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
if (!isJsonObject(value)) {
|
|
228
|
+
throw new Error(`${field} must be an object`);
|
|
229
|
+
}
|
|
230
|
+
const vocabulary = RULE_VOCABULARY[name];
|
|
231
|
+
const states: Record<string, RuleState> = {};
|
|
232
|
+
for (const [rule, state] of Object.entries(value)) {
|
|
233
|
+
if (!vocabulary.all.includes(rule)) {
|
|
234
|
+
throw new Error(`${field} has an unknown rule: ${rule}`);
|
|
235
|
+
}
|
|
236
|
+
if (state !== "off" && state !== "on" && state !== "error") {
|
|
237
|
+
throw new Error(`${field}.${rule} must be "off", "on" or "error"`);
|
|
238
|
+
}
|
|
239
|
+
if (state === "error" && !vocabulary.promotes) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`${field}.${rule}: ${name} cannot treat a rule as an error`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (state === "off" && !vocabulary.optIn.includes(rule)) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`${field}.${rule}: ${name} cannot turn off a rule that is on by default`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
states[rule] = state;
|
|
250
|
+
}
|
|
251
|
+
return states;
|
|
252
|
+
}
|
|
253
|
+
|
|
152
254
|
function readBaseline(
|
|
153
255
|
value: unknown,
|
|
154
256
|
source: string,
|
|
@@ -188,9 +290,10 @@ function parseEngines(
|
|
|
188
290
|
}
|
|
189
291
|
|
|
190
292
|
type ParsedEngineOptions = EngineOptions & {
|
|
191
|
-
enable?:
|
|
192
|
-
error?:
|
|
293
|
+
enable?: boolean;
|
|
294
|
+
error?: boolean;
|
|
193
295
|
projects?: string[];
|
|
296
|
+
rules?: Record<string, RuleState>;
|
|
194
297
|
};
|
|
195
298
|
|
|
196
299
|
/**
|
|
@@ -207,19 +310,24 @@ function parseEngineExtras(
|
|
|
207
310
|
name === "document-style-check" ||
|
|
208
311
|
name === "tsdoc-check"
|
|
209
312
|
) {
|
|
210
|
-
const enable =
|
|
313
|
+
const enable = readBoolean(
|
|
211
314
|
value.enable,
|
|
212
315
|
`${source}: config.${name}.enable`,
|
|
213
316
|
);
|
|
214
317
|
if (enable !== undefined) {
|
|
215
318
|
extras.enable = enable;
|
|
216
319
|
}
|
|
320
|
+
const rules = readRuleStates(
|
|
321
|
+
value.rules,
|
|
322
|
+
`${source}: config.${name}.rules`,
|
|
323
|
+
name,
|
|
324
|
+
);
|
|
325
|
+
if (rules !== undefined) {
|
|
326
|
+
extras.rules = rules;
|
|
327
|
+
}
|
|
217
328
|
}
|
|
218
329
|
if (name === "tsdoc-check") {
|
|
219
|
-
const error =
|
|
220
|
-
value.error,
|
|
221
|
-
`${source}: config.${name}.error`,
|
|
222
|
-
);
|
|
330
|
+
const error = readBoolean(value.error, `${source}: config.${name}.error`);
|
|
223
331
|
if (error !== undefined) {
|
|
224
332
|
extras.error = error;
|
|
225
333
|
}
|
|
@@ -284,7 +392,7 @@ function parseEngineConfig(
|
|
|
284
392
|
if (!isJsonObject(value)) {
|
|
285
393
|
throw new Error(`${source}: config must be an object`);
|
|
286
394
|
}
|
|
287
|
-
const config: Partial<
|
|
395
|
+
const config: Partial<Record<EngineName, ParsedEngineOptions>> = {};
|
|
288
396
|
for (const [name, options] of Object.entries(value)) {
|
|
289
397
|
if (!isEngineName(name)) {
|
|
290
398
|
throw new Error(`${source}: unknown engine in config: ${name}`);
|
|
@@ -294,7 +402,9 @@ function parseEngineConfig(
|
|
|
294
402
|
}
|
|
295
403
|
config[name] = parseEngineOptions(options, source, name);
|
|
296
404
|
}
|
|
297
|
-
|
|
405
|
+
|
|
406
|
+
// rule名はengineが公開するunionで縛る 実行時の入力は文字列として届くため検証済みの値をここで型へ寄せる
|
|
407
|
+
return config as Partial<EngineConfigMap>;
|
|
298
408
|
}
|
|
299
409
|
|
|
300
410
|
/**
|
package/src/engines.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
type EngineOptions,
|
|
6
6
|
engineConfig,
|
|
7
7
|
type QualityConfig,
|
|
8
|
+
RULE_VOCABULARY,
|
|
9
|
+
type RuleState,
|
|
8
10
|
} from "./config";
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -194,12 +196,76 @@ function buildTypecheckCommand(
|
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
/**
|
|
197
|
-
*
|
|
199
|
+
* 設定fileが持つruleの指定
|
|
198
200
|
*/
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
interface RuleSelection {
|
|
202
|
+
enable?: boolean;
|
|
203
|
+
error?: boolean;
|
|
204
|
+
rules?: Record<string, RuleState>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* engineが公開するrule語彙
|
|
209
|
+
*/
|
|
210
|
+
type RuleVocabulary = (typeof RULE_VOCABULARY)[EngineName];
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 選んだruleを有効の一覧と違反の一覧へ反映する
|
|
214
|
+
*
|
|
215
|
+
* @param vocabulary - 反映先engineのrule語彙
|
|
216
|
+
* @param selected - 反映先のrule名の集合
|
|
217
|
+
* @param rule - 反映するrule名
|
|
218
|
+
* @param state - 反映する状態
|
|
219
|
+
*/
|
|
220
|
+
function applyRuleState(
|
|
221
|
+
vocabulary: RuleVocabulary,
|
|
222
|
+
selected: { enable: Set<string>; error: Set<string> },
|
|
223
|
+
rule: string,
|
|
224
|
+
state: RuleState,
|
|
225
|
+
): void {
|
|
226
|
+
if (state === "off") {
|
|
227
|
+
selected.enable.delete(rule);
|
|
228
|
+
selected.error.delete(rule);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (state === "on") {
|
|
232
|
+
selected.error.delete(rule);
|
|
233
|
+
} else {
|
|
234
|
+
selected.error.add(rule);
|
|
235
|
+
}
|
|
236
|
+
if (vocabulary.optIn.includes(rule)) {
|
|
237
|
+
selected.enable.add(rule);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* ruleの一括指定と個別指定をengineへ渡すruleの一覧へ展開する
|
|
243
|
+
*
|
|
244
|
+
* @param name - 展開するengine名
|
|
245
|
+
* @param options - engineへ渡す起動条件
|
|
246
|
+
* @returns --enableへ渡すrule名と--errorへ渡すrule名の組
|
|
247
|
+
*/
|
|
248
|
+
function selectRules(
|
|
249
|
+
name: EngineName,
|
|
250
|
+
options: EngineOptions | null | undefined,
|
|
251
|
+
): { enable: string[]; error: string[] } {
|
|
252
|
+
const selection = (options ?? {}) as RuleSelection;
|
|
253
|
+
const vocabulary = RULE_VOCABULARY[name];
|
|
254
|
+
const selected = { enable: new Set<string>(), error: new Set<string>() };
|
|
255
|
+
if (selection.enable === true) {
|
|
256
|
+
for (const rule of vocabulary.optIn) {
|
|
257
|
+
selected.enable.add(rule);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (selection.error === true) {
|
|
261
|
+
for (const rule of vocabulary.all) {
|
|
262
|
+
applyRuleState(vocabulary, selected, rule, "error");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const [rule, state] of Object.entries(selection.rules ?? {})) {
|
|
266
|
+
applyRuleState(vocabulary, selected, rule, state);
|
|
267
|
+
}
|
|
268
|
+
return { enable: [...selected.enable], error: [...selected.error] };
|
|
203
269
|
}
|
|
204
270
|
|
|
205
271
|
/**
|
|
@@ -217,6 +283,12 @@ export function buildEngineCommand(
|
|
|
217
283
|
): string[] {
|
|
218
284
|
const options: EngineOptions = engineConfig(context.config, name) ?? {};
|
|
219
285
|
const limits = ENGINE_LIMITS[name];
|
|
286
|
+
const selection = selectRules(name, options);
|
|
287
|
+
const enableArguments = selection.enable.flatMap((rule) => [
|
|
288
|
+
"--enable",
|
|
289
|
+
rule,
|
|
290
|
+
]);
|
|
291
|
+
const errorArguments = selection.error.flatMap((rule) => ["--error", rule]);
|
|
220
292
|
const extra = options.args ?? [];
|
|
221
293
|
const ignores =
|
|
222
294
|
limits.ignore === undefined
|
|
@@ -228,8 +300,6 @@ export function buildEngineCommand(
|
|
|
228
300
|
: (options.targets ?? ["."]);
|
|
229
301
|
const targets = limits.targets === undefined ? requested : [];
|
|
230
302
|
const ignoreArguments = ignores.flatMap((ignore) => ["--ignore", ignore]);
|
|
231
|
-
const rules = engineConfig(context.config, "tsdoc-check")?.error ?? [];
|
|
232
|
-
const errorArguments = rules.flatMap((rule) => ["--error", rule]);
|
|
233
303
|
if (name === "biome") {
|
|
234
304
|
return [
|
|
235
305
|
executable,
|
|
@@ -251,7 +321,7 @@ export function buildEngineCommand(
|
|
|
251
321
|
"--json",
|
|
252
322
|
"--baseline",
|
|
253
323
|
context.rawBaseline,
|
|
254
|
-
...enableArguments
|
|
324
|
+
...enableArguments,
|
|
255
325
|
...targets,
|
|
256
326
|
...ignoreArguments,
|
|
257
327
|
...extra,
|
|
@@ -262,7 +332,7 @@ export function buildEngineCommand(
|
|
|
262
332
|
executable,
|
|
263
333
|
"lint",
|
|
264
334
|
"--json",
|
|
265
|
-
...enableArguments
|
|
335
|
+
...enableArguments,
|
|
266
336
|
...targets,
|
|
267
337
|
...ignoreArguments,
|
|
268
338
|
...extra,
|
|
@@ -274,7 +344,7 @@ export function buildEngineCommand(
|
|
|
274
344
|
return [
|
|
275
345
|
executable,
|
|
276
346
|
"--json",
|
|
277
|
-
...enableArguments
|
|
347
|
+
...enableArguments,
|
|
278
348
|
...errorArguments,
|
|
279
349
|
...targets,
|
|
280
350
|
...ignoreArguments,
|