executable-stories-formatters 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +473 -247
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +315 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -5
- package/dist/index.d.ts +100 -5
- package/dist/index.js +308 -102
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { parseArgs } from "util";
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
5
|
+
import * as fs17 from "fs";
|
|
6
|
+
import * as path18 from "path";
|
|
7
7
|
|
|
8
8
|
// src/validation/schema-validator.ts
|
|
9
9
|
import Ajv from "ajv/dist/2020.js";
|
|
@@ -588,17 +588,17 @@ function validateRawRun(data) {
|
|
|
588
588
|
return { valid: true, errors: [] };
|
|
589
589
|
}
|
|
590
590
|
const errors = (validate.errors ?? []).map((err) => {
|
|
591
|
-
const
|
|
591
|
+
const path19 = err.instancePath || "/";
|
|
592
592
|
const message = err.message ?? "unknown error";
|
|
593
593
|
if (err.keyword === "additionalProperties") {
|
|
594
594
|
const extra = err.params.additionalProperty;
|
|
595
|
-
return `${
|
|
595
|
+
return `${path19}: ${message} \u2014 '${extra}'`;
|
|
596
596
|
}
|
|
597
597
|
if (err.keyword === "enum") {
|
|
598
598
|
const allowed = err.params.allowedValues;
|
|
599
|
-
return `${
|
|
599
|
+
return `${path19}: ${message} \u2014 allowed: ${JSON.stringify(allowed)}`;
|
|
600
600
|
}
|
|
601
|
-
return `${
|
|
601
|
+
return `${path19}: ${message}`;
|
|
602
602
|
});
|
|
603
603
|
return { valid: false, errors };
|
|
604
604
|
}
|
|
@@ -711,7 +711,7 @@ function tokenize(text2) {
|
|
|
711
711
|
}
|
|
712
712
|
function behaviourFingerprint(input) {
|
|
713
713
|
const steps = input.steps.map((step) => `${step.keyword.toLowerCase()}:${normalizeText(step.text)}`).join("\n");
|
|
714
|
-
const covers = [...input.covers ?? []].map((
|
|
714
|
+
const covers = [...input.covers ?? []].map((path19) => path19.trim()).filter(Boolean).sort().join(",");
|
|
715
715
|
if (steps.length === 0 && covers.length === 0) return "";
|
|
716
716
|
return createHash("sha1").update(`${steps}\0${covers}`).digest("hex").slice(0, 16);
|
|
717
717
|
}
|
|
@@ -1108,7 +1108,7 @@ ${result.errors.join("\n")}`);
|
|
|
1108
1108
|
|
|
1109
1109
|
// src/index.ts
|
|
1110
1110
|
import "fs";
|
|
1111
|
-
import * as
|
|
1111
|
+
import * as path10 from "path";
|
|
1112
1112
|
import * as fsPromises from "fs/promises";
|
|
1113
1113
|
|
|
1114
1114
|
// src/converters/acl/lines.ts
|
|
@@ -15012,12 +15012,12 @@ function highlightStepParams(text2, deps) {
|
|
|
15012
15012
|
var MIN_METRIC_SAMPLES = 5;
|
|
15013
15013
|
|
|
15014
15014
|
// src/formatters/html/renderers/scenario.ts
|
|
15015
|
-
function renderTicket(ticket, template,
|
|
15015
|
+
function renderTicket(ticket, template, escapeHtml5) {
|
|
15016
15016
|
const url = ticket.url ?? (template ? template.replace("{ticket}", ticket.id) : void 0);
|
|
15017
15017
|
if (url) {
|
|
15018
|
-
return `<a class="tag ticket-tag" href="${
|
|
15018
|
+
return `<a class="tag ticket-tag" href="${escapeHtml5(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml5(ticket.id)}</a>`;
|
|
15019
15019
|
}
|
|
15020
|
-
return `<span class="tag ticket-tag">${
|
|
15020
|
+
return `<span class="tag ticket-tag">${escapeHtml5(ticket.id)}</span>`;
|
|
15021
15021
|
}
|
|
15022
15022
|
function renderScenario(args, deps) {
|
|
15023
15023
|
const { tc } = args;
|
|
@@ -15217,7 +15217,7 @@ function flattenTree(roots) {
|
|
|
15217
15217
|
}
|
|
15218
15218
|
return result;
|
|
15219
15219
|
}
|
|
15220
|
-
function buildTooltip(span,
|
|
15220
|
+
function buildTooltip(span, escapeHtml5) {
|
|
15221
15221
|
const parts = [];
|
|
15222
15222
|
parts.push(`${span.name} (${formatDuration(span.durationMs)})`);
|
|
15223
15223
|
if (span.statusMessage) {
|
|
@@ -15235,7 +15235,7 @@ function buildTooltip(span, escapeHtml4) {
|
|
|
15235
15235
|
if (text2.length > TOOLTIP_MAX_LENGTH) {
|
|
15236
15236
|
text2 = text2.slice(0, TOOLTIP_MAX_LENGTH - 3) + "...";
|
|
15237
15237
|
}
|
|
15238
|
-
return
|
|
15238
|
+
return escapeHtml5(text2);
|
|
15239
15239
|
}
|
|
15240
15240
|
function renderTraceView(args, deps) {
|
|
15241
15241
|
if (!args.spans || args.spans.length === 0) return "";
|
|
@@ -16530,14 +16530,14 @@ var TraceabilityMatrixFormatter = class {
|
|
|
16530
16530
|
lines.push("");
|
|
16531
16531
|
lines.push(`Status: ${renderRequirementStatus(req.status)}`);
|
|
16532
16532
|
if (req.covers.length > 0) {
|
|
16533
|
-
lines.push(`Covers: ${req.covers.map((
|
|
16533
|
+
lines.push(`Covers: ${req.covers.map((path19) => `\`${path19}\``).join(", ")}`);
|
|
16534
16534
|
}
|
|
16535
16535
|
lines.push("");
|
|
16536
16536
|
lines.push("| Status | Scenario | Source | Covers |");
|
|
16537
16537
|
lines.push("| --- | --- | --- | --- |");
|
|
16538
16538
|
for (const scenario of req.scenarios) {
|
|
16539
16539
|
const source = `${scenario.sourceFile}:${scenario.sourceLine}`;
|
|
16540
|
-
const covers = scenario.covers.length > 0 ? scenario.covers.map((
|
|
16540
|
+
const covers = scenario.covers.length > 0 ? scenario.covers.map((path19) => `\`${path19}\``).join(", ") : "";
|
|
16541
16541
|
lines.push(`| ${scenario.status} | ${escapePipe2(scenario.title)} | \`${source}\` | ${covers} |`);
|
|
16542
16542
|
}
|
|
16543
16543
|
lines.push("");
|
|
@@ -16639,8 +16639,8 @@ function extractFeatureName(testCases, uri) {
|
|
|
16639
16639
|
return tc.titlePath[0];
|
|
16640
16640
|
}
|
|
16641
16641
|
}
|
|
16642
|
-
const
|
|
16643
|
-
return
|
|
16642
|
+
const basename6 = uri.replace(/^.*[\\/]/, "").replace(/\.[^.]+$/, "");
|
|
16643
|
+
return basename6.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
16644
16644
|
}
|
|
16645
16645
|
function synthesizeFeature(uri, testCases) {
|
|
16646
16646
|
const featureName = extractFeatureName(testCases, uri);
|
|
@@ -17252,8 +17252,8 @@ function extractDocAttachments(step) {
|
|
|
17252
17252
|
}
|
|
17253
17253
|
return attachments;
|
|
17254
17254
|
}
|
|
17255
|
-
function guessMediaType(
|
|
17256
|
-
const lower =
|
|
17255
|
+
function guessMediaType(path19) {
|
|
17256
|
+
const lower = path19.toLowerCase();
|
|
17257
17257
|
if (lower.endsWith(".png")) return "image/png";
|
|
17258
17258
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
17259
17259
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -17394,11 +17394,11 @@ var CucumberHtmlFormatter = class {
|
|
|
17394
17394
|
for (const envelope of envelopes) {
|
|
17395
17395
|
const accepted = htmlStream.write(envelope);
|
|
17396
17396
|
if (!accepted) {
|
|
17397
|
-
await new Promise((
|
|
17397
|
+
await new Promise((resolve13) => htmlStream.once("drain", resolve13));
|
|
17398
17398
|
}
|
|
17399
17399
|
}
|
|
17400
|
-
await new Promise((
|
|
17401
|
-
collector.on("finish",
|
|
17400
|
+
await new Promise((resolve13, reject) => {
|
|
17401
|
+
collector.on("finish", resolve13);
|
|
17402
17402
|
collector.on("error", reject);
|
|
17403
17403
|
htmlStream.end();
|
|
17404
17404
|
});
|
|
@@ -19192,7 +19192,7 @@ function toRun(data, inputType, synthesize) {
|
|
|
19192
19192
|
if (synthesize) raw = synthesizeStories(raw);
|
|
19193
19193
|
return canonicalizeRun(raw);
|
|
19194
19194
|
}
|
|
19195
|
-
async function
|
|
19195
|
+
async function regenerateRun(options, deps = {}) {
|
|
19196
19196
|
const read = deps.readFile ?? ((filePath) => fs6.readFileSync(filePath, "utf8"));
|
|
19197
19197
|
const data = JSON.parse(read(path7.resolve(options.input)));
|
|
19198
19198
|
const run = toRun(data, options.inputType ?? "raw", options.synthesize !== false);
|
|
@@ -19202,7 +19202,10 @@ async function regenerateArtifacts(options, deps = {}) {
|
|
|
19202
19202
|
outputName: options.outputName
|
|
19203
19203
|
});
|
|
19204
19204
|
const result = await generator.generate(run);
|
|
19205
|
-
return [...result.values()].flat();
|
|
19205
|
+
return { files: [...result.values()].flat(), run };
|
|
19206
|
+
}
|
|
19207
|
+
async function regenerateArtifacts(options, deps = {}) {
|
|
19208
|
+
return (await regenerateRun(options, deps)).files;
|
|
19206
19209
|
}
|
|
19207
19210
|
function startWatch(options, deps = {}) {
|
|
19208
19211
|
const log = deps.log ?? ((message) => console.log(message));
|
|
@@ -19245,6 +19248,203 @@ function startWatch(options, deps = {}) {
|
|
|
19245
19248
|
};
|
|
19246
19249
|
}
|
|
19247
19250
|
|
|
19251
|
+
// src/serve.ts
|
|
19252
|
+
import * as fs7 from "fs";
|
|
19253
|
+
import * as http from "http";
|
|
19254
|
+
import * as path8 from "path";
|
|
19255
|
+
function advanceState(prev, run) {
|
|
19256
|
+
if (prev.sessionBaseline === null) {
|
|
19257
|
+
return { sessionBaseline: run, previous: null, current: run, runCount: 1 };
|
|
19258
|
+
}
|
|
19259
|
+
return {
|
|
19260
|
+
sessionBaseline: prev.sessionBaseline,
|
|
19261
|
+
previous: prev.current,
|
|
19262
|
+
current: run,
|
|
19263
|
+
runCount: prev.runCount + 1
|
|
19264
|
+
};
|
|
19265
|
+
}
|
|
19266
|
+
function computeDeltas(state) {
|
|
19267
|
+
if (state.current === null || state.sessionBaseline === null || state.runCount <= 1) {
|
|
19268
|
+
return { session: null, iteration: null };
|
|
19269
|
+
}
|
|
19270
|
+
return {
|
|
19271
|
+
session: diffRuns(state.sessionBaseline, state.current),
|
|
19272
|
+
iteration: state.previous ? diffRuns(state.previous, state.current) : null
|
|
19273
|
+
};
|
|
19274
|
+
}
|
|
19275
|
+
function pluralize(n, word) {
|
|
19276
|
+
return `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
19277
|
+
}
|
|
19278
|
+
function summarizeDiff(diff) {
|
|
19279
|
+
const s = diff.summary;
|
|
19280
|
+
const parts = [];
|
|
19281
|
+
if (s.fixed > 0) parts.push(`+${pluralize(s.fixed, "passing")}`);
|
|
19282
|
+
if (s.regressed > 0) parts.push(`${pluralize(s.regressed, "regressed")}`);
|
|
19283
|
+
if (s.added > 0) parts.push(`${pluralize(s.added, "new behaviour")}`);
|
|
19284
|
+
if (s.removed > 0) parts.push(`${pluralize(s.removed, "removed")}`);
|
|
19285
|
+
const moved = s.renamed + s.moved;
|
|
19286
|
+
if (moved > 0) parts.push(`${pluralize(moved, "renamed")}`);
|
|
19287
|
+
if (s.changed > 0) parts.push(`${pluralize(s.changed, "changed")}`);
|
|
19288
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
19289
|
+
}
|
|
19290
|
+
function escapeHtml3(text2) {
|
|
19291
|
+
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
19292
|
+
}
|
|
19293
|
+
function renderDeltaStrip(state) {
|
|
19294
|
+
if (state.current === null) return "";
|
|
19295
|
+
const { session, iteration } = computeDeltas(state);
|
|
19296
|
+
if (session === null) {
|
|
19297
|
+
const label = `Run #${state.runCount} captured \u2014 baseline pinned. Watching for changes\u2026`;
|
|
19298
|
+
return `<div data-es-live="strip"><strong>Live</strong> \xB7 ${escapeHtml3(label)}</div>`;
|
|
19299
|
+
}
|
|
19300
|
+
const sessionLine = summarizeDiff(session) ?? "no change yet";
|
|
19301
|
+
let detail = "";
|
|
19302
|
+
if (iteration) {
|
|
19303
|
+
const iterationLine = summarizeDiff(iteration);
|
|
19304
|
+
if (iterationLine) detail = ` \xB7 <span data-es-live="iteration">this iteration: ${escapeHtml3(iterationLine)}</span>`;
|
|
19305
|
+
}
|
|
19306
|
+
return [
|
|
19307
|
+
`<div data-es-live="strip">`,
|
|
19308
|
+
`<strong>Live</strong> \xB7 run #${state.runCount} \xB7 `,
|
|
19309
|
+
`<span data-es-live="session">since you started: ${escapeHtml3(sessionLine)}</span>`,
|
|
19310
|
+
detail,
|
|
19311
|
+
`</div>`
|
|
19312
|
+
].join("");
|
|
19313
|
+
}
|
|
19314
|
+
var RELOAD_CLIENT = `<script data-es-live="client">
|
|
19315
|
+
(function () {
|
|
19316
|
+
try {
|
|
19317
|
+
var es = new EventSource("/__es_reload");
|
|
19318
|
+
es.onmessage = function (e) { if (e.data === "reload") location.reload(); };
|
|
19319
|
+
} catch (err) { /* SSE unavailable: stay static */ }
|
|
19320
|
+
})();
|
|
19321
|
+
</script>`;
|
|
19322
|
+
var STRIP_STYLE = `<style data-es-live="style">
|
|
19323
|
+
[data-es-live="strip"]{position:sticky;top:0;z-index:9999;font:14px/1.5 system-ui,sans-serif;
|
|
19324
|
+
padding:8px 16px;background:#0b1021;color:#e6e9f5;border-bottom:1px solid #2a3052}
|
|
19325
|
+
[data-es-live="strip"] strong{color:#7dd3fc}
|
|
19326
|
+
</style>`;
|
|
19327
|
+
function injectLiveBits(html, stripHtml) {
|
|
19328
|
+
let out = html;
|
|
19329
|
+
const bodyOpen = out.match(/<body[^>]*>/i);
|
|
19330
|
+
if (bodyOpen) {
|
|
19331
|
+
const at = bodyOpen.index + bodyOpen[0].length;
|
|
19332
|
+
out = out.slice(0, at) + stripHtml + out.slice(at);
|
|
19333
|
+
} else {
|
|
19334
|
+
out = stripHtml + out;
|
|
19335
|
+
}
|
|
19336
|
+
const tail = STRIP_STYLE + RELOAD_CLIENT;
|
|
19337
|
+
if (/<\/body>/i.test(out)) {
|
|
19338
|
+
out = out.replace(/<\/body>/i, tail + "</body>");
|
|
19339
|
+
} else {
|
|
19340
|
+
out += tail;
|
|
19341
|
+
}
|
|
19342
|
+
return out;
|
|
19343
|
+
}
|
|
19344
|
+
var CONTENT_TYPES = {
|
|
19345
|
+
".html": "text/html; charset=utf-8",
|
|
19346
|
+
".css": "text/css; charset=utf-8",
|
|
19347
|
+
".js": "text/javascript; charset=utf-8",
|
|
19348
|
+
".json": "application/json; charset=utf-8",
|
|
19349
|
+
".svg": "image/svg+xml",
|
|
19350
|
+
".png": "image/png"
|
|
19351
|
+
};
|
|
19352
|
+
function watchInputDir(filePath, listener) {
|
|
19353
|
+
const dir = path8.dirname(filePath);
|
|
19354
|
+
const base = path8.basename(filePath);
|
|
19355
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
19356
|
+
const watcher = fs7.watch(dir, (_event, changed) => {
|
|
19357
|
+
if (!changed || changed === base) listener();
|
|
19358
|
+
});
|
|
19359
|
+
return { close: () => watcher.close() };
|
|
19360
|
+
}
|
|
19361
|
+
function plainText(html) {
|
|
19362
|
+
return html.replace(/<[^>]+>/g, "").trim();
|
|
19363
|
+
}
|
|
19364
|
+
function startServe(options, deps = {}) {
|
|
19365
|
+
const log = deps.log ?? ((message) => console.log(message));
|
|
19366
|
+
const read = deps.readFile ?? ((filePath) => fs7.readFileSync(filePath, "utf8"));
|
|
19367
|
+
const port = options.port ?? 4321;
|
|
19368
|
+
const host = options.host ?? "127.0.0.1";
|
|
19369
|
+
let state = { sessionBaseline: null, previous: null, current: null, runCount: 0 };
|
|
19370
|
+
let htmlPath = null;
|
|
19371
|
+
let stripHtml = renderDeltaStrip(state);
|
|
19372
|
+
const clients = /* @__PURE__ */ new Set();
|
|
19373
|
+
const pushReload = () => {
|
|
19374
|
+
for (const res of clients) res.write("data: reload\n\n");
|
|
19375
|
+
};
|
|
19376
|
+
const handler = (req, res) => {
|
|
19377
|
+
const url = (req.url ?? "/").split("?")[0];
|
|
19378
|
+
if (url === "/__es_reload") {
|
|
19379
|
+
res.writeHead(200, {
|
|
19380
|
+
"Content-Type": "text/event-stream",
|
|
19381
|
+
"Cache-Control": "no-cache",
|
|
19382
|
+
Connection: "keep-alive"
|
|
19383
|
+
});
|
|
19384
|
+
res.write("retry: 1000\n\n");
|
|
19385
|
+
clients.add(res);
|
|
19386
|
+
req.on("close", () => clients.delete(res));
|
|
19387
|
+
return;
|
|
19388
|
+
}
|
|
19389
|
+
if (url === "/" || url === "/index.html") {
|
|
19390
|
+
const html = htmlPath ? read(htmlPath) : "<!doctype html><html><body><h1>executable-stories</h1></body></html>";
|
|
19391
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
19392
|
+
res.end(injectLiveBits(html, stripHtml));
|
|
19393
|
+
return;
|
|
19394
|
+
}
|
|
19395
|
+
const safe = path8.normalize(url).replace(/^(\.\.[/\\])+/, "");
|
|
19396
|
+
const filePath = path8.join(path8.resolve(options.outputDir), safe);
|
|
19397
|
+
if (filePath.startsWith(path8.resolve(options.outputDir)) && fs7.existsSync(filePath)) {
|
|
19398
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
19399
|
+
res.writeHead(200, { "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream" });
|
|
19400
|
+
res.end(read(filePath));
|
|
19401
|
+
return;
|
|
19402
|
+
}
|
|
19403
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
19404
|
+
res.end("Not found");
|
|
19405
|
+
};
|
|
19406
|
+
const server = deps.createServer ? deps.createServer(handler) : http.createServer(handler);
|
|
19407
|
+
const watchOptions = {
|
|
19408
|
+
input: options.input,
|
|
19409
|
+
outputDir: options.outputDir,
|
|
19410
|
+
outputName: options.outputName,
|
|
19411
|
+
formats: options.formats,
|
|
19412
|
+
inputType: options.inputType ?? "raw",
|
|
19413
|
+
synthesize: options.synthesize !== false,
|
|
19414
|
+
debounceMs: options.debounceMs
|
|
19415
|
+
};
|
|
19416
|
+
const watchHandle = startWatch(watchOptions, {
|
|
19417
|
+
readFile: read,
|
|
19418
|
+
watch: deps.watch ?? watchInputDir,
|
|
19419
|
+
log: () => {
|
|
19420
|
+
},
|
|
19421
|
+
// serve emits its own per-run line below
|
|
19422
|
+
regenerate: async (input) => {
|
|
19423
|
+
if (!fs7.existsSync(path8.resolve(input))) return [];
|
|
19424
|
+
const { files, run } = await regenerateRun({ ...watchOptions, input }, { readFile: read });
|
|
19425
|
+
htmlPath = files.find((f) => f.endsWith(".html")) ?? htmlPath;
|
|
19426
|
+
state = advanceState(state, run);
|
|
19427
|
+
stripHtml = renderDeltaStrip(state);
|
|
19428
|
+
log(`Run #${state.runCount}: ${plainText(stripHtml)}`);
|
|
19429
|
+
pushReload();
|
|
19430
|
+
return files;
|
|
19431
|
+
}
|
|
19432
|
+
});
|
|
19433
|
+
server.listen(port, host);
|
|
19434
|
+
const address = server.address();
|
|
19435
|
+
const boundPort = typeof address === "object" && address ? address.port : port;
|
|
19436
|
+
log(`Live docs: http://${host}:${boundPort} (Ctrl+C to stop)`);
|
|
19437
|
+
return {
|
|
19438
|
+
port: boundPort,
|
|
19439
|
+
close: () => {
|
|
19440
|
+
watchHandle.close();
|
|
19441
|
+
for (const res of clients) res.end();
|
|
19442
|
+
clients.clear();
|
|
19443
|
+
server.close();
|
|
19444
|
+
}
|
|
19445
|
+
};
|
|
19446
|
+
}
|
|
19447
|
+
|
|
19248
19448
|
// src/behavior-diff.ts
|
|
19249
19449
|
function classifyStatusChange(baseline, current) {
|
|
19250
19450
|
if (baseline === void 0) return "added";
|
|
@@ -20894,18 +21094,18 @@ function deriveChangeType(tags) {
|
|
|
20894
21094
|
}
|
|
20895
21095
|
return "unknown";
|
|
20896
21096
|
}
|
|
20897
|
-
function extensionOf(
|
|
20898
|
-
const base =
|
|
21097
|
+
function extensionOf(path19) {
|
|
21098
|
+
const base = path19.split("/").pop() ?? path19;
|
|
20899
21099
|
const dot = base.lastIndexOf(".");
|
|
20900
21100
|
return dot === -1 ? "" : base.slice(dot + 1).toLowerCase();
|
|
20901
21101
|
}
|
|
20902
|
-
function isTestFile(
|
|
20903
|
-
return TEST_INFIX.test(
|
|
21102
|
+
function isTestFile(path19) {
|
|
21103
|
+
return TEST_INFIX.test(path19);
|
|
20904
21104
|
}
|
|
20905
|
-
function isReviewableSource(
|
|
20906
|
-
if (isTestFile(
|
|
20907
|
-
if (
|
|
20908
|
-
return CODE_EXTENSIONS.has(extensionOf(
|
|
21105
|
+
function isReviewableSource(path19) {
|
|
21106
|
+
if (isTestFile(path19)) return false;
|
|
21107
|
+
if (path19.endsWith(".d.ts")) return false;
|
|
21108
|
+
return CODE_EXTENSIONS.has(extensionOf(path19));
|
|
20909
21109
|
}
|
|
20910
21110
|
function testBaseKey(testFile) {
|
|
20911
21111
|
return testFile.replace(TEST_INFIX, "");
|
|
@@ -21009,7 +21209,7 @@ function toClaim(testCase, changedSourcePaths) {
|
|
|
21009
21209
|
const { strength, reasons } = gradeEvidence(testCase, audience);
|
|
21010
21210
|
const key = testBaseKey(testCase.sourceFile);
|
|
21011
21211
|
const coversFiles = changedSourcePaths.filter(
|
|
21012
|
-
(
|
|
21212
|
+
(path19) => sourceBaseKey(path19) === key
|
|
21013
21213
|
);
|
|
21014
21214
|
return {
|
|
21015
21215
|
id: testCase.id,
|
|
@@ -21258,7 +21458,7 @@ var ReviewMarkdownFormatter = class {
|
|
|
21258
21458
|
};
|
|
21259
21459
|
|
|
21260
21460
|
// src/formatters/review-html.ts
|
|
21261
|
-
function
|
|
21461
|
+
function escapeHtml4(value) {
|
|
21262
21462
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
21263
21463
|
}
|
|
21264
21464
|
var STRENGTH_LABEL = {
|
|
@@ -21280,22 +21480,22 @@ function statusIcon3(status) {
|
|
|
21280
21480
|
}
|
|
21281
21481
|
}
|
|
21282
21482
|
function formatStep3(step) {
|
|
21283
|
-
return `<li><strong>${
|
|
21483
|
+
return `<li><strong>${escapeHtml4(step.keyword)}</strong> ${escapeHtml4(step.text)}</li>`;
|
|
21284
21484
|
}
|
|
21285
21485
|
function inlineDoc(doc) {
|
|
21286
21486
|
switch (doc.kind) {
|
|
21287
21487
|
case "note":
|
|
21288
|
-
return
|
|
21488
|
+
return escapeHtml4(doc.text);
|
|
21289
21489
|
case "section":
|
|
21290
|
-
return `<strong>${
|
|
21490
|
+
return `<strong>${escapeHtml4(doc.title)}</strong>: ${escapeHtml4(doc.markdown)}`;
|
|
21291
21491
|
case "kv":
|
|
21292
|
-
return `${
|
|
21492
|
+
return `${escapeHtml4(doc.label)}: ${escapeHtml4(String(doc.value))}`;
|
|
21293
21493
|
case "code":
|
|
21294
|
-
return `${
|
|
21494
|
+
return `${escapeHtml4(doc.label)}: <code>${escapeHtml4(doc.content)}</code>`;
|
|
21295
21495
|
case "link":
|
|
21296
|
-
return `${
|
|
21496
|
+
return `${escapeHtml4(doc.label)}: ${escapeHtml4(doc.url)}`;
|
|
21297
21497
|
default:
|
|
21298
|
-
return
|
|
21498
|
+
return escapeHtml4(doc.kind);
|
|
21299
21499
|
}
|
|
21300
21500
|
}
|
|
21301
21501
|
function renderEvidenceArtifacts(testCase) {
|
|
@@ -21303,7 +21503,7 @@ function renderEvidenceArtifacts(testCase) {
|
|
|
21303
21503
|
for (const att of testCase.attachments) {
|
|
21304
21504
|
if (att.mediaType.startsWith("image/") && att.contentEncoding === "BASE64") {
|
|
21305
21505
|
parts.push(
|
|
21306
|
-
`<img class="shot" alt="${
|
|
21506
|
+
`<img class="shot" alt="${escapeHtml4(att.name)}" src="data:${escapeHtml4(att.mediaType)};base64,${att.body}" />`
|
|
21307
21507
|
);
|
|
21308
21508
|
}
|
|
21309
21509
|
}
|
|
@@ -21318,22 +21518,22 @@ function renderTicketPills(claim) {
|
|
|
21318
21518
|
const tickets = claim.testCase.story.tickets ?? [];
|
|
21319
21519
|
if (tickets.length === 0) return "";
|
|
21320
21520
|
return `<div class="ticket-row">${tickets.map((ticket) => {
|
|
21321
|
-
const label =
|
|
21521
|
+
const label = escapeHtml4(ticket.id);
|
|
21322
21522
|
if (ticket.url) {
|
|
21323
|
-
return `<a class="ticket-pill" href="${
|
|
21523
|
+
return `<a class="ticket-pill" href="${escapeHtml4(ticket.url)}" target="_blank" rel="noopener noreferrer">${label}</a>`;
|
|
21324
21524
|
}
|
|
21325
21525
|
return `<span class="ticket-pill">${label}</span>`;
|
|
21326
21526
|
}).join("")}</div>`;
|
|
21327
21527
|
}
|
|
21328
21528
|
function renderClaimCard(claim) {
|
|
21329
21529
|
const ticketSearch = (claim.testCase.story.tickets ?? []).map((ticket) => ticket.id).join(" ");
|
|
21330
|
-
const search =
|
|
21530
|
+
const search = escapeHtml4(
|
|
21331
21531
|
`${claim.scenario} ${claim.sourceFile} ${claim.changeType} ${claim.audience} ${claim.strength} ${ticketSearch}`
|
|
21332
21532
|
).toLowerCase();
|
|
21333
21533
|
const steps = claim.testCase.story.steps.length > 0 ? `<ul class="step-list">${claim.testCase.story.steps.map(formatStep3).join("")}</ul>` : "";
|
|
21334
|
-
const reasons = `<ul class="reasons">${claim.strengthReasons.map((r) => `<li>${
|
|
21335
|
-
const intent = claim.intent !== void 0 ? `<div class="intent"><span class="intent-label">Why</span> ${
|
|
21336
|
-
const covers = claim.coversFiles.length > 0 ? `<p class="covers">Covers ${claim.coversFiles.map((f) => `<code>${
|
|
21534
|
+
const reasons = `<ul class="reasons">${claim.strengthReasons.map((r) => `<li>${escapeHtml4(r)}</li>`).join("")}</ul>`;
|
|
21535
|
+
const intent = claim.intent !== void 0 ? `<div class="intent"><span class="intent-label">Why</span> ${escapeHtml4(claim.intent)}</div>` : "";
|
|
21536
|
+
const covers = claim.coversFiles.length > 0 ? `<p class="covers">Covers ${claim.coversFiles.map((f) => `<code>${escapeHtml4(f)}</code>`).join(", ")}</p>` : "";
|
|
21337
21537
|
const docs = (claim.testCase.story.docs ?? []).filter(
|
|
21338
21538
|
(d) => d.kind === "section" || d.kind === "note"
|
|
21339
21539
|
);
|
|
@@ -21343,9 +21543,9 @@ function renderClaimCard(claim) {
|
|
|
21343
21543
|
<header class="claim-header">
|
|
21344
21544
|
<div>
|
|
21345
21545
|
<span class="strength-badge strength-${claim.strength}">${STRENGTH_LABEL[claim.strength]}</span>
|
|
21346
|
-
${claim.changeType !== "unknown" ? `<span class="change-pill">${
|
|
21347
|
-
<h3>${statusIcon3(claim.status)} ${
|
|
21348
|
-
<p class="source">${
|
|
21546
|
+
${claim.changeType !== "unknown" ? `<span class="change-pill">${escapeHtml4(claim.changeType)}</span>` : ""}
|
|
21547
|
+
<h3>${statusIcon3(claim.status)} ${escapeHtml4(claim.scenario)}</h3>
|
|
21548
|
+
<p class="source">${escapeHtml4(`${claim.sourceFile}:${claim.sourceLine}`)}</p>
|
|
21349
21549
|
${renderTicketPills(claim)}
|
|
21350
21550
|
</div>
|
|
21351
21551
|
</header>
|
|
@@ -21360,18 +21560,18 @@ function renderClaimCard(claim) {
|
|
|
21360
21560
|
</article>`;
|
|
21361
21561
|
}
|
|
21362
21562
|
function renderChangedFileRow(file) {
|
|
21363
|
-
const claims = file.claims.length > 0 ? file.claims.map((c) => `${
|
|
21563
|
+
const claims = file.claims.length > 0 ? file.claims.map((c) => `${escapeHtml4(c.scenario)} <em>(${c.strength})</em>`).join(", ") : "\u2014";
|
|
21364
21564
|
return `<tr data-band="${file.band}">
|
|
21365
21565
|
<td><span class="band-dot band-${file.band}"></span></td>
|
|
21366
|
-
<td><code>${
|
|
21367
|
-
<td>${
|
|
21566
|
+
<td><code>${escapeHtml4(file.path)}</code></td>
|
|
21567
|
+
<td>${escapeHtml4(file.changeKind)}</td>
|
|
21368
21568
|
<td>${claims}</td>
|
|
21369
21569
|
</tr>`;
|
|
21370
21570
|
}
|
|
21371
21571
|
function renderAudienceSection2(title, claims) {
|
|
21372
21572
|
if (claims.length === 0) return "";
|
|
21373
21573
|
return `<section class="audience-section">
|
|
21374
|
-
<h2>${
|
|
21574
|
+
<h2>${escapeHtml4(title)} <span class="count">${claims.length}</span></h2>
|
|
21375
21575
|
<div class="claim-list">${claims.map(renderClaimCard).join("\n")}</div>
|
|
21376
21576
|
</section>`;
|
|
21377
21577
|
}
|
|
@@ -21461,13 +21661,13 @@ var ReviewHtmlFormatter = class {
|
|
|
21461
21661
|
const themeInitJs = this.darkMode ? `${JS_THEME_TOGGLE2}
|
|
21462
21662
|
applyTheme(getEffectiveTheme());` : "";
|
|
21463
21663
|
const themeAttr = this.darkMode ? ' data-theme="light"' : "";
|
|
21464
|
-
const refsLine = context.baseRef || context.headRef ? `<p class="subtle">Comparing ${
|
|
21664
|
+
const refsLine = context.baseRef || context.headRef ? `<p class="subtle">Comparing ${escapeHtml4(context.baseRef ?? "base")} \u2192 ${escapeHtml4(context.headRef ?? "head")}</p>` : "";
|
|
21465
21665
|
return `<!doctype html>
|
|
21466
21666
|
<html lang="en"${themeAttr}>
|
|
21467
21667
|
<head>
|
|
21468
21668
|
<meta charset="utf-8" />
|
|
21469
21669
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
21470
|
-
<title>${
|
|
21670
|
+
<title>${escapeHtml4(this.title)}</title>
|
|
21471
21671
|
<style>
|
|
21472
21672
|
${this.theme.css}
|
|
21473
21673
|
${REVIEW_CSS}
|
|
@@ -21477,7 +21677,7 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21477
21677
|
<main>
|
|
21478
21678
|
<div class="hero-card card">
|
|
21479
21679
|
<div class="review-header">
|
|
21480
|
-
<h1>${
|
|
21680
|
+
<h1>${escapeHtml4(this.title)}</h1>
|
|
21481
21681
|
${themeToggleHtml}
|
|
21482
21682
|
</div>
|
|
21483
21683
|
${refsLine}
|
|
@@ -21492,7 +21692,7 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21492
21692
|
</section>
|
|
21493
21693
|
<section class="card priority-banner">
|
|
21494
21694
|
<h2>Review priority</h2>
|
|
21495
|
-
<p class="subtle">${
|
|
21695
|
+
<p class="subtle">${escapeHtml4(priority)}</p>
|
|
21496
21696
|
</section>
|
|
21497
21697
|
${changedFilesPanel}
|
|
21498
21698
|
<section class="toolbar">
|
|
@@ -21540,8 +21740,8 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21540
21740
|
};
|
|
21541
21741
|
|
|
21542
21742
|
// src/deploy/ledger.ts
|
|
21543
|
-
import * as
|
|
21544
|
-
import * as
|
|
21743
|
+
import * as fs8 from "fs";
|
|
21744
|
+
import * as path9 from "path";
|
|
21545
21745
|
function createEmptyLedger() {
|
|
21546
21746
|
return {
|
|
21547
21747
|
deployments: [],
|
|
@@ -21549,12 +21749,12 @@ function createEmptyLedger() {
|
|
|
21549
21749
|
};
|
|
21550
21750
|
}
|
|
21551
21751
|
function loadLedger(ledgerPath) {
|
|
21552
|
-
const resolved =
|
|
21553
|
-
if (!
|
|
21752
|
+
const resolved = path9.resolve(ledgerPath);
|
|
21753
|
+
if (!fs8.existsSync(resolved)) {
|
|
21554
21754
|
return createEmptyLedger();
|
|
21555
21755
|
}
|
|
21556
21756
|
try {
|
|
21557
|
-
const raw = JSON.parse(
|
|
21757
|
+
const raw = JSON.parse(fs8.readFileSync(resolved, "utf8"));
|
|
21558
21758
|
if (raw.schemaVersion !== 1) {
|
|
21559
21759
|
throw new Error(`Unsupported ledger schemaVersion: ${raw.schemaVersion}`);
|
|
21560
21760
|
}
|
|
@@ -21565,10 +21765,10 @@ function loadLedger(ledgerPath) {
|
|
|
21565
21765
|
}
|
|
21566
21766
|
}
|
|
21567
21767
|
function saveLedger(ledger, ledgerPath) {
|
|
21568
|
-
const resolved =
|
|
21569
|
-
const dir =
|
|
21570
|
-
|
|
21571
|
-
|
|
21768
|
+
const resolved = path9.resolve(ledgerPath);
|
|
21769
|
+
const dir = path9.dirname(resolved);
|
|
21770
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
21771
|
+
fs8.writeFileSync(resolved, JSON.stringify(ledger, null, 2), "utf8");
|
|
21572
21772
|
}
|
|
21573
21773
|
function getLatestDeployment(ledger, environment) {
|
|
21574
21774
|
return [...ledger.deployments].reverse().find((d) => d.environment === environment);
|
|
@@ -21688,11 +21888,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21688
21888
|
const ext = FORMAT_EXTENSIONS[format];
|
|
21689
21889
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
21690
21890
|
if (mode === "aggregated") {
|
|
21691
|
-
return toPosix(
|
|
21891
|
+
return toPosix(path10.join(baseOutputDir, joinNameAndExt(effectiveName, ext)));
|
|
21692
21892
|
}
|
|
21693
21893
|
const normalizedSource = toPosix(sourceFile);
|
|
21694
|
-
const dirOfSource =
|
|
21695
|
-
let baseName =
|
|
21894
|
+
const dirOfSource = path10.posix.dirname(normalizedSource);
|
|
21895
|
+
let baseName = path10.posix.basename(normalizedSource);
|
|
21696
21896
|
for (const testExt of TEST_EXTENSIONS) {
|
|
21697
21897
|
if (baseName.endsWith(testExt)) {
|
|
21698
21898
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -21701,12 +21901,12 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21701
21901
|
}
|
|
21702
21902
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
21703
21903
|
if (colocatedStyle === "adjacent") {
|
|
21704
|
-
return toPosix(
|
|
21904
|
+
return toPosix(path10.posix.join(dirOfSource, fileName));
|
|
21705
21905
|
}
|
|
21706
21906
|
if (colocatedStyle === "flat") {
|
|
21707
|
-
return toPosix(
|
|
21907
|
+
return toPosix(path10.posix.join(baseOutputDir, `${cleanTestStem(normalizedSource)}${ext}`));
|
|
21708
21908
|
}
|
|
21709
|
-
return toPosix(
|
|
21909
|
+
return toPosix(path10.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
21710
21910
|
}
|
|
21711
21911
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
21712
21912
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -21919,8 +22119,8 @@ var ReportGenerator = class {
|
|
|
21919
22119
|
if (astroPaths) {
|
|
21920
22120
|
for (const mdPath of astroPaths) {
|
|
21921
22121
|
const content = await fsPromises.readFile(mdPath, "utf8");
|
|
21922
|
-
const mdDir =
|
|
21923
|
-
const assetsDir =
|
|
22122
|
+
const mdDir = path10.dirname(mdPath);
|
|
22123
|
+
const assetsDir = path10.resolve(this.options.astro.assetsDir);
|
|
21924
22124
|
const result = copyMarkdownAssets({
|
|
21925
22125
|
markdown: content,
|
|
21926
22126
|
markdownDir: mdDir,
|
|
@@ -21951,9 +22151,9 @@ var ReportGenerator = class {
|
|
|
21951
22151
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
21952
22152
|
const ext = FORMAT_EXTENSIONS[format];
|
|
21953
22153
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
21954
|
-
const outputPath = toPosix(
|
|
22154
|
+
const outputPath = toPosix(path10.join(this.options.outputDir, joinNameAndExt(effectiveName, ext)));
|
|
21955
22155
|
const content = await this.formatContent(run, format);
|
|
21956
|
-
const dir =
|
|
22156
|
+
const dir = path10.dirname(outputPath);
|
|
21957
22157
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
21958
22158
|
await this.deps.writeFile(outputPath, content);
|
|
21959
22159
|
return [outputPath];
|
|
@@ -21965,7 +22165,7 @@ var ReportGenerator = class {
|
|
|
21965
22165
|
testCases
|
|
21966
22166
|
};
|
|
21967
22167
|
const content = await this.formatContent(groupRun, format);
|
|
21968
|
-
const dir =
|
|
22168
|
+
const dir = path10.dirname(outputPath);
|
|
21969
22169
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
21970
22170
|
await this.deps.writeFile(outputPath, content);
|
|
21971
22171
|
writtenPaths.push(outputPath);
|
|
@@ -22114,7 +22314,7 @@ async function generateRunComparison(args) {
|
|
|
22114
22314
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
22115
22315
|
for (const format of args.formats) {
|
|
22116
22316
|
const ext = format === "html" ? ".html" : ".md";
|
|
22117
|
-
const outputPath = toPosix(
|
|
22317
|
+
const outputPath = toPosix(path10.join(outputDir, `${outputName}${ext}`));
|
|
22118
22318
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
22119
22319
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
22120
22320
|
files.push(outputPath);
|
|
@@ -22123,21 +22323,21 @@ async function generateRunComparison(args) {
|
|
|
22123
22323
|
}
|
|
22124
22324
|
|
|
22125
22325
|
// src/init-astro.ts
|
|
22126
|
-
import * as
|
|
22127
|
-
import * as
|
|
22326
|
+
import * as fs10 from "fs";
|
|
22327
|
+
import * as path11 from "path";
|
|
22128
22328
|
import { fileURLToPath } from "url";
|
|
22129
|
-
var __dirname =
|
|
22329
|
+
var __dirname = path11.dirname(fileURLToPath(import.meta.url));
|
|
22130
22330
|
var FRAMEWORK_DIRS = ["src/components", "src/lib", "src/styles", "src/pages"];
|
|
22131
22331
|
var FRAMEWORK_FILES = ["tsconfig.json"];
|
|
22132
22332
|
function isScaffoldedAstroSite(dir) {
|
|
22133
|
-
return
|
|
22333
|
+
return fs10.existsSync(path11.join(dir, "astro.config.mjs"));
|
|
22134
22334
|
}
|
|
22135
22335
|
function initAstro(options = {}) {
|
|
22136
22336
|
const targetDir = options.targetDir ?? "./story-docs";
|
|
22137
22337
|
const force = options.force ?? false;
|
|
22138
22338
|
const update = options.update ?? false;
|
|
22139
|
-
const templateDir =
|
|
22140
|
-
if (!
|
|
22339
|
+
const templateDir = path11.resolve(__dirname, "..", "templates", "astro-starlight");
|
|
22340
|
+
if (!fs10.existsSync(templateDir)) {
|
|
22141
22341
|
throw new Error(
|
|
22142
22342
|
`Template directory not found at ${templateDir}. Ensure the package is installed correctly.`
|
|
22143
22343
|
);
|
|
@@ -22145,8 +22345,8 @@ function initAstro(options = {}) {
|
|
|
22145
22345
|
if (update) {
|
|
22146
22346
|
return updateFrameworkFiles(templateDir, targetDir);
|
|
22147
22347
|
}
|
|
22148
|
-
if (
|
|
22149
|
-
const entries =
|
|
22348
|
+
if (fs10.existsSync(targetDir)) {
|
|
22349
|
+
const entries = fs10.readdirSync(targetDir);
|
|
22150
22350
|
if (entries.length > 0 && !force) {
|
|
22151
22351
|
throw new Error(
|
|
22152
22352
|
`Directory "${targetDir}" already exists and is not empty. Use --force to overwrite, or --update to refresh framework files only.`
|
|
@@ -22164,25 +22364,25 @@ function updateFrameworkFiles(templateDir, targetDir) {
|
|
|
22164
22364
|
}
|
|
22165
22365
|
const updated = [];
|
|
22166
22366
|
for (const dir of FRAMEWORK_DIRS) {
|
|
22167
|
-
const src =
|
|
22168
|
-
if (!
|
|
22169
|
-
copyDirRecursive(src,
|
|
22367
|
+
const src = path11.join(templateDir, dir);
|
|
22368
|
+
if (!fs10.existsSync(src)) continue;
|
|
22369
|
+
copyDirRecursive(src, path11.join(targetDir, dir), (rel) => updated.push(path11.join(dir, rel)));
|
|
22170
22370
|
}
|
|
22171
22371
|
for (const file of FRAMEWORK_FILES) {
|
|
22172
|
-
const src =
|
|
22173
|
-
if (!
|
|
22174
|
-
|
|
22372
|
+
const src = path11.join(templateDir, file);
|
|
22373
|
+
if (!fs10.existsSync(src)) continue;
|
|
22374
|
+
fs10.copyFileSync(src, path11.join(targetDir, file));
|
|
22175
22375
|
updated.push(file);
|
|
22176
22376
|
}
|
|
22177
22377
|
if (mergeDependencies(templateDir, targetDir)) updated.push("package.json (deps)");
|
|
22178
22378
|
return { targetDir, updatedFiles: updated };
|
|
22179
22379
|
}
|
|
22180
22380
|
function mergeDependencies(templateDir, targetDir) {
|
|
22181
|
-
const tmplPkgPath =
|
|
22182
|
-
const userPkgPath =
|
|
22183
|
-
if (!
|
|
22184
|
-
const tmpl = JSON.parse(
|
|
22185
|
-
const user = JSON.parse(
|
|
22381
|
+
const tmplPkgPath = path11.join(templateDir, "package.json");
|
|
22382
|
+
const userPkgPath = path11.join(targetDir, "package.json");
|
|
22383
|
+
if (!fs10.existsSync(tmplPkgPath) || !fs10.existsSync(userPkgPath)) return false;
|
|
22384
|
+
const tmpl = JSON.parse(fs10.readFileSync(tmplPkgPath, "utf8"));
|
|
22385
|
+
const user = JSON.parse(fs10.readFileSync(userPkgPath, "utf8"));
|
|
22186
22386
|
user.dependencies = user.dependencies ?? {};
|
|
22187
22387
|
let changed = false;
|
|
22188
22388
|
for (const [name, version] of Object.entries(tmpl.dependencies ?? {})) {
|
|
@@ -22192,30 +22392,30 @@ function mergeDependencies(templateDir, targetDir) {
|
|
|
22192
22392
|
}
|
|
22193
22393
|
}
|
|
22194
22394
|
if (changed) {
|
|
22195
|
-
|
|
22395
|
+
fs10.writeFileSync(userPkgPath, `${JSON.stringify(user, null, 2)}
|
|
22196
22396
|
`, "utf8");
|
|
22197
22397
|
}
|
|
22198
22398
|
return changed;
|
|
22199
22399
|
}
|
|
22200
22400
|
function copyDirRecursive(src, dest, onFile, baseSrc = src) {
|
|
22201
|
-
|
|
22202
|
-
const entries =
|
|
22401
|
+
fs10.mkdirSync(dest, { recursive: true });
|
|
22402
|
+
const entries = fs10.readdirSync(src, { withFileTypes: true });
|
|
22203
22403
|
for (const entry of entries) {
|
|
22204
|
-
const srcPath =
|
|
22404
|
+
const srcPath = path11.join(src, entry.name);
|
|
22205
22405
|
const destName = entry.name === "gitignore" ? ".gitignore" : entry.name;
|
|
22206
|
-
const destPath =
|
|
22406
|
+
const destPath = path11.join(dest, destName);
|
|
22207
22407
|
if (entry.isDirectory()) {
|
|
22208
22408
|
copyDirRecursive(srcPath, destPath, onFile, baseSrc);
|
|
22209
22409
|
} else {
|
|
22210
|
-
|
|
22211
|
-
onFile?.(
|
|
22410
|
+
fs10.copyFileSync(srcPath, destPath);
|
|
22411
|
+
onFile?.(path11.relative(baseSrc, srcPath));
|
|
22212
22412
|
}
|
|
22213
22413
|
}
|
|
22214
22414
|
}
|
|
22215
22415
|
|
|
22216
22416
|
// src/scaffold-doc.ts
|
|
22217
|
-
import * as
|
|
22218
|
-
import * as
|
|
22417
|
+
import * as fs11 from "fs";
|
|
22418
|
+
import * as path12 from "path";
|
|
22219
22419
|
var TEMPLATES = [
|
|
22220
22420
|
"adr",
|
|
22221
22421
|
"runbook",
|
|
@@ -22232,7 +22432,7 @@ function isoDate(today) {
|
|
|
22232
22432
|
function nextSeq(dir) {
|
|
22233
22433
|
let max = 0;
|
|
22234
22434
|
try {
|
|
22235
|
-
for (const entry of
|
|
22435
|
+
for (const entry of fs11.readdirSync(dir)) {
|
|
22236
22436
|
const match = /^(\d{1,4})-/.exec(entry);
|
|
22237
22437
|
if (match) max = Math.max(max, Number.parseInt(match[1], 10));
|
|
22238
22438
|
}
|
|
@@ -22389,12 +22589,12 @@ function scaffoldDoc(options) {
|
|
|
22389
22589
|
);
|
|
22390
22590
|
}
|
|
22391
22591
|
const spec = TEMPLATE_SPECS[template];
|
|
22392
|
-
const baseDir = options.baseDir ??
|
|
22592
|
+
const baseDir = options.baseDir ?? path12.join("src", "content", "docs");
|
|
22393
22593
|
const today = options.today ?? /* @__PURE__ */ new Date();
|
|
22394
22594
|
const name = (options.name ?? "").trim() || defaultName(template);
|
|
22395
22595
|
const slug2 = slugify3(name);
|
|
22396
22596
|
const scenarioId = normalizeScenarioId(options.scenarioId);
|
|
22397
|
-
const dir =
|
|
22597
|
+
const dir = path12.join(baseDir, spec.subdir);
|
|
22398
22598
|
if (template === "scenario-note" && !scenarioId) {
|
|
22399
22599
|
throw new Error(`Template "scenario-note" requires --scenario-id.`);
|
|
22400
22600
|
}
|
|
@@ -22406,14 +22606,14 @@ function scaffoldDoc(options) {
|
|
|
22406
22606
|
seq: nextSeq(dir)
|
|
22407
22607
|
};
|
|
22408
22608
|
const filename = `${spec.filename(slug2, ctx)}.mdx`;
|
|
22409
|
-
const filePath =
|
|
22410
|
-
if (
|
|
22609
|
+
const filePath = path12.join(dir, filename);
|
|
22610
|
+
if (fs11.existsSync(filePath) && !options.force) {
|
|
22411
22611
|
throw new Error(
|
|
22412
22612
|
`File "${filePath}" already exists. Use --force to overwrite.`
|
|
22413
22613
|
);
|
|
22414
22614
|
}
|
|
22415
|
-
|
|
22416
|
-
|
|
22615
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
22616
|
+
fs11.writeFileSync(filePath, spec.content(ctx), "utf8");
|
|
22417
22617
|
return { template, path: filePath, title: titleFor2(template, ctx) };
|
|
22418
22618
|
}
|
|
22419
22619
|
function defaultName(template) {
|
|
@@ -22454,20 +22654,20 @@ function normalizeScenarioId(input) {
|
|
|
22454
22654
|
}
|
|
22455
22655
|
|
|
22456
22656
|
// src/check-links.ts
|
|
22457
|
-
import * as
|
|
22458
|
-
import * as
|
|
22657
|
+
import * as fs13 from "fs";
|
|
22658
|
+
import * as path14 from "path";
|
|
22459
22659
|
|
|
22460
22660
|
// src/utils/markdown-files.ts
|
|
22461
|
-
import * as
|
|
22462
|
-
import * as
|
|
22661
|
+
import * as fs12 from "fs";
|
|
22662
|
+
import * as path13 from "path";
|
|
22463
22663
|
function collectMarkdownFiles(target) {
|
|
22464
|
-
if (!
|
|
22465
|
-
if (
|
|
22664
|
+
if (!fs12.existsSync(target)) return [];
|
|
22665
|
+
if (fs12.statSync(target).isFile()) return [target];
|
|
22466
22666
|
const out = [];
|
|
22467
22667
|
const walk = (dir) => {
|
|
22468
|
-
for (const entry of
|
|
22668
|
+
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
22469
22669
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
22470
|
-
const full =
|
|
22670
|
+
const full = path13.join(dir, entry.name);
|
|
22471
22671
|
if (entry.isDirectory()) walk(full);
|
|
22472
22672
|
else if (/\.mdx?$/u.test(entry.name)) out.push(full);
|
|
22473
22673
|
}
|
|
@@ -22506,17 +22706,17 @@ function classifyLink(link2) {
|
|
|
22506
22706
|
function resolutionCandidates(fromFile, link2) {
|
|
22507
22707
|
const withoutAnchor = link2.split("#")[0];
|
|
22508
22708
|
if (!withoutAnchor) return [];
|
|
22509
|
-
const base =
|
|
22709
|
+
const base = path14.resolve(path14.dirname(fromFile), withoutAnchor);
|
|
22510
22710
|
const candidates = [base];
|
|
22511
|
-
if (!
|
|
22711
|
+
if (!path14.extname(base)) {
|
|
22512
22712
|
candidates.push(`${base}.md`, `${base}.mdx`);
|
|
22513
|
-
candidates.push(
|
|
22713
|
+
candidates.push(path14.join(base, "index.md"), path14.join(base, "index.mdx"));
|
|
22514
22714
|
}
|
|
22515
22715
|
return candidates;
|
|
22516
22716
|
}
|
|
22517
22717
|
function resolvesOnDisk(fromFile, link2) {
|
|
22518
22718
|
return resolutionCandidates(fromFile, link2).some(
|
|
22519
|
-
(candidate) =>
|
|
22719
|
+
(candidate) => fs13.existsSync(candidate) && fs13.statSync(candidate).isFile()
|
|
22520
22720
|
);
|
|
22521
22721
|
}
|
|
22522
22722
|
async function isExternalAlive(url, timeoutMs) {
|
|
@@ -22542,7 +22742,7 @@ async function isExternalAlive(url, timeoutMs) {
|
|
|
22542
22742
|
}
|
|
22543
22743
|
async function checkLinks(options) {
|
|
22544
22744
|
const { target, checkExternal = false, externalTimeoutMs = 8e3 } = options;
|
|
22545
|
-
if (!
|
|
22745
|
+
if (!fs13.existsSync(target)) {
|
|
22546
22746
|
throw new Error(`Path not found: ${target}`);
|
|
22547
22747
|
}
|
|
22548
22748
|
const files = collectMarkdownFiles(target);
|
|
@@ -22552,7 +22752,7 @@ async function checkLinks(options) {
|
|
|
22552
22752
|
let skipped = 0;
|
|
22553
22753
|
const externalCache = /* @__PURE__ */ new Map();
|
|
22554
22754
|
for (const file of files) {
|
|
22555
|
-
const content =
|
|
22755
|
+
const content = fs13.readFileSync(file, "utf8");
|
|
22556
22756
|
for (const link2 of extractLinks(content)) {
|
|
22557
22757
|
const kind = classifyLink(link2);
|
|
22558
22758
|
if (kind === "anchor" || kind === "mail" || kind === "root") {
|
|
@@ -22608,8 +22808,8 @@ function formatLinkReport(report) {
|
|
|
22608
22808
|
}
|
|
22609
22809
|
|
|
22610
22810
|
// src/import-openapi.ts
|
|
22611
|
-
import * as
|
|
22612
|
-
import * as
|
|
22811
|
+
import * as fs14 from "fs";
|
|
22812
|
+
import * as path15 from "path";
|
|
22613
22813
|
import { parse as parseYamlString } from "yaml";
|
|
22614
22814
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head"];
|
|
22615
22815
|
function parseYaml(raw, specPath) {
|
|
@@ -22622,9 +22822,9 @@ function parseYaml(raw, specPath) {
|
|
|
22622
22822
|
}
|
|
22623
22823
|
}
|
|
22624
22824
|
function parseSpec(specPath) {
|
|
22625
|
-
if (!
|
|
22626
|
-
const raw =
|
|
22627
|
-
const ext =
|
|
22825
|
+
if (!fs14.existsSync(specPath)) throw new Error(`Spec not found: ${specPath}`);
|
|
22826
|
+
const raw = fs14.readFileSync(specPath, "utf8");
|
|
22827
|
+
const ext = path15.extname(specPath).toLowerCase();
|
|
22628
22828
|
if (ext === ".json") return JSON.parse(raw);
|
|
22629
22829
|
if (ext === ".yaml" || ext === ".yml") return parseYaml(raw, specPath);
|
|
22630
22830
|
try {
|
|
@@ -22655,8 +22855,8 @@ function extractEndpoints(spec) {
|
|
|
22655
22855
|
}
|
|
22656
22856
|
function loadScenarios(runFile) {
|
|
22657
22857
|
if (!runFile) return [];
|
|
22658
|
-
if (!
|
|
22659
|
-
const report = JSON.parse(
|
|
22858
|
+
if (!fs14.existsSync(runFile)) throw new Error(`Run file not found: ${runFile}`);
|
|
22859
|
+
const report = JSON.parse(fs14.readFileSync(runFile, "utf8"));
|
|
22660
22860
|
return (report.features ?? []).flatMap((f) => f.scenarios ?? []);
|
|
22661
22861
|
}
|
|
22662
22862
|
function endpointRefs(endpoint) {
|
|
@@ -22763,25 +22963,25 @@ async function importOpenApi(options) {
|
|
|
22763
22963
|
list.push(item);
|
|
22764
22964
|
groups.set(item.endpoint.tag, list);
|
|
22765
22965
|
}
|
|
22766
|
-
const outputDir = options.outputDir ??
|
|
22767
|
-
if (
|
|
22768
|
-
const entries =
|
|
22966
|
+
const outputDir = options.outputDir ?? path15.join("src", "content", "docs", "api");
|
|
22967
|
+
if (fs14.existsSync(outputDir) && !options.force) {
|
|
22968
|
+
const entries = fs14.readdirSync(outputDir);
|
|
22769
22969
|
if (entries.length > 0) {
|
|
22770
22970
|
throw new Error(`Output directory "${outputDir}" is not empty. Use --force to overwrite.`);
|
|
22771
22971
|
}
|
|
22772
22972
|
}
|
|
22773
|
-
|
|
22973
|
+
fs14.mkdirSync(outputDir, { recursive: true });
|
|
22774
22974
|
const coveredCount = coverage.filter((c) => c.status === "covered").length;
|
|
22775
22975
|
const uncoveredCount = coverage.filter((c) => c.status === "uncovered").length;
|
|
22776
|
-
|
|
22777
|
-
|
|
22976
|
+
fs14.writeFileSync(
|
|
22977
|
+
path15.join(outputDir, "index.mdx"),
|
|
22778
22978
|
renderIndex(groups, hasRun, { endpointCount: endpoints.length, coveredCount, uncoveredCount }),
|
|
22779
22979
|
"utf8"
|
|
22780
22980
|
);
|
|
22781
22981
|
for (const [tag, rows] of groups) {
|
|
22782
|
-
const dir =
|
|
22783
|
-
|
|
22784
|
-
|
|
22982
|
+
const dir = path15.join(outputDir, slug(tag));
|
|
22983
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
22984
|
+
fs14.writeFileSync(path15.join(dir, "index.mdx"), renderTagPage(tag, rows, hasRun), "utf8");
|
|
22785
22985
|
}
|
|
22786
22986
|
return {
|
|
22787
22987
|
outputDir,
|
|
@@ -22793,8 +22993,8 @@ async function importOpenApi(options) {
|
|
|
22793
22993
|
}
|
|
22794
22994
|
|
|
22795
22995
|
// src/build-docs.ts
|
|
22796
|
-
import * as
|
|
22797
|
-
import * as
|
|
22996
|
+
import * as fs16 from "fs";
|
|
22997
|
+
import * as path17 from "path";
|
|
22798
22998
|
|
|
22799
22999
|
// src/scenario-links.ts
|
|
22800
23000
|
function scenarioAnchor(title) {
|
|
@@ -22891,8 +23091,8 @@ ${body.join("\n")}
|
|
|
22891
23091
|
}
|
|
22892
23092
|
|
|
22893
23093
|
// src/notes-index.ts
|
|
22894
|
-
import * as
|
|
22895
|
-
import * as
|
|
23094
|
+
import * as fs15 from "fs";
|
|
23095
|
+
import * as path16 from "path";
|
|
22896
23096
|
import { slug as githubSlug } from "github-slugger";
|
|
22897
23097
|
import { parse as parseYaml2 } from "yaml";
|
|
22898
23098
|
function buildScenarioNotesIndex(notesDir) {
|
|
@@ -22903,8 +23103,8 @@ function buildScenarioNotesIndex(notesDir) {
|
|
|
22903
23103
|
};
|
|
22904
23104
|
}
|
|
22905
23105
|
function writeNotesIndex(index, outPath) {
|
|
22906
|
-
|
|
22907
|
-
|
|
23106
|
+
fs15.mkdirSync(path16.dirname(outPath), { recursive: true });
|
|
23107
|
+
fs15.writeFileSync(outPath, JSON.stringify(index, null, 2), "utf8");
|
|
22908
23108
|
return index;
|
|
22909
23109
|
}
|
|
22910
23110
|
function notesByScenarioId(index) {
|
|
@@ -22921,10 +23121,10 @@ function noteLinkMarkdown(note) {
|
|
|
22921
23121
|
return `[Business context \u2192](${noteHref(note)})`;
|
|
22922
23122
|
}
|
|
22923
23123
|
function readScenarioNote(filePath, notesDir) {
|
|
22924
|
-
const relative5 =
|
|
23124
|
+
const relative5 = path16.relative(notesDir, filePath);
|
|
22925
23125
|
const stem = relative5.replace(/\.(?:md|mdx)$/u, "");
|
|
22926
|
-
const frontmatter = parseFrontmatter(
|
|
22927
|
-
const scenarioId = typeof frontmatter.scenarioId === "string" && frontmatter.scenarioId.trim().length > 0 ? frontmatter.scenarioId.trim() :
|
|
23126
|
+
const frontmatter = parseFrontmatter(fs15.readFileSync(filePath, "utf8"));
|
|
23127
|
+
const scenarioId = typeof frontmatter.scenarioId === "string" && frontmatter.scenarioId.trim().length > 0 ? frontmatter.scenarioId.trim() : path16.basename(stem);
|
|
22928
23128
|
const title = typeof frontmatter.title === "string" && frontmatter.title.trim().length > 0 ? frontmatter.title.trim() : `Business context \u2014 ${scenarioId}`;
|
|
22929
23129
|
return {
|
|
22930
23130
|
scenarioId,
|
|
@@ -22939,7 +23139,7 @@ function parseFrontmatter(source) {
|
|
|
22939
23139
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
22940
23140
|
}
|
|
22941
23141
|
function toRouteSlug(stem) {
|
|
22942
|
-
return stem.split(
|
|
23142
|
+
return stem.split(path16.sep).map((segment) => githubSlug(segment)).join("/").replace(/\/index$/u, "");
|
|
22943
23143
|
}
|
|
22944
23144
|
|
|
22945
23145
|
// src/overview-page.ts
|
|
@@ -23042,22 +23242,22 @@ var BuildDocsError = class extends Error {
|
|
|
23042
23242
|
};
|
|
23043
23243
|
var isRemote = (p) => /^(?:https?:|data:)/i.test(p);
|
|
23044
23244
|
function bundleExplorerAssets(reportPath, assetsDir, baseUrl = "/stories/assets") {
|
|
23045
|
-
if (!
|
|
23046
|
-
const report = JSON.parse(
|
|
23245
|
+
if (!fs16.existsSync(reportPath)) return 0;
|
|
23246
|
+
const report = JSON.parse(fs16.readFileSync(reportPath, "utf8"));
|
|
23047
23247
|
let copied = 0;
|
|
23048
23248
|
const bundle = (value) => {
|
|
23049
|
-
const rel = copyAsset(
|
|
23249
|
+
const rel = copyAsset(path17.resolve(value), assetsDir);
|
|
23050
23250
|
copied++;
|
|
23051
|
-
return `${baseUrl}/${
|
|
23251
|
+
return `${baseUrl}/${path17.basename(rel)}`;
|
|
23052
23252
|
};
|
|
23053
23253
|
const visit = (entries) => {
|
|
23054
23254
|
for (const entry of entries ?? []) {
|
|
23055
23255
|
const e = entry;
|
|
23056
23256
|
if (e.kind === "screenshot" || e.kind === "video" || e.kind === "html") {
|
|
23057
|
-
if (typeof e.path === "string" && !isRemote(e.path) &&
|
|
23257
|
+
if (typeof e.path === "string" && !isRemote(e.path) && fs16.existsSync(e.path)) {
|
|
23058
23258
|
e.path = bundle(e.path);
|
|
23059
23259
|
}
|
|
23060
|
-
if (typeof e.poster === "string" && !isRemote(e.poster) &&
|
|
23260
|
+
if (typeof e.poster === "string" && !isRemote(e.poster) && fs16.existsSync(e.poster)) {
|
|
23061
23261
|
e.poster = bundle(e.poster);
|
|
23062
23262
|
}
|
|
23063
23263
|
}
|
|
@@ -23070,7 +23270,7 @@ function bundleExplorerAssets(reportPath, assetsDir, baseUrl = "/stories/assets"
|
|
|
23070
23270
|
}
|
|
23071
23271
|
}
|
|
23072
23272
|
if (copied > 0) {
|
|
23073
|
-
|
|
23273
|
+
fs16.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
|
|
23074
23274
|
}
|
|
23075
23275
|
return copied;
|
|
23076
23276
|
}
|
|
@@ -23093,9 +23293,9 @@ function changeBadgeLookup(diff) {
|
|
|
23093
23293
|
return (tc) => byKey.get(scenarioKey(tc.sourceFile, tc.story.scenario));
|
|
23094
23294
|
}
|
|
23095
23295
|
function readStoryReport(reportPath) {
|
|
23096
|
-
if (!
|
|
23296
|
+
if (!fs16.existsSync(reportPath)) return null;
|
|
23097
23297
|
try {
|
|
23098
|
-
return JSON.parse(
|
|
23298
|
+
return JSON.parse(fs16.readFileSync(reportPath, "utf8"));
|
|
23099
23299
|
} catch {
|
|
23100
23300
|
return null;
|
|
23101
23301
|
}
|
|
@@ -23120,28 +23320,28 @@ function writeScenarioLinks(reportPath, outDir, options = {}) {
|
|
|
23120
23320
|
const report = readStoryReport(reportPath);
|
|
23121
23321
|
if (!report) return null;
|
|
23122
23322
|
const index = buildScenarioLinks(report, { audienceSplit: options.audienceSplit });
|
|
23123
|
-
|
|
23124
|
-
|
|
23323
|
+
fs16.writeFileSync(
|
|
23324
|
+
path17.join(outDir, "scenario-links.json"),
|
|
23125
23325
|
JSON.stringify(index, null, 2),
|
|
23126
23326
|
"utf8"
|
|
23127
23327
|
);
|
|
23128
23328
|
return index;
|
|
23129
23329
|
}
|
|
23130
23330
|
function clearGeneratedPages(dir) {
|
|
23131
|
-
if (!
|
|
23132
|
-
for (const entry of
|
|
23133
|
-
const full =
|
|
23331
|
+
if (!fs16.existsSync(dir)) return;
|
|
23332
|
+
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
23333
|
+
const full = path17.join(dir, entry.name);
|
|
23134
23334
|
if (entry.isDirectory()) {
|
|
23135
23335
|
clearGeneratedPages(full);
|
|
23136
|
-
if (
|
|
23336
|
+
if (fs16.readdirSync(full).length === 0) fs16.rmdirSync(full);
|
|
23137
23337
|
} else if (/\.mdx?$/.test(entry.name)) {
|
|
23138
|
-
|
|
23338
|
+
fs16.rmSync(full);
|
|
23139
23339
|
}
|
|
23140
23340
|
}
|
|
23141
23341
|
}
|
|
23142
23342
|
function loadCanonicalRun(rawRunPath, synthesize) {
|
|
23143
23343
|
try {
|
|
23144
|
-
const data = JSON.parse(
|
|
23344
|
+
const data = JSON.parse(fs16.readFileSync(path17.resolve(rawRunPath), "utf8"));
|
|
23145
23345
|
if (data.schemaVersion !== 1) {
|
|
23146
23346
|
throw new BuildDocsError(`Unsupported schemaVersion ${data.schemaVersion}. Supported: 1.`, "schema");
|
|
23147
23347
|
}
|
|
@@ -23164,19 +23364,19 @@ ${schemaResult.errors.map((e) => ` ${e}`).join("\n")}`,
|
|
|
23164
23364
|
}
|
|
23165
23365
|
}
|
|
23166
23366
|
async function buildDocs(options) {
|
|
23167
|
-
const siteDir =
|
|
23367
|
+
const siteDir = path17.resolve(options.siteDir);
|
|
23168
23368
|
if (!isScaffoldedAstroSite(siteDir)) {
|
|
23169
23369
|
throw new BuildDocsError(
|
|
23170
23370
|
`"${siteDir}" is not a scaffolded Astro docs site (no astro.config.mjs). Run "executable-stories init-astro <dir>" first, then pass it with --site-dir <dir>.`,
|
|
23171
23371
|
"usage"
|
|
23172
23372
|
);
|
|
23173
23373
|
}
|
|
23174
|
-
const storiesPublicDir =
|
|
23175
|
-
const assetsDir =
|
|
23176
|
-
const storyPagesDir =
|
|
23177
|
-
const notesDir =
|
|
23178
|
-
const apiDir =
|
|
23179
|
-
const reportPath =
|
|
23374
|
+
const storiesPublicDir = path17.join(siteDir, "public", "stories");
|
|
23375
|
+
const assetsDir = path17.join(storiesPublicDir, "assets");
|
|
23376
|
+
const storyPagesDir = path17.join(siteDir, "src", "content", "docs", "stories");
|
|
23377
|
+
const notesDir = path17.join(siteDir, "src", "content", "docs", "notes");
|
|
23378
|
+
const apiDir = path17.join(siteDir, "src", "content", "docs", "api");
|
|
23379
|
+
const reportPath = path17.join(storiesPublicDir, "story-report.json");
|
|
23180
23380
|
const canonical = loadCanonicalRun(options.rawRunPath, options.synthesizeStories ?? true);
|
|
23181
23381
|
try {
|
|
23182
23382
|
await new ReportGenerator({
|
|
@@ -23187,7 +23387,7 @@ async function buildDocs(options) {
|
|
|
23187
23387
|
const currentReport = readStoryReport(reportPath);
|
|
23188
23388
|
let diff;
|
|
23189
23389
|
if (options.baselinePath) {
|
|
23190
|
-
const baselineResolved =
|
|
23390
|
+
const baselineResolved = path17.resolve(options.baselinePath);
|
|
23191
23391
|
const baseline = readStoryReport(baselineResolved);
|
|
23192
23392
|
if (!baseline) {
|
|
23193
23393
|
throw new BuildDocsError(
|
|
@@ -23225,7 +23425,7 @@ async function buildDocs(options) {
|
|
|
23225
23425
|
const sub = partitioned[audience];
|
|
23226
23426
|
audiences[audience] = sub.testCases.length;
|
|
23227
23427
|
if (sub.testCases.length === 0) continue;
|
|
23228
|
-
await genPages(sub,
|
|
23428
|
+
await genPages(sub, path17.join(storyPagesDir, audience));
|
|
23229
23429
|
}
|
|
23230
23430
|
} else {
|
|
23231
23431
|
await genPages(canonical, storyPagesDir);
|
|
@@ -23235,30 +23435,30 @@ async function buildDocs(options) {
|
|
|
23235
23435
|
audienceSplit: options.audienceSplit ?? false
|
|
23236
23436
|
});
|
|
23237
23437
|
const scenarioLinks = linksIndex ? Object.keys(linksIndex.scenarios).length : 0;
|
|
23238
|
-
writeNotesIndex(notesIndex,
|
|
23438
|
+
writeNotesIndex(notesIndex, path17.join(storiesPublicDir, "notes-index.json"));
|
|
23239
23439
|
const notesIndexed = notesIndex.notes.length;
|
|
23240
23440
|
if (linksIndex) {
|
|
23241
|
-
|
|
23242
|
-
|
|
23441
|
+
fs16.writeFileSync(
|
|
23442
|
+
path17.join(storyPagesDir, "index.md"),
|
|
23243
23443
|
renderOverviewPage(linksIndex, notesIndex),
|
|
23244
23444
|
"utf8"
|
|
23245
23445
|
);
|
|
23246
23446
|
}
|
|
23247
|
-
const changesJsonPath =
|
|
23248
|
-
const changesMdPath =
|
|
23447
|
+
const changesJsonPath = path17.join(storiesPublicDir, "changes.json");
|
|
23448
|
+
const changesMdPath = path17.join(storyPagesDir, "changes.md");
|
|
23249
23449
|
let changes;
|
|
23250
23450
|
if (diff && linksIndex) {
|
|
23251
|
-
|
|
23252
|
-
|
|
23451
|
+
fs16.writeFileSync(changesJsonPath, JSON.stringify(diff, null, 2), "utf8");
|
|
23452
|
+
fs16.writeFileSync(changesMdPath, renderChangesPage(diff, linksIndex), "utf8");
|
|
23253
23453
|
changes = diff.summary;
|
|
23254
23454
|
} else {
|
|
23255
|
-
|
|
23256
|
-
|
|
23455
|
+
fs16.rmSync(changesJsonPath, { force: true });
|
|
23456
|
+
fs16.rmSync(changesMdPath, { force: true });
|
|
23257
23457
|
}
|
|
23258
23458
|
let apiPages = 0;
|
|
23259
23459
|
if (options.openapiPath) {
|
|
23260
23460
|
const res = await importOpenApi({
|
|
23261
|
-
specPath:
|
|
23461
|
+
specPath: path17.resolve(options.openapiPath),
|
|
23262
23462
|
outputDir: apiDir,
|
|
23263
23463
|
runFile: reportPath,
|
|
23264
23464
|
force: true
|
|
@@ -23273,11 +23473,11 @@ async function buildDocs(options) {
|
|
|
23273
23473
|
}
|
|
23274
23474
|
|
|
23275
23475
|
// src/config.ts
|
|
23276
|
-
import { existsSync as
|
|
23277
|
-
import { resolve as
|
|
23476
|
+
import { existsSync as existsSync14 } from "fs";
|
|
23477
|
+
import { resolve as resolve11 } from "path";
|
|
23278
23478
|
async function loadConfig(configPath) {
|
|
23279
|
-
const resolved = configPath ?
|
|
23280
|
-
if (!
|
|
23479
|
+
const resolved = configPath ? resolve11(configPath) : resolve11(process.cwd(), "executable-stories.config.js");
|
|
23480
|
+
if (!existsSync14(resolved)) return {};
|
|
23281
23481
|
const mod = await import(resolved);
|
|
23282
23482
|
const config = mod.default;
|
|
23283
23483
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
@@ -23305,6 +23505,7 @@ USAGE
|
|
|
23305
23505
|
executable-stories format <file> [options]
|
|
23306
23506
|
executable-stories format --stdin [options]
|
|
23307
23507
|
executable-stories watch <raw-run.json> [options]
|
|
23508
|
+
executable-stories serve <raw-run.json> [--port <n>] [--host <host>] [options]
|
|
23308
23509
|
executable-stories compare <baseline-file> <current-file> [options]
|
|
23309
23510
|
executable-stories gate-release <dev-run.json> <rc-run.json> [options]
|
|
23310
23511
|
executable-stories review <file> --changed-files <path> [options]
|
|
@@ -23328,6 +23529,7 @@ USAGE
|
|
|
23328
23529
|
SUBCOMMANDS
|
|
23329
23530
|
format Read raw test results and generate reports
|
|
23330
23531
|
watch Regenerate reports whenever the raw-run file changes (live agent index)
|
|
23532
|
+
serve Live docs URL: regenerate + browser reload + "what changed since you started" (for agent loops)
|
|
23331
23533
|
compare Compare two runs and generate a diff report
|
|
23332
23534
|
gate-release Verify a release candidate against the dev test baseline (RC gate)
|
|
23333
23535
|
review Generate an Evidence Review of AI-authored changes (correlate a run to the diff)
|
|
@@ -23546,9 +23748,9 @@ async function parseCliArgs(argv) {
|
|
|
23546
23748
|
process.exit(EXIT_SUCCESS);
|
|
23547
23749
|
}
|
|
23548
23750
|
const subcommand = args[0];
|
|
23549
|
-
if (subcommand !== "format" && subcommand !== "watch" && subcommand !== "compare" && subcommand !== "gate-release" && subcommand !== "deploy" && subcommand !== "review" && subcommand !== "list" && subcommand !== "check" && subcommand !== "goal" && subcommand !== "triage" && subcommand !== "validate" && subcommand !== "init-astro" && subcommand !== "build-docs" && subcommand !== "new" && subcommand !== "check-links" && subcommand !== "import-openapi" && subcommand !== "publish-confluence" && subcommand !== "publish-jira") {
|
|
23751
|
+
if (subcommand !== "format" && subcommand !== "watch" && subcommand !== "serve" && subcommand !== "compare" && subcommand !== "gate-release" && subcommand !== "deploy" && subcommand !== "review" && subcommand !== "list" && subcommand !== "check" && subcommand !== "goal" && subcommand !== "triage" && subcommand !== "validate" && subcommand !== "init-astro" && subcommand !== "build-docs" && subcommand !== "new" && subcommand !== "check-links" && subcommand !== "import-openapi" && subcommand !== "publish-confluence" && subcommand !== "publish-jira") {
|
|
23550
23752
|
console.error(
|
|
23551
|
-
`Unknown subcommand: "${subcommand}". Use "format", "watch", "compare", "gate-release", "deploy", "review", "list", "check", "goal", "triage", "validate", "init-astro", "build-docs", "new", "check-links", "import-openapi", "publish-confluence", or "publish-jira".`
|
|
23753
|
+
`Unknown subcommand: "${subcommand}". Use "format", "watch", "serve", "compare", "gate-release", "deploy", "review", "list", "check", "goal", "triage", "validate", "init-astro", "build-docs", "new", "check-links", "import-openapi", "publish-confluence", or "publish-jira".`
|
|
23552
23754
|
);
|
|
23553
23755
|
process.exit(EXIT_USAGE);
|
|
23554
23756
|
}
|
|
@@ -23666,6 +23868,8 @@ async function parseCliArgs(argv) {
|
|
|
23666
23868
|
"webhook-hmac-timestamp": { type: "boolean", default: false },
|
|
23667
23869
|
"asset-mode": { type: "string", default: "none" },
|
|
23668
23870
|
"allow-missing-assets": { type: "boolean", default: false },
|
|
23871
|
+
port: { type: "string" },
|
|
23872
|
+
host: { type: "string" },
|
|
23669
23873
|
"pr-summary": { type: "boolean", default: false },
|
|
23670
23874
|
"pr-summary-file": { type: "string" },
|
|
23671
23875
|
"fail-on-regression": { type: "boolean", default: false },
|
|
@@ -23888,7 +24092,9 @@ async function parseCliArgs(argv) {
|
|
|
23888
24092
|
headRef: values["head-ref"],
|
|
23889
24093
|
failOn: failOnRaw,
|
|
23890
24094
|
minEvidence: minEvidenceRaw,
|
|
23891
|
-
config: values["config"]
|
|
24095
|
+
config: values["config"],
|
|
24096
|
+
servePort: values["port"] ? Number.parseInt(values["port"], 10) : 4321,
|
|
24097
|
+
serveHost: values["host"] ?? "127.0.0.1"
|
|
23892
24098
|
};
|
|
23893
24099
|
return { args: cliArgs, pluginConfig, customRequested };
|
|
23894
24100
|
}
|
|
@@ -23896,27 +24102,27 @@ async function readInput(args) {
|
|
|
23896
24102
|
if (args.stdin) {
|
|
23897
24103
|
return readStdin();
|
|
23898
24104
|
}
|
|
23899
|
-
const filePath =
|
|
23900
|
-
if (!
|
|
24105
|
+
const filePath = path18.resolve(args.inputFile);
|
|
24106
|
+
if (!fs17.existsSync(filePath)) {
|
|
23901
24107
|
console.error(`Error: File not found: ${filePath}`);
|
|
23902
24108
|
process.exit(EXIT_USAGE);
|
|
23903
24109
|
}
|
|
23904
|
-
return
|
|
24110
|
+
return fs17.readFileSync(filePath, "utf8");
|
|
23905
24111
|
}
|
|
23906
24112
|
function readFileInput(filePath) {
|
|
23907
|
-
const resolved =
|
|
23908
|
-
if (!
|
|
24113
|
+
const resolved = path18.resolve(filePath);
|
|
24114
|
+
if (!fs17.existsSync(resolved)) {
|
|
23909
24115
|
console.error(`Error: File not found: ${resolved}`);
|
|
23910
24116
|
process.exit(EXIT_USAGE);
|
|
23911
24117
|
}
|
|
23912
|
-
return
|
|
24118
|
+
return fs17.readFileSync(resolved, "utf8");
|
|
23913
24119
|
}
|
|
23914
24120
|
function readStdin() {
|
|
23915
|
-
return new Promise((
|
|
24121
|
+
return new Promise((resolve13, reject) => {
|
|
23916
24122
|
const chunks = [];
|
|
23917
24123
|
process.stdin.setEncoding("utf8");
|
|
23918
24124
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
23919
|
-
process.stdin.on("end", () =>
|
|
24125
|
+
process.stdin.on("end", () => resolve13(chunks.join("")));
|
|
23920
24126
|
process.stdin.on("error", reject);
|
|
23921
24127
|
});
|
|
23922
24128
|
}
|
|
@@ -24042,14 +24248,14 @@ function tryNormalizeRunFromText(text2, args) {
|
|
|
24042
24248
|
}
|
|
24043
24249
|
}
|
|
24044
24250
|
function listBaselineCandidates(currentFile, args) {
|
|
24045
|
-
const baselineDir =
|
|
24046
|
-
const currentResolved =
|
|
24047
|
-
if (!
|
|
24251
|
+
const baselineDir = path18.resolve(args.baselineDir ?? path18.dirname(currentFile));
|
|
24252
|
+
const currentResolved = path18.resolve(currentFile);
|
|
24253
|
+
if (!fs17.existsSync(baselineDir)) {
|
|
24048
24254
|
console.error(`Error: baseline directory not found: ${baselineDir}`);
|
|
24049
24255
|
process.exit(EXIT_USAGE);
|
|
24050
24256
|
}
|
|
24051
|
-
const entries =
|
|
24052
|
-
return entries.filter((entry) => entry.isFile()).map((entry) =>
|
|
24257
|
+
const entries = fs17.readdirSync(baselineDir, { withFileTypes: true });
|
|
24258
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => path18.join(baselineDir, entry.name)).filter((candidate) => path18.resolve(candidate) !== currentResolved).filter(
|
|
24053
24259
|
(candidate) => args.inputType === "ndjson" ? candidate.endsWith(".ndjson") : candidate.endsWith(".json")
|
|
24054
24260
|
);
|
|
24055
24261
|
}
|
|
@@ -24057,14 +24263,14 @@ function resolveBaselineAuto(currentFile, currentRun, args) {
|
|
|
24057
24263
|
const candidates = listBaselineCandidates(currentFile, args);
|
|
24058
24264
|
const comparable = [];
|
|
24059
24265
|
for (const candidate of candidates) {
|
|
24060
|
-
const run = tryNormalizeRunFromText(
|
|
24266
|
+
const run = tryNormalizeRunFromText(fs17.readFileSync(candidate, "utf8"), args);
|
|
24061
24267
|
if (run) {
|
|
24062
24268
|
comparable.push({ file: candidate, run });
|
|
24063
24269
|
}
|
|
24064
24270
|
}
|
|
24065
24271
|
if (comparable.length === 0) {
|
|
24066
24272
|
console.error(
|
|
24067
|
-
`Error: no compatible baseline files found in ${
|
|
24273
|
+
`Error: no compatible baseline files found in ${path18.resolve(args.baselineDir ?? path18.dirname(currentFile))}.`
|
|
24068
24274
|
);
|
|
24069
24275
|
process.exit(EXIT_USAGE);
|
|
24070
24276
|
}
|
|
@@ -24266,6 +24472,24 @@ async function main() {
|
|
|
24266
24472
|
});
|
|
24267
24473
|
return;
|
|
24268
24474
|
}
|
|
24475
|
+
if (args.subcommand === "serve") {
|
|
24476
|
+
if (!args.inputFile) {
|
|
24477
|
+
console.error("Error: serve requires an input file (the raw-run JSON the framework writes).");
|
|
24478
|
+
process.exit(EXIT_USAGE);
|
|
24479
|
+
}
|
|
24480
|
+
const serveFormats = args.formats.includes("html") ? args.formats : [...args.formats, "html"];
|
|
24481
|
+
startServe({
|
|
24482
|
+
input: args.inputFile,
|
|
24483
|
+
outputDir: args.outputDir,
|
|
24484
|
+
outputName: args.outputName,
|
|
24485
|
+
formats: serveFormats,
|
|
24486
|
+
inputType: args.inputType === "canonical" ? "canonical" : "raw",
|
|
24487
|
+
synthesize: args.synthesizeStories,
|
|
24488
|
+
port: args.servePort,
|
|
24489
|
+
host: args.serveHost
|
|
24490
|
+
});
|
|
24491
|
+
return;
|
|
24492
|
+
}
|
|
24269
24493
|
const text2 = await readInput(args);
|
|
24270
24494
|
if (args.inputType === "ndjson") {
|
|
24271
24495
|
if (args.subcommand === "validate") {
|
|
@@ -24309,9 +24533,9 @@ async function main() {
|
|
|
24309
24533
|
process.exit(EXIT_SCHEMA_VALIDATION);
|
|
24310
24534
|
}
|
|
24311
24535
|
if (args.emitCanonical) {
|
|
24312
|
-
const outPath =
|
|
24313
|
-
|
|
24314
|
-
|
|
24536
|
+
const outPath = path18.resolve(args.emitCanonical);
|
|
24537
|
+
fs17.mkdirSync(path18.dirname(outPath), { recursive: true });
|
|
24538
|
+
fs17.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
|
|
24315
24539
|
}
|
|
24316
24540
|
try {
|
|
24317
24541
|
const result = await generateReports(run, args);
|
|
@@ -24368,9 +24592,9 @@ ${msg}`);
|
|
|
24368
24592
|
}
|
|
24369
24593
|
const run = data;
|
|
24370
24594
|
if (args.emitCanonical) {
|
|
24371
|
-
const outPath =
|
|
24372
|
-
|
|
24373
|
-
|
|
24595
|
+
const outPath = path18.resolve(args.emitCanonical);
|
|
24596
|
+
fs17.mkdirSync(path18.dirname(outPath), { recursive: true });
|
|
24597
|
+
fs17.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
|
|
24374
24598
|
}
|
|
24375
24599
|
try {
|
|
24376
24600
|
const result = await generateReports(run, args);
|
|
@@ -24426,9 +24650,9 @@ ${msg}`);
|
|
|
24426
24650
|
process.exit(EXIT_CANONICAL_VALIDATION);
|
|
24427
24651
|
}
|
|
24428
24652
|
if (args.emitCanonical) {
|
|
24429
|
-
const outPath =
|
|
24430
|
-
|
|
24431
|
-
|
|
24653
|
+
const outPath = path18.resolve(args.emitCanonical);
|
|
24654
|
+
fs17.mkdirSync(path18.dirname(outPath), { recursive: true });
|
|
24655
|
+
fs17.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
|
|
24432
24656
|
}
|
|
24433
24657
|
try {
|
|
24434
24658
|
const result = await generateReports(canonical, args, droppedMissingStory);
|
|
@@ -24453,9 +24677,9 @@ function runCustomFormatters(run, customRequested, formatters, args) {
|
|
|
24453
24677
|
const ext = formatter.fileExtension ?? formatName;
|
|
24454
24678
|
const baseName = args.outputName ?? "report";
|
|
24455
24679
|
const filename = args.outputNameTimestamp ? `${baseName}-${Math.floor(run.startedAtMs / 1e3)}.${ext}` : `${baseName}.${ext}`;
|
|
24456
|
-
const filepath =
|
|
24457
|
-
|
|
24458
|
-
|
|
24680
|
+
const filepath = path18.join(outputDir, filename);
|
|
24681
|
+
fs17.mkdirSync(outputDir, { recursive: true });
|
|
24682
|
+
fs17.writeFileSync(filepath, content, "utf8");
|
|
24459
24683
|
console.log(`Generated: ${filepath}`);
|
|
24460
24684
|
} catch (err) {
|
|
24461
24685
|
console.error(`Error running custom formatter "${formatName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -24505,13 +24729,13 @@ async function dispatchNotifications(run, args) {
|
|
|
24505
24729
|
}
|
|
24506
24730
|
function runHistoryPipeline(run, args) {
|
|
24507
24731
|
if (!args.historyFile) return;
|
|
24508
|
-
const historyPath =
|
|
24732
|
+
const historyPath = path18.resolve(args.historyFile);
|
|
24509
24733
|
const store = loadHistory(
|
|
24510
24734
|
{ filePath: historyPath },
|
|
24511
24735
|
{
|
|
24512
24736
|
readFile: (p) => {
|
|
24513
24737
|
try {
|
|
24514
|
-
return
|
|
24738
|
+
return fs17.readFileSync(p, "utf8");
|
|
24515
24739
|
} catch {
|
|
24516
24740
|
return void 0;
|
|
24517
24741
|
}
|
|
@@ -24524,11 +24748,11 @@ function runHistoryPipeline(run, args) {
|
|
|
24524
24748
|
run,
|
|
24525
24749
|
maxRuns: args.maxHistoryRuns
|
|
24526
24750
|
});
|
|
24527
|
-
const dir =
|
|
24528
|
-
|
|
24751
|
+
const dir = path18.dirname(historyPath);
|
|
24752
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
24529
24753
|
saveHistory(
|
|
24530
24754
|
{ filePath: historyPath, store: updated },
|
|
24531
|
-
{ writeFile: (p, content) =>
|
|
24755
|
+
{ writeFile: (p, content) => fs17.writeFileSync(p, content, "utf8") }
|
|
24532
24756
|
);
|
|
24533
24757
|
let metricsCount = 0;
|
|
24534
24758
|
for (const testId of Object.keys(updated.tests)) {
|
|
@@ -24676,11 +24900,11 @@ function writeReviewReport(review, args) {
|
|
|
24676
24900
|
const outputDir = args.outputDir ?? "reports";
|
|
24677
24901
|
const baseName = args.outputName ?? "evidence-review";
|
|
24678
24902
|
const suffix = args.outputNameTimestamp ? `-${Math.floor(review.run.startedAtMs / 1e3)}` : "";
|
|
24679
|
-
|
|
24680
|
-
const mdPath =
|
|
24681
|
-
const htmlPath =
|
|
24682
|
-
|
|
24683
|
-
|
|
24903
|
+
fs17.mkdirSync(outputDir, { recursive: true });
|
|
24904
|
+
const mdPath = path18.join(outputDir, `${baseName}${suffix}.md`);
|
|
24905
|
+
const htmlPath = path18.join(outputDir, `${baseName}${suffix}.html`);
|
|
24906
|
+
fs17.writeFileSync(mdPath, markdown, "utf8");
|
|
24907
|
+
fs17.writeFileSync(htmlPath, html, "utf8");
|
|
24684
24908
|
return [mdPath, htmlPath];
|
|
24685
24909
|
}
|
|
24686
24910
|
function evaluateReviewGate(review, args) {
|
|
@@ -24726,9 +24950,9 @@ function printResult(result, args, startMs, droppedMissingStory = 0) {
|
|
|
24726
24950
|
function printCompareResult(result, args, startMs) {
|
|
24727
24951
|
const durationMs = Date.now() - startMs;
|
|
24728
24952
|
if (result.prSummary && args.prSummaryFile) {
|
|
24729
|
-
const outputPath =
|
|
24730
|
-
|
|
24731
|
-
|
|
24953
|
+
const outputPath = path18.resolve(args.prSummaryFile);
|
|
24954
|
+
fs17.mkdirSync(path18.dirname(outputPath), { recursive: true });
|
|
24955
|
+
fs17.writeFileSync(outputPath, result.prSummary, "utf8");
|
|
24732
24956
|
}
|
|
24733
24957
|
if (args.jsonSummary) {
|
|
24734
24958
|
console.log(
|
|
@@ -24757,13 +24981,13 @@ function printCompareResult(result, args, startMs) {
|
|
|
24757
24981
|
}
|
|
24758
24982
|
}
|
|
24759
24983
|
function loadReleasePolicy(policyPath) {
|
|
24760
|
-
const resolved =
|
|
24761
|
-
if (!
|
|
24984
|
+
const resolved = path18.resolve(policyPath);
|
|
24985
|
+
if (!fs17.existsSync(resolved)) {
|
|
24762
24986
|
console.error(`Error: release policy file not found: ${resolved}`);
|
|
24763
24987
|
process.exit(EXIT_USAGE);
|
|
24764
24988
|
}
|
|
24765
24989
|
try {
|
|
24766
|
-
const raw = JSON.parse(
|
|
24990
|
+
const raw = JSON.parse(fs17.readFileSync(resolved, "utf8"));
|
|
24767
24991
|
return {
|
|
24768
24992
|
allowedOmissions: Array.isArray(raw.allowedOmissions) ? raw.allowedOmissions : [],
|
|
24769
24993
|
allowedRegressions: Array.isArray(raw.allowedRegressions) ? raw.allowedRegressions : [],
|
|
@@ -24867,7 +25091,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
|
|
|
24867
25091
|
console.error("Error: missing ADF file argument. Run with --help for usage.");
|
|
24868
25092
|
process.exit(EXIT_USAGE);
|
|
24869
25093
|
}
|
|
24870
|
-
if (!
|
|
25094
|
+
if (!fs17.existsSync(inputFile)) {
|
|
24871
25095
|
console.error(`Error: file not found: ${inputFile}`);
|
|
24872
25096
|
process.exit(EXIT_USAGE);
|
|
24873
25097
|
}
|
|
@@ -24895,7 +25119,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
|
|
|
24895
25119
|
console.error("Error: --title is required when creating a new page");
|
|
24896
25120
|
process.exit(EXIT_USAGE);
|
|
24897
25121
|
}
|
|
24898
|
-
const adf =
|
|
25122
|
+
const adf = fs17.readFileSync(path18.resolve(inputFile), "utf8");
|
|
24899
25123
|
if (dryRun) {
|
|
24900
25124
|
console.log(
|
|
24901
25125
|
JSON.stringify(
|
|
@@ -24974,7 +25198,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
|
|
|
24974
25198
|
console.error("Error: missing ADF file argument. Run with --help for usage.");
|
|
24975
25199
|
process.exit(EXIT_USAGE);
|
|
24976
25200
|
}
|
|
24977
|
-
if (!
|
|
25201
|
+
if (!fs17.existsSync(inputFile)) {
|
|
24978
25202
|
console.error(`Error: file not found: ${inputFile}`);
|
|
24979
25203
|
process.exit(EXIT_USAGE);
|
|
24980
25204
|
}
|
|
@@ -25001,7 +25225,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
|
|
|
25001
25225
|
process.exit(EXIT_USAGE);
|
|
25002
25226
|
}
|
|
25003
25227
|
const mode = modeRaw;
|
|
25004
|
-
const adf =
|
|
25228
|
+
const adf = fs17.readFileSync(path18.resolve(inputFile), "utf8");
|
|
25005
25229
|
if (dryRun) {
|
|
25006
25230
|
console.log(
|
|
25007
25231
|
JSON.stringify(
|
|
@@ -25189,7 +25413,7 @@ async function runBuildDocs(rawArgs) {
|
|
|
25189
25413
|
` \u2022 What's changed \u2192 src/content/docs/stories/changes.md (+${c.added} added, ${c.regressed} regressed, ${c.fixed} fixed, ${c.removed} removed)`
|
|
25190
25414
|
);
|
|
25191
25415
|
}
|
|
25192
|
-
const rel =
|
|
25416
|
+
const rel = path18.relative(process.cwd(), result.siteDir) || ".";
|
|
25193
25417
|
console.log(`
|
|
25194
25418
|
Preview: cd ${rel} && npm run dev`);
|
|
25195
25419
|
return EXIT_SUCCESS;
|
|
@@ -25416,7 +25640,9 @@ function createDefaultCliArgs() {
|
|
|
25416
25640
|
failOnAddedFailures: false,
|
|
25417
25641
|
failOnRemoval: false,
|
|
25418
25642
|
failOnNew: false,
|
|
25419
|
-
baselineMode: "explicit"
|
|
25643
|
+
baselineMode: "explicit",
|
|
25644
|
+
servePort: 4321,
|
|
25645
|
+
serveHost: "127.0.0.1"
|
|
25420
25646
|
};
|
|
25421
25647
|
}
|
|
25422
25648
|
main().catch((err) => {
|