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.cjs
CHANGED
|
@@ -161,7 +161,7 @@ function report(copied, skipped, dryRun, label) {
|
|
|
161
161
|
|
|
162
162
|
// src/cli/commands/report.ts
|
|
163
163
|
var import_promises13 = require("fs/promises");
|
|
164
|
-
var
|
|
164
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
165
165
|
|
|
166
166
|
// src/core/config.ts
|
|
167
167
|
var import_promises2 = require("fs/promises");
|
|
@@ -169,13 +169,11 @@ var import_node_path4 = __toESM(require("path"), 1);
|
|
|
169
169
|
var import_yaml = require("yaml");
|
|
170
170
|
var defaultConfig = {
|
|
171
171
|
paths: {
|
|
172
|
-
specDir: ".qfai/spec",
|
|
173
|
-
decisionsDir: ".qfai/spec/decisions",
|
|
174
|
-
scenariosDir: ".qfai/spec/scenarios",
|
|
175
172
|
contractsDir: ".qfai/contracts",
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
specsDir: ".qfai/specs",
|
|
174
|
+
rulesDir: ".qfai/rules",
|
|
175
|
+
outDir: ".qfai/out",
|
|
176
|
+
promptsDir: ".qfai/prompts",
|
|
179
177
|
srcDir: "src",
|
|
180
178
|
testsDir: "tests"
|
|
181
179
|
},
|
|
@@ -200,8 +198,7 @@ var defaultConfig = {
|
|
|
200
198
|
}
|
|
201
199
|
},
|
|
202
200
|
output: {
|
|
203
|
-
|
|
204
|
-
jsonPath: ".qfai/out/validate.json"
|
|
201
|
+
validateJsonPath: ".qfai/out/validate.json"
|
|
205
202
|
}
|
|
206
203
|
};
|
|
207
204
|
function getConfigPath(root) {
|
|
@@ -250,27 +247,6 @@ function normalizePaths(raw, configPath, issues) {
|
|
|
250
247
|
return base;
|
|
251
248
|
}
|
|
252
249
|
return {
|
|
253
|
-
specDir: readString(
|
|
254
|
-
raw.specDir,
|
|
255
|
-
base.specDir,
|
|
256
|
-
"paths.specDir",
|
|
257
|
-
configPath,
|
|
258
|
-
issues
|
|
259
|
-
),
|
|
260
|
-
decisionsDir: readString(
|
|
261
|
-
raw.decisionsDir,
|
|
262
|
-
base.decisionsDir,
|
|
263
|
-
"paths.decisionsDir",
|
|
264
|
-
configPath,
|
|
265
|
-
issues
|
|
266
|
-
),
|
|
267
|
-
scenariosDir: readString(
|
|
268
|
-
raw.scenariosDir,
|
|
269
|
-
base.scenariosDir,
|
|
270
|
-
"paths.scenariosDir",
|
|
271
|
-
configPath,
|
|
272
|
-
issues
|
|
273
|
-
),
|
|
274
250
|
contractsDir: readString(
|
|
275
251
|
raw.contractsDir,
|
|
276
252
|
base.contractsDir,
|
|
@@ -278,24 +254,31 @@ function normalizePaths(raw, configPath, issues) {
|
|
|
278
254
|
configPath,
|
|
279
255
|
issues
|
|
280
256
|
),
|
|
281
|
-
|
|
282
|
-
raw.
|
|
283
|
-
base.
|
|
284
|
-
"paths.
|
|
257
|
+
specsDir: readString(
|
|
258
|
+
raw.specsDir,
|
|
259
|
+
base.specsDir,
|
|
260
|
+
"paths.specsDir",
|
|
261
|
+
configPath,
|
|
262
|
+
issues
|
|
263
|
+
),
|
|
264
|
+
rulesDir: readString(
|
|
265
|
+
raw.rulesDir,
|
|
266
|
+
base.rulesDir,
|
|
267
|
+
"paths.rulesDir",
|
|
285
268
|
configPath,
|
|
286
269
|
issues
|
|
287
270
|
),
|
|
288
|
-
|
|
289
|
-
raw.
|
|
290
|
-
base.
|
|
291
|
-
"paths.
|
|
271
|
+
outDir: readString(
|
|
272
|
+
raw.outDir,
|
|
273
|
+
base.outDir,
|
|
274
|
+
"paths.outDir",
|
|
292
275
|
configPath,
|
|
293
276
|
issues
|
|
294
277
|
),
|
|
295
|
-
|
|
296
|
-
raw.
|
|
297
|
-
base.
|
|
298
|
-
"paths.
|
|
278
|
+
promptsDir: readString(
|
|
279
|
+
raw.promptsDir,
|
|
280
|
+
base.promptsDir,
|
|
281
|
+
"paths.promptsDir",
|
|
299
282
|
configPath,
|
|
300
283
|
issues
|
|
301
284
|
),
|
|
@@ -418,17 +401,10 @@ function normalizeOutput(raw, configPath, issues) {
|
|
|
418
401
|
return base;
|
|
419
402
|
}
|
|
420
403
|
return {
|
|
421
|
-
|
|
422
|
-
raw.
|
|
423
|
-
base.
|
|
424
|
-
"output.
|
|
425
|
-
configPath,
|
|
426
|
-
issues
|
|
427
|
-
),
|
|
428
|
-
jsonPath: readString(
|
|
429
|
-
raw.jsonPath,
|
|
430
|
-
base.jsonPath,
|
|
431
|
-
"output.jsonPath",
|
|
404
|
+
validateJsonPath: readString(
|
|
405
|
+
raw.validateJsonPath,
|
|
406
|
+
base.validateJsonPath,
|
|
407
|
+
"output.validateJsonPath",
|
|
432
408
|
configPath,
|
|
433
409
|
issues
|
|
434
410
|
)
|
|
@@ -495,20 +471,6 @@ function readTraceabilitySeverity(value, fallback, label, configPath, issues) {
|
|
|
495
471
|
}
|
|
496
472
|
return fallback;
|
|
497
473
|
}
|
|
498
|
-
function readOutputFormat(value, fallback, label, configPath, issues) {
|
|
499
|
-
if (value === "text" || value === "json" || value === "github") {
|
|
500
|
-
return value;
|
|
501
|
-
}
|
|
502
|
-
if (value !== void 0) {
|
|
503
|
-
issues.push(
|
|
504
|
-
configIssue(
|
|
505
|
-
configPath,
|
|
506
|
-
`${label} \u306F text|json|github \u306E\u3044\u305A\u308C\u304B\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`
|
|
507
|
-
)
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
return fallback;
|
|
511
|
-
}
|
|
512
474
|
function configIssue(file, message) {
|
|
513
475
|
return {
|
|
514
476
|
code: "QFAI_CONFIG_INVALID",
|
|
@@ -536,6 +498,7 @@ function isRecord(value) {
|
|
|
536
498
|
|
|
537
499
|
// src/core/report.ts
|
|
538
500
|
var import_promises12 = require("fs/promises");
|
|
501
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
539
502
|
|
|
540
503
|
// src/core/discovery.ts
|
|
541
504
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
@@ -596,10 +559,24 @@ async function exists2(target) {
|
|
|
596
559
|
}
|
|
597
560
|
|
|
598
561
|
// src/core/discovery.ts
|
|
599
|
-
var
|
|
600
|
-
async function
|
|
601
|
-
const files = await collectFiles(
|
|
602
|
-
|
|
562
|
+
var SPEC_PACK_DIR_PATTERN = /^spec-\d{3}$/;
|
|
563
|
+
async function collectSpecPackDirs(specsRoot) {
|
|
564
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
565
|
+
const packs = /* @__PURE__ */ new Set();
|
|
566
|
+
for (const file of files) {
|
|
567
|
+
if (isSpecPackFile(file, "spec.md")) {
|
|
568
|
+
packs.add(import_node_path6.default.dirname(file));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return Array.from(packs).sort();
|
|
572
|
+
}
|
|
573
|
+
async function collectSpecFiles(specsRoot) {
|
|
574
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
575
|
+
return files.filter((file) => isSpecPackFile(file, "spec.md"));
|
|
576
|
+
}
|
|
577
|
+
async function collectScenarioFiles(specsRoot) {
|
|
578
|
+
const files = await collectFiles(specsRoot, { extensions: [".md"] });
|
|
579
|
+
return files.filter((file) => isSpecPackFile(file, "scenario.md"));
|
|
603
580
|
}
|
|
604
581
|
async function collectUiContractFiles(uiRoot) {
|
|
605
582
|
return collectFiles(uiRoot, { extensions: [".yaml", ".yml"] });
|
|
@@ -618,9 +595,12 @@ async function collectContractFiles(uiRoot, apiRoot, dataRoot) {
|
|
|
618
595
|
]);
|
|
619
596
|
return { ui, api, db };
|
|
620
597
|
}
|
|
621
|
-
function
|
|
622
|
-
|
|
623
|
-
|
|
598
|
+
function isSpecPackFile(filePath, baseName) {
|
|
599
|
+
if (import_node_path6.default.basename(filePath).toLowerCase() !== baseName) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
const dirName = import_node_path6.default.basename(import_node_path6.default.dirname(filePath)).toLowerCase();
|
|
603
|
+
return SPEC_PACK_DIR_PATTERN.test(dirName);
|
|
624
604
|
}
|
|
625
605
|
|
|
626
606
|
// src/core/ids.ts
|
|
@@ -684,8 +664,8 @@ var import_promises4 = require("fs/promises");
|
|
|
684
664
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
685
665
|
var import_node_url2 = require("url");
|
|
686
666
|
async function resolveToolVersion() {
|
|
687
|
-
if ("0.3.
|
|
688
|
-
return "0.3.
|
|
667
|
+
if ("0.3.1".length > 0) {
|
|
668
|
+
return "0.3.1";
|
|
689
669
|
}
|
|
690
670
|
try {
|
|
691
671
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -705,6 +685,7 @@ function resolvePackageJsonPath() {
|
|
|
705
685
|
|
|
706
686
|
// src/core/validators/contracts.ts
|
|
707
687
|
var import_promises5 = require("fs/promises");
|
|
688
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
708
689
|
|
|
709
690
|
// src/core/contracts.ts
|
|
710
691
|
var import_node_path8 = __toESM(require("path"), 1);
|
|
@@ -760,19 +741,10 @@ var SQL_DANGEROUS_PATTERNS = [
|
|
|
760
741
|
];
|
|
761
742
|
async function validateContracts(root, config) {
|
|
762
743
|
const issues = [];
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
);
|
|
766
|
-
issues.push(
|
|
767
|
-
...await validateApiContracts(
|
|
768
|
-
resolvePath(root, config, "apiContractsDir")
|
|
769
|
-
)
|
|
770
|
-
);
|
|
771
|
-
issues.push(
|
|
772
|
-
...await validateDataContracts(
|
|
773
|
-
resolvePath(root, config, "dataContractsDir")
|
|
774
|
-
)
|
|
775
|
-
);
|
|
744
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
745
|
+
issues.push(...await validateUiContracts(import_node_path9.default.join(contractsRoot, "ui")));
|
|
746
|
+
issues.push(...await validateApiContracts(import_node_path9.default.join(contractsRoot, "api")));
|
|
747
|
+
issues.push(...await validateDataContracts(import_node_path9.default.join(contractsRoot, "db")));
|
|
776
748
|
return issues;
|
|
777
749
|
}
|
|
778
750
|
async function validateUiContracts(uiRoot) {
|
|
@@ -1005,72 +977,78 @@ function issue(code, message, severity, file, rule, refs) {
|
|
|
1005
977
|
return issue7;
|
|
1006
978
|
}
|
|
1007
979
|
|
|
1008
|
-
// src/core/validators/
|
|
980
|
+
// src/core/validators/delta.ts
|
|
1009
981
|
var import_promises6 = require("fs/promises");
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
var
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
const status = extractField(md, "Status");
|
|
1021
|
-
const context = extractField(md, "Context");
|
|
1022
|
-
const decision = extractField(md, "Decision");
|
|
1023
|
-
const consequences = extractField(md, "Consequences");
|
|
1024
|
-
const related = extractField(md, "Related");
|
|
1025
|
-
if (status) fields.status = status;
|
|
1026
|
-
if (context) fields.context = context;
|
|
1027
|
-
if (decision) fields.decision = decision;
|
|
1028
|
-
if (consequences) fields.consequences = consequences;
|
|
1029
|
-
if (related) fields.related = related;
|
|
1030
|
-
const parsed = {
|
|
1031
|
-
file,
|
|
1032
|
-
fields
|
|
1033
|
-
};
|
|
1034
|
-
if (adrId) {
|
|
1035
|
-
parsed.adrId = adrId;
|
|
1036
|
-
}
|
|
1037
|
-
return parsed;
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
// src/core/validators/decisions.ts
|
|
1041
|
-
var REQUIRED_FIELDS = [
|
|
1042
|
-
{ key: "status", label: "Status" },
|
|
1043
|
-
{ key: "context", label: "Context" },
|
|
1044
|
-
{ key: "decision", label: "Decision" },
|
|
1045
|
-
{ key: "consequences", label: "Consequences" }
|
|
1046
|
-
];
|
|
1047
|
-
async function validateDecisions(root, config) {
|
|
1048
|
-
const decisionsRoot = resolvePath(root, config, "decisionsDir");
|
|
1049
|
-
const files = await collectFiles(decisionsRoot, { extensions: [".md"] });
|
|
1050
|
-
if (files.length === 0) {
|
|
982
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
983
|
+
var SECTION_RE = /^##\s+変更区分/m;
|
|
984
|
+
var COMPAT_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Compatibility\b/m;
|
|
985
|
+
var CHANGE_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Change\/Improvement\b/m;
|
|
986
|
+
var COMPAT_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Compatibility\b/m;
|
|
987
|
+
var CHANGE_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Change\/Improvement\b/m;
|
|
988
|
+
async function validateDeltas(root, config) {
|
|
989
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
990
|
+
const packs = await collectSpecPackDirs(specsRoot);
|
|
991
|
+
if (packs.length === 0) {
|
|
1051
992
|
return [];
|
|
1052
993
|
}
|
|
1053
994
|
const issues = [];
|
|
1054
|
-
for (const
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
(
|
|
1059
|
-
)
|
|
1060
|
-
|
|
995
|
+
for (const pack of packs) {
|
|
996
|
+
const deltaPath = import_node_path10.default.join(pack, "delta.md");
|
|
997
|
+
let text;
|
|
998
|
+
try {
|
|
999
|
+
text = await (0, import_promises6.readFile)(deltaPath, "utf-8");
|
|
1000
|
+
} catch (error2) {
|
|
1001
|
+
if (isMissingFileError(error2)) {
|
|
1002
|
+
issues.push(
|
|
1003
|
+
issue2(
|
|
1004
|
+
"QFAI-DELTA-001",
|
|
1005
|
+
"delta.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
|
|
1006
|
+
"error",
|
|
1007
|
+
deltaPath,
|
|
1008
|
+
"delta.exists"
|
|
1009
|
+
)
|
|
1010
|
+
);
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
throw error2;
|
|
1014
|
+
}
|
|
1015
|
+
const hasSection = SECTION_RE.test(text);
|
|
1016
|
+
const hasCompatibility = COMPAT_LINE_RE.test(text);
|
|
1017
|
+
const hasChange = CHANGE_LINE_RE.test(text);
|
|
1018
|
+
if (!hasSection || !hasCompatibility || !hasChange) {
|
|
1061
1019
|
issues.push(
|
|
1062
1020
|
issue2(
|
|
1063
|
-
"QFAI-
|
|
1064
|
-
|
|
1021
|
+
"QFAI-DELTA-002",
|
|
1022
|
+
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
1065
1023
|
"error",
|
|
1066
|
-
|
|
1067
|
-
"
|
|
1024
|
+
deltaPath,
|
|
1025
|
+
"delta.section"
|
|
1026
|
+
)
|
|
1027
|
+
);
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
const compatibilityChecked = COMPAT_CHECKED_RE.test(text);
|
|
1031
|
+
const changeChecked = CHANGE_CHECKED_RE.test(text);
|
|
1032
|
+
if (compatibilityChecked === changeChecked) {
|
|
1033
|
+
issues.push(
|
|
1034
|
+
issue2(
|
|
1035
|
+
"QFAI-DELTA-003",
|
|
1036
|
+
"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",
|
|
1037
|
+
"error",
|
|
1038
|
+
deltaPath,
|
|
1039
|
+
"delta.classification"
|
|
1068
1040
|
)
|
|
1069
1041
|
);
|
|
1070
1042
|
}
|
|
1071
1043
|
}
|
|
1072
1044
|
return issues;
|
|
1073
1045
|
}
|
|
1046
|
+
function isMissingFileError(error2) {
|
|
1047
|
+
if (!error2 || typeof error2 !== "object") {
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
return error2.code === "ENOENT";
|
|
1051
|
+
}
|
|
1074
1052
|
function issue2(code, message, severity, file, rule, refs) {
|
|
1075
1053
|
const issue7 = {
|
|
1076
1054
|
code,
|
|
@@ -1091,14 +1069,16 @@ function issue2(code, message, severity, file, rule, refs) {
|
|
|
1091
1069
|
|
|
1092
1070
|
// src/core/validators/ids.ts
|
|
1093
1071
|
var import_promises8 = require("fs/promises");
|
|
1094
|
-
var
|
|
1072
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
1095
1073
|
|
|
1096
1074
|
// src/core/contractIndex.ts
|
|
1097
1075
|
var import_promises7 = require("fs/promises");
|
|
1076
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
1098
1077
|
async function buildContractIndex(root, config) {
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
const
|
|
1078
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
1079
|
+
const uiRoot = import_node_path11.default.join(contractsRoot, "ui");
|
|
1080
|
+
const apiRoot = import_node_path11.default.join(contractsRoot, "api");
|
|
1081
|
+
const dataRoot = import_node_path11.default.join(contractsRoot, "db");
|
|
1102
1082
|
const [uiFiles, apiFiles, dataFiles] = await Promise.all([
|
|
1103
1083
|
collectUiContractFiles(uiRoot),
|
|
1104
1084
|
collectApiContractFiles(apiRoot),
|
|
@@ -1154,7 +1134,7 @@ function record(index, id, file) {
|
|
|
1154
1134
|
|
|
1155
1135
|
// src/core/parse/gherkin.ts
|
|
1156
1136
|
var FEATURE_RE = /^\s*Feature:\s+/;
|
|
1157
|
-
var SCENARIO_RE = /^\s*Scenario
|
|
1137
|
+
var SCENARIO_RE = /^\s*Scenario(?: Outline)?:\s*(.+)\s*$/;
|
|
1158
1138
|
var TAG_LINE_RE = /^\s*@/;
|
|
1159
1139
|
function parseTags(line) {
|
|
1160
1140
|
return line.trim().split(/\s+/).filter((tag) => tag.startsWith("@")).map((tag) => tag.replace(/^@/, ""));
|
|
@@ -1163,24 +1143,52 @@ function parseGherkinFeature(text, file) {
|
|
|
1163
1143
|
const lines = text.split(/\r?\n/);
|
|
1164
1144
|
const scenarios = [];
|
|
1165
1145
|
let featurePresent = false;
|
|
1146
|
+
let featureTags = [];
|
|
1147
|
+
let pendingTags = [];
|
|
1148
|
+
let current = null;
|
|
1149
|
+
const flush = () => {
|
|
1150
|
+
if (!current) return;
|
|
1151
|
+
scenarios.push({
|
|
1152
|
+
...current,
|
|
1153
|
+
body: current.body.trim()
|
|
1154
|
+
});
|
|
1155
|
+
current = null;
|
|
1156
|
+
};
|
|
1166
1157
|
for (let i = 0; i < lines.length; i++) {
|
|
1167
1158
|
const line = lines[i] ?? "";
|
|
1168
|
-
|
|
1159
|
+
const trimmed = line.trim();
|
|
1160
|
+
if (TAG_LINE_RE.test(trimmed)) {
|
|
1161
|
+
pendingTags.push(...parseTags(trimmed));
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (FEATURE_RE.test(trimmed)) {
|
|
1169
1165
|
featurePresent = true;
|
|
1166
|
+
featureTags = [...pendingTags];
|
|
1167
|
+
pendingTags = [];
|
|
1168
|
+
continue;
|
|
1170
1169
|
}
|
|
1171
|
-
const match =
|
|
1172
|
-
if (
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1170
|
+
const match = trimmed.match(SCENARIO_RE);
|
|
1171
|
+
if (match) {
|
|
1172
|
+
const scenarioName = match[1]?.trim();
|
|
1173
|
+
if (!scenarioName) {
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
flush();
|
|
1177
|
+
current = {
|
|
1178
|
+
name: scenarioName,
|
|
1179
|
+
line: i + 1,
|
|
1180
|
+
tags: [...featureTags, ...pendingTags],
|
|
1181
|
+
body: ""
|
|
1182
|
+
};
|
|
1183
|
+
pendingTags = [];
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
if (current) {
|
|
1187
|
+
current.body += `${line}
|
|
1188
|
+
`;
|
|
1181
1189
|
}
|
|
1182
|
-
scenarios.push({ name: scenarioName, line: i + 1, tags });
|
|
1183
1190
|
}
|
|
1191
|
+
flush();
|
|
1184
1192
|
return { file, featurePresent, scenarios };
|
|
1185
1193
|
}
|
|
1186
1194
|
|
|
@@ -1227,9 +1235,9 @@ function extractH2Sections(md) {
|
|
|
1227
1235
|
|
|
1228
1236
|
// src/core/parse/spec.ts
|
|
1229
1237
|
var SPEC_ID_RE = /\bSPEC-\d{4}\b/;
|
|
1230
|
-
var BR_LINE_RE = /^\s
|
|
1231
|
-
var BR_LINE_ANY_PRIORITY_RE = /^\s
|
|
1232
|
-
var BR_LINE_NO_PRIORITY_RE = /^\s
|
|
1238
|
+
var BR_LINE_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[0-3])\]\s*(.+)$/;
|
|
1239
|
+
var BR_LINE_ANY_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[^\]]+)\]\s*(.+)$/;
|
|
1240
|
+
var BR_LINE_NO_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\](?!\s*\[P)\s*(.*\S.*)$/;
|
|
1233
1241
|
var BR_SECTION_TITLE = "\u696D\u52D9\u30EB\u30FC\u30EB";
|
|
1234
1242
|
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
1235
1243
|
function parseSpec(md, file) {
|
|
@@ -1306,12 +1314,9 @@ function parseSpec(md, file) {
|
|
|
1306
1314
|
var SC_TAG_RE = /^SC-\d{4}$/;
|
|
1307
1315
|
async function validateDefinedIds(root, config) {
|
|
1308
1316
|
const issues = [];
|
|
1309
|
-
const
|
|
1310
|
-
const
|
|
1311
|
-
const
|
|
1312
|
-
const scenarioFiles = await collectFiles(scenarioRoot, {
|
|
1313
|
-
extensions: [".feature"]
|
|
1314
|
-
});
|
|
1317
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1318
|
+
const specFiles = await collectSpecFiles(specsRoot);
|
|
1319
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
1315
1320
|
const defined = /* @__PURE__ */ new Map();
|
|
1316
1321
|
await collectSpecDefinitionIds(specFiles, defined);
|
|
1317
1322
|
await collectScenarioDefinitionIds(scenarioFiles, defined);
|
|
@@ -1368,7 +1373,7 @@ function recordId(out, id, file) {
|
|
|
1368
1373
|
}
|
|
1369
1374
|
function formatFileList(files, root) {
|
|
1370
1375
|
return files.map((file) => {
|
|
1371
|
-
const relative =
|
|
1376
|
+
const relative = import_node_path12.default.relative(root, file);
|
|
1372
1377
|
return relative.length > 0 ? relative : file;
|
|
1373
1378
|
}).join(", ");
|
|
1374
1379
|
}
|
|
@@ -1399,17 +1404,15 @@ var SC_TAG_RE2 = /^SC-\d{4}$/;
|
|
|
1399
1404
|
var SPEC_TAG_RE = /^SPEC-\d{4}$/;
|
|
1400
1405
|
var BR_TAG_RE = /^BR-\d{4}$/;
|
|
1401
1406
|
async function validateScenarios(root, config) {
|
|
1402
|
-
const
|
|
1403
|
-
const files = await
|
|
1404
|
-
extensions: [".feature"]
|
|
1405
|
-
});
|
|
1407
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1408
|
+
const files = await collectScenarioFiles(specsRoot);
|
|
1406
1409
|
if (files.length === 0) {
|
|
1407
1410
|
return [
|
|
1408
1411
|
issue4(
|
|
1409
1412
|
"QFAI-SC-000",
|
|
1410
1413
|
"Scenario \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
|
|
1411
1414
|
"info",
|
|
1412
|
-
|
|
1415
|
+
specsRoot,
|
|
1413
1416
|
"scenario.files"
|
|
1414
1417
|
)
|
|
1415
1418
|
];
|
|
@@ -1475,8 +1478,11 @@ function validateScenarioContent(text, file) {
|
|
|
1475
1478
|
continue;
|
|
1476
1479
|
}
|
|
1477
1480
|
const missingTags = [];
|
|
1478
|
-
|
|
1479
|
-
|
|
1481
|
+
const scTags = scenario.tags.filter((tag) => SC_TAG_RE2.test(tag));
|
|
1482
|
+
if (scTags.length === 0) {
|
|
1483
|
+
missingTags.push("SC(0\u4EF6)");
|
|
1484
|
+
} else if (scTags.length > 1) {
|
|
1485
|
+
missingTags.push(`SC(${scTags.length}\u4EF6/1\u4EF6\u5FC5\u9808)`);
|
|
1480
1486
|
}
|
|
1481
1487
|
if (!scenario.tags.some((tag) => SPEC_TAG_RE.test(tag))) {
|
|
1482
1488
|
missingTags.push("SPEC");
|
|
@@ -1496,26 +1502,28 @@ function validateScenarioContent(text, file) {
|
|
|
1496
1502
|
);
|
|
1497
1503
|
}
|
|
1498
1504
|
}
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1505
|
+
for (const scenario of parsed.scenarios) {
|
|
1506
|
+
const missingSteps = [];
|
|
1507
|
+
if (!GIVEN_PATTERN.test(scenario.body)) {
|
|
1508
|
+
missingSteps.push("Given");
|
|
1509
|
+
}
|
|
1510
|
+
if (!WHEN_PATTERN.test(scenario.body)) {
|
|
1511
|
+
missingSteps.push("When");
|
|
1512
|
+
}
|
|
1513
|
+
if (!THEN_PATTERN.test(scenario.body)) {
|
|
1514
|
+
missingSteps.push("Then");
|
|
1515
|
+
}
|
|
1516
|
+
if (missingSteps.length > 0) {
|
|
1517
|
+
issues.push(
|
|
1518
|
+
issue4(
|
|
1519
|
+
"QFAI-SC-005",
|
|
1520
|
+
`Given/When/Then \u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059: ${missingSteps.join(", ")} (${scenario.name})`,
|
|
1521
|
+
"warning",
|
|
1522
|
+
file,
|
|
1523
|
+
"scenario.steps"
|
|
1524
|
+
)
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1519
1527
|
}
|
|
1520
1528
|
return issues;
|
|
1521
1529
|
}
|
|
@@ -1540,14 +1548,14 @@ function issue4(code, message, severity, file, rule, refs) {
|
|
|
1540
1548
|
// src/core/validators/spec.ts
|
|
1541
1549
|
var import_promises10 = require("fs/promises");
|
|
1542
1550
|
async function validateSpecs(root, config) {
|
|
1543
|
-
const specsRoot = resolvePath(root, config, "
|
|
1551
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1544
1552
|
const files = await collectSpecFiles(specsRoot);
|
|
1545
1553
|
if (files.length === 0) {
|
|
1546
|
-
const expected = "spec-
|
|
1554
|
+
const expected = "spec-001/spec.md";
|
|
1547
1555
|
return [
|
|
1548
1556
|
issue5(
|
|
1549
1557
|
"QFAI-SPEC-000",
|
|
1550
|
-
`Spec \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.
|
|
1558
|
+
`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}`,
|
|
1551
1559
|
"info",
|
|
1552
1560
|
specsRoot,
|
|
1553
1561
|
"spec.files"
|
|
@@ -1693,18 +1701,11 @@ var API_TAG_RE = /^API-\d{4}$/;
|
|
|
1693
1701
|
var DATA_TAG_RE = /^DATA-\d{4}$/;
|
|
1694
1702
|
async function validateTraceability(root, config) {
|
|
1695
1703
|
const issues = [];
|
|
1696
|
-
const specsRoot = resolvePath(root, config, "
|
|
1697
|
-
const decisionsRoot = resolvePath(root, config, "decisionsDir");
|
|
1698
|
-
const scenariosRoot = resolvePath(root, config, "scenariosDir");
|
|
1704
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
1699
1705
|
const srcRoot = resolvePath(root, config, "srcDir");
|
|
1700
1706
|
const testsRoot = resolvePath(root, config, "testsDir");
|
|
1701
1707
|
const specFiles = await collectSpecFiles(specsRoot);
|
|
1702
|
-
const
|
|
1703
|
-
extensions: [".md"]
|
|
1704
|
-
});
|
|
1705
|
-
const scenarioFiles = await collectFiles(scenariosRoot, {
|
|
1706
|
-
extensions: [".feature"]
|
|
1707
|
-
});
|
|
1708
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
1708
1709
|
const upstreamIds = /* @__PURE__ */ new Set();
|
|
1709
1710
|
const specIds = /* @__PURE__ */ new Set();
|
|
1710
1711
|
const brIdsInSpecs = /* @__PURE__ */ new Set();
|
|
@@ -1752,10 +1753,6 @@ async function validateTraceability(root, config) {
|
|
|
1752
1753
|
specToBrIds.set(parsed.specId, current);
|
|
1753
1754
|
}
|
|
1754
1755
|
}
|
|
1755
|
-
for (const file of decisionFiles) {
|
|
1756
|
-
const text = await (0, import_promises11.readFile)(file, "utf-8");
|
|
1757
|
-
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
1758
|
-
}
|
|
1759
1756
|
for (const file of scenarioFiles) {
|
|
1760
1757
|
const text = await (0, import_promises11.readFile)(file, "utf-8");
|
|
1761
1758
|
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
@@ -1899,7 +1896,7 @@ async function validateTraceability(root, config) {
|
|
|
1899
1896
|
", "
|
|
1900
1897
|
)}`,
|
|
1901
1898
|
"error",
|
|
1902
|
-
|
|
1899
|
+
specsRoot,
|
|
1903
1900
|
"traceability.scMustTouchContracts",
|
|
1904
1901
|
scWithoutContracts
|
|
1905
1902
|
)
|
|
@@ -1917,7 +1914,7 @@ async function validateTraceability(root, config) {
|
|
|
1917
1914
|
"QFAI_CONTRACT_ORPHAN",
|
|
1918
1915
|
`\u5951\u7D04\u304C SC \u304B\u3089\u53C2\u7167\u3055\u308C\u3066\u3044\u307E\u305B\u3093: ${orphanContracts.join(", ")}`,
|
|
1919
1916
|
"error",
|
|
1920
|
-
|
|
1917
|
+
specsRoot,
|
|
1921
1918
|
"traceability.allowOrphanContracts",
|
|
1922
1919
|
orphanContracts
|
|
1923
1920
|
)
|
|
@@ -2002,8 +1999,8 @@ async function validateProject(root, configResult) {
|
|
|
2002
1999
|
const issues = [
|
|
2003
2000
|
...configIssues,
|
|
2004
2001
|
...await validateSpecs(root, config),
|
|
2002
|
+
...await validateDeltas(root, config),
|
|
2005
2003
|
...await validateScenarios(root, config),
|
|
2006
|
-
...await validateDecisions(root, config),
|
|
2007
2004
|
...await validateContracts(root, config),
|
|
2008
2005
|
...await validateDefinedIds(root, config),
|
|
2009
2006
|
...await validateTraceability(root, config)
|
|
@@ -2032,21 +2029,15 @@ async function createReportData(root, validation, configResult) {
|
|
|
2032
2029
|
const resolved = configResult ?? await loadConfig(root);
|
|
2033
2030
|
const config = resolved.config;
|
|
2034
2031
|
const configPath = resolved.configPath;
|
|
2035
|
-
const
|
|
2036
|
-
const
|
|
2037
|
-
const
|
|
2038
|
-
const
|
|
2039
|
-
const
|
|
2040
|
-
const dbRoot = resolvePath(root, config, "dataContractsDir");
|
|
2032
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
2033
|
+
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
2034
|
+
const apiRoot = import_node_path13.default.join(contractsRoot, "api");
|
|
2035
|
+
const uiRoot = import_node_path13.default.join(contractsRoot, "ui");
|
|
2036
|
+
const dbRoot = import_node_path13.default.join(contractsRoot, "db");
|
|
2041
2037
|
const srcRoot = resolvePath(root, config, "srcDir");
|
|
2042
2038
|
const testsRoot = resolvePath(root, config, "testsDir");
|
|
2043
|
-
const specFiles = await collectSpecFiles(
|
|
2044
|
-
const scenarioFiles = await
|
|
2045
|
-
extensions: [".feature"]
|
|
2046
|
-
});
|
|
2047
|
-
const decisionFiles = await collectFiles(decisionsRoot, {
|
|
2048
|
-
extensions: [".md"]
|
|
2049
|
-
});
|
|
2039
|
+
const specFiles = await collectSpecFiles(specsRoot);
|
|
2040
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
2050
2041
|
const {
|
|
2051
2042
|
api: apiFiles,
|
|
2052
2043
|
ui: uiFiles,
|
|
@@ -2055,7 +2046,6 @@ async function createReportData(root, validation, configResult) {
|
|
|
2055
2046
|
const idsByPrefix = await collectIds([
|
|
2056
2047
|
...specFiles,
|
|
2057
2048
|
...scenarioFiles,
|
|
2058
|
-
...decisionFiles,
|
|
2059
2049
|
...apiFiles,
|
|
2060
2050
|
...uiFiles,
|
|
2061
2051
|
...dbFiles
|
|
@@ -2080,7 +2070,6 @@ async function createReportData(root, validation, configResult) {
|
|
|
2080
2070
|
summary: {
|
|
2081
2071
|
specs: specFiles.length,
|
|
2082
2072
|
scenarios: scenarioFiles.length,
|
|
2083
|
-
decisions: decisionFiles.length,
|
|
2084
2073
|
contracts: {
|
|
2085
2074
|
api: apiFiles.length,
|
|
2086
2075
|
ui: uiFiles.length,
|
|
@@ -2114,7 +2103,6 @@ function formatReportMarkdown(data) {
|
|
|
2114
2103
|
lines.push("## \u6982\u8981");
|
|
2115
2104
|
lines.push(`- specs: ${data.summary.specs}`);
|
|
2116
2105
|
lines.push(`- scenarios: ${data.summary.scenarios}`);
|
|
2117
|
-
lines.push(`- decisions: ${data.summary.decisions}`);
|
|
2118
2106
|
lines.push(
|
|
2119
2107
|
`- contracts: api ${data.summary.contracts.api} / ui ${data.summary.contracts.ui} / db ${data.summary.contracts.db}`
|
|
2120
2108
|
);
|
|
@@ -2273,21 +2261,22 @@ function buildHotspots(issues) {
|
|
|
2273
2261
|
|
|
2274
2262
|
// src/cli/commands/report.ts
|
|
2275
2263
|
async function runReport(options) {
|
|
2276
|
-
const root =
|
|
2264
|
+
const root = import_node_path14.default.resolve(options.root);
|
|
2277
2265
|
const configResult = await loadConfig(root);
|
|
2278
|
-
const input =
|
|
2279
|
-
const inputPath =
|
|
2266
|
+
const input = configResult.config.output.validateJsonPath;
|
|
2267
|
+
const inputPath = import_node_path14.default.isAbsolute(input) ? input : import_node_path14.default.resolve(root, input);
|
|
2280
2268
|
let validation;
|
|
2281
2269
|
try {
|
|
2282
2270
|
validation = await readValidationResult(inputPath);
|
|
2283
2271
|
} catch (err) {
|
|
2284
|
-
if (
|
|
2272
|
+
if (isMissingFileError2(err)) {
|
|
2285
2273
|
error(
|
|
2286
2274
|
[
|
|
2287
2275
|
`qfai report: \u5165\u529B\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${inputPath}`,
|
|
2288
2276
|
"",
|
|
2289
|
-
"\u307E\u305A validate
|
|
2290
|
-
|
|
2277
|
+
"\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
|
|
2278
|
+
" qfai validate",
|
|
2279
|
+
"\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/out/validate.json\uFF09",
|
|
2291
2280
|
"",
|
|
2292
2281
|
"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"
|
|
2293
2282
|
].join("\n")
|
|
@@ -2299,10 +2288,11 @@ async function runReport(options) {
|
|
|
2299
2288
|
}
|
|
2300
2289
|
const data = await createReportData(root, validation, configResult);
|
|
2301
2290
|
const output = options.format === "json" ? formatReportJson(data) : formatReportMarkdown(data);
|
|
2302
|
-
const
|
|
2291
|
+
const outRoot = resolvePath(root, configResult.config, "outDir");
|
|
2292
|
+
const defaultOut = options.format === "json" ? import_node_path14.default.join(outRoot, "report.json") : import_node_path14.default.join(outRoot, "report.md");
|
|
2303
2293
|
const out = options.outPath ?? defaultOut;
|
|
2304
|
-
const outPath =
|
|
2305
|
-
await (0, import_promises13.mkdir)(
|
|
2294
|
+
const outPath = import_node_path14.default.isAbsolute(out) ? out : import_node_path14.default.resolve(root, out);
|
|
2295
|
+
await (0, import_promises13.mkdir)(import_node_path14.default.dirname(outPath), { recursive: true });
|
|
2306
2296
|
await (0, import_promises13.writeFile)(outPath, `${output}
|
|
2307
2297
|
`, "utf-8");
|
|
2308
2298
|
info(
|
|
@@ -2343,7 +2333,7 @@ function isValidationResult(value) {
|
|
|
2343
2333
|
}
|
|
2344
2334
|
return typeof counts.info === "number" && typeof counts.warning === "number" && typeof counts.error === "number";
|
|
2345
2335
|
}
|
|
2346
|
-
function
|
|
2336
|
+
function isMissingFileError2(error2) {
|
|
2347
2337
|
if (!error2 || typeof error2 !== "object") {
|
|
2348
2338
|
return false;
|
|
2349
2339
|
}
|
|
@@ -2353,7 +2343,7 @@ function isMissingFileError(error2) {
|
|
|
2353
2343
|
|
|
2354
2344
|
// src/cli/commands/validate.ts
|
|
2355
2345
|
var import_promises14 = require("fs/promises");
|
|
2356
|
-
var
|
|
2346
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
2357
2347
|
|
|
2358
2348
|
// src/cli/lib/failOn.ts
|
|
2359
2349
|
function shouldFail(result, failOn) {
|
|
@@ -2368,24 +2358,17 @@ function shouldFail(result, failOn) {
|
|
|
2368
2358
|
|
|
2369
2359
|
// src/cli/commands/validate.ts
|
|
2370
2360
|
async function runValidate(options) {
|
|
2371
|
-
const root =
|
|
2361
|
+
const root = import_node_path15.default.resolve(options.root);
|
|
2372
2362
|
const configResult = await loadConfig(root);
|
|
2373
2363
|
const result = await validateProject(root, configResult);
|
|
2374
|
-
const format = options.format ??
|
|
2375
|
-
const explicitJsonPath = options.jsonPath;
|
|
2364
|
+
const format = options.format ?? "text";
|
|
2376
2365
|
if (format === "text") {
|
|
2377
2366
|
emitText(result);
|
|
2378
2367
|
}
|
|
2379
2368
|
if (format === "github") {
|
|
2380
2369
|
result.issues.forEach(emitGitHub);
|
|
2381
2370
|
}
|
|
2382
|
-
|
|
2383
|
-
if (shouldWriteJson) {
|
|
2384
|
-
const jsonPath = format === "json" ? options.jsonPath ?? configResult.config.output.jsonPath : explicitJsonPath;
|
|
2385
|
-
if (jsonPath) {
|
|
2386
|
-
await emitJson(result, root, jsonPath);
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2371
|
+
await emitJson(result, root, configResult.config.output.validateJsonPath);
|
|
2389
2372
|
const failOn = resolveFailOn(options, configResult.config.validation.failOn);
|
|
2390
2373
|
return shouldFail(result, failOn) ? 1 : 0;
|
|
2391
2374
|
}
|
|
@@ -2424,8 +2407,8 @@ function emitGitHub(issue7) {
|
|
|
2424
2407
|
);
|
|
2425
2408
|
}
|
|
2426
2409
|
async function emitJson(result, root, jsonPath) {
|
|
2427
|
-
const abs =
|
|
2428
|
-
await (0, import_promises14.mkdir)(
|
|
2410
|
+
const abs = import_node_path15.default.isAbsolute(jsonPath) ? jsonPath : import_node_path15.default.resolve(root, jsonPath);
|
|
2411
|
+
await (0, import_promises14.mkdir)(import_node_path15.default.dirname(abs), { recursive: true });
|
|
2429
2412
|
await (0, import_promises14.writeFile)(abs, `${JSON.stringify(result, null, 2)}
|
|
2430
2413
|
`, "utf-8");
|
|
2431
2414
|
}
|
|
@@ -2486,15 +2469,6 @@ function parseArgs(argv, cwd) {
|
|
|
2486
2469
|
i += 1;
|
|
2487
2470
|
break;
|
|
2488
2471
|
}
|
|
2489
|
-
case "--json-path":
|
|
2490
|
-
{
|
|
2491
|
-
const next = args[i + 1];
|
|
2492
|
-
if (next) {
|
|
2493
|
-
options.jsonPath = next;
|
|
2494
|
-
}
|
|
2495
|
-
}
|
|
2496
|
-
i += 1;
|
|
2497
|
-
break;
|
|
2498
2472
|
case "--out":
|
|
2499
2473
|
{
|
|
2500
2474
|
const next = args[i + 1];
|
|
@@ -2525,7 +2499,7 @@ function applyFormatOption(command, value, options) {
|
|
|
2525
2499
|
return;
|
|
2526
2500
|
}
|
|
2527
2501
|
if (command === "validate") {
|
|
2528
|
-
if (value === "text" || value === "
|
|
2502
|
+
if (value === "text" || value === "github") {
|
|
2529
2503
|
options.validateFormat = value;
|
|
2530
2504
|
}
|
|
2531
2505
|
return;
|
|
@@ -2533,7 +2507,7 @@ function applyFormatOption(command, value, options) {
|
|
|
2533
2507
|
if (value === "md" || value === "json") {
|
|
2534
2508
|
options.reportFormat = value;
|
|
2535
2509
|
}
|
|
2536
|
-
if (value === "text" || value === "
|
|
2510
|
+
if (value === "text" || value === "github") {
|
|
2537
2511
|
options.validateFormat = value;
|
|
2538
2512
|
}
|
|
2539
2513
|
}
|
|
@@ -2559,15 +2533,13 @@ async function run(argv, cwd) {
|
|
|
2559
2533
|
root: options.root,
|
|
2560
2534
|
strict: options.strict,
|
|
2561
2535
|
format: options.validateFormat,
|
|
2562
|
-
...options.failOn !== void 0 ? { failOn: options.failOn } : {}
|
|
2563
|
-
...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {}
|
|
2536
|
+
...options.failOn !== void 0 ? { failOn: options.failOn } : {}
|
|
2564
2537
|
});
|
|
2565
2538
|
return;
|
|
2566
2539
|
case "report":
|
|
2567
2540
|
await runReport({
|
|
2568
2541
|
root: options.root,
|
|
2569
2542
|
format: options.reportFormat,
|
|
2570
|
-
...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {},
|
|
2571
2543
|
...options.reportOut !== void 0 ? { outPath: options.reportOut } : {}
|
|
2572
2544
|
});
|
|
2573
2545
|
return;
|
|
@@ -2589,14 +2561,13 @@ Options:
|
|
|
2589
2561
|
--root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
|
|
2590
2562
|
--dir <path> init \u306E\u51FA\u529B\u5148
|
|
2591
2563
|
--force \u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D
|
|
2592
|
-
--yes init: \
|
|
2564
|
+
--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
|
|
2593
2565
|
--dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
|
|
2594
|
-
--format <text|
|
|
2566
|
+
--format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
|
|
2595
2567
|
--format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
|
|
2596
|
-
--strict
|
|
2568
|
+
--strict validate: warning \u4EE5\u4E0A\u3067 exit 1
|
|
2597
2569
|
--fail-on <error|warning|never> validate: \u5931\u6557\u6761\u4EF6
|
|
2598
|
-
--
|
|
2599
|
-
--out <path> report: \u51FA\u529B\u5148
|
|
2570
|
+
--out <path> report: \u51FA\u529B\u5148
|
|
2600
2571
|
-h, --help \u30D8\u30EB\u30D7\u8868\u793A
|
|
2601
2572
|
`;
|
|
2602
2573
|
}
|