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