@yuu1111/quality-check 0.1.0 → 0.3.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 +38 -15
- package/README.md +45 -20
- package/package.json +1 -1
- package/src/cli.ts +2 -10
- package/src/config.ts +130 -47
- package/src/engines.ts +89 -12
- package/src/report.ts +4 -0
- package/src/run.ts +22 -5
package/README.ja.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @yuu1111/quality-check
|
|
4
4
|
|
|
5
|
-
Projectごとのscriptから個別に呼んでいたBiome、Knip、comment-check、TSDoc checkerを1つのCLIへまとめる baselineの差分判定もここで行う
|
|
5
|
+
Projectごとのscriptから個別に呼んでいたBiome、Knip、comment-check、document-style-check、TSDoc checkerを1つのCLIへまとめる baselineの差分判定もここで行う
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ bun add -D @yuu1111/quality-check
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
-
`quality.config.ts`
|
|
15
|
+
`quality.config.ts` で起動するengineとengineごとの起動条件を指定し、scriptから起動する
|
|
16
16
|
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
@@ -26,13 +26,17 @@ bun add -D @yuu1111/quality-check
|
|
|
26
26
|
import { defineConfig } from "@yuu1111/quality-check";
|
|
27
27
|
|
|
28
28
|
export default defineConfig({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
engines: {
|
|
30
|
+
biome: true,
|
|
31
|
+
knip: true,
|
|
32
|
+
"comment-check": true,
|
|
33
|
+
"document-style-check": true,
|
|
34
|
+
"tsdoc-check": true,
|
|
35
|
+
},
|
|
36
|
+
config: {
|
|
37
|
+
"comment-check": { ignore: ["FFXIVReplayAnalyzer"] },
|
|
38
|
+
"tsdoc-check": { error: ["missing-doc"] },
|
|
39
|
+
},
|
|
36
40
|
});
|
|
37
41
|
```
|
|
38
42
|
|
|
@@ -56,13 +60,23 @@ quality-check: 1 of 3 engines failed
|
|
|
56
60
|
|
|
57
61
|
| Field | Description |
|
|
58
62
|
|-------|-------------|
|
|
59
|
-
| `engines` | 起動するengine 値は `true` または `
|
|
60
|
-
| `
|
|
63
|
+
| `engines` | 起動するengine 値は `true` または `false` |
|
|
64
|
+
| `config` | engineごとの起動条件 |
|
|
61
65
|
| `baseline` | baseline fileのpath `false` なら差分判定を行わない |
|
|
62
|
-
| `targets` | file走査engineへ渡す対象path |
|
|
63
66
|
|
|
64
|
-
engineは `biome` → `knip` → `comment-check` → `tsdoc-check` の順に実行する
|
|
65
|
-
|
|
67
|
+
engineは `biome` → `knip` → `comment-check` → `document-style-check` → `tsdoc-check` の順に実行する
|
|
68
|
+
|
|
69
|
+
`engines` は起動の委任だけを表し、起動条件は `config` のengineの下へ置く engineが受け取る条件は次のとおり
|
|
70
|
+
|
|
71
|
+
| engine | 受け取る条件 |
|
|
72
|
+
|--------|--------------|
|
|
73
|
+
| `biome` | `targets`、`args` 除外pathは `biome.json` が持つ |
|
|
74
|
+
| `knip` | `args` 設定は `knip.ts` が持つ |
|
|
75
|
+
| `comment-check` | `ignore`、`targets`、`args` |
|
|
76
|
+
| `document-style-check` | `ignore`、`targets`、`args` |
|
|
77
|
+
| `tsdoc-check` | `ignore`、`targets`、`args`、`error`(違反として扱うrule名) |
|
|
78
|
+
|
|
79
|
+
`args` はengineの既定引数の後ろへ足す 設定fileで表せない起動条件や、engineの引数が変わったときの逃げ道として使う
|
|
66
80
|
|
|
67
81
|
## Options
|
|
68
82
|
|
|
@@ -74,11 +88,20 @@ engineは `biome` → `knip` → `comment-check` → `tsdoc-check` の順に実
|
|
|
74
88
|
| `--update-baseline` | 現在の検出でbaselineを置き換える |
|
|
75
89
|
| `--json` | engineごとの結果をJSONで出力する |
|
|
76
90
|
|
|
91
|
+
`--ignore` と位置引数の対象pathは、その条件を受け取るengineへだけ渡す
|
|
92
|
+
|
|
77
93
|
## Notes
|
|
78
94
|
|
|
79
95
|
終了codeは0が全engine成功、1が失敗したengineあり、2が設定またはengine起動の失敗
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
engineが受け取らない条件を書いた場合は渡さず、その旨をそのengineのsectionへ出す
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
== biome ==
|
|
101
|
+
biome: ignore skipped (biome.json holds its settings)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
BiomeとKnipの除外pathは `biome.json` と `knip.ts` が持つ このCLIは同じfileからengineの起動と結果の集約だけを行う
|
|
82
105
|
|
|
83
106
|
`comment-check` はbaseline差分を無効化する未作成のpathを渡して起動する 新規と解消済みの判定はengineごとではなく統合CLIが1つのbaseline fileで行うため、既存の `comment-baseline.json` がある場合は `--update-baseline` で移す
|
|
84
107
|
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# @yuu1111/quality-check
|
|
4
4
|
|
|
5
|
-
Runs Biome, Knip, comment-check, and the TSDoc checker
|
|
6
|
-
one script per project. The baseline diff lives here too.
|
|
5
|
+
Runs Biome, Knip, comment-check, document-style-check, and the TSDoc checker
|
|
6
|
+
from one CLI instead of one script per project. The baseline diff lives here too.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
@@ -13,8 +13,8 @@ bun add -D @yuu1111/quality-check
|
|
|
13
13
|
|
|
14
14
|
## Usage
|
|
15
15
|
|
|
16
|
-
Declare the engines and the
|
|
17
|
-
CLI from a script:
|
|
16
|
+
Declare the engines to run and the conditions of each engine in
|
|
17
|
+
`quality.config.ts`, then call the CLI from a script:
|
|
18
18
|
|
|
19
19
|
```json
|
|
20
20
|
{
|
|
@@ -28,13 +28,17 @@ CLI from a script:
|
|
|
28
28
|
import { defineConfig } from "@yuu1111/quality-check";
|
|
29
29
|
|
|
30
30
|
export default defineConfig({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
engines: {
|
|
32
|
+
biome: true,
|
|
33
|
+
knip: true,
|
|
34
|
+
"comment-check": true,
|
|
35
|
+
"document-style-check": true,
|
|
36
|
+
"tsdoc-check": true,
|
|
37
|
+
},
|
|
38
|
+
config: {
|
|
39
|
+
"comment-check": { ignore: ["FFXIVReplayAnalyzer"] },
|
|
40
|
+
"tsdoc-check": { error: ["missing-doc"] },
|
|
41
|
+
},
|
|
38
42
|
});
|
|
39
43
|
```
|
|
40
44
|
|
|
@@ -59,14 +63,26 @@ quality-check: 1 of 3 engines failed
|
|
|
59
63
|
|
|
60
64
|
| Field | Description |
|
|
61
65
|
|-------|-------------|
|
|
62
|
-
| `engines` | Engines to run, as `true` or `
|
|
63
|
-
| `
|
|
66
|
+
| `engines` | Engines to run, as `true` or `false` |
|
|
67
|
+
| `config` | Conditions of each engine |
|
|
64
68
|
| `baseline` | Baseline file path; `false` disables the diff |
|
|
65
|
-
| `targets` | Targets passed to the file-scanning engines |
|
|
66
69
|
|
|
67
|
-
Engines run in the order `biome`, `knip`, `comment-check`,
|
|
68
|
-
`
|
|
69
|
-
|
|
70
|
+
Engines run in the order `biome`, `knip`, `comment-check`,
|
|
71
|
+
`document-style-check`, `tsdoc-check`.
|
|
72
|
+
|
|
73
|
+
`engines` only delegates the start-up, and the conditions belong under the
|
|
74
|
+
engine in `config`. Each engine takes:
|
|
75
|
+
|
|
76
|
+
| Engine | Conditions |
|
|
77
|
+
|--------|------------|
|
|
78
|
+
| `biome` | `targets`, `args`; `biome.json` holds the excluded paths |
|
|
79
|
+
| `knip` | `args`; `knip.ts` holds the settings |
|
|
80
|
+
| `comment-check` | `ignore`, `targets`, `args` |
|
|
81
|
+
| `document-style-check` | `ignore`, `targets`, `args` |
|
|
82
|
+
| `tsdoc-check` | `ignore`, `targets`, `args`, `error` (rule names to fail on) |
|
|
83
|
+
|
|
84
|
+
`args` is appended after the engine defaults, for conditions the config cannot
|
|
85
|
+
express and for the case where an engine changes its arguments.
|
|
70
86
|
|
|
71
87
|
## Options
|
|
72
88
|
|
|
@@ -78,14 +94,23 @@ Engines run in the order `biome`, `knip`, `comment-check`, `tsdoc-check`.
|
|
|
78
94
|
| `--update-baseline` | Replace the baseline with the current findings |
|
|
79
95
|
| `--json` | Print the per-engine results as JSON |
|
|
80
96
|
|
|
97
|
+
`--ignore` and the positional targets reach only the engines that take them.
|
|
98
|
+
|
|
81
99
|
## Notes
|
|
82
100
|
|
|
83
101
|
Exit code 0 means every engine passed, 1 that at least one failed, and 2 that the
|
|
84
102
|
configuration or an engine could not start.
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
A condition that an engine does not take is not passed on, and the section says
|
|
105
|
+
so:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
== biome ==
|
|
109
|
+
biome: ignore skipped (biome.json holds its settings)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Biome and Knip keep their excluded paths in `biome.json` and `knip.ts`. This
|
|
113
|
+
CLI starts the engines from one file and aggregates the results.
|
|
89
114
|
|
|
90
115
|
`comment-check` runs with an unwritten baseline path so that it reports every
|
|
91
116
|
finding. The new-and-resolved diff is done by this CLI from a single baseline
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -98,13 +98,6 @@ function parseArguments(argv: string[]): Options {
|
|
|
98
98
|
return options;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function mergeIgnores(config: QualityConfig, ignores: string[]): QualityConfig {
|
|
102
|
-
if (ignores.length === 0) {
|
|
103
|
-
return config;
|
|
104
|
-
}
|
|
105
|
-
return { ...config, ignore: [...(config.ignore ?? []), ...ignores] };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
101
|
function resolveBaselinePath(
|
|
109
102
|
options: Options,
|
|
110
103
|
config: QualityConfig,
|
|
@@ -138,7 +131,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
138
131
|
console.error(`quality.config.ts not found in ${cwd}`);
|
|
139
132
|
return 2;
|
|
140
133
|
}
|
|
141
|
-
const config =
|
|
134
|
+
const config = await loadConfig(configPath);
|
|
142
135
|
const baselinePath = resolveBaselinePath(options, config, cwd);
|
|
143
136
|
const results = await runEngines({
|
|
144
137
|
baseline:
|
|
@@ -147,9 +140,8 @@ async function main(argv: string[]): Promise<number> {
|
|
|
147
140
|
: readBaseline(baselinePath),
|
|
148
141
|
config,
|
|
149
142
|
cwd,
|
|
143
|
+
overrides: { ignore: options.ignores, targets: options.targets },
|
|
150
144
|
rawBaseline: join(tmpdir(), `quality-check-raw-${process.pid}.json`),
|
|
151
|
-
targets:
|
|
152
|
-
options.targets.length > 0 ? options.targets : (config.targets ?? ["."]),
|
|
153
145
|
});
|
|
154
146
|
if (options.update) {
|
|
155
147
|
if (baselinePath === null) {
|
package/src/config.ts
CHANGED
|
@@ -9,6 +9,7 @@ export const ENGINE_NAMES = [
|
|
|
9
9
|
"biome",
|
|
10
10
|
"knip",
|
|
11
11
|
"comment-check",
|
|
12
|
+
"document-style-check",
|
|
12
13
|
"tsdoc-check",
|
|
13
14
|
] as const;
|
|
14
15
|
|
|
@@ -18,25 +19,46 @@ export const ENGINE_NAMES = [
|
|
|
18
19
|
export type EngineName = (typeof ENGINE_NAMES)[number];
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
* engine
|
|
22
|
+
* engineへ渡す起動条件
|
|
22
23
|
*/
|
|
23
24
|
export interface EngineOptions {
|
|
25
|
+
/** 検査から外すpath engineが受け取れないときはreportへ出す */
|
|
26
|
+
ignore?: string[];
|
|
27
|
+
/** 検査する対象path engineが受け取れないときはreportへ出す */
|
|
28
|
+
targets?: string[];
|
|
24
29
|
/** engineの既定引数の後ろへ足す引数 */
|
|
25
30
|
args?: string[];
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
/**
|
|
34
|
+
* TSDoc検査へ渡す起動条件
|
|
35
|
+
*/
|
|
36
|
+
export interface TsdocCheckOptions extends EngineOptions {
|
|
37
|
+
/** 違反として扱うrule名 */
|
|
38
|
+
error?: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* engine名ごとの起動条件
|
|
43
|
+
*/
|
|
44
|
+
export interface EngineConfigMap {
|
|
45
|
+
biome: EngineOptions;
|
|
46
|
+
knip: EngineOptions;
|
|
47
|
+
"comment-check": EngineOptions;
|
|
48
|
+
"document-style-check": EngineOptions;
|
|
49
|
+
"tsdoc-check": TsdocCheckOptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
28
52
|
/**
|
|
29
53
|
* quality.config.tsが受け付ける統合検査の設定
|
|
30
54
|
*/
|
|
31
55
|
export interface QualityConfig {
|
|
32
56
|
/** 起動するengine 値がfalseまたは未指定のengineは起動しない */
|
|
33
|
-
engines: Partial<Record<EngineName, boolean
|
|
34
|
-
/**
|
|
35
|
-
|
|
57
|
+
engines: Partial<Record<EngineName, boolean>>;
|
|
58
|
+
/** engineごとの起動条件 省略したengineは既定値で起動する */
|
|
59
|
+
config?: Partial<EngineConfigMap>;
|
|
36
60
|
/** baseline fileのpath falseなら差分判定を行わない */
|
|
37
61
|
baseline?: string | false;
|
|
38
|
-
/** file走査engineへ渡す対象path */
|
|
39
|
-
targets?: string[];
|
|
40
62
|
}
|
|
41
63
|
|
|
42
64
|
/**
|
|
@@ -71,6 +93,14 @@ function isEngineName(value: string): value is EngineName {
|
|
|
71
93
|
return (ENGINE_NAMES as readonly string[]).includes(value);
|
|
72
94
|
}
|
|
73
95
|
|
|
96
|
+
const ENGINE_OPTION_KEYS: Record<EngineName, readonly string[]> = {
|
|
97
|
+
biome: ["args", "ignore", "targets"],
|
|
98
|
+
knip: ["args", "ignore", "targets"],
|
|
99
|
+
"comment-check": ["args", "ignore", "targets"],
|
|
100
|
+
"document-style-check": ["args", "ignore", "targets"],
|
|
101
|
+
"tsdoc-check": ["args", "error", "ignore", "targets"],
|
|
102
|
+
};
|
|
103
|
+
|
|
74
104
|
function readStringArray(value: unknown, field: string): string[] | undefined {
|
|
75
105
|
if (value === undefined) {
|
|
76
106
|
return undefined;
|
|
@@ -97,27 +127,96 @@ function readBaseline(
|
|
|
97
127
|
throw new Error(`${source}: baseline must be a path string or false`);
|
|
98
128
|
}
|
|
99
129
|
|
|
100
|
-
function
|
|
130
|
+
function parseEngines(
|
|
101
131
|
value: unknown,
|
|
102
132
|
source: string,
|
|
103
|
-
|
|
104
|
-
): boolean | EngineOptions {
|
|
105
|
-
if (typeof value === "boolean") {
|
|
106
|
-
return value;
|
|
107
|
-
}
|
|
133
|
+
): Partial<Record<EngineName, boolean>> {
|
|
108
134
|
if (!isJsonObject(value)) {
|
|
109
|
-
throw new Error(
|
|
110
|
-
`${source}: engines.${name} must be a boolean or an object`,
|
|
111
|
-
);
|
|
135
|
+
throw new Error(`${source} must export an engines object`);
|
|
112
136
|
}
|
|
113
|
-
const
|
|
137
|
+
const engines: Partial<Record<EngineName, boolean>> = {};
|
|
138
|
+
for (const [name, enabled] of Object.entries(value)) {
|
|
139
|
+
if (!isEngineName(name)) {
|
|
140
|
+
throw new Error(`${source}: unknown engine: ${name}`);
|
|
141
|
+
}
|
|
142
|
+
if (typeof enabled !== "boolean") {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`${source}: engines.${name} must be a boolean 起動条件はconfigへ置く`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
engines[name] = enabled;
|
|
148
|
+
}
|
|
149
|
+
if (ENGINE_NAMES.every((name) => !engines[name])) {
|
|
150
|
+
throw new Error(`${source} must enable at least one engine`);
|
|
151
|
+
}
|
|
152
|
+
return engines;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseEngineOptions(
|
|
156
|
+
value: JsonObject,
|
|
157
|
+
source: string,
|
|
158
|
+
name: EngineName,
|
|
159
|
+
): TsdocCheckOptions {
|
|
160
|
+
const unknown = Object.keys(value).filter(
|
|
161
|
+
(key) => !ENGINE_OPTION_KEYS[name].includes(key),
|
|
162
|
+
);
|
|
114
163
|
if (unknown.length > 0) {
|
|
115
164
|
throw new Error(
|
|
116
|
-
`${source}:
|
|
165
|
+
`${source}: config.${name} has an unknown option: ${unknown[0]}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
const options: TsdocCheckOptions = {};
|
|
169
|
+
const ignore = readStringArray(
|
|
170
|
+
value.ignore,
|
|
171
|
+
`${source}: config.${name}.ignore`,
|
|
172
|
+
);
|
|
173
|
+
if (ignore !== undefined) {
|
|
174
|
+
options.ignore = ignore;
|
|
175
|
+
}
|
|
176
|
+
const targets = readStringArray(
|
|
177
|
+
value.targets,
|
|
178
|
+
`${source}: config.${name}.targets`,
|
|
179
|
+
);
|
|
180
|
+
if (targets !== undefined) {
|
|
181
|
+
options.targets = targets;
|
|
182
|
+
}
|
|
183
|
+
const args = readStringArray(value.args, `${source}: config.${name}.args`);
|
|
184
|
+
if (args !== undefined) {
|
|
185
|
+
options.args = args;
|
|
186
|
+
}
|
|
187
|
+
if (name === "tsdoc-check") {
|
|
188
|
+
const error = readStringArray(
|
|
189
|
+
value.error,
|
|
190
|
+
`${source}: config.${name}.error`,
|
|
117
191
|
);
|
|
192
|
+
if (error !== undefined) {
|
|
193
|
+
options.error = error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return options;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function parseEngineConfig(
|
|
200
|
+
value: unknown,
|
|
201
|
+
source: string,
|
|
202
|
+
): Partial<EngineConfigMap> | undefined {
|
|
203
|
+
if (value === undefined) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
if (!isJsonObject(value)) {
|
|
207
|
+
throw new Error(`${source}: config must be an object`);
|
|
208
|
+
}
|
|
209
|
+
const config: Partial<EngineConfigMap> = {};
|
|
210
|
+
for (const [name, options] of Object.entries(value)) {
|
|
211
|
+
if (!isEngineName(name)) {
|
|
212
|
+
throw new Error(`${source}: unknown engine in config: ${name}`);
|
|
213
|
+
}
|
|
214
|
+
if (!isJsonObject(options)) {
|
|
215
|
+
throw new Error(`${source}: config.${name} must be an object`);
|
|
216
|
+
}
|
|
217
|
+
config[name] = parseEngineOptions(options, source, name);
|
|
118
218
|
}
|
|
119
|
-
|
|
120
|
-
return args === undefined ? {} : { args };
|
|
219
|
+
return config;
|
|
121
220
|
}
|
|
122
221
|
|
|
123
222
|
/**
|
|
@@ -127,32 +226,17 @@ export function parseConfig(value: unknown, source: string): QualityConfig {
|
|
|
127
226
|
if (!isJsonObject(value)) {
|
|
128
227
|
throw new Error(`${source} must export a config object`);
|
|
129
228
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
throw new Error(`${source}: unknown engine: ${name}`);
|
|
137
|
-
}
|
|
138
|
-
engines[name] = parseEngineOptions(options, source, name);
|
|
229
|
+
const config: QualityConfig = {
|
|
230
|
+
engines: parseEngines(value.engines, source),
|
|
231
|
+
};
|
|
232
|
+
const engineConfig = parseEngineConfig(value.config, source);
|
|
233
|
+
if (engineConfig !== undefined) {
|
|
234
|
+
config.config = engineConfig;
|
|
139
235
|
}
|
|
140
|
-
if (ENGINE_NAMES.every((name) => !engines[name])) {
|
|
141
|
-
throw new Error(`${source} must enable at least one engine`);
|
|
142
|
-
}
|
|
143
|
-
const config: QualityConfig = { engines };
|
|
144
236
|
const baseline = readBaseline(value.baseline, source);
|
|
145
237
|
if (baseline !== undefined) {
|
|
146
238
|
config.baseline = baseline;
|
|
147
239
|
}
|
|
148
|
-
const ignore = readStringArray(value.ignore, `${source}: ignore`);
|
|
149
|
-
if (ignore !== undefined) {
|
|
150
|
-
config.ignore = ignore;
|
|
151
|
-
}
|
|
152
|
-
const targets = readStringArray(value.targets, `${source}: targets`);
|
|
153
|
-
if (targets !== undefined) {
|
|
154
|
-
config.targets = targets;
|
|
155
|
-
}
|
|
156
240
|
return config;
|
|
157
241
|
}
|
|
158
242
|
|
|
@@ -164,17 +248,16 @@ export function enabledEngines(config: QualityConfig): EngineName[] {
|
|
|
164
248
|
}
|
|
165
249
|
|
|
166
250
|
/**
|
|
167
|
-
* engine
|
|
251
|
+
* engineの起動条件を返す 無効なengineにはnullを返す
|
|
168
252
|
*/
|
|
169
|
-
export function
|
|
253
|
+
export function engineConfig<K extends EngineName>(
|
|
170
254
|
config: QualityConfig,
|
|
171
|
-
name:
|
|
172
|
-
):
|
|
173
|
-
|
|
174
|
-
if (!value) {
|
|
255
|
+
name: K,
|
|
256
|
+
): EngineConfigMap[K] | null {
|
|
257
|
+
if (!config.engines[name]) {
|
|
175
258
|
return null;
|
|
176
259
|
}
|
|
177
|
-
return
|
|
260
|
+
return config.config?.[name] ?? ({} as EngineConfigMap[K]);
|
|
178
261
|
}
|
|
179
262
|
|
|
180
263
|
/**
|
package/src/engines.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type EngineName,
|
|
5
|
+
type EngineOptions,
|
|
6
|
+
engineConfig,
|
|
7
|
+
type QualityConfig,
|
|
8
|
+
} from "./config";
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* 子プロセスとして起動したengineの生の結果
|
|
@@ -25,6 +30,7 @@ export type EngineRunner = (
|
|
|
25
30
|
export const ENGINE_BINS: Record<EngineName, string> = {
|
|
26
31
|
biome: "biome",
|
|
27
32
|
"comment-check": "comment-check",
|
|
33
|
+
"document-style-check": "document-style-check",
|
|
28
34
|
knip: "knip",
|
|
29
35
|
"tsdoc-check": "tsdoc-check",
|
|
30
36
|
};
|
|
@@ -32,22 +38,72 @@ export const ENGINE_BINS: Record<EngineName, string> = {
|
|
|
32
38
|
const WINDOWS_SHIMS = [".exe", ".cmd", ".bat", ""];
|
|
33
39
|
const POSIX_SHIMS = [""];
|
|
34
40
|
|
|
41
|
+
/**
|
|
42
|
+
* コマンドラインから渡された起動条件の上書き
|
|
43
|
+
*/
|
|
44
|
+
export interface RunOverrides {
|
|
45
|
+
ignore: string[];
|
|
46
|
+
targets: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
/**
|
|
36
50
|
* engineのコマンドを組み立てるための実行条件
|
|
37
51
|
*/
|
|
38
52
|
export interface EngineCommandContext {
|
|
39
53
|
config: QualityConfig;
|
|
40
|
-
|
|
54
|
+
/** 対応するengineへだけ足す上書き */
|
|
55
|
+
overrides: RunOverrides;
|
|
41
56
|
/** comment-checkのbaseline差分を無効化するために渡す未作成のpath */
|
|
42
57
|
rawBaseline: string;
|
|
43
|
-
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* engineが受け取らない起動条件と、その設定の持ち主
|
|
62
|
+
*/
|
|
63
|
+
export const ENGINE_LIMITS: Record<
|
|
64
|
+
EngineName,
|
|
65
|
+
Partial<Record<"ignore" | "targets", string>>
|
|
66
|
+
> = {
|
|
67
|
+
biome: { ignore: "biome.json holds its settings" },
|
|
68
|
+
knip: {
|
|
69
|
+
ignore: "knip.ts holds its settings",
|
|
70
|
+
targets: "knip analyzes the whole project",
|
|
71
|
+
},
|
|
72
|
+
"comment-check": {},
|
|
73
|
+
"document-style-check": {},
|
|
74
|
+
"tsdoc-check": {},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* engineが受け取らないため渡さなかった起動条件を返す
|
|
79
|
+
*/
|
|
80
|
+
export function skippedEngineOptions(
|
|
81
|
+
name: EngineName,
|
|
82
|
+
options: EngineOptions | undefined,
|
|
83
|
+
): string[] {
|
|
84
|
+
if (options === undefined) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const limits = ENGINE_LIMITS[name];
|
|
88
|
+
const skipped: string[] = [];
|
|
89
|
+
for (const key of ["ignore", "targets"] as const) {
|
|
90
|
+
const reason = limits[key];
|
|
91
|
+
if (reason !== undefined && options[key] !== undefined) {
|
|
92
|
+
skipped.push(`${key} skipped (${reason})`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return skipped;
|
|
44
96
|
}
|
|
45
97
|
|
|
46
98
|
/**
|
|
47
99
|
* 検出をJSONで返すengineか
|
|
48
100
|
*/
|
|
49
101
|
export function isFindingEngine(name: EngineName): boolean {
|
|
50
|
-
return
|
|
102
|
+
return (
|
|
103
|
+
name === "comment-check" ||
|
|
104
|
+
name === "document-style-check" ||
|
|
105
|
+
name === "tsdoc-check"
|
|
106
|
+
);
|
|
51
107
|
}
|
|
52
108
|
|
|
53
109
|
/**
|
|
@@ -90,24 +146,44 @@ export function buildEngineCommand(
|
|
|
90
146
|
executable: string,
|
|
91
147
|
context: EngineCommandContext,
|
|
92
148
|
): string[] {
|
|
93
|
-
const
|
|
149
|
+
const options: EngineOptions = engineConfig(context.config, name) ?? {};
|
|
150
|
+
const limits = ENGINE_LIMITS[name];
|
|
151
|
+
const extra = options.args ?? [];
|
|
152
|
+
const ignores =
|
|
153
|
+
limits.ignore === undefined
|
|
154
|
+
? [...(options.ignore ?? []), ...context.overrides.ignore]
|
|
155
|
+
: [];
|
|
156
|
+
const requested =
|
|
157
|
+
context.overrides.targets.length > 0
|
|
158
|
+
? context.overrides.targets
|
|
159
|
+
: (options.targets ?? ["."]);
|
|
160
|
+
const targets = limits.targets === undefined ? requested : [];
|
|
161
|
+
const ignoreArguments = ignores.flatMap((ignore) => ["--ignore", ignore]);
|
|
162
|
+
const rules = engineConfig(context.config, "tsdoc-check")?.error ?? [];
|
|
163
|
+
const errorArguments = rules.flatMap((rule) => ["--error", rule]);
|
|
94
164
|
if (name === "biome") {
|
|
95
|
-
return [executable, "check", ...
|
|
165
|
+
return [executable, "check", ...targets, ...extra];
|
|
96
166
|
}
|
|
97
167
|
if (name === "knip") {
|
|
98
168
|
return [executable, ...extra];
|
|
99
169
|
}
|
|
100
|
-
const ignoreArguments = context.ignores.flatMap((ignore) => [
|
|
101
|
-
"--ignore",
|
|
102
|
-
ignore,
|
|
103
|
-
]);
|
|
104
170
|
if (name === "comment-check") {
|
|
105
171
|
return [
|
|
106
172
|
executable,
|
|
107
173
|
"--json",
|
|
108
174
|
"--baseline",
|
|
109
175
|
context.rawBaseline,
|
|
110
|
-
...
|
|
176
|
+
...targets,
|
|
177
|
+
...ignoreArguments,
|
|
178
|
+
...extra,
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
if (name === "document-style-check") {
|
|
182
|
+
return [
|
|
183
|
+
executable,
|
|
184
|
+
"lint",
|
|
185
|
+
"--json",
|
|
186
|
+
...targets,
|
|
111
187
|
...ignoreArguments,
|
|
112
188
|
...extra,
|
|
113
189
|
];
|
|
@@ -115,7 +191,8 @@ export function buildEngineCommand(
|
|
|
115
191
|
return [
|
|
116
192
|
executable,
|
|
117
193
|
"--json",
|
|
118
|
-
...
|
|
194
|
+
...errorArguments,
|
|
195
|
+
...targets,
|
|
119
196
|
...ignoreArguments,
|
|
120
197
|
...extra,
|
|
121
198
|
];
|
package/src/report.ts
CHANGED
|
@@ -25,6 +25,9 @@ function describeStatus(result: EngineResult): string {
|
|
|
25
25
|
*/
|
|
26
26
|
export function formatEngineSection(result: EngineResult): string {
|
|
27
27
|
const lines = [`== ${result.name} ==`];
|
|
28
|
+
for (const skipped of result.skipped) {
|
|
29
|
+
lines.push(`${result.name}: ${skipped}`);
|
|
30
|
+
}
|
|
28
31
|
if (result.output !== "" && !isFindingEngine(result.name)) {
|
|
29
32
|
lines.push(result.output);
|
|
30
33
|
}
|
|
@@ -73,6 +76,7 @@ export function toJsonReport(results: EngineResult[]): unknown {
|
|
|
73
76
|
name: result.name,
|
|
74
77
|
reported: result.reported,
|
|
75
78
|
resolved: result.resolved,
|
|
79
|
+
skipped: result.skipped,
|
|
76
80
|
status: result.status,
|
|
77
81
|
warnings: result.warnings,
|
|
78
82
|
})),
|
package/src/run.ts
CHANGED
|
@@ -7,8 +7,10 @@ import {
|
|
|
7
7
|
type EngineProcessResult,
|
|
8
8
|
type EngineRunner,
|
|
9
9
|
isFindingEngine,
|
|
10
|
+
type RunOverrides,
|
|
10
11
|
resolveExecutable,
|
|
11
12
|
runEngineProcess,
|
|
13
|
+
skippedEngineOptions,
|
|
12
14
|
} from "./engines";
|
|
13
15
|
import { type NormalizedFinding, parseFindings } from "./findings";
|
|
14
16
|
|
|
@@ -30,6 +32,8 @@ export interface EngineResult {
|
|
|
30
32
|
/** baseline適用後に残った阻害する検出 */
|
|
31
33
|
reported: NormalizedFinding[];
|
|
32
34
|
resolved: number;
|
|
35
|
+
/** engineが受け取らなかった起動条件 */
|
|
36
|
+
skipped: string[];
|
|
33
37
|
status: EngineStatus;
|
|
34
38
|
warnings: NormalizedFinding[];
|
|
35
39
|
}
|
|
@@ -42,8 +46,8 @@ export interface RunOptions {
|
|
|
42
46
|
baseline: BaselineFile | null;
|
|
43
47
|
config: QualityConfig;
|
|
44
48
|
cwd: string;
|
|
45
|
-
/**
|
|
46
|
-
|
|
49
|
+
/** コマンドラインから渡された起動条件の上書き */
|
|
50
|
+
overrides: RunOverrides;
|
|
47
51
|
/** comment-checkのbaseline差分を無効化する未作成のpath */
|
|
48
52
|
rawBaseline: string;
|
|
49
53
|
/** engineの実行fileを解決する関数 テストでは差し替える */
|
|
@@ -63,6 +67,7 @@ function baseResult(
|
|
|
63
67
|
output,
|
|
64
68
|
reported: [],
|
|
65
69
|
resolved: 0,
|
|
70
|
+
skipped: [],
|
|
66
71
|
warnings: [],
|
|
67
72
|
};
|
|
68
73
|
}
|
|
@@ -144,7 +149,7 @@ async function runProcessEngine(
|
|
|
144
149
|
};
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
async function
|
|
152
|
+
async function executeEngine(
|
|
148
153
|
name: EngineName,
|
|
149
154
|
options: RunOptions,
|
|
150
155
|
context: EngineCommandContext,
|
|
@@ -164,6 +169,19 @@ async function runEngine(
|
|
|
164
169
|
return await runProcessEngine(name, options, executable, context, runner);
|
|
165
170
|
}
|
|
166
171
|
|
|
172
|
+
async function runEngine(
|
|
173
|
+
name: EngineName,
|
|
174
|
+
options: RunOptions,
|
|
175
|
+
context: EngineCommandContext,
|
|
176
|
+
runner: EngineRunner,
|
|
177
|
+
): Promise<EngineResult> {
|
|
178
|
+
const result = await executeEngine(name, options, context, runner);
|
|
179
|
+
return {
|
|
180
|
+
...result,
|
|
181
|
+
skipped: skippedEngineOptions(name, options.config.config?.[name]),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
167
185
|
/**
|
|
168
186
|
* 設定で有効なengineを順に起動する
|
|
169
187
|
*/
|
|
@@ -171,9 +189,8 @@ export async function runEngines(options: RunOptions): Promise<EngineResult[]> {
|
|
|
171
189
|
const runner = options.runner ?? runEngineProcess;
|
|
172
190
|
const context: EngineCommandContext = {
|
|
173
191
|
config: options.config,
|
|
174
|
-
|
|
192
|
+
overrides: options.overrides,
|
|
175
193
|
rawBaseline: options.rawBaseline,
|
|
176
|
-
targets: options.targets,
|
|
177
194
|
};
|
|
178
195
|
const results: EngineResult[] = [];
|
|
179
196
|
for (const name of enabledEngines(options.config)) {
|