qfai 0.4.2 → 0.4.4
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.md +14 -0
- package/assets/init/.qfai/README.md +1 -0
- package/assets/init/.qfai/contracts/README.md +17 -8
- package/assets/init/.qfai/contracts/api/api-0001-sample.yaml +3 -2
- package/assets/init/.qfai/contracts/db/db-0001-sample.sql +2 -1
- package/assets/init/.qfai/contracts/ui/ui-0001-sample.yaml +3 -1
- package/assets/init/.qfai/promptpack/modes/change.md +3 -2
- package/assets/init/.qfai/promptpack/modes/compatibility.md +2 -0
- package/assets/init/.qfai/prompts/require-to-spec.md +4 -2
- package/assets/init/.qfai/specs/README.md +9 -2
- package/assets/init/.qfai/specs/spec-0001/spec.md +2 -0
- package/assets/init/root/qfai.config.yaml +0 -1
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +30 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/report.d.ts +7 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +80 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +57 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.cjs +504 -328
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +504 -328
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib/args.d.ts +18 -0
- package/dist/cli/lib/args.d.ts.map +1 -0
- package/dist/cli/lib/args.js +98 -0
- package/dist/cli/lib/args.js.map +1 -0
- package/dist/cli/lib/assets.d.ts +2 -0
- package/dist/cli/lib/assets.d.ts.map +1 -0
- package/dist/cli/lib/assets.js +24 -0
- package/dist/cli/lib/assets.js.map +1 -0
- package/dist/cli/lib/failOn.d.ts +5 -0
- package/dist/cli/lib/failOn.d.ts.map +1 -0
- package/dist/cli/lib/failOn.js +10 -0
- package/dist/cli/lib/failOn.js.map +1 -0
- package/dist/cli/lib/fs.d.ts +11 -0
- package/dist/cli/lib/fs.d.ts.map +1 -0
- package/dist/cli/lib/fs.js +91 -0
- package/dist/cli/lib/fs.js.map +1 -0
- package/dist/cli/lib/logger.d.ts +4 -0
- package/dist/cli/lib/logger.d.ts.map +1 -0
- package/dist/cli/lib/logger.js +10 -0
- package/dist/cli/lib/logger.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +66 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/core/config.d.ts +47 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +224 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contractIndex.d.ts +12 -0
- package/dist/core/contractIndex.d.ts.map +1 -0
- package/dist/core/contractIndex.js +38 -0
- package/dist/core/contractIndex.js.map +1 -0
- package/dist/core/contracts.d.ts +5 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +42 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/contractsDecl.d.ts +3 -0
- package/dist/core/contractsDecl.d.ts.map +1 -0
- package/dist/core/contractsDecl.js +19 -0
- package/dist/core/contractsDecl.js.map +1 -0
- package/dist/core/discovery.d.ts +14 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +55 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/fs.d.ts +11 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +68 -0
- package/dist/core/fs.js.map +1 -0
- package/dist/core/gherkin/parse.d.ts +7 -0
- package/dist/core/gherkin/parse.d.ts.map +1 -0
- package/dist/core/gherkin/parse.js +25 -0
- package/dist/core/gherkin/parse.js.map +1 -0
- package/dist/core/ids.d.ts +6 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +52 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parse/adr.d.ts +13 -0
- package/dist/core/parse/adr.d.ts.map +1 -0
- package/dist/core/parse/adr.js +33 -0
- package/dist/core/parse/adr.js.map +1 -0
- package/dist/core/parse/gherkin.d.ts +12 -0
- package/dist/core/parse/gherkin.d.ts.map +1 -0
- package/dist/core/parse/gherkin.js +22 -0
- package/dist/core/parse/gherkin.js.map +1 -0
- package/dist/core/parse/markdown.d.ts +14 -0
- package/dist/core/parse/markdown.d.ts.map +1 -0
- package/dist/core/parse/markdown.js +45 -0
- package/dist/core/parse/markdown.js.map +1 -0
- package/dist/core/parse/spec.d.ts +36 -0
- package/dist/core/parse/spec.d.ts.map +1 -0
- package/dist/core/parse/spec.js +123 -0
- package/dist/core/parse/spec.js.map +1 -0
- package/dist/core/report.d.ts +55 -0
- package/dist/core/report.d.ts.map +1 -0
- package/dist/core/report.js +393 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/scenarioModel.d.ts +33 -0
- package/dist/core/scenarioModel.d.ts.map +1 -0
- package/dist/core/scenarioModel.js +128 -0
- package/dist/core/scenarioModel.js.map +1 -0
- package/dist/core/specLayout.d.ts +8 -0
- package/dist/core/specLayout.d.ts.map +1 -0
- package/dist/core/specLayout.js +36 -0
- package/dist/core/specLayout.js.map +1 -0
- package/dist/core/traceability.d.ts +26 -0
- package/dist/core/traceability.d.ts.map +1 -0
- package/dist/core/traceability.js +157 -0
- package/dist/core/traceability.js.map +1 -0
- package/dist/core/types.d.ts +31 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validate.d.ts +4 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +45 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/core/validators/contracts.d.ts +5 -0
- package/dist/core/validators/contracts.d.ts.map +1 -0
- package/dist/core/validators/contracts.js +189 -0
- package/dist/core/validators/contracts.js.map +1 -0
- package/dist/core/validators/delta.d.ts +4 -0
- package/dist/core/validators/delta.d.ts.map +1 -0
- package/dist/core/validators/delta.js +68 -0
- package/dist/core/validators/delta.js.map +1 -0
- package/dist/core/validators/ids.d.ts +4 -0
- package/dist/core/validators/ids.d.ts.map +1 -0
- package/dist/core/validators/ids.js +88 -0
- package/dist/core/validators/ids.js.map +1 -0
- package/dist/core/validators/scenario.d.ts +5 -0
- package/dist/core/validators/scenario.d.ts.map +1 -0
- package/dist/core/validators/scenario.js +127 -0
- package/dist/core/validators/scenario.js.map +1 -0
- package/dist/core/validators/spec.d.ts +5 -0
- package/dist/core/validators/spec.d.ts.map +1 -0
- package/dist/core/validators/spec.js +94 -0
- package/dist/core/validators/spec.js.map +1 -0
- package/dist/core/validators/traceability.d.ts +4 -0
- package/dist/core/validators/traceability.d.ts.map +1 -0
- package/dist/core/validators/traceability.js +222 -0
- package/dist/core/validators/traceability.js.map +1 -0
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +25 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.cjs +504 -328
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -4
- package/dist/index.d.ts +2 -156
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +504 -328
- package/dist/index.mjs.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,20 @@ npx qfai report
|
|
|
32
32
|
設定はリポジトリ直下の `qfai.config.yaml` で行います。
|
|
33
33
|
命名規約は `docs/rules/naming.md` を参照してください。
|
|
34
34
|
|
|
35
|
+
## Contracts
|
|
36
|
+
|
|
37
|
+
Spec では `QFAI-CONTRACT-REF:` 行で参照する契約IDを宣言します(`none` 可、宣言行は必須)。
|
|
38
|
+
契約ファイルは `QFAI-CONTRACT-ID: <ID>` を **1ファイル1ID** で宣言します。
|
|
39
|
+
Contract ID prefix は `UI-0001` / `API-0001` / `DB-0001` です。
|
|
40
|
+
宣言では大文字(`UI-0001`)、ファイル名は小文字(`ui-0001-...`)を使用します。
|
|
41
|
+
|
|
42
|
+
契約関連の検証は `validation.traceability` で制御します。
|
|
43
|
+
|
|
44
|
+
- `validation.traceability.allowOrphanContracts`: Spec から参照されない契約の許可(default: `false`)
|
|
45
|
+
- `validation.traceability.unknownContractIdSeverity`: Spec が参照した契約 ID が存在しない場合の severity(default: `error`、`error` / `warning` のみ)
|
|
46
|
+
|
|
47
|
+
`npx qfai init` は `.qfai/contracts/` 配下に UI/API/DB のサンプルを生成します。
|
|
48
|
+
|
|
35
49
|
SC→Test の参照はテストコード内の `QFAI:SC-xxxx` アノテーションで宣言します。
|
|
36
50
|
SC→Test の対象ファイルは `validation.traceability.testFileGlobs` で指定します。
|
|
37
51
|
除外は `validation.traceability.testFileExcludeGlobs` で指定できます。
|
|
@@ -6,11 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
- UI: `ui/ui-0001-<slug>.yaml` または `.yml`
|
|
8
8
|
- API: `api/api-0001-<slug>.yaml` / `.yml` / `.json`(OpenAPI)
|
|
9
|
-
- DB: `db/db-0001-<slug>.sql`(ID は `
|
|
9
|
+
- DB: `db/db-0001-<slug>.sql`(ID は `DB-xxxx`)
|
|
10
|
+
|
|
11
|
+
## 契約ID宣言(必須)
|
|
12
|
+
|
|
13
|
+
- 1ファイル = 1ID
|
|
14
|
+
- ファイル内に `QFAI-CONTRACT-ID: <ID>` をコメント行で宣言する
|
|
15
|
+
- 例: `# QFAI-CONTRACT-ID: API-0001` / `// QFAI-CONTRACT-ID: UI-0001` / `-- QFAI-CONTRACT-ID: DB-0001`
|
|
10
16
|
|
|
11
17
|
## 最小例(UI)
|
|
12
18
|
|
|
13
19
|
```yaml
|
|
20
|
+
# QFAI-CONTRACT-ID: UI-0001
|
|
14
21
|
id: UI-0001
|
|
15
22
|
name: 受注登録画面
|
|
16
23
|
refs:
|
|
@@ -20,6 +27,7 @@ refs:
|
|
|
20
27
|
## 最小例(API)
|
|
21
28
|
|
|
22
29
|
```yaml
|
|
30
|
+
# QFAI-CONTRACT-ID: API-0001
|
|
23
31
|
openapi: 3.0.0
|
|
24
32
|
info:
|
|
25
33
|
title: Sample API
|
|
@@ -36,7 +44,7 @@ paths:
|
|
|
36
44
|
## 最小例(DB)
|
|
37
45
|
|
|
38
46
|
```sql
|
|
39
|
-
--
|
|
47
|
+
-- QFAI-CONTRACT-ID: DB-0001
|
|
40
48
|
CREATE TABLE sample_table (
|
|
41
49
|
id INTEGER PRIMARY KEY
|
|
42
50
|
);
|
|
@@ -44,16 +52,17 @@ CREATE TABLE sample_table (
|
|
|
44
52
|
|
|
45
53
|
## CI でチェックされること(抜粋)
|
|
46
54
|
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
- API:
|
|
55
|
+
- 契約宣言: `QFAI-CONTRACT-ID` の未記載/複数宣言/重複IDは error
|
|
56
|
+
- 契約ID: 配置ディレクトリ(ui/api/db)と prefix(UI/API/DB)の不整合は error
|
|
57
|
+
- UI/API: パース失敗は error
|
|
58
|
+
- API: OpenAPI 定義があること(`openapi`)
|
|
50
59
|
- DB: 危険 SQL(DROP/TRUNCATE 等)の警告
|
|
51
|
-
- 共通: ID 形式(`PREFIX-0001
|
|
60
|
+
- 共通: ID 形式(`PREFIX-0001`)、ID の重複検知
|
|
52
61
|
|
|
53
62
|
## 依存関係
|
|
54
63
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
64
|
+
- Spec → Contracts(`QFAI-CONTRACT-REF` で宣言、`none` 可)
|
|
65
|
+
- Scenario → Contracts(UI/API/DB の参照は任意)
|
|
57
66
|
|
|
58
67
|
## 良い例 / 悪い例
|
|
59
68
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
# QFAI-CONTRACT-ID: API-0001
|
|
1
2
|
# OpenAPI contract sample (API-0001)
|
|
3
|
+
# - 必須: QFAI-CONTRACT-ID (1ファイル1ID)
|
|
2
4
|
# - 必須: openapi, info, paths
|
|
3
|
-
# - 重要: operationId に API-0001 のような ID を含める(validate が参照整合に利用)
|
|
4
5
|
openapi: 3.0.0
|
|
5
6
|
info:
|
|
6
7
|
title: QFAI Sample API
|
|
@@ -8,7 +9,7 @@ info:
|
|
|
8
9
|
paths:
|
|
9
10
|
/health:
|
|
10
11
|
get:
|
|
11
|
-
operationId:
|
|
12
|
+
operationId: healthCheck
|
|
12
13
|
responses:
|
|
13
14
|
"200":
|
|
14
15
|
description: OK
|
|
@@ -19,12 +19,14 @@
|
|
|
19
19
|
## Rules
|
|
20
20
|
|
|
21
21
|
- **推測しない**。要件に書かれていないことは `TBD` と明記する。
|
|
22
|
-
- ID は `PREFIX-0001` 形式(SPEC/BR/SC/UI/API/
|
|
22
|
+
- ID は `PREFIX-0001` 形式(SPEC/BR/SC/UI/API/DB)。
|
|
23
23
|
- `spec.md` の必須セクションは `qfai.config.yaml` の設定に従う。
|
|
24
24
|
- BR は `## 業務ルール` にのみ定義し、`- [BR-0001][P1] ...` 形式で書く。
|
|
25
|
+
- `spec.md` に `QFAI-CONTRACT-REF:` を必ず記載する(不要なら `none`)。
|
|
25
26
|
- `scenario.md` は Gherkin で書き、Feature に `@SPEC-xxxx` を付与する。
|
|
26
27
|
- 各 Scenario は `@SC-xxxx` を **ちょうど1つ**、`@BR-xxxx` を **1つ以上**持つこと。
|
|
27
|
-
-
|
|
28
|
+
- 契約ファイルには `QFAI-CONTRACT-ID: <ID>` を宣言する。
|
|
29
|
+
- 契約 ID(UI/API/DB)を Scenario で参照する場合はタグまたは本文に明示する。
|
|
28
30
|
- `delta.md` の「変更区分」は **Compatibility / Change/Improvement のいずれか1つにチェック**する。
|
|
29
31
|
- 判断できない場合は `Compatibility` を選び、`TBD` を理由欄に記載する。
|
|
30
32
|
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
```md
|
|
16
16
|
# SPEC-0001: 注文登録の最小要件
|
|
17
17
|
|
|
18
|
+
QFAI-CONTRACT-REF: UI-0001, API-0001, DB-0001
|
|
19
|
+
|
|
18
20
|
## 背景
|
|
19
21
|
|
|
20
22
|
- 例: 受注の登録ルールを明文化し、手戻りを減らすため
|
|
@@ -30,6 +32,11 @@
|
|
|
30
32
|
- Priority は **P0/P1/P2/P3** のいずれかを必ず付与
|
|
31
33
|
- BR 定義は **`## 業務ルール` セクション内のみ**(他セクションは参照扱い)
|
|
32
34
|
|
|
35
|
+
### Contract 参照の書き方
|
|
36
|
+
|
|
37
|
+
- `QFAI-CONTRACT-REF:` 行で契約IDを宣言する(複数行可)
|
|
38
|
+
- 参照不要な場合は `QFAI-CONTRACT-REF: none`
|
|
39
|
+
|
|
33
40
|
## Delta(delta.md)
|
|
34
41
|
|
|
35
42
|
- 互換維持 / 仕様変更の **どちらか1つ**に必ずチェックする
|
|
@@ -44,8 +51,8 @@
|
|
|
44
51
|
|
|
45
52
|
## CI でチェックされること(抜粋)
|
|
46
53
|
|
|
47
|
-
- Spec: 必須セクション、SPEC/BR ID、BR Priority、ID 形式、Contract
|
|
54
|
+
- Spec: 必須セクション、SPEC/BR ID、BR Priority、ID 形式、Contract 参照の実在性、Contract 参照の必須宣言
|
|
48
55
|
- Delta: 変更区分(互換/変更)のチェック状態
|
|
49
56
|
- Scenario: Feature/Scenario の存在、タグ要件、Given/When/Then
|
|
50
|
-
- Traceability: BR→SC、SC
|
|
57
|
+
- Traceability: BR→SC、Spec→Contract、SC→Test の接続、BR の所属 SPEC 整合
|
|
51
58
|
- IDs: 定義 ID の重複検知(Spec/Scenario/Contracts)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBjE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { copyTemplateTree } from "../lib/fs.js";
|
|
3
|
+
import { getInitAssetsDir } from "../lib/assets.js";
|
|
4
|
+
import { info } from "../lib/logger.js";
|
|
5
|
+
export async function runInit(options) {
|
|
6
|
+
const assetsRoot = getInitAssetsDir();
|
|
7
|
+
const rootAssets = path.join(assetsRoot, "root");
|
|
8
|
+
const qfaiAssets = path.join(assetsRoot, ".qfai");
|
|
9
|
+
const destRoot = path.resolve(options.dir);
|
|
10
|
+
const destQfai = path.join(destRoot, ".qfai");
|
|
11
|
+
const rootResult = await copyTemplateTree(rootAssets, destRoot, {
|
|
12
|
+
force: options.force,
|
|
13
|
+
dryRun: options.dryRun,
|
|
14
|
+
});
|
|
15
|
+
const qfaiResult = await copyTemplateTree(qfaiAssets, destQfai, {
|
|
16
|
+
force: options.force,
|
|
17
|
+
dryRun: options.dryRun,
|
|
18
|
+
});
|
|
19
|
+
report([...rootResult.copied, ...qfaiResult.copied], [...rootResult.skipped, ...qfaiResult.skipped], options.dryRun, "init");
|
|
20
|
+
}
|
|
21
|
+
function report(copied, skipped, dryRun, label) {
|
|
22
|
+
info(`qfai ${label}: ${dryRun ? "dry-run" : "done"}`);
|
|
23
|
+
if (copied.length > 0) {
|
|
24
|
+
info(` created: ${copied.length}`);
|
|
25
|
+
}
|
|
26
|
+
if (skipped.length > 0) {
|
|
27
|
+
info(` skipped: ${skipped.length}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AASxC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAoB;IAChD,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE9C,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE;QAC9D,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE;QAC9D,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,MAAM,CACJ,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,EAC5C,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,EAC9C,OAAO,CAAC,MAAM,EACd,MAAM,CACP,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CACb,MAAgB,EAChB,OAAiB,EACjB,MAAe,EACf,KAAa;IAEb,IAAI,CAAC,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,cAAc,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,cAAc,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDrE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadConfig, resolvePath } from "../../core/config.js";
|
|
4
|
+
import { createReportData, formatReportJson, formatReportMarkdown, } from "../../core/report.js";
|
|
5
|
+
import { error, info } from "../lib/logger.js";
|
|
6
|
+
export async function runReport(options) {
|
|
7
|
+
const root = path.resolve(options.root);
|
|
8
|
+
const configResult = await loadConfig(root);
|
|
9
|
+
const input = configResult.config.output.validateJsonPath;
|
|
10
|
+
const inputPath = path.isAbsolute(input) ? input : path.resolve(root, input);
|
|
11
|
+
let validation;
|
|
12
|
+
try {
|
|
13
|
+
validation = await readValidationResult(inputPath);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
if (isMissingFileError(err)) {
|
|
17
|
+
error([
|
|
18
|
+
`qfai report: 入力ファイルが見つかりません: ${inputPath}`,
|
|
19
|
+
"",
|
|
20
|
+
"まず qfai validate を実行してください。例:",
|
|
21
|
+
" qfai validate",
|
|
22
|
+
"(デフォルトの出力先: .qfai/out/validate.json)",
|
|
23
|
+
"",
|
|
24
|
+
"GitHub Actions テンプレを使っている場合は、workflow の validate ジョブを先に実行してください。",
|
|
25
|
+
].join("\n"));
|
|
26
|
+
process.exitCode = 2;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
const data = await createReportData(root, validation, configResult);
|
|
32
|
+
const output = options.format === "json"
|
|
33
|
+
? formatReportJson(data)
|
|
34
|
+
: formatReportMarkdown(data);
|
|
35
|
+
const outRoot = resolvePath(root, configResult.config, "outDir");
|
|
36
|
+
const defaultOut = options.format === "json"
|
|
37
|
+
? path.join(outRoot, "report.json")
|
|
38
|
+
: path.join(outRoot, "report.md");
|
|
39
|
+
const out = options.outPath ?? defaultOut;
|
|
40
|
+
const outPath = path.isAbsolute(out) ? out : path.resolve(root, out);
|
|
41
|
+
await mkdir(path.dirname(outPath), { recursive: true });
|
|
42
|
+
await writeFile(outPath, `${output}\n`, "utf-8");
|
|
43
|
+
info(`report: info=${validation.counts.info} warning=${validation.counts.warning} error=${validation.counts.error}`);
|
|
44
|
+
info(`wrote report: ${outPath}`);
|
|
45
|
+
}
|
|
46
|
+
async function readValidationResult(inputPath) {
|
|
47
|
+
const raw = await readFile(inputPath, "utf-8");
|
|
48
|
+
const parsed = JSON.parse(raw);
|
|
49
|
+
if (!isValidationResult(parsed)) {
|
|
50
|
+
throw new Error(`validate.json の形式が不正です: ${inputPath}`);
|
|
51
|
+
}
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
function isValidationResult(value) {
|
|
55
|
+
if (!value || typeof value !== "object") {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const record = value;
|
|
59
|
+
if (typeof record.toolVersion !== "string") {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(record.issues)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const counts = record.counts;
|
|
66
|
+
if (!counts) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return (typeof counts.info === "number" &&
|
|
70
|
+
typeof counts.warning === "number" &&
|
|
71
|
+
typeof counts.error === "number");
|
|
72
|
+
}
|
|
73
|
+
function isMissingFileError(error) {
|
|
74
|
+
if (!error || typeof error !== "object") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const record = error;
|
|
78
|
+
return record.code === "ENOENT";
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FailOn, OutputFormat } from "../../core/config.js";
|
|
2
|
+
export type ValidateOptions = {
|
|
3
|
+
root: string;
|
|
4
|
+
strict: boolean;
|
|
5
|
+
failOn?: FailOn;
|
|
6
|
+
format?: OutputFormat;
|
|
7
|
+
};
|
|
8
|
+
export declare function runValidate(options: ValidateOptions): Promise<number>;
|
|
9
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMjE,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB3E"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadConfig } from "../../core/config.js";
|
|
4
|
+
import { validateProject } from "../../core/validate.js";
|
|
5
|
+
import { shouldFail } from "../lib/failOn.js";
|
|
6
|
+
export async function runValidate(options) {
|
|
7
|
+
const root = path.resolve(options.root);
|
|
8
|
+
const configResult = await loadConfig(root);
|
|
9
|
+
const result = await validateProject(root, configResult);
|
|
10
|
+
const format = options.format ?? "text";
|
|
11
|
+
if (format === "text") {
|
|
12
|
+
emitText(result);
|
|
13
|
+
}
|
|
14
|
+
if (format === "github") {
|
|
15
|
+
result.issues.forEach(emitGitHub);
|
|
16
|
+
}
|
|
17
|
+
await emitJson(result, root, configResult.config.output.validateJsonPath);
|
|
18
|
+
const failOn = resolveFailOn(options, configResult.config.validation.failOn);
|
|
19
|
+
return shouldFail(result, failOn) ? 1 : 0;
|
|
20
|
+
}
|
|
21
|
+
function resolveFailOn(options, fallback) {
|
|
22
|
+
if (options.failOn) {
|
|
23
|
+
return options.failOn;
|
|
24
|
+
}
|
|
25
|
+
if (options.strict) {
|
|
26
|
+
return "warning";
|
|
27
|
+
}
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
function emitText(result) {
|
|
31
|
+
for (const item of result.issues) {
|
|
32
|
+
const location = item.file ? ` (${item.file})` : "";
|
|
33
|
+
const refs = item.refs && item.refs.length > 0 ? ` refs=${item.refs.join(",")}` : "";
|
|
34
|
+
process.stdout.write(`[${item.severity}] ${item.code} ${item.message}${location}${refs}\n`);
|
|
35
|
+
}
|
|
36
|
+
process.stdout.write(`counts: info=${result.counts.info} warning=${result.counts.warning} error=${result.counts.error}\n`);
|
|
37
|
+
}
|
|
38
|
+
function emitGitHub(issue) {
|
|
39
|
+
const level = issue.severity === "error"
|
|
40
|
+
? "error"
|
|
41
|
+
: issue.severity === "warning"
|
|
42
|
+
? "warning"
|
|
43
|
+
: "notice";
|
|
44
|
+
const file = issue.file ? `file=${issue.file}` : "";
|
|
45
|
+
const line = issue.loc?.line ? `,line=${issue.loc.line}` : "";
|
|
46
|
+
const column = issue.loc?.column ? `,col=${issue.loc.column}` : "";
|
|
47
|
+
const location = file ? ` ${file}${line}${column}` : "";
|
|
48
|
+
process.stdout.write(`::${level}${location}::${issue.code}: ${issue.message}\n`);
|
|
49
|
+
}
|
|
50
|
+
async function emitJson(result, root, jsonPath) {
|
|
51
|
+
const abs = path.isAbsolute(jsonPath)
|
|
52
|
+
? jsonPath
|
|
53
|
+
: path.resolve(root, jsonPath);
|
|
54
|
+
await mkdir(path.dirname(abs), { recursive: true });
|
|
55
|
+
await writeFile(abs, `${JSON.stringify(result, null, 2)}\n`, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"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"}
|