qfai 0.3.0 → 0.3.1
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 +6 -8
- package/assets/init/.qfai/README.md +6 -5
- package/assets/init/.qfai/rules/conventions.md +21 -0
- package/assets/init/.qfai/specs/README.md +51 -0
- package/assets/init/.qfai/specs/spec-001/delta.md +30 -0
- package/assets/init/.qfai/specs/spec-001/scenario.md +10 -0
- package/assets/init/.qfai/{spec/spec-0001-sample.md → specs/spec-001/spec.md} +3 -2
- package/assets/init/root/.github/workflows/qfai.yml +1 -1
- package/assets/init/root/qfai.config.yaml +5 -8
- package/dist/cli/index.cjs +244 -273
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +244 -273
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +220 -232
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -12
- package/dist/index.d.ts +8 -12
- package/dist/index.mjs +219 -231
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/assets/init/.qfai/spec/README.md +0 -82
- package/assets/init/.qfai/spec/decisions/ADR-0001.md +0 -9
- package/assets/init/.qfai/spec/decisions/README.md +0 -37
- package/assets/init/.qfai/spec/scenarios/scenarios.feature +0 -6
package/dist/cli/index.mjs
CHANGED
|
@@ -138,7 +138,7 @@ function report(copied, skipped, dryRun, label) {
|
|
|
138
138
|
|
|
139
139
|
// src/cli/commands/report.ts
|
|
140
140
|
import { mkdir as mkdir2, readFile as readFile11, writeFile } from "fs/promises";
|
|
141
|
-
import
|
|
141
|
+
import path14 from "path";
|
|
142
142
|
|
|
143
143
|
// src/core/config.ts
|
|
144
144
|
import { readFile } from "fs/promises";
|
|
@@ -146,13 +146,11 @@ import path4 from "path";
|
|
|
146
146
|
import { parse as parseYaml } from "yaml";
|
|
147
147
|
var defaultConfig = {
|
|
148
148
|
paths: {
|
|
149
|
-
specDir: ".qfai/spec",
|
|
150
|
-
decisionsDir: ".qfai/spec/decisions",
|
|
151
|
-
scenariosDir: ".qfai/spec/scenarios",
|
|
152
149
|
contractsDir: ".qfai/contracts",
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
specsDir: ".qfai/specs",
|
|
151
|
+
rulesDir: ".qfai/rules",
|
|
152
|
+
outDir: ".qfai/out",
|
|
153
|
+
promptsDir: ".qfai/prompts",
|
|
156
154
|
srcDir: "src",
|
|
157
155
|
testsDir: "tests"
|
|
158
156
|
},
|
|
@@ -177,8 +175,7 @@ var defaultConfig = {
|
|
|
177
175
|
}
|
|
178
176
|
},
|
|
179
177
|
output: {
|
|
180
|
-
|
|
181
|
-
jsonPath: ".qfai/out/validate.json"
|
|
178
|
+
validateJsonPath: ".qfai/out/validate.json"
|
|
182
179
|
}
|
|
183
180
|
};
|
|
184
181
|
function getConfigPath(root) {
|
|
@@ -227,27 +224,6 @@ function normalizePaths(raw, configPath, issues) {
|
|
|
227
224
|
return base;
|
|
228
225
|
}
|
|
229
226
|
return {
|
|
230
|
-
specDir: readString(
|
|
231
|
-
raw.specDir,
|
|
232
|
-
base.specDir,
|
|
233
|
-
"paths.specDir",
|
|
234
|
-
configPath,
|
|
235
|
-
issues
|
|
236
|
-
),
|
|
237
|
-
decisionsDir: readString(
|
|
238
|
-
raw.decisionsDir,
|
|
239
|
-
base.decisionsDir,
|
|
240
|
-
"paths.decisionsDir",
|
|
241
|
-
configPath,
|
|
242
|
-
issues
|
|
243
|
-
),
|
|
244
|
-
scenariosDir: readString(
|
|
245
|
-
raw.scenariosDir,
|
|
246
|
-
base.scenariosDir,
|
|
247
|
-
"paths.scenariosDir",
|
|
248
|
-
configPath,
|
|
249
|
-
issues
|
|
250
|
-
),
|
|
251
227
|
contractsDir: readString(
|
|
252
228
|
raw.contractsDir,
|
|
253
229
|
base.contractsDir,
|
|
@@ -255,24 +231,31 @@ function normalizePaths(raw, configPath, issues) {
|
|
|
255
231
|
configPath,
|
|
256
232
|
issues
|
|
257
233
|
),
|
|
258
|
-
|
|
259
|
-
raw.
|
|
260
|
-
base.
|
|
261
|
-
"paths.
|
|
234
|
+
specsDir: readString(
|
|
235
|
+
raw.specsDir,
|
|
236
|
+
base.specsDir,
|
|
237
|
+
"paths.specsDir",
|
|
238
|
+
configPath,
|
|
239
|
+
issues
|
|
240
|
+
),
|
|
241
|
+
rulesDir: readString(
|
|
242
|
+
raw.rulesDir,
|
|
243
|
+
base.rulesDir,
|
|
244
|
+
"paths.rulesDir",
|
|
262
245
|
configPath,
|
|
263
246
|
issues
|
|
264
247
|
),
|
|
265
|
-
|
|
266
|
-
raw.
|
|
267
|
-
base.
|
|
268
|
-
"paths.
|
|
248
|
+
outDir: readString(
|
|
249
|
+
raw.outDir,
|
|
250
|
+
base.outDir,
|
|
251
|
+
"paths.outDir",
|
|
269
252
|
configPath,
|
|
270
253
|
issues
|
|
271
254
|
),
|
|
272
|
-
|
|
273
|
-
raw.
|
|
274
|
-
base.
|
|
275
|
-
"paths.
|
|
255
|
+
promptsDir: readString(
|
|
256
|
+
raw.promptsDir,
|
|
257
|
+
base.promptsDir,
|
|
258
|
+
"paths.promptsDir",
|
|
276
259
|
configPath,
|
|
277
260
|
issues
|
|
278
261
|
),
|
|
@@ -395,17 +378,10 @@ function normalizeOutput(raw, configPath, issues) {
|
|
|
395
378
|
return base;
|
|
396
379
|
}
|
|
397
380
|
return {
|
|
398
|
-
|
|
399
|
-
raw.
|
|
400
|
-
base.
|
|
401
|
-
"output.
|
|
402
|
-
configPath,
|
|
403
|
-
issues
|
|
404
|
-
),
|
|
405
|
-
jsonPath: readString(
|
|
406
|
-
raw.jsonPath,
|
|
407
|
-
base.jsonPath,
|
|
408
|
-
"output.jsonPath",
|
|
381
|
+
validateJsonPath: readString(
|
|
382
|
+
raw.validateJsonPath,
|
|
383
|
+
base.validateJsonPath,
|
|
384
|
+
"output.validateJsonPath",
|
|
409
385
|
configPath,
|
|
410
386
|
issues
|
|
411
387
|
)
|
|
@@ -472,20 +448,6 @@ function readTraceabilitySeverity(value, fallback, label, configPath, issues) {
|
|
|
472
448
|
}
|
|
473
449
|
return fallback;
|
|
474
450
|
}
|
|
475
|
-
function readOutputFormat(value, fallback, label, configPath, issues) {
|
|
476
|
-
if (value === "text" || value === "json" || value === "github") {
|
|
477
|
-
return value;
|
|
478
|
-
}
|
|
479
|
-
if (value !== void 0) {
|
|
480
|
-
issues.push(
|
|
481
|
-
configIssue(
|
|
482
|
-
configPath,
|
|
483
|
-
`${label} \u306F text|json|github \u306E\u3044\u305A\u308C\u304B\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`
|
|
484
|
-
)
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
return fallback;
|
|
488
|
-
}
|
|
489
451
|
function configIssue(file, message) {
|
|
490
452
|
return {
|
|
491
453
|
code: "QFAI_CONFIG_INVALID",
|
|
@@ -513,6 +475,7 @@ function isRecord(value) {
|
|
|
513
475
|
|
|
514
476
|
// src/core/report.ts
|
|
515
477
|
import { readFile as readFile10 } from "fs/promises";
|
|
478
|
+
import path13 from "path";
|
|
516
479
|
|
|
517
480
|
// src/core/discovery.ts
|
|
518
481
|
import path6 from "path";
|
|
@@ -573,10 +536,24 @@ async function exists2(target) {
|
|
|
573
536
|
}
|
|
574
537
|
|
|
575
538
|
// src/core/discovery.ts
|
|
576
|
-
var
|
|
577
|
-
async function
|
|
578
|
-
const files = await collectFiles(
|
|
579
|
-
|
|
539
|
+
var SPEC_PACK_DIR_PATTERN = /^spec-\d{3}$/;
|
|
540
|
+
async function collectSpecPackDirs(specsRoot) {
|
|
541
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
542
|
+
const packs = /* @__PURE__ */ new Set();
|
|
543
|
+
for (const file of files) {
|
|
544
|
+
if (isSpecPackFile(file, "spec.md")) {
|
|
545
|
+
packs.add(path6.dirname(file));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return Array.from(packs).sort();
|
|
549
|
+
}
|
|
550
|
+
async function collectSpecFiles(specsRoot) {
|
|
551
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
552
|
+
return files.filter((file) => isSpecPackFile(file, "spec.md"));
|
|
553
|
+
}
|
|
554
|
+
async function collectScenarioFiles(specsRoot) {
|
|
555
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
556
|
+
return files.filter((file) => isSpecPackFile(file, "scenario.md"));
|
|
580
557
|
}
|
|
581
558
|
async function collectUiContractFiles(uiRoot) {
|
|
582
559
|
return collectFiles(uiRoot, { extensions: [".yaml", ".yml"] });
|
|
@@ -595,9 +572,12 @@ async function collectContractFiles(uiRoot, apiRoot, dataRoot) {
|
|
|
595
572
|
]);
|
|
596
573
|
return { ui, api, db };
|
|
597
574
|
}
|
|
598
|
-
function
|
|
599
|
-
|
|
600
|
-
|
|
575
|
+
function isSpecPackFile(filePath, baseName) {
|
|
576
|
+
if (path6.basename(filePath).toLowerCase() !== baseName) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
const dirName = path6.basename(path6.dirname(filePath)).toLowerCase();
|
|
580
|
+
return SPEC_PACK_DIR_PATTERN.test(dirName);
|
|
601
581
|
}
|
|
602
582
|
|
|
603
583
|
// src/core/ids.ts
|
|
@@ -661,8 +641,8 @@ import { readFile as readFile2 } from "fs/promises";
|
|
|
661
641
|
import path7 from "path";
|
|
662
642
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
663
643
|
async function resolveToolVersion() {
|
|
664
|
-
if ("0.3.
|
|
665
|
-
return "0.3.
|
|
644
|
+
if ("0.3.1".length > 0) {
|
|
645
|
+
return "0.3.1";
|
|
666
646
|
}
|
|
667
647
|
try {
|
|
668
648
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -682,6 +662,7 @@ function resolvePackageJsonPath() {
|
|
|
682
662
|
|
|
683
663
|
// src/core/validators/contracts.ts
|
|
684
664
|
import { readFile as readFile3 } from "fs/promises";
|
|
665
|
+
import path9 from "path";
|
|
685
666
|
|
|
686
667
|
// src/core/contracts.ts
|
|
687
668
|
import path8 from "path";
|
|
@@ -737,19 +718,10 @@ var SQL_DANGEROUS_PATTERNS = [
|
|
|
737
718
|
];
|
|
738
719
|
async function validateContracts(root, config) {
|
|
739
720
|
const issues = [];
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
);
|
|
743
|
-
issues.push(
|
|
744
|
-
...await validateApiContracts(
|
|
745
|
-
resolvePath(root, config, "apiContractsDir")
|
|
746
|
-
)
|
|
747
|
-
);
|
|
748
|
-
issues.push(
|
|
749
|
-
...await validateDataContracts(
|
|
750
|
-
resolvePath(root, config, "dataContractsDir")
|
|
751
|
-
)
|
|
752
|
-
);
|
|
721
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
722
|
+
issues.push(...await validateUiContracts(path9.join(contractsRoot, "ui")));
|
|
723
|
+
issues.push(...await validateApiContracts(path9.join(contractsRoot, "api")));
|
|
724
|
+
issues.push(...await validateDataContracts(path9.join(contractsRoot, "db")));
|
|
753
725
|
return issues;
|
|
754
726
|
}
|
|
755
727
|
async function validateUiContracts(uiRoot) {
|
|
@@ -982,72 +954,78 @@ function issue(code, message, severity, file, rule, refs) {
|
|
|
982
954
|
return issue7;
|
|
983
955
|
}
|
|
984
956
|
|
|
985
|
-
// src/core/validators/
|
|
957
|
+
// src/core/validators/delta.ts
|
|
986
958
|
import { readFile as readFile4 } from "fs/promises";
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
var
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
const status = extractField(md, "Status");
|
|
998
|
-
const context = extractField(md, "Context");
|
|
999
|
-
const decision = extractField(md, "Decision");
|
|
1000
|
-
const consequences = extractField(md, "Consequences");
|
|
1001
|
-
const related = extractField(md, "Related");
|
|
1002
|
-
if (status) fields.status = status;
|
|
1003
|
-
if (context) fields.context = context;
|
|
1004
|
-
if (decision) fields.decision = decision;
|
|
1005
|
-
if (consequences) fields.consequences = consequences;
|
|
1006
|
-
if (related) fields.related = related;
|
|
1007
|
-
const parsed = {
|
|
1008
|
-
file,
|
|
1009
|
-
fields
|
|
1010
|
-
};
|
|
1011
|
-
if (adrId) {
|
|
1012
|
-
parsed.adrId = adrId;
|
|
1013
|
-
}
|
|
1014
|
-
return parsed;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
// src/core/validators/decisions.ts
|
|
1018
|
-
var REQUIRED_FIELDS = [
|
|
1019
|
-
{ key: "status", label: "Status" },
|
|
1020
|
-
{ key: "context", label: "Context" },
|
|
1021
|
-
{ key: "decision", label: "Decision" },
|
|
1022
|
-
{ key: "consequences", label: "Consequences" }
|
|
1023
|
-
];
|
|
1024
|
-
async function validateDecisions(root, config) {
|
|
1025
|
-
const decisionsRoot = resolvePath(root, config, "decisionsDir");
|
|
1026
|
-
const files = await collectFiles(decisionsRoot, { extensions: [".md"] });
|
|
1027
|
-
if (files.length === 0) {
|
|
959
|
+
import path10 from "path";
|
|
960
|
+
var SECTION_RE = /^##\s+変更区分/m;
|
|
961
|
+
var COMPAT_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Compatibility\b/m;
|
|
962
|
+
var CHANGE_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Change\/Improvement\b/m;
|
|
963
|
+
var COMPAT_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Compatibility\b/m;
|
|
964
|
+
var CHANGE_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Change\/Improvement\b/m;
|
|
965
|
+
async function validateDeltas(root, config) {
|
|
966
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
967
|
+
const packs = await collectSpecPackDirs(specsRoot);
|
|
968
|
+
if (packs.length === 0) {
|
|
1028
969
|
return [];
|
|
1029
970
|
}
|
|
1030
971
|
const issues = [];
|
|
1031
|
-
for (const
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
(
|
|
1036
|
-
)
|
|
1037
|
-
|
|
972
|
+
for (const pack of packs) {
|
|
973
|
+
const deltaPath = path10.join(pack, "delta.md");
|
|
974
|
+
let text;
|
|
975
|
+
try {
|
|
976
|
+
text = await readFile4(deltaPath, "utf-8");
|
|
977
|
+
} catch (error2) {
|
|
978
|
+
if (isMissingFileError(error2)) {
|
|
979
|
+
issues.push(
|
|
980
|
+
issue2(
|
|
981
|
+
"QFAI-DELTA-001",
|
|
982
|
+
"delta.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
|
|
983
|
+
"error",
|
|
984
|
+
deltaPath,
|
|
985
|
+
"delta.exists"
|
|
986
|
+
)
|
|
987
|
+
);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
throw error2;
|
|
991
|
+
}
|
|
992
|
+
const hasSection = SECTION_RE.test(text);
|
|
993
|
+
const hasCompatibility = COMPAT_LINE_RE.test(text);
|
|
994
|
+
const hasChange = CHANGE_LINE_RE.test(text);
|
|
995
|
+
if (!hasSection || !hasCompatibility || !hasChange) {
|
|
1038
996
|
issues.push(
|
|
1039
997
|
issue2(
|
|
1040
|
-
"QFAI-
|
|
1041
|
-
|
|
998
|
+
"QFAI-DELTA-002",
|
|
999
|
+
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
1042
1000
|
"error",
|
|
1043
|
-
|
|
1044
|
-
"
|
|
1001
|
+
deltaPath,
|
|
1002
|
+
"delta.section"
|
|
1003
|
+
)
|
|
1004
|
+
);
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
const compatibilityChecked = COMPAT_CHECKED_RE.test(text);
|
|
1008
|
+
const changeChecked = CHANGE_CHECKED_RE.test(text);
|
|
1009
|
+
if (compatibilityChecked === changeChecked) {
|
|
1010
|
+
issues.push(
|
|
1011
|
+
issue2(
|
|
1012
|
+
"QFAI-DELTA-003",
|
|
1013
|
+
"delta.md \u306E\u5909\u66F4\u533A\u5206\u306F\u3069\u3061\u3089\u304B1\u3064\u3060\u3051\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4E21\u65B9ON/\u4E21\u65B9OFF\u306F\u7121\u52B9\u3067\u3059\uFF09\u3002",
|
|
1014
|
+
"error",
|
|
1015
|
+
deltaPath,
|
|
1016
|
+
"delta.classification"
|
|
1045
1017
|
)
|
|
1046
1018
|
);
|
|
1047
1019
|
}
|
|
1048
1020
|
}
|
|
1049
1021
|
return issues;
|
|
1050
1022
|
}
|
|
1023
|
+
function isMissingFileError(error2) {
|
|
1024
|
+
if (!error2 || typeof error2 !== "object") {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
return error2.code === "ENOENT";
|
|
1028
|
+
}
|
|
1051
1029
|
function issue2(code, message, severity, file, rule, refs) {
|
|
1052
1030
|
const issue7 = {
|
|
1053
1031
|
code,
|
|
@@ -1068,14 +1046,16 @@ function issue2(code, message, severity, file, rule, refs) {
|
|
|
1068
1046
|
|
|
1069
1047
|
// src/core/validators/ids.ts
|
|
1070
1048
|
import { readFile as readFile6 } from "fs/promises";
|
|
1071
|
-
import
|
|
1049
|
+
import path12 from "path";
|
|
1072
1050
|
|
|
1073
1051
|
// src/core/contractIndex.ts
|
|
1074
1052
|
import { readFile as readFile5 } from "fs/promises";
|
|
1053
|
+
import path11 from "path";
|
|
1075
1054
|
async function buildContractIndex(root, config) {
|
|
1076
|
-
const
|
|
1077
|
-
const
|
|
1078
|
-
const
|
|
1055
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
1056
|
+
const uiRoot = path11.join(contractsRoot, "ui");
|
|
1057
|
+
const apiRoot = path11.join(contractsRoot, "api");
|
|
1058
|
+
const dataRoot = path11.join(contractsRoot, "db");
|
|
1079
1059
|
const [uiFiles, apiFiles, dataFiles] = await Promise.all([
|
|
1080
1060
|
collectUiContractFiles(uiRoot),
|
|
1081
1061
|
collectApiContractFiles(apiRoot),
|
|
@@ -1131,7 +1111,7 @@ function record(index, id, file) {
|
|
|
1131
1111
|
|
|
1132
1112
|
// src/core/parse/gherkin.ts
|
|
1133
1113
|
var FEATURE_RE = /^\s*Feature:\s+/;
|
|
1134
|
-
var SCENARIO_RE = /^\s*Scenario
|
|
1114
|
+
var SCENARIO_RE = /^\s*Scenario(?: Outline)?:\s*(.+)\s*$/;
|
|
1135
1115
|
var TAG_LINE_RE = /^\s*@/;
|
|
1136
1116
|
function parseTags(line) {
|
|
1137
1117
|
return line.trim().split(/\s+/).filter((tag) => tag.startsWith("@")).map((tag) => tag.replace(/^@/, ""));
|
|
@@ -1140,24 +1120,52 @@ function parseGherkinFeature(text, file) {
|
|
|
1140
1120
|
const lines = text.split(/\r?\n/);
|
|
1141
1121
|
const scenarios = [];
|
|
1142
1122
|
let featurePresent = false;
|
|
1123
|
+
let featureTags = [];
|
|
1124
|
+
let pendingTags = [];
|
|
1125
|
+
let current = null;
|
|
1126
|
+
const flush = () => {
|
|
1127
|
+
if (!current) return;
|
|
1128
|
+
scenarios.push({
|
|
1129
|
+
...current,
|
|
1130
|
+
body: current.body.trim()
|
|
1131
|
+
});
|
|
1132
|
+
current = null;
|
|
1133
|
+
};
|
|
1143
1134
|
for (let i = 0; i < lines.length; i++) {
|
|
1144
1135
|
const line = lines[i] ?? "";
|
|
1145
|
-
|
|
1136
|
+
const trimmed = line.trim();
|
|
1137
|
+
if (TAG_LINE_RE.test(trimmed)) {
|
|
1138
|
+
pendingTags.push(...parseTags(trimmed));
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (FEATURE_RE.test(trimmed)) {
|
|
1146
1142
|
featurePresent = true;
|
|
1143
|
+
featureTags = [...pendingTags];
|
|
1144
|
+
pendingTags = [];
|
|
1145
|
+
continue;
|
|
1147
1146
|
}
|
|
1148
|
-
const match =
|
|
1149
|
-
if (
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1147
|
+
const match = trimmed.match(SCENARIO_RE);
|
|
1148
|
+
if (match) {
|
|
1149
|
+
const scenarioName = match[1]?.trim();
|
|
1150
|
+
if (!scenarioName) {
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
flush();
|
|
1154
|
+
current = {
|
|
1155
|
+
name: scenarioName,
|
|
1156
|
+
line: i + 1,
|
|
1157
|
+
tags: [...featureTags, ...pendingTags],
|
|
1158
|
+
body: ""
|
|
1159
|
+
};
|
|
1160
|
+
pendingTags = [];
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
if (current) {
|
|
1164
|
+
current.body += `${line}
|
|
1165
|
+
`;
|
|
1158
1166
|
}
|
|
1159
|
-
scenarios.push({ name: scenarioName, line: i + 1, tags });
|
|
1160
1167
|
}
|
|
1168
|
+
flush();
|
|
1161
1169
|
return { file, featurePresent, scenarios };
|
|
1162
1170
|
}
|
|
1163
1171
|
|
|
@@ -1204,9 +1212,9 @@ function extractH2Sections(md) {
|
|
|
1204
1212
|
|
|
1205
1213
|
// src/core/parse/spec.ts
|
|
1206
1214
|
var SPEC_ID_RE = /\bSPEC-\d{4}\b/;
|
|
1207
|
-
var BR_LINE_RE = /^\s
|
|
1208
|
-
var BR_LINE_ANY_PRIORITY_RE = /^\s
|
|
1209
|
-
var BR_LINE_NO_PRIORITY_RE = /^\s
|
|
1215
|
+
var BR_LINE_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[0-3])\]\s*(.+)$/;
|
|
1216
|
+
var BR_LINE_ANY_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[^\]]+)\]\s*(.+)$/;
|
|
1217
|
+
var BR_LINE_NO_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\](?!\s*\[P)\s*(.*\S.*)$/;
|
|
1210
1218
|
var BR_SECTION_TITLE = "\u696D\u52D9\u30EB\u30FC\u30EB";
|
|
1211
1219
|
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
1212
1220
|
function parseSpec(md, file) {
|
|
@@ -1283,12 +1291,9 @@ function parseSpec(md, file) {
|
|
|
1283
1291
|
var SC_TAG_RE = /^SC-\d{4}$/;
|
|
1284
1292
|
async function validateDefinedIds(root, config) {
|
|
1285
1293
|
const issues = [];
|
|
1286
|
-
const
|
|
1287
|
-
const
|
|
1288
|
-
const
|
|
1289
|
-
const scenarioFiles = await collectFiles(scenarioRoot, {
|
|
1290
|
-
extensions: [".feature"]
|
|
1291
|
-
});
|
|
1294
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1295
|
+
const specFiles = await collectSpecFiles(specsRoot);
|
|
1296
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
1292
1297
|
const defined = /* @__PURE__ */ new Map();
|
|
1293
1298
|
await collectSpecDefinitionIds(specFiles, defined);
|
|
1294
1299
|
await collectScenarioDefinitionIds(scenarioFiles, defined);
|
|
@@ -1345,7 +1350,7 @@ function recordId(out, id, file) {
|
|
|
1345
1350
|
}
|
|
1346
1351
|
function formatFileList(files, root) {
|
|
1347
1352
|
return files.map((file) => {
|
|
1348
|
-
const relative =
|
|
1353
|
+
const relative = path12.relative(root, file);
|
|
1349
1354
|
return relative.length > 0 ? relative : file;
|
|
1350
1355
|
}).join(", ");
|
|
1351
1356
|
}
|
|
@@ -1376,17 +1381,15 @@ var SC_TAG_RE2 = /^SC-\d{4}$/;
|
|
|
1376
1381
|
var SPEC_TAG_RE = /^SPEC-\d{4}$/;
|
|
1377
1382
|
var BR_TAG_RE = /^BR-\d{4}$/;
|
|
1378
1383
|
async function validateScenarios(root, config) {
|
|
1379
|
-
const
|
|
1380
|
-
const files = await
|
|
1381
|
-
extensions: [".feature"]
|
|
1382
|
-
});
|
|
1384
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1385
|
+
const files = await collectScenarioFiles(specsRoot);
|
|
1383
1386
|
if (files.length === 0) {
|
|
1384
1387
|
return [
|
|
1385
1388
|
issue4(
|
|
1386
1389
|
"QFAI-SC-000",
|
|
1387
1390
|
"Scenario \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
|
|
1388
1391
|
"info",
|
|
1389
|
-
|
|
1392
|
+
specsRoot,
|
|
1390
1393
|
"scenario.files"
|
|
1391
1394
|
)
|
|
1392
1395
|
];
|
|
@@ -1452,8 +1455,11 @@ function validateScenarioContent(text, file) {
|
|
|
1452
1455
|
continue;
|
|
1453
1456
|
}
|
|
1454
1457
|
const missingTags = [];
|
|
1455
|
-
|
|
1456
|
-
|
|
1458
|
+
const scTags = scenario.tags.filter((tag) => SC_TAG_RE2.test(tag));
|
|
1459
|
+
if (scTags.length === 0) {
|
|
1460
|
+
missingTags.push("SC(0\u4EF6)");
|
|
1461
|
+
} else if (scTags.length > 1) {
|
|
1462
|
+
missingTags.push(`SC(${scTags.length}\u4EF6/1\u4EF6\u5FC5\u9808)`);
|
|
1457
1463
|
}
|
|
1458
1464
|
if (!scenario.tags.some((tag) => SPEC_TAG_RE.test(tag))) {
|
|
1459
1465
|
missingTags.push("SPEC");
|
|
@@ -1473,26 +1479,28 @@ function validateScenarioContent(text, file) {
|
|
|
1473
1479
|
);
|
|
1474
1480
|
}
|
|
1475
1481
|
}
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1482
|
+
for (const scenario of parsed.scenarios) {
|
|
1483
|
+
const missingSteps = [];
|
|
1484
|
+
if (!GIVEN_PATTERN.test(scenario.body)) {
|
|
1485
|
+
missingSteps.push("Given");
|
|
1486
|
+
}
|
|
1487
|
+
if (!WHEN_PATTERN.test(scenario.body)) {
|
|
1488
|
+
missingSteps.push("When");
|
|
1489
|
+
}
|
|
1490
|
+
if (!THEN_PATTERN.test(scenario.body)) {
|
|
1491
|
+
missingSteps.push("Then");
|
|
1492
|
+
}
|
|
1493
|
+
if (missingSteps.length > 0) {
|
|
1494
|
+
issues.push(
|
|
1495
|
+
issue4(
|
|
1496
|
+
"QFAI-SC-005",
|
|
1497
|
+
`Given/When/Then \u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059: ${missingSteps.join(", ")} (${scenario.name})`,
|
|
1498
|
+
"warning",
|
|
1499
|
+
file,
|
|
1500
|
+
"scenario.steps"
|
|
1501
|
+
)
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1496
1504
|
}
|
|
1497
1505
|
return issues;
|
|
1498
1506
|
}
|
|
@@ -1517,14 +1525,14 @@ function issue4(code, message, severity, file, rule, refs) {
|
|
|
1517
1525
|
// src/core/validators/spec.ts
|
|
1518
1526
|
import { readFile as readFile8 } from "fs/promises";
|
|
1519
1527
|
async function validateSpecs(root, config) {
|
|
1520
|
-
const specsRoot = resolvePath(root, config, "
|
|
1528
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1521
1529
|
const files = await collectSpecFiles(specsRoot);
|
|
1522
1530
|
if (files.length === 0) {
|
|
1523
|
-
const expected = "spec-
|
|
1531
|
+
const expected = "spec-001/spec.md";
|
|
1524
1532
|
return [
|
|
1525
1533
|
issue5(
|
|
1526
1534
|
"QFAI-SPEC-000",
|
|
1527
|
-
`Spec \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.
|
|
1535
|
+
`Spec \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.specsDir} / \u671F\u5F85\u30D1\u30BF\u30FC\u30F3: ${expected}`,
|
|
1528
1536
|
"info",
|
|
1529
1537
|
specsRoot,
|
|
1530
1538
|
"spec.files"
|
|
@@ -1670,18 +1678,11 @@ var API_TAG_RE = /^API-\d{4}$/;
|
|
|
1670
1678
|
var DATA_TAG_RE = /^DATA-\d{4}$/;
|
|
1671
1679
|
async function validateTraceability(root, config) {
|
|
1672
1680
|
const issues = [];
|
|
1673
|
-
const specsRoot = resolvePath(root, config, "
|
|
1674
|
-
const decisionsRoot = resolvePath(root, config, "decisionsDir");
|
|
1675
|
-
const scenariosRoot = resolvePath(root, config, "scenariosDir");
|
|
1681
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1676
1682
|
const srcRoot = resolvePath(root, config, "srcDir");
|
|
1677
1683
|
const testsRoot = resolvePath(root, config, "testsDir");
|
|
1678
1684
|
const specFiles = await collectSpecFiles(specsRoot);
|
|
1679
|
-
const
|
|
1680
|
-
extensions: [".md"]
|
|
1681
|
-
});
|
|
1682
|
-
const scenarioFiles = await collectFiles(scenariosRoot, {
|
|
1683
|
-
extensions: [".feature"]
|
|
1684
|
-
});
|
|
1685
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
1685
1686
|
const upstreamIds = /* @__PURE__ */ new Set();
|
|
1686
1687
|
const specIds = /* @__PURE__ */ new Set();
|
|
1687
1688
|
const brIdsInSpecs = /* @__PURE__ */ new Set();
|
|
@@ -1729,10 +1730,6 @@ async function validateTraceability(root, config) {
|
|
|
1729
1730
|
specToBrIds.set(parsed.specId, current);
|
|
1730
1731
|
}
|
|
1731
1732
|
}
|
|
1732
|
-
for (const file of decisionFiles) {
|
|
1733
|
-
const text = await readFile9(file, "utf-8");
|
|
1734
|
-
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
1735
|
-
}
|
|
1736
1733
|
for (const file of scenarioFiles) {
|
|
1737
1734
|
const text = await readFile9(file, "utf-8");
|
|
1738
1735
|
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
@@ -1876,7 +1873,7 @@ async function validateTraceability(root, config) {
|
|
|
1876
1873
|
", "
|
|
1877
1874
|
)}`,
|
|
1878
1875
|
"error",
|
|
1879
|
-
|
|
1876
|
+
specsRoot,
|
|
1880
1877
|
"traceability.scMustTouchContracts",
|
|
1881
1878
|
scWithoutContracts
|
|
1882
1879
|
)
|
|
@@ -1894,7 +1891,7 @@ async function validateTraceability(root, config) {
|
|
|
1894
1891
|
"QFAI_CONTRACT_ORPHAN",
|
|
1895
1892
|
`\u5951\u7D04\u304C SC \u304B\u3089\u53C2\u7167\u3055\u308C\u3066\u3044\u307E\u305B\u3093: ${orphanContracts.join(", ")}`,
|
|
1896
1893
|
"error",
|
|
1897
|
-
|
|
1894
|
+
specsRoot,
|
|
1898
1895
|
"traceability.allowOrphanContracts",
|
|
1899
1896
|
orphanContracts
|
|
1900
1897
|
)
|
|
@@ -1979,8 +1976,8 @@ async function validateProject(root, configResult) {
|
|
|
1979
1976
|
const issues = [
|
|
1980
1977
|
...configIssues,
|
|
1981
1978
|
...await validateSpecs(root, config),
|
|
1979
|
+
...await validateDeltas(root, config),
|
|
1982
1980
|
...await validateScenarios(root, config),
|
|
1983
|
-
...await validateDecisions(root, config),
|
|
1984
1981
|
...await validateContracts(root, config),
|
|
1985
1982
|
...await validateDefinedIds(root, config),
|
|
1986
1983
|
...await validateTraceability(root, config)
|
|
@@ -2009,21 +2006,15 @@ async function createReportData(root, validation, configResult) {
|
|
|
2009
2006
|
const resolved = configResult ?? await loadConfig(root);
|
|
2010
2007
|
const config = resolved.config;
|
|
2011
2008
|
const configPath = resolved.configPath;
|
|
2012
|
-
const
|
|
2013
|
-
const
|
|
2014
|
-
const
|
|
2015
|
-
const
|
|
2016
|
-
const
|
|
2017
|
-
const dbRoot = resolvePath(root, config, "dataContractsDir");
|
|
2009
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
2010
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
2011
|
+
const apiRoot = path13.join(contractsRoot, "api");
|
|
2012
|
+
const uiRoot = path13.join(contractsRoot, "ui");
|
|
2013
|
+
const dbRoot = path13.join(contractsRoot, "db");
|
|
2018
2014
|
const srcRoot = resolvePath(root, config, "srcDir");
|
|
2019
2015
|
const testsRoot = resolvePath(root, config, "testsDir");
|
|
2020
|
-
const specFiles = await collectSpecFiles(
|
|
2021
|
-
const scenarioFiles = await
|
|
2022
|
-
extensions: [".feature"]
|
|
2023
|
-
});
|
|
2024
|
-
const decisionFiles = await collectFiles(decisionsRoot, {
|
|
2025
|
-
extensions: [".md"]
|
|
2026
|
-
});
|
|
2016
|
+
const specFiles = await collectSpecFiles(specsRoot);
|
|
2017
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
2027
2018
|
const {
|
|
2028
2019
|
api: apiFiles,
|
|
2029
2020
|
ui: uiFiles,
|
|
@@ -2032,7 +2023,6 @@ async function createReportData(root, validation, configResult) {
|
|
|
2032
2023
|
const idsByPrefix = await collectIds([
|
|
2033
2024
|
...specFiles,
|
|
2034
2025
|
...scenarioFiles,
|
|
2035
|
-
...decisionFiles,
|
|
2036
2026
|
...apiFiles,
|
|
2037
2027
|
...uiFiles,
|
|
2038
2028
|
...dbFiles
|
|
@@ -2057,7 +2047,6 @@ async function createReportData(root, validation, configResult) {
|
|
|
2057
2047
|
summary: {
|
|
2058
2048
|
specs: specFiles.length,
|
|
2059
2049
|
scenarios: scenarioFiles.length,
|
|
2060
|
-
decisions: decisionFiles.length,
|
|
2061
2050
|
contracts: {
|
|
2062
2051
|
api: apiFiles.length,
|
|
2063
2052
|
ui: uiFiles.length,
|
|
@@ -2091,7 +2080,6 @@ function formatReportMarkdown(data) {
|
|
|
2091
2080
|
lines.push("## \u6982\u8981");
|
|
2092
2081
|
lines.push(`- specs: ${data.summary.specs}`);
|
|
2093
2082
|
lines.push(`- scenarios: ${data.summary.scenarios}`);
|
|
2094
|
-
lines.push(`- decisions: ${data.summary.decisions}`);
|
|
2095
2083
|
lines.push(
|
|
2096
2084
|
`- contracts: api ${data.summary.contracts.api} / ui ${data.summary.contracts.ui} / db ${data.summary.contracts.db}`
|
|
2097
2085
|
);
|
|
@@ -2250,21 +2238,22 @@ function buildHotspots(issues) {
|
|
|
2250
2238
|
|
|
2251
2239
|
// src/cli/commands/report.ts
|
|
2252
2240
|
async function runReport(options) {
|
|
2253
|
-
const root =
|
|
2241
|
+
const root = path14.resolve(options.root);
|
|
2254
2242
|
const configResult = await loadConfig(root);
|
|
2255
|
-
const input =
|
|
2256
|
-
const inputPath =
|
|
2243
|
+
const input = configResult.config.output.validateJsonPath;
|
|
2244
|
+
const inputPath = path14.isAbsolute(input) ? input : path14.resolve(root, input);
|
|
2257
2245
|
let validation;
|
|
2258
2246
|
try {
|
|
2259
2247
|
validation = await readValidationResult(inputPath);
|
|
2260
2248
|
} catch (err) {
|
|
2261
|
-
if (
|
|
2249
|
+
if (isMissingFileError2(err)) {
|
|
2262
2250
|
error(
|
|
2263
2251
|
[
|
|
2264
2252
|
`qfai report: \u5165\u529B\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${inputPath}`,
|
|
2265
2253
|
"",
|
|
2266
|
-
"\u307E\u305A validate
|
|
2267
|
-
|
|
2254
|
+
"\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
|
|
2255
|
+
" qfai validate",
|
|
2256
|
+
"\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/out/validate.json\uFF09",
|
|
2268
2257
|
"",
|
|
2269
2258
|
"GitHub Actions \u30C6\u30F3\u30D7\u30EC\u3092\u4F7F\u3063\u3066\u3044\u308B\u5834\u5408\u306F\u3001workflow \u306E validate \u30B8\u30E7\u30D6\u3092\u5148\u306B\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2270
2259
|
].join("\n")
|
|
@@ -2276,10 +2265,11 @@ async function runReport(options) {
|
|
|
2276
2265
|
}
|
|
2277
2266
|
const data = await createReportData(root, validation, configResult);
|
|
2278
2267
|
const output = options.format === "json" ? formatReportJson(data) : formatReportMarkdown(data);
|
|
2279
|
-
const
|
|
2268
|
+
const outRoot = resolvePath(root, configResult.config, "outDir");
|
|
2269
|
+
const defaultOut = options.format === "json" ? path14.join(outRoot, "report.json") : path14.join(outRoot, "report.md");
|
|
2280
2270
|
const out = options.outPath ?? defaultOut;
|
|
2281
|
-
const outPath =
|
|
2282
|
-
await mkdir2(
|
|
2271
|
+
const outPath = path14.isAbsolute(out) ? out : path14.resolve(root, out);
|
|
2272
|
+
await mkdir2(path14.dirname(outPath), { recursive: true });
|
|
2283
2273
|
await writeFile(outPath, `${output}
|
|
2284
2274
|
`, "utf-8");
|
|
2285
2275
|
info(
|
|
@@ -2320,7 +2310,7 @@ function isValidationResult(value) {
|
|
|
2320
2310
|
}
|
|
2321
2311
|
return typeof counts.info === "number" && typeof counts.warning === "number" && typeof counts.error === "number";
|
|
2322
2312
|
}
|
|
2323
|
-
function
|
|
2313
|
+
function isMissingFileError2(error2) {
|
|
2324
2314
|
if (!error2 || typeof error2 !== "object") {
|
|
2325
2315
|
return false;
|
|
2326
2316
|
}
|
|
@@ -2330,7 +2320,7 @@ function isMissingFileError(error2) {
|
|
|
2330
2320
|
|
|
2331
2321
|
// src/cli/commands/validate.ts
|
|
2332
2322
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
2333
|
-
import
|
|
2323
|
+
import path15 from "path";
|
|
2334
2324
|
|
|
2335
2325
|
// src/cli/lib/failOn.ts
|
|
2336
2326
|
function shouldFail(result, failOn) {
|
|
@@ -2345,24 +2335,17 @@ function shouldFail(result, failOn) {
|
|
|
2345
2335
|
|
|
2346
2336
|
// src/cli/commands/validate.ts
|
|
2347
2337
|
async function runValidate(options) {
|
|
2348
|
-
const root =
|
|
2338
|
+
const root = path15.resolve(options.root);
|
|
2349
2339
|
const configResult = await loadConfig(root);
|
|
2350
2340
|
const result = await validateProject(root, configResult);
|
|
2351
|
-
const format = options.format ??
|
|
2352
|
-
const explicitJsonPath = options.jsonPath;
|
|
2341
|
+
const format = options.format ?? "text";
|
|
2353
2342
|
if (format === "text") {
|
|
2354
2343
|
emitText(result);
|
|
2355
2344
|
}
|
|
2356
2345
|
if (format === "github") {
|
|
2357
2346
|
result.issues.forEach(emitGitHub);
|
|
2358
2347
|
}
|
|
2359
|
-
|
|
2360
|
-
if (shouldWriteJson) {
|
|
2361
|
-
const jsonPath = format === "json" ? options.jsonPath ?? configResult.config.output.jsonPath : explicitJsonPath;
|
|
2362
|
-
if (jsonPath) {
|
|
2363
|
-
await emitJson(result, root, jsonPath);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2348
|
+
await emitJson(result, root, configResult.config.output.validateJsonPath);
|
|
2366
2349
|
const failOn = resolveFailOn(options, configResult.config.validation.failOn);
|
|
2367
2350
|
return shouldFail(result, failOn) ? 1 : 0;
|
|
2368
2351
|
}
|
|
@@ -2401,8 +2384,8 @@ function emitGitHub(issue7) {
|
|
|
2401
2384
|
);
|
|
2402
2385
|
}
|
|
2403
2386
|
async function emitJson(result, root, jsonPath) {
|
|
2404
|
-
const abs =
|
|
2405
|
-
await mkdir3(
|
|
2387
|
+
const abs = path15.isAbsolute(jsonPath) ? jsonPath : path15.resolve(root, jsonPath);
|
|
2388
|
+
await mkdir3(path15.dirname(abs), { recursive: true });
|
|
2406
2389
|
await writeFile2(abs, `${JSON.stringify(result, null, 2)}
|
|
2407
2390
|
`, "utf-8");
|
|
2408
2391
|
}
|
|
@@ -2463,15 +2446,6 @@ function parseArgs(argv, cwd) {
|
|
|
2463
2446
|
i += 1;
|
|
2464
2447
|
break;
|
|
2465
2448
|
}
|
|
2466
|
-
case "--json-path":
|
|
2467
|
-
{
|
|
2468
|
-
const next = args[i + 1];
|
|
2469
|
-
if (next) {
|
|
2470
|
-
options.jsonPath = next;
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
i += 1;
|
|
2474
|
-
break;
|
|
2475
2449
|
case "--out":
|
|
2476
2450
|
{
|
|
2477
2451
|
const next = args[i + 1];
|
|
@@ -2502,7 +2476,7 @@ function applyFormatOption(command, value, options) {
|
|
|
2502
2476
|
return;
|
|
2503
2477
|
}
|
|
2504
2478
|
if (command === "validate") {
|
|
2505
|
-
if (value === "text" || value === "
|
|
2479
|
+
if (value === "text" || value === "github") {
|
|
2506
2480
|
options.validateFormat = value;
|
|
2507
2481
|
}
|
|
2508
2482
|
return;
|
|
@@ -2510,7 +2484,7 @@ function applyFormatOption(command, value, options) {
|
|
|
2510
2484
|
if (value === "md" || value === "json") {
|
|
2511
2485
|
options.reportFormat = value;
|
|
2512
2486
|
}
|
|
2513
|
-
if (value === "text" || value === "
|
|
2487
|
+
if (value === "text" || value === "github") {
|
|
2514
2488
|
options.validateFormat = value;
|
|
2515
2489
|
}
|
|
2516
2490
|
}
|
|
@@ -2536,15 +2510,13 @@ async function run(argv, cwd) {
|
|
|
2536
2510
|
root: options.root,
|
|
2537
2511
|
strict: options.strict,
|
|
2538
2512
|
format: options.validateFormat,
|
|
2539
|
-
...options.failOn !== void 0 ? { failOn: options.failOn } : {}
|
|
2540
|
-
...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {}
|
|
2513
|
+
...options.failOn !== void 0 ? { failOn: options.failOn } : {}
|
|
2541
2514
|
});
|
|
2542
2515
|
return;
|
|
2543
2516
|
case "report":
|
|
2544
2517
|
await runReport({
|
|
2545
2518
|
root: options.root,
|
|
2546
2519
|
format: options.reportFormat,
|
|
2547
|
-
...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {},
|
|
2548
2520
|
...options.reportOut !== void 0 ? { outPath: options.reportOut } : {}
|
|
2549
2521
|
});
|
|
2550
2522
|
return;
|
|
@@ -2566,14 +2538,13 @@ Options:
|
|
|
2566
2538
|
--root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
|
|
2567
2539
|
--dir <path> init \u306E\u51FA\u529B\u5148
|
|
2568
2540
|
--force \u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D
|
|
2569
|
-
--yes init: \
|
|
2541
|
+
--yes init: \u4E88\u7D04\u30D5\u30E9\u30B0\uFF08\u73FE\u72B6\u306F\u975E\u5BFE\u8A71\u306E\u305F\u3081\u6319\u52D5\u5DEE\u306A\u3057\u3002\u5C06\u6765\u306E\u5BFE\u8A71\u5C0E\u5165\u6642\u306B\u81EA\u52D5Yes\uFF09
|
|
2570
2542
|
--dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
|
|
2571
|
-
--format <text|
|
|
2543
|
+
--format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
|
|
2572
2544
|
--format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
|
|
2573
|
-
--strict
|
|
2545
|
+
--strict validate: warning \u4EE5\u4E0A\u3067 exit 1
|
|
2574
2546
|
--fail-on <error|warning|never> validate: \u5931\u6557\u6761\u4EF6
|
|
2575
|
-
--
|
|
2576
|
-
--out <path> report: \u51FA\u529B\u5148
|
|
2547
|
+
--out <path> report: \u51FA\u529B\u5148
|
|
2577
2548
|
-h, --help \u30D8\u30EB\u30D7\u8868\u793A
|
|
2578
2549
|
`;
|
|
2579
2550
|
}
|