@yuu1111/quality-check 0.11.0 → 4.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 +102 -45
- package/README.md +105 -48
- package/dist/cli.js +21908 -344
- package/package.json +11 -14
- package/schema.json +688 -0
- package/src/config.ts +0 -388
- package/src/engines.ts +0 -333
package/src/config.ts
DELETED
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import type { OptInRuleId as CommentCheckRuleName } from "@yuu1111/comment-check/rule-ids";
|
|
5
|
-
import type { OptInRuleId as DocumentStyleCheckRuleName } from "@yuu1111/document-style-check/rule-ids";
|
|
6
|
-
import type {
|
|
7
|
-
OptInRuleId as TsdocCheckRuleName,
|
|
8
|
-
TsdocRule,
|
|
9
|
-
} from "@yuu1111/tsdoc-check/rule-ids";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 統合CLIが起動できるengineの名前 並び順が実行順になる
|
|
13
|
-
*/
|
|
14
|
-
export const ENGINE_NAMES = [
|
|
15
|
-
"biome",
|
|
16
|
-
"typecheck",
|
|
17
|
-
"knip",
|
|
18
|
-
"code-style-check",
|
|
19
|
-
"comment-check",
|
|
20
|
-
"document-style-check",
|
|
21
|
-
"tsdoc-check",
|
|
22
|
-
] as const;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* engine名のunion型
|
|
26
|
-
*/
|
|
27
|
-
export type EngineName = (typeof ENGINE_NAMES)[number];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* engineへ渡す起動条件
|
|
31
|
-
*/
|
|
32
|
-
export interface EngineOptions {
|
|
33
|
-
/** 検査から外すpath engineが受け取れないときはreportへ出す */
|
|
34
|
-
ignore?: string[];
|
|
35
|
-
/** 検査する対象path engineが受け取れないときはreportへ出す */
|
|
36
|
-
targets?: string[];
|
|
37
|
-
/** engineの既定引数の後ろへ足す引数 */
|
|
38
|
-
args?: string[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* comment-checkへ渡す起動条件
|
|
43
|
-
*/
|
|
44
|
-
export interface CommentCheckOptions extends EngineOptions {
|
|
45
|
-
/** 既定で無効のopt-in ruleのうち有効にするrule名 comment-checkが公開するunionで縛る */
|
|
46
|
-
enable?: CommentCheckRuleName[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* document-style-checkへ渡す起動条件
|
|
51
|
-
*/
|
|
52
|
-
export interface DocumentStyleCheckOptions extends EngineOptions {
|
|
53
|
-
/** 既定で無効のopt-in ruleのうち有効にするrule名 document-style-checkが公開するunionで縛る */
|
|
54
|
-
enable?: DocumentStyleCheckRuleName[];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* TSDoc検査へ渡す起動条件
|
|
59
|
-
*/
|
|
60
|
-
export interface TsdocCheckOptions extends EngineOptions {
|
|
61
|
-
/** 既定で無効のopt-in ruleのうち有効にするrule名 tsdoc-checkが公開するunionで縛る */
|
|
62
|
-
enable?: TsdocCheckRuleName[];
|
|
63
|
-
/** 違反として扱うrule名 tsdoc-checkが公開するunionで縛る */
|
|
64
|
-
error?: TsdocRule[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 型検査へ渡す起動条件
|
|
69
|
-
*/
|
|
70
|
-
export interface TypecheckOptions extends EngineOptions {
|
|
71
|
-
/** 型検査するtsconfigのpath 省略時はカレントのtsconfig.jsonを1回だけ読む */
|
|
72
|
-
projects?: string[];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* engine名ごとの起動条件
|
|
77
|
-
*/
|
|
78
|
-
export interface EngineConfigMap {
|
|
79
|
-
biome: EngineOptions;
|
|
80
|
-
typecheck: TypecheckOptions;
|
|
81
|
-
knip: EngineOptions;
|
|
82
|
-
"code-style-check": EngineOptions;
|
|
83
|
-
"comment-check": CommentCheckOptions;
|
|
84
|
-
"document-style-check": DocumentStyleCheckOptions;
|
|
85
|
-
"tsdoc-check": TsdocCheckOptions;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* quality.config.tsが受け付ける統合検査の設定
|
|
90
|
-
*/
|
|
91
|
-
export interface QualityConfig {
|
|
92
|
-
/** 起動するengine 値がfalseまたは未指定のengineは起動しない */
|
|
93
|
-
engines: Partial<Record<EngineName, boolean>>;
|
|
94
|
-
/** engineごとの起動条件 省略したengineは既定値で起動する */
|
|
95
|
-
config?: Partial<EngineConfigMap>;
|
|
96
|
-
/** baseline fileのpath falseなら差分判定を行わない */
|
|
97
|
-
baseline?: string | false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* 設定fileを型付けするための恒等関数
|
|
102
|
-
*
|
|
103
|
-
* @param config - 型付けする統合検査の設定
|
|
104
|
-
* @returns 引数をそのまま返した統合検査の設定
|
|
105
|
-
*/
|
|
106
|
-
export function defineConfig(config: QualityConfig): QualityConfig {
|
|
107
|
-
return config;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* config fileを探索する既定のfile名
|
|
112
|
-
*/
|
|
113
|
-
export const DEFAULT_CONFIG_FILES = [
|
|
114
|
-
"quality.config.ts",
|
|
115
|
-
"quality.config.mts",
|
|
116
|
-
"quality.config.js",
|
|
117
|
-
"quality.config.mjs",
|
|
118
|
-
] as const;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* baseline fileの既定名
|
|
122
|
-
*/
|
|
123
|
-
export const DEFAULT_BASELINE_FILE = "quality-baseline.json";
|
|
124
|
-
|
|
125
|
-
type JsonObject = Record<string, unknown>;
|
|
126
|
-
|
|
127
|
-
function isJsonObject(value: unknown): value is JsonObject {
|
|
128
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function isEngineName(value: string): value is EngineName {
|
|
132
|
-
return (ENGINE_NAMES as readonly string[]).includes(value);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const ENGINE_OPTION_KEYS: Record<EngineName, readonly string[]> = {
|
|
136
|
-
biome: ["args", "ignore", "targets"],
|
|
137
|
-
typecheck: ["args", "ignore", "projects", "targets"],
|
|
138
|
-
knip: ["args", "ignore", "targets"],
|
|
139
|
-
"code-style-check": ["args", "ignore", "targets"],
|
|
140
|
-
"comment-check": ["args", "enable", "ignore", "targets"],
|
|
141
|
-
"document-style-check": ["args", "enable", "ignore", "targets"],
|
|
142
|
-
"tsdoc-check": ["args", "enable", "error", "ignore", "targets"],
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
function readStringArray(value: unknown, field: string): string[] | undefined {
|
|
146
|
-
if (value === undefined) {
|
|
147
|
-
return undefined;
|
|
148
|
-
}
|
|
149
|
-
if (
|
|
150
|
-
!Array.isArray(value) ||
|
|
151
|
-
value.some((entry) => typeof entry !== "string" || entry === "")
|
|
152
|
-
) {
|
|
153
|
-
throw new Error(`${field} must be an array of non-empty strings`);
|
|
154
|
-
}
|
|
155
|
-
return [...value] as string[];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function readBaseline(
|
|
159
|
-
value: unknown,
|
|
160
|
-
source: string,
|
|
161
|
-
): string | false | undefined {
|
|
162
|
-
if (value === undefined || value === false) {
|
|
163
|
-
return value;
|
|
164
|
-
}
|
|
165
|
-
if (typeof value === "string" && value !== "") {
|
|
166
|
-
return value;
|
|
167
|
-
}
|
|
168
|
-
throw new Error(`${source}: baseline must be a path string or false`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function parseEngines(
|
|
172
|
-
value: unknown,
|
|
173
|
-
source: string,
|
|
174
|
-
): Partial<Record<EngineName, boolean>> {
|
|
175
|
-
if (!isJsonObject(value)) {
|
|
176
|
-
throw new Error(`${source} must export an engines object`);
|
|
177
|
-
}
|
|
178
|
-
const engines: Partial<Record<EngineName, boolean>> = {};
|
|
179
|
-
for (const [name, enabled] of Object.entries(value)) {
|
|
180
|
-
if (!isEngineName(name)) {
|
|
181
|
-
throw new Error(`${source}: unknown engine: ${name}`);
|
|
182
|
-
}
|
|
183
|
-
if (typeof enabled !== "boolean") {
|
|
184
|
-
throw new Error(
|
|
185
|
-
`${source}: engines.${name} must be a boolean 起動条件はconfigへ置く`,
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
engines[name] = enabled;
|
|
189
|
-
}
|
|
190
|
-
if (ENGINE_NAMES.every((name) => !engines[name])) {
|
|
191
|
-
throw new Error(`${source} must enable at least one engine`);
|
|
192
|
-
}
|
|
193
|
-
return engines;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
type ParsedEngineOptions = EngineOptions & {
|
|
197
|
-
enable?: string[];
|
|
198
|
-
error?: string[];
|
|
199
|
-
projects?: string[];
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* engine名ごとにしか受け取らない起動条件を読み取る
|
|
204
|
-
*/
|
|
205
|
-
function parseEngineExtras(
|
|
206
|
-
value: JsonObject,
|
|
207
|
-
source: string,
|
|
208
|
-
name: EngineName,
|
|
209
|
-
): ParsedEngineOptions {
|
|
210
|
-
const extras: ParsedEngineOptions = {};
|
|
211
|
-
if (
|
|
212
|
-
name === "comment-check" ||
|
|
213
|
-
name === "document-style-check" ||
|
|
214
|
-
name === "tsdoc-check"
|
|
215
|
-
) {
|
|
216
|
-
const enable = readStringArray(
|
|
217
|
-
value.enable,
|
|
218
|
-
`${source}: config.${name}.enable`,
|
|
219
|
-
);
|
|
220
|
-
if (enable !== undefined) {
|
|
221
|
-
extras.enable = enable;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
if (name === "tsdoc-check") {
|
|
225
|
-
const error = readStringArray(
|
|
226
|
-
value.error,
|
|
227
|
-
`${source}: config.${name}.error`,
|
|
228
|
-
);
|
|
229
|
-
if (error !== undefined) {
|
|
230
|
-
extras.error = error;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (name === "typecheck") {
|
|
234
|
-
const projects = readStringArray(
|
|
235
|
-
value.projects,
|
|
236
|
-
`${source}: config.${name}.projects`,
|
|
237
|
-
);
|
|
238
|
-
if (projects !== undefined) {
|
|
239
|
-
if (projects.length === 0) {
|
|
240
|
-
throw new Error(`${source}: config.${name}.projects must not be empty`);
|
|
241
|
-
}
|
|
242
|
-
extras.projects = projects;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return extras;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function parseEngineOptions(
|
|
249
|
-
value: JsonObject,
|
|
250
|
-
source: string,
|
|
251
|
-
name: EngineName,
|
|
252
|
-
): ParsedEngineOptions {
|
|
253
|
-
const unknown = Object.keys(value).filter(
|
|
254
|
-
(key) => !ENGINE_OPTION_KEYS[name].includes(key),
|
|
255
|
-
);
|
|
256
|
-
if (unknown.length > 0) {
|
|
257
|
-
throw new Error(
|
|
258
|
-
`${source}: config.${name} has an unknown option: ${unknown[0]}`,
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
const options: ParsedEngineOptions = {};
|
|
262
|
-
const ignore = readStringArray(
|
|
263
|
-
value.ignore,
|
|
264
|
-
`${source}: config.${name}.ignore`,
|
|
265
|
-
);
|
|
266
|
-
if (ignore !== undefined) {
|
|
267
|
-
options.ignore = ignore;
|
|
268
|
-
}
|
|
269
|
-
const targets = readStringArray(
|
|
270
|
-
value.targets,
|
|
271
|
-
`${source}: config.${name}.targets`,
|
|
272
|
-
);
|
|
273
|
-
if (targets !== undefined) {
|
|
274
|
-
options.targets = targets;
|
|
275
|
-
}
|
|
276
|
-
const args = readStringArray(value.args, `${source}: config.${name}.args`);
|
|
277
|
-
if (args !== undefined) {
|
|
278
|
-
options.args = args;
|
|
279
|
-
}
|
|
280
|
-
return { ...options, ...parseEngineExtras(value, source, name) };
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function parseEngineConfig(
|
|
284
|
-
value: unknown,
|
|
285
|
-
source: string,
|
|
286
|
-
): Partial<EngineConfigMap> | undefined {
|
|
287
|
-
if (value === undefined) {
|
|
288
|
-
return undefined;
|
|
289
|
-
}
|
|
290
|
-
if (!isJsonObject(value)) {
|
|
291
|
-
throw new Error(`${source}: config must be an object`);
|
|
292
|
-
}
|
|
293
|
-
const config: Partial<Record<EngineName, ParsedEngineOptions>> = {};
|
|
294
|
-
for (const [name, options] of Object.entries(value)) {
|
|
295
|
-
if (!isEngineName(name)) {
|
|
296
|
-
throw new Error(`${source}: unknown engine in config: ${name}`);
|
|
297
|
-
}
|
|
298
|
-
if (!isJsonObject(options)) {
|
|
299
|
-
throw new Error(`${source}: config.${name} must be an object`);
|
|
300
|
-
}
|
|
301
|
-
config[name] = parseEngineOptions(options, source, name);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// rule名はengineが公開するunionで縛る 実行時の入力は文字列として届くため検証済みの値をここで型へ寄せる
|
|
305
|
-
return config as Partial<EngineConfigMap>;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* 読み込んだ設定を検証して不足分を補う
|
|
310
|
-
*
|
|
311
|
-
* @param value - config fileがexportした検証前の値
|
|
312
|
-
* @param source - errorメッセージへ載せるconfig fileのpath
|
|
313
|
-
* @returns 検証して既定値を補った統合検査の設定
|
|
314
|
-
*/
|
|
315
|
-
export function parseConfig(value: unknown, source: string): QualityConfig {
|
|
316
|
-
if (!isJsonObject(value)) {
|
|
317
|
-
throw new Error(`${source} must export a config object`);
|
|
318
|
-
}
|
|
319
|
-
const config: QualityConfig = {
|
|
320
|
-
engines: parseEngines(value.engines, source),
|
|
321
|
-
};
|
|
322
|
-
const engineConfig = parseEngineConfig(value.config, source);
|
|
323
|
-
if (engineConfig !== undefined) {
|
|
324
|
-
config.config = engineConfig;
|
|
325
|
-
}
|
|
326
|
-
const baseline = readBaseline(value.baseline, source);
|
|
327
|
-
if (baseline !== undefined) {
|
|
328
|
-
config.baseline = baseline;
|
|
329
|
-
}
|
|
330
|
-
return config;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* 有効なengineを実行順で返す
|
|
335
|
-
*
|
|
336
|
-
* @param config - engineの有効無効を持つ統合検査の設定
|
|
337
|
-
* @returns 有効なengine名を実行順に並べた配列
|
|
338
|
-
*/
|
|
339
|
-
export function enabledEngines(config: QualityConfig): EngineName[] {
|
|
340
|
-
return ENGINE_NAMES.filter((name) => Boolean(config.engines[name]));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* engineの起動条件を返す 無効なengineにはnullを返す
|
|
345
|
-
*
|
|
346
|
-
* @typeParam K - 起動条件を取り出すengine名の型
|
|
347
|
-
* @param config - engineごとの起動条件を持つ統合検査の設定
|
|
348
|
-
* @param name - 起動条件を取り出すengine名
|
|
349
|
-
* @returns 指定したengineの起動条件 無効なengineならnull
|
|
350
|
-
*/
|
|
351
|
-
export function engineConfig<K extends EngineName>(
|
|
352
|
-
config: QualityConfig,
|
|
353
|
-
name: K,
|
|
354
|
-
): EngineConfigMap[K] | null {
|
|
355
|
-
if (!config.engines[name]) {
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
return config.config?.[name] ?? ({} as EngineConfigMap[K]);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* 作業ディレクトリからconfig fileを探す
|
|
363
|
-
*
|
|
364
|
-
* @param cwd - 探索を開始する作業ディレクトリのpath
|
|
365
|
-
* @returns 見つけたconfig fileのpath 見つからなければnull
|
|
366
|
-
*/
|
|
367
|
-
export function findConfigFile(cwd: string): string | null {
|
|
368
|
-
for (const name of DEFAULT_CONFIG_FILES) {
|
|
369
|
-
const candidate = resolve(cwd, name);
|
|
370
|
-
if (existsSync(candidate)) {
|
|
371
|
-
return candidate;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* config fileを読み込んで検証する
|
|
379
|
-
*
|
|
380
|
-
* @param path - 読み込むconfig fileのpath
|
|
381
|
-
* @returns 読み込んで検証した統合検査の設定
|
|
382
|
-
*/
|
|
383
|
-
export async function loadConfig(path: string): Promise<QualityConfig> {
|
|
384
|
-
const module: unknown = await import(pathToFileURL(path).href);
|
|
385
|
-
const value =
|
|
386
|
-
isJsonObject(module) && "default" in module ? module.default : module;
|
|
387
|
-
return parseConfig(value, path);
|
|
388
|
-
}
|
package/src/engines.ts
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
type EngineName,
|
|
5
|
-
type EngineOptions,
|
|
6
|
-
engineConfig,
|
|
7
|
-
type QualityConfig,
|
|
8
|
-
} from "./config";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 子プロセスとして起動したengineの生の結果
|
|
12
|
-
*/
|
|
13
|
-
export interface EngineProcessResult {
|
|
14
|
-
exitCode: number | null;
|
|
15
|
-
stderr: string;
|
|
16
|
-
stdout: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* engineを起動して出力を返す関数 テストでは差し替える
|
|
21
|
-
*/
|
|
22
|
-
export type EngineRunner = (
|
|
23
|
-
command: string[],
|
|
24
|
-
options: { cwd: string },
|
|
25
|
-
) => Promise<EngineProcessResult>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* engineごとの実行file名
|
|
29
|
-
*/
|
|
30
|
-
export const ENGINE_BINS: Record<EngineName, string> = {
|
|
31
|
-
biome: "biome",
|
|
32
|
-
"code-style-check": "code-style-check",
|
|
33
|
-
"comment-check": "comment-check",
|
|
34
|
-
"document-style-check": "document-style-check",
|
|
35
|
-
knip: "knip",
|
|
36
|
-
"tsdoc-check": "tsdoc-check",
|
|
37
|
-
typecheck: "tsc",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const WINDOWS_SHIMS = [".exe", ".cmd", ".bat", ""];
|
|
41
|
-
const POSIX_SHIMS = [""];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* コマンドラインから渡された起動条件の上書き
|
|
45
|
-
*/
|
|
46
|
-
export interface RunOverrides {
|
|
47
|
-
ignore: string[];
|
|
48
|
-
targets: string[];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* engineのコマンドを組み立てるための実行条件
|
|
53
|
-
*/
|
|
54
|
-
export interface EngineCommandContext {
|
|
55
|
-
config: QualityConfig;
|
|
56
|
-
/** engine自身の出力へ色を付けるか */
|
|
57
|
-
color: boolean;
|
|
58
|
-
/** 対応するengineへだけ足す上書き */
|
|
59
|
-
overrides: RunOverrides;
|
|
60
|
-
/** comment-checkのbaseline差分を無効化するために渡す未作成のpath */
|
|
61
|
-
rawBaseline: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* engineが受け取らない起動条件と、その設定の持ち主
|
|
66
|
-
*/
|
|
67
|
-
export const ENGINE_LIMITS: Record<
|
|
68
|
-
EngineName,
|
|
69
|
-
Partial<Record<"ignore" | "targets", string>>
|
|
70
|
-
> = {
|
|
71
|
-
biome: { ignore: "biome.json holds its settings" },
|
|
72
|
-
typecheck: {
|
|
73
|
-
ignore: "tsconfig.json holds its settings",
|
|
74
|
-
targets: "tsconfig.json and projects hold its settings",
|
|
75
|
-
},
|
|
76
|
-
knip: {
|
|
77
|
-
ignore: "knip.ts holds its settings",
|
|
78
|
-
targets: "knip analyzes the whole project",
|
|
79
|
-
},
|
|
80
|
-
"code-style-check": {},
|
|
81
|
-
"comment-check": {},
|
|
82
|
-
"document-style-check": {},
|
|
83
|
-
"tsdoc-check": {},
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* engineが受け取らないため渡さなかった起動条件を返す
|
|
88
|
-
*
|
|
89
|
-
* @param name - 受け取れない起動条件を引くengine名
|
|
90
|
-
* @param options - engineへ渡そうとした起動条件
|
|
91
|
-
* @returns 渡さなかった起動条件とその理由の一覧
|
|
92
|
-
*/
|
|
93
|
-
export function skippedEngineOptions(
|
|
94
|
-
name: EngineName,
|
|
95
|
-
options: EngineOptions | undefined,
|
|
96
|
-
): string[] {
|
|
97
|
-
if (options === undefined) {
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
const limits = ENGINE_LIMITS[name];
|
|
101
|
-
const skipped: string[] = [];
|
|
102
|
-
for (const key of ["ignore", "targets"] as const) {
|
|
103
|
-
const reason = limits[key];
|
|
104
|
-
if (reason !== undefined && options[key] !== undefined) {
|
|
105
|
-
skipped.push(`${key} skipped (${reason})`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return skipped;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* 検出をJSONで返すengineか
|
|
113
|
-
*
|
|
114
|
-
* @param name - 判定するengine名
|
|
115
|
-
* @returns 検出をJSONで返すengineならtrue
|
|
116
|
-
*/
|
|
117
|
-
export function isFindingEngine(name: EngineName): boolean {
|
|
118
|
-
return (
|
|
119
|
-
name === "code-style-check" ||
|
|
120
|
-
name === "comment-check" ||
|
|
121
|
-
name === "document-style-check" ||
|
|
122
|
-
name === "tsdoc-check"
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* node_modules/.binとPATHからengineの実行fileを探す
|
|
128
|
-
*
|
|
129
|
-
* @param name - 実行fileを探すengine名
|
|
130
|
-
* @param cwd - 探索を開始する作業ディレクトリのpath
|
|
131
|
-
* @returns 見つけた実行fileのpath PATHにも無ければnull
|
|
132
|
-
*/
|
|
133
|
-
export function resolveExecutable(
|
|
134
|
-
name: EngineName,
|
|
135
|
-
cwd: string,
|
|
136
|
-
): string | null {
|
|
137
|
-
const shims = process.platform === "win32" ? WINDOWS_SHIMS : POSIX_SHIMS;
|
|
138
|
-
const bin = ENGINE_BINS[name];
|
|
139
|
-
let directory = cwd;
|
|
140
|
-
for (;;) {
|
|
141
|
-
for (const shim of shims) {
|
|
142
|
-
const candidate = join(
|
|
143
|
-
directory,
|
|
144
|
-
"node_modules",
|
|
145
|
-
".bin",
|
|
146
|
-
`${bin}${shim}`,
|
|
147
|
-
);
|
|
148
|
-
if (existsSync(candidate)) {
|
|
149
|
-
return candidate;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const parent = dirname(directory);
|
|
153
|
-
if (parent === directory) {
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
directory = parent;
|
|
157
|
-
}
|
|
158
|
-
const onPath = Bun.which(bin);
|
|
159
|
-
return onPath ?? null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* engine自身の出力へ色を付けるための引数を返す
|
|
164
|
-
*/
|
|
165
|
-
function colorArguments(name: EngineName, color: boolean): string[] {
|
|
166
|
-
if (!color) {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
if (name === "biome") {
|
|
170
|
-
return ["--colors=force"];
|
|
171
|
-
}
|
|
172
|
-
if (name === "typecheck") {
|
|
173
|
-
return ["--pretty"];
|
|
174
|
-
}
|
|
175
|
-
return [];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 型検査のコマンドを組み立てる projectを渡すとそのtsconfigを読む
|
|
180
|
-
*/
|
|
181
|
-
function buildTypecheckCommand(
|
|
182
|
-
executable: string,
|
|
183
|
-
context: EngineCommandContext,
|
|
184
|
-
project: string | undefined,
|
|
185
|
-
): string[] {
|
|
186
|
-
const options = engineConfig(context.config, "typecheck") ?? {};
|
|
187
|
-
return [
|
|
188
|
-
executable,
|
|
189
|
-
"--noEmit",
|
|
190
|
-
...(project === undefined ? [] : ["--project", project]),
|
|
191
|
-
...colorArguments("typecheck", context.color),
|
|
192
|
-
...(options.args ?? []),
|
|
193
|
-
];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* opt-in ruleを有効にするコマンド引数へ展開する
|
|
198
|
-
*/
|
|
199
|
-
function enableArguments(
|
|
200
|
-
options: { enable?: string[] } | null | undefined,
|
|
201
|
-
): string[] {
|
|
202
|
-
return (options?.enable ?? []).flatMap((rule) => ["--enable", rule]);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* engineへ渡すコマンドを組み立てる
|
|
207
|
-
*
|
|
208
|
-
* @param name - コマンドを組み立てるengine名
|
|
209
|
-
* @param executable - 起動するengineの実行fileのpath
|
|
210
|
-
* @param context - 設定と上書きを持つ実行条件
|
|
211
|
-
* @returns engineへ渡す引数を並べたコマンド
|
|
212
|
-
*/
|
|
213
|
-
export function buildEngineCommand(
|
|
214
|
-
name: EngineName,
|
|
215
|
-
executable: string,
|
|
216
|
-
context: EngineCommandContext,
|
|
217
|
-
): string[] {
|
|
218
|
-
const options: EngineOptions = engineConfig(context.config, name) ?? {};
|
|
219
|
-
const limits = ENGINE_LIMITS[name];
|
|
220
|
-
const extra = options.args ?? [];
|
|
221
|
-
const ignores =
|
|
222
|
-
limits.ignore === undefined
|
|
223
|
-
? [...(options.ignore ?? []), ...context.overrides.ignore]
|
|
224
|
-
: [];
|
|
225
|
-
const requested =
|
|
226
|
-
context.overrides.targets.length > 0
|
|
227
|
-
? context.overrides.targets
|
|
228
|
-
: (options.targets ?? ["."]);
|
|
229
|
-
const targets = limits.targets === undefined ? requested : [];
|
|
230
|
-
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
|
-
if (name === "biome") {
|
|
234
|
-
return [
|
|
235
|
-
executable,
|
|
236
|
-
"check",
|
|
237
|
-
...colorArguments(name, context.color),
|
|
238
|
-
...targets,
|
|
239
|
-
...extra,
|
|
240
|
-
];
|
|
241
|
-
}
|
|
242
|
-
if (name === "typecheck") {
|
|
243
|
-
return buildTypecheckCommand(executable, context, undefined);
|
|
244
|
-
}
|
|
245
|
-
if (name === "knip") {
|
|
246
|
-
return [executable, ...extra];
|
|
247
|
-
}
|
|
248
|
-
if (name === "comment-check") {
|
|
249
|
-
return [
|
|
250
|
-
executable,
|
|
251
|
-
"--json",
|
|
252
|
-
"--baseline",
|
|
253
|
-
context.rawBaseline,
|
|
254
|
-
...enableArguments(engineConfig(context.config, "comment-check")),
|
|
255
|
-
...targets,
|
|
256
|
-
...ignoreArguments,
|
|
257
|
-
...extra,
|
|
258
|
-
];
|
|
259
|
-
}
|
|
260
|
-
if (name === "document-style-check") {
|
|
261
|
-
return [
|
|
262
|
-
executable,
|
|
263
|
-
"lint",
|
|
264
|
-
"--json",
|
|
265
|
-
...enableArguments(engineConfig(context.config, "document-style-check")),
|
|
266
|
-
...targets,
|
|
267
|
-
...ignoreArguments,
|
|
268
|
-
...extra,
|
|
269
|
-
];
|
|
270
|
-
}
|
|
271
|
-
if (name === "code-style-check") {
|
|
272
|
-
return [executable, "--json", ...targets, ...ignoreArguments, ...extra];
|
|
273
|
-
}
|
|
274
|
-
return [
|
|
275
|
-
executable,
|
|
276
|
-
"--json",
|
|
277
|
-
...enableArguments(engineConfig(context.config, "tsdoc-check")),
|
|
278
|
-
...errorArguments,
|
|
279
|
-
...targets,
|
|
280
|
-
...ignoreArguments,
|
|
281
|
-
...extra,
|
|
282
|
-
];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* engineの起動コマンドを順番に返す 型検査だけはprojectsごとに起動する
|
|
287
|
-
*
|
|
288
|
-
* @param name - コマンドを組み立てるengine名
|
|
289
|
-
* @param executable - 起動するengineの実行fileのpath
|
|
290
|
-
* @param context - 設定と上書きを持つ実行条件
|
|
291
|
-
* @returns 起動する順に並べたコマンドの配列
|
|
292
|
-
*/
|
|
293
|
-
export function buildEngineCommands(
|
|
294
|
-
name: EngineName,
|
|
295
|
-
executable: string,
|
|
296
|
-
context: EngineCommandContext,
|
|
297
|
-
): string[][] {
|
|
298
|
-
const projects =
|
|
299
|
-
name === "typecheck"
|
|
300
|
-
? (engineConfig(context.config, "typecheck")?.projects ?? [])
|
|
301
|
-
: [];
|
|
302
|
-
if (projects.length === 0) {
|
|
303
|
-
return [buildEngineCommand(name, executable, context)];
|
|
304
|
-
}
|
|
305
|
-
return projects.map((project) =>
|
|
306
|
-
buildTypecheckCommand(executable, context, project),
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Bun.spawnでengineを起動する既定のrunner
|
|
312
|
-
*
|
|
313
|
-
* @param command - 実行fileと引数を並べたコマンド
|
|
314
|
-
* @param options - engineを起動する作業ディレクトリを持つ条件
|
|
315
|
-
* @returns 終了codeと標準出力と標準エラーを持つ結果
|
|
316
|
-
*/
|
|
317
|
-
export async function runEngineProcess(
|
|
318
|
-
command: string[],
|
|
319
|
-
options: { cwd: string },
|
|
320
|
-
): Promise<EngineProcessResult> {
|
|
321
|
-
const child = Bun.spawn({
|
|
322
|
-
cmd: command,
|
|
323
|
-
cwd: options.cwd,
|
|
324
|
-
stderr: "pipe",
|
|
325
|
-
stdout: "pipe",
|
|
326
|
-
});
|
|
327
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
328
|
-
new Response(child.stdout).text(),
|
|
329
|
-
new Response(child.stderr).text(),
|
|
330
|
-
child.exited,
|
|
331
|
-
]);
|
|
332
|
-
return { exitCode, stderr, stdout };
|
|
333
|
-
}
|