@yuu1111/quality-check 0.4.0 → 0.5.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 +5 -1
- package/README.md +7 -1
- package/package.json +1 -1
- package/src/cli.ts +10 -3
- package/src/color.ts +47 -0
- package/src/engines.ts +31 -2
- package/src/report.ts +35 -17
- package/src/run.ts +3 -0
package/README.ja.md
CHANGED
|
@@ -35,7 +35,7 @@ export default defineConfig({
|
|
|
35
35
|
"tsdoc-check": true,
|
|
36
36
|
},
|
|
37
37
|
config: {
|
|
38
|
-
"comment-check": { ignore: ["
|
|
38
|
+
"comment-check": { ignore: ["another-project"] },
|
|
39
39
|
"tsdoc-check": { error: ["missing-doc"] },
|
|
40
40
|
},
|
|
41
41
|
});
|
|
@@ -96,6 +96,10 @@ engineは `biome` → `typecheck` → `knip` → `comment-check` → `document-s
|
|
|
96
96
|
|
|
97
97
|
終了codeは0が全engine成功、1が失敗したengineあり、2が設定またはengine起動の失敗
|
|
98
98
|
|
|
99
|
+
色は標準出力が端末のときだけ付ける `NO_COLOR` で無効にし、`FORCE_COLOR` で強制できる `--json` の出力には付けない
|
|
100
|
+
|
|
101
|
+
色を扱えるengineへは自身の出力の色も許可する Biomeは `--colors=force`、tscは `--pretty` を受け取る
|
|
102
|
+
|
|
99
103
|
engineが受け取らない条件を書いた場合は渡さず、その旨をそのengineのsectionへ出す
|
|
100
104
|
|
|
101
105
|
```text
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ export default defineConfig({
|
|
|
38
38
|
"tsdoc-check": true,
|
|
39
39
|
},
|
|
40
40
|
config: {
|
|
41
|
-
"comment-check": { ignore: ["
|
|
41
|
+
"comment-check": { ignore: ["another-project"] },
|
|
42
42
|
"tsdoc-check": { error: ["missing-doc"] },
|
|
43
43
|
},
|
|
44
44
|
});
|
|
@@ -104,6 +104,12 @@ express and for the case where an engine changes its arguments.
|
|
|
104
104
|
Exit code 0 means every engine passed, 1 that at least one failed, and 2 that the
|
|
105
105
|
configuration or an engine could not start.
|
|
106
106
|
|
|
107
|
+
Color is added only when stdout is a terminal. `NO_COLOR` turns it off and
|
|
108
|
+
`FORCE_COLOR` turns it on; the `--json` output stays plain.
|
|
109
|
+
|
|
110
|
+
The engines that can color their own output receive the same permission: Biome
|
|
111
|
+
gets `--colors=force` and tsc gets `--pretty`.
|
|
112
|
+
|
|
107
113
|
A condition that an engine does not take is not passed on, and the section says
|
|
108
114
|
so:
|
|
109
115
|
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { createBaseline, readBaseline, writeBaseline } from "./baseline";
|
|
5
|
+
import { ansiPainter, colorEnabled, plainPainter } from "./color";
|
|
5
6
|
import {
|
|
6
7
|
DEFAULT_BASELINE_FILE,
|
|
7
8
|
findConfigFile,
|
|
@@ -125,6 +126,8 @@ async function main(argv: string[]): Promise<number> {
|
|
|
125
126
|
return 0;
|
|
126
127
|
}
|
|
127
128
|
const options = parseArguments(argv);
|
|
129
|
+
const color = colorEnabled(process.stdout, process.env);
|
|
130
|
+
const paint = color ? ansiPainter() : plainPainter;
|
|
128
131
|
const cwd = process.cwd();
|
|
129
132
|
const configPath = resolveConfigPath(options, cwd);
|
|
130
133
|
if (configPath === null) {
|
|
@@ -138,6 +141,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
138
141
|
options.update || baselinePath === null
|
|
139
142
|
? null
|
|
140
143
|
: readBaseline(baselinePath),
|
|
144
|
+
color,
|
|
141
145
|
config,
|
|
142
146
|
cwd,
|
|
143
147
|
overrides: { ignore: options.ignores, targets: options.targets },
|
|
@@ -153,7 +157,10 @@ async function main(argv: string[]): Promise<number> {
|
|
|
153
157
|
);
|
|
154
158
|
writeBaseline(baselinePath, baseline);
|
|
155
159
|
console.log(
|
|
156
|
-
|
|
160
|
+
paint(
|
|
161
|
+
`Recorded ${baseline.entries.length} entries in ${baselinePath}`,
|
|
162
|
+
"pass",
|
|
163
|
+
),
|
|
157
164
|
);
|
|
158
165
|
return 0;
|
|
159
166
|
}
|
|
@@ -161,9 +168,9 @@ async function main(argv: string[]): Promise<number> {
|
|
|
161
168
|
console.log(JSON.stringify(toJsonReport(results), null, "\t"));
|
|
162
169
|
} else {
|
|
163
170
|
for (const result of results) {
|
|
164
|
-
console.log(formatEngineSection(result));
|
|
171
|
+
console.log(formatEngineSection(result, paint));
|
|
165
172
|
}
|
|
166
|
-
console.log(formatSummary(results));
|
|
173
|
+
console.log(formatSummary(results, paint));
|
|
167
174
|
}
|
|
168
175
|
return results.some((result) => result.status !== "passed") ? 1 : 0;
|
|
169
176
|
}
|
package/src/color.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 出力行へ割り当てる装飾の種類
|
|
3
|
+
*/
|
|
4
|
+
export type Tone = "error" | "header" | "muted" | "pass" | "warn";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* toneに応じて文字列を装飾する関数
|
|
8
|
+
*/
|
|
9
|
+
export type Painter = (text: string, tone: Tone) => string;
|
|
10
|
+
|
|
11
|
+
const ANSI_CODES: Record<Tone, string> = {
|
|
12
|
+
error: "31",
|
|
13
|
+
header: "36",
|
|
14
|
+
muted: "2",
|
|
15
|
+
pass: "32",
|
|
16
|
+
warn: "33",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 装飾を付けずにそのまま返すpainter
|
|
21
|
+
*/
|
|
22
|
+
export function plainPainter(text: string, _tone: Tone): string {
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* ANSI escapeでtoneを色へ割り当てるpainterを作る
|
|
28
|
+
*/
|
|
29
|
+
export function ansiPainter(): Painter {
|
|
30
|
+
return (text, tone) => `\u001b[${ANSI_CODES[tone]}m${text}\u001b[0m`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 出力先と環境変数から装飾の可否を決める
|
|
35
|
+
*/
|
|
36
|
+
export function colorEnabled(
|
|
37
|
+
stream: { isTTY?: boolean | undefined },
|
|
38
|
+
env: Record<string, string | undefined>,
|
|
39
|
+
): boolean {
|
|
40
|
+
if (env.NO_COLOR !== undefined && env.NO_COLOR !== "") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== "") {
|
|
44
|
+
return env.FORCE_COLOR !== "0";
|
|
45
|
+
}
|
|
46
|
+
return stream.isTTY === true;
|
|
47
|
+
}
|
package/src/engines.ts
CHANGED
|
@@ -52,6 +52,8 @@ export interface RunOverrides {
|
|
|
52
52
|
*/
|
|
53
53
|
export interface EngineCommandContext {
|
|
54
54
|
config: QualityConfig;
|
|
55
|
+
/** engine自身の出力へ色を付けるか */
|
|
56
|
+
color: boolean;
|
|
55
57
|
/** 対応するengineへだけ足す上書き */
|
|
56
58
|
overrides: RunOverrides;
|
|
57
59
|
/** comment-checkのbaseline差分を無効化するために渡す未作成のpath */
|
|
@@ -143,6 +145,22 @@ export function resolveExecutable(
|
|
|
143
145
|
return onPath ?? null;
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* engine自身の出力へ色を付けるための引数を返す
|
|
150
|
+
*/
|
|
151
|
+
function colorArguments(name: EngineName, color: boolean): string[] {
|
|
152
|
+
if (!color) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
if (name === "biome") {
|
|
156
|
+
return ["--colors=force"];
|
|
157
|
+
}
|
|
158
|
+
if (name === "typecheck") {
|
|
159
|
+
return ["--pretty"];
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
146
164
|
/**
|
|
147
165
|
* engineへ渡すコマンドを組み立てる
|
|
148
166
|
*/
|
|
@@ -167,10 +185,21 @@ export function buildEngineCommand(
|
|
|
167
185
|
const rules = engineConfig(context.config, "tsdoc-check")?.error ?? [];
|
|
168
186
|
const errorArguments = rules.flatMap((rule) => ["--error", rule]);
|
|
169
187
|
if (name === "biome") {
|
|
170
|
-
return [
|
|
188
|
+
return [
|
|
189
|
+
executable,
|
|
190
|
+
"check",
|
|
191
|
+
...colorArguments(name, context.color),
|
|
192
|
+
...targets,
|
|
193
|
+
...extra,
|
|
194
|
+
];
|
|
171
195
|
}
|
|
172
196
|
if (name === "typecheck") {
|
|
173
|
-
return [
|
|
197
|
+
return [
|
|
198
|
+
executable,
|
|
199
|
+
"--noEmit",
|
|
200
|
+
...colorArguments(name, context.color),
|
|
201
|
+
...extra,
|
|
202
|
+
];
|
|
174
203
|
}
|
|
175
204
|
if (name === "knip") {
|
|
176
205
|
return [executable, ...extra];
|
package/src/report.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { type Painter, plainPainter } from "./color";
|
|
1
2
|
import { isFindingEngine } from "./engines";
|
|
2
3
|
import type { NormalizedFinding } from "./findings";
|
|
3
4
|
import type { EngineResult } from "./run";
|
|
4
5
|
|
|
5
|
-
function describeFinding(
|
|
6
|
-
|
|
6
|
+
function describeFinding(
|
|
7
|
+
finding: NormalizedFinding,
|
|
8
|
+
paint: Painter,
|
|
9
|
+
tone: "error" | "warn",
|
|
10
|
+
): string {
|
|
11
|
+
return `${finding.file}:${finding.line}:${finding.column} ${finding.rule} ${paint(finding.severity, tone)} ${finding.text}`;
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
function describeCounts(result: EngineResult): string {
|
|
@@ -13,38 +18,48 @@ function describeCounts(result: EngineResult): string {
|
|
|
13
18
|
return `${result.reported.length} new, ${result.resolved} resolved, ${result.warnings.length} warnings`;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
function describeStatus(result: EngineResult): string {
|
|
21
|
+
function describeStatus(result: EngineResult, paint: Painter): string {
|
|
17
22
|
if (result.status === "error") {
|
|
18
|
-
return "error";
|
|
23
|
+
return paint("error", "error");
|
|
19
24
|
}
|
|
20
|
-
|
|
25
|
+
const tone = result.status === "passed" ? "pass" : "error";
|
|
26
|
+
return `${paint(result.status, tone)} (${describeCounts(result)})`;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* engine1つ分の出力sectionを組み立てる
|
|
25
31
|
*/
|
|
26
|
-
export function formatEngineSection(
|
|
27
|
-
|
|
32
|
+
export function formatEngineSection(
|
|
33
|
+
result: EngineResult,
|
|
34
|
+
paint: Painter = plainPainter,
|
|
35
|
+
): string {
|
|
36
|
+
const lines = [paint(`== ${result.name} ==`, "header")];
|
|
28
37
|
for (const skipped of result.skipped) {
|
|
29
|
-
lines.push(`${result.name}: ${skipped}
|
|
38
|
+
lines.push(paint(`${result.name}: ${skipped}`, "muted"));
|
|
30
39
|
}
|
|
31
40
|
if (result.output !== "" && !isFindingEngine(result.name)) {
|
|
32
41
|
lines.push(result.output);
|
|
33
42
|
}
|
|
34
|
-
for (const finding of
|
|
35
|
-
lines.push(describeFinding(finding));
|
|
43
|
+
for (const finding of result.reported) {
|
|
44
|
+
lines.push(describeFinding(finding, paint, "error"));
|
|
45
|
+
}
|
|
46
|
+
for (const finding of result.warnings) {
|
|
47
|
+
lines.push(describeFinding(finding, paint, "warn"));
|
|
36
48
|
}
|
|
37
49
|
if (result.message !== undefined) {
|
|
38
|
-
lines.push(result.message);
|
|
50
|
+
lines.push(paint(result.message, "error"));
|
|
39
51
|
}
|
|
40
|
-
lines.push(`${result.name}: ${describeStatus(result)}`);
|
|
52
|
+
lines.push(`${result.name}: ${describeStatus(result, paint)}`);
|
|
41
53
|
return lines.join("\n");
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
/**
|
|
45
57
|
* 失敗したengineを列挙した集約summaryを組み立てる
|
|
46
58
|
*/
|
|
47
|
-
export function formatSummary(
|
|
59
|
+
export function formatSummary(
|
|
60
|
+
results: EngineResult[],
|
|
61
|
+
paint: Painter = plainPainter,
|
|
62
|
+
): string {
|
|
48
63
|
const failed = results
|
|
49
64
|
.filter((result) => result.status !== "passed")
|
|
50
65
|
.map((result) => result.name);
|
|
@@ -52,14 +67,17 @@ export function formatSummary(results: EngineResult[]): string {
|
|
|
52
67
|
.filter((result) => result.status === "passed")
|
|
53
68
|
.map((result) => result.name);
|
|
54
69
|
if (failed.length === 0) {
|
|
55
|
-
return `quality-check: ${results.length} engines passed
|
|
70
|
+
return paint(`quality-check: ${results.length} engines passed`, "pass");
|
|
56
71
|
}
|
|
57
72
|
const lines = [
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
paint(
|
|
74
|
+
`quality-check: ${failed.length} of ${results.length} engines failed`,
|
|
75
|
+
"error",
|
|
76
|
+
),
|
|
77
|
+
` ${paint("failed:", "error")} ${failed.join(", ")}`,
|
|
60
78
|
];
|
|
61
79
|
if (passed.length > 0) {
|
|
62
|
-
lines.push(` passed: ${passed.join(", ")}`);
|
|
80
|
+
lines.push(` ${paint("passed:", "pass")} ${passed.join(", ")}`);
|
|
63
81
|
}
|
|
64
82
|
return lines.join("\n");
|
|
65
83
|
}
|
package/src/run.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface EngineResult {
|
|
|
44
44
|
export interface RunOptions {
|
|
45
45
|
/** 適用するbaseline nullなら差分判定を行わない */
|
|
46
46
|
baseline: BaselineFile | null;
|
|
47
|
+
/** engine自身の出力へ色を付けるか */
|
|
48
|
+
color: boolean;
|
|
47
49
|
config: QualityConfig;
|
|
48
50
|
cwd: string;
|
|
49
51
|
/** コマンドラインから渡された起動条件の上書き */
|
|
@@ -188,6 +190,7 @@ async function runEngine(
|
|
|
188
190
|
export async function runEngines(options: RunOptions): Promise<EngineResult[]> {
|
|
189
191
|
const runner = options.runner ?? runEngineProcess;
|
|
190
192
|
const context: EngineCommandContext = {
|
|
193
|
+
color: options.color,
|
|
191
194
|
config: options.config,
|
|
192
195
|
overrides: options.overrides,
|
|
193
196
|
rawBaseline: options.rawBaseline,
|