playwright-checkpoint 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/{chunk-KG37WSYS.js → chunk-M3BRR3LT.js} +9 -3
- package/dist/{chunk-KG37WSYS.js.map → chunk-M3BRR3LT.js.map} +1 -1
- package/dist/{chunk-X5IPL32H.js → chunk-WXZOP7XI.js} +153 -35
- package/dist/chunk-WXZOP7XI.js.map +1 -0
- package/dist/{chunk-K5DX32TO.js → chunk-YUFXGGZM.js} +2 -2
- package/dist/cli/bin.cjs +2501 -2386
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +3 -2
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/index.cjs +1405 -68
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +3 -2
- package/dist/{core-CD4jHGgI.d.cts → core-6gyzs35M.d.ts} +2 -1
- package/dist/{core-CZvnc0rE.d.ts → core-Dd3WLuTs.d.cts} +2 -1
- package/dist/core.cjs +8 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/{index-BjYQX_hK.d.ts → index-CvcgBzvl.d.ts} +1 -1
- package/dist/{index-Cabk31qi.d.cts → index-OQx9qcVO.d.cts} +1 -1
- package/dist/index.cjs +212 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +69 -15
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +148 -34
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +4 -4
- package/dist/teardown.cjs +1409 -72
- package/dist/teardown.cjs.map +1 -1
- package/dist/teardown.js +3 -2
- package/dist/teardown.js.map +1 -1
- package/dist/{types-G7w4n8kR.d.cts → types-wX4eB9mb.d.cts} +16 -1
- package/dist/{types-G7w4n8kR.d.ts → types-wX4eB9mb.d.ts} +16 -1
- package/package.json +2 -1
- package/dist/chunk-X5IPL32H.js.map +0 -1
- /package/dist/{chunk-K5DX32TO.js.map → chunk-YUFXGGZM.js.map} +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
warn
|
|
3
|
+
} from "./chunk-M3BRR3LT.js";
|
|
4
|
+
|
|
1
5
|
// src/report/story-utils.ts
|
|
2
6
|
function groupByStory(runs) {
|
|
3
7
|
const stories = /* @__PURE__ */ new Map();
|
|
@@ -630,6 +634,28 @@ function stripTags(value) {
|
|
|
630
634
|
const stripped = value.replace(/\s+@[a-z0-9-]+/gi, " ").replace(/\s+/g, " ").trim();
|
|
631
635
|
return stripped || value.trim() || "Untitled story";
|
|
632
636
|
}
|
|
637
|
+
function articleTitle(run) {
|
|
638
|
+
const override = run.article?.title?.trim();
|
|
639
|
+
return override || stripTags(run.title);
|
|
640
|
+
}
|
|
641
|
+
function articleDescription(run) {
|
|
642
|
+
const description = run.article?.description?.trim();
|
|
643
|
+
return description ? description : null;
|
|
644
|
+
}
|
|
645
|
+
function articleSlug(run) {
|
|
646
|
+
const override = run.article?.slug?.trim();
|
|
647
|
+
return slugify2(override || stripTags(run.title));
|
|
648
|
+
}
|
|
649
|
+
function uniqueArticleSlug(baseSlug, usedSlugs) {
|
|
650
|
+
if (!usedSlugs.has(baseSlug)) {
|
|
651
|
+
return baseSlug;
|
|
652
|
+
}
|
|
653
|
+
let index = 1;
|
|
654
|
+
while (usedSlugs.has(`${baseSlug}-${index}`)) {
|
|
655
|
+
index += 1;
|
|
656
|
+
}
|
|
657
|
+
return `${baseSlug}-${index}`;
|
|
658
|
+
}
|
|
633
659
|
function normalizeConfig(config) {
|
|
634
660
|
return {
|
|
635
661
|
storiesDir: typeof config.storiesDir === "string" ? config.storiesDir : ".",
|
|
@@ -640,7 +666,8 @@ function normalizeConfig(config) {
|
|
|
640
666
|
footer: typeof config.footer === "string" ? config.footer : void 0,
|
|
641
667
|
frontmatter: config.frontmatter === true || config.frontmatter === false || config.frontmatter != null && typeof config.frontmatter === "object" && !Array.isArray(config.frontmatter) ? config.frontmatter : false,
|
|
642
668
|
imagePathPrefix: typeof config.imagePathPrefix === "string" ? config.imagePathPrefix : void 0,
|
|
643
|
-
copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true
|
|
669
|
+
copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true,
|
|
670
|
+
requireExplicitStep: typeof config.requireExplicitStep === "boolean" ? config.requireExplicitStep : false
|
|
644
671
|
};
|
|
645
672
|
}
|
|
646
673
|
function normalizeTags(tags) {
|
|
@@ -652,6 +679,9 @@ function shouldIncludeRun(run, config) {
|
|
|
652
679
|
const runTags = new Set(normalizeTags(run.tags));
|
|
653
680
|
return includeTags.some((tag) => runTags.has(tag));
|
|
654
681
|
}
|
|
682
|
+
if (run.articles) {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
655
685
|
return run.checkpoints.some((checkpoint) => {
|
|
656
686
|
const hasDescription = typeof checkpoint.description === "string" && checkpoint.description.trim().length > 0;
|
|
657
687
|
return hasDescription || typeof checkpoint.step === "number";
|
|
@@ -770,17 +800,22 @@ async function materializeScreenshot(args) {
|
|
|
770
800
|
if (!sourcePath) {
|
|
771
801
|
return null;
|
|
772
802
|
}
|
|
803
|
+
const cachedTargetPath = args.screenshotCopies?.get(sourcePath);
|
|
804
|
+
if (cachedTargetPath) {
|
|
805
|
+
return rewriteImagePath(args.markdownFile, cachedTargetPath, args.outputDir, args.config.imagePathPrefix);
|
|
806
|
+
}
|
|
773
807
|
const extension = path2.extname(sourcePath) || ".png";
|
|
774
808
|
const targetPath = path2.join(
|
|
775
809
|
args.outputDir,
|
|
776
810
|
args.config.screenshotsDir ?? "screenshots",
|
|
777
|
-
args.
|
|
778
|
-
`${
|
|
811
|
+
args.screenshotDirSlug,
|
|
812
|
+
`${args.screenshotFileSlug}${extension}`
|
|
779
813
|
);
|
|
780
814
|
try {
|
|
781
815
|
if (args.config.copyScreenshots !== false) {
|
|
782
816
|
await fs2.mkdir(path2.dirname(targetPath), { recursive: true });
|
|
783
817
|
await fs2.copyFile(sourcePath, targetPath);
|
|
818
|
+
args.screenshotCopies?.set(sourcePath, targetPath);
|
|
784
819
|
args.writtenFiles.add(targetPath);
|
|
785
820
|
return rewriteImagePath(args.markdownFile, targetPath, args.outputDir, args.config.imagePathPrefix);
|
|
786
821
|
}
|
|
@@ -800,10 +835,31 @@ function orderedCheckpoints(checkpoints) {
|
|
|
800
835
|
});
|
|
801
836
|
}
|
|
802
837
|
async function buildSteps(args) {
|
|
803
|
-
|
|
838
|
+
let checkpoints;
|
|
839
|
+
if (args.stepNames && args.stepNames.length > 0) {
|
|
840
|
+
const byName = /* @__PURE__ */ new Map();
|
|
841
|
+
for (const checkpoint of args.run.checkpoints) {
|
|
842
|
+
if (byName.has(checkpoint.name)) {
|
|
843
|
+
warn(`Duplicate checkpoint name "${checkpoint.name}" in "${args.run.title}". Using the latest capture for article generation.`);
|
|
844
|
+
}
|
|
845
|
+
byName.set(checkpoint.name, checkpoint);
|
|
846
|
+
}
|
|
847
|
+
checkpoints = args.stepNames.map((stepName) => {
|
|
848
|
+
const checkpoint = byName.get(stepName);
|
|
849
|
+
if (!checkpoint) {
|
|
850
|
+
warn(`Markdown article step "${stepName}" was not captured in "${args.run.title}". Skipping step.`);
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
return checkpoint;
|
|
854
|
+
}).filter((checkpoint) => checkpoint !== null);
|
|
855
|
+
} else {
|
|
856
|
+
checkpoints = orderedCheckpoints(args.run.checkpoints).filter(
|
|
857
|
+
(checkpoint) => !args.config.requireExplicitStep || typeof checkpoint.step === "number"
|
|
858
|
+
);
|
|
859
|
+
}
|
|
804
860
|
const steps = [];
|
|
805
861
|
for (const [index, checkpoint] of checkpoints.entries()) {
|
|
806
|
-
const order = typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
|
|
862
|
+
const order = args.stepNames ? index + 1 : typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
|
|
807
863
|
steps.push({
|
|
808
864
|
checkpoint,
|
|
809
865
|
order,
|
|
@@ -812,12 +868,13 @@ async function buildSteps(args) {
|
|
|
812
868
|
imagePath: await materializeScreenshot({
|
|
813
869
|
run: args.run,
|
|
814
870
|
checkpoint,
|
|
815
|
-
|
|
816
|
-
|
|
871
|
+
screenshotDirSlug: args.screenshotDirSlug,
|
|
872
|
+
screenshotFileSlug: args.stepNames ? checkpoint.slug : `${String(order).padStart(2, "0")}-${slugify2(checkpoint.name)}`,
|
|
817
873
|
outputDir: args.outputDir,
|
|
818
874
|
markdownFile: args.markdownFile,
|
|
819
875
|
config: args.config,
|
|
820
|
-
writtenFiles: args.writtenFiles
|
|
876
|
+
writtenFiles: args.writtenFiles,
|
|
877
|
+
screenshotCopies: args.screenshotCopies
|
|
821
878
|
}),
|
|
822
879
|
urlLabel: urlLabel(checkpoint.url),
|
|
823
880
|
breadcrumbLabel: breadcrumbLabel(checkpoint.url),
|
|
@@ -828,13 +885,14 @@ async function buildSteps(args) {
|
|
|
828
885
|
}
|
|
829
886
|
function renderMarkdown(args) {
|
|
830
887
|
const frontmatterFields = args.config.frontmatter === true || typeof args.config.frontmatter === "object" ? {
|
|
831
|
-
title: args.title,
|
|
832
888
|
project: args.run.project,
|
|
833
|
-
testId: args.run.testId,
|
|
834
889
|
tags: args.run.tags,
|
|
890
|
+
...args.config.frontmatter && typeof args.config.frontmatter === "object" ? args.config.frontmatter : {},
|
|
891
|
+
...args.article?.frontmatter ?? {},
|
|
892
|
+
testId: args.run.testId,
|
|
835
893
|
startedAt: args.run.startedAt,
|
|
836
894
|
generatedAt: args.generatedAt,
|
|
837
|
-
|
|
895
|
+
title: args.title
|
|
838
896
|
} : null;
|
|
839
897
|
const sections = args.steps.map((step) => {
|
|
840
898
|
const lines = [`## Step ${step.order}: ${step.heading}`, ""];
|
|
@@ -854,6 +912,7 @@ function renderMarkdown(args) {
|
|
|
854
912
|
const parts = [
|
|
855
913
|
frontmatterFields ? serializeFrontmatter(frontmatterFields) : "",
|
|
856
914
|
`# ${args.title}`,
|
|
915
|
+
args.description?.trim() ?? "",
|
|
857
916
|
args.config.header ? args.config.header.trim() : "",
|
|
858
917
|
sections,
|
|
859
918
|
args.config.footer ? args.config.footer.trim() : ""
|
|
@@ -861,6 +920,42 @@ function renderMarkdown(args) {
|
|
|
861
920
|
return `${parts.join("\n\n")}
|
|
862
921
|
`;
|
|
863
922
|
}
|
|
923
|
+
function resolveArticles(run) {
|
|
924
|
+
const multiArticles = (run.articles ?? []).filter((article) => Array.isArray(article.steps));
|
|
925
|
+
if (run.articles && multiArticles.length === 0) {
|
|
926
|
+
warn(`Markdown reporter received an empty articles array for "${run.title}". Falling back to the default single-article output.`);
|
|
927
|
+
}
|
|
928
|
+
if (multiArticles.length === 0) {
|
|
929
|
+
return [
|
|
930
|
+
{
|
|
931
|
+
title: articleTitle(run),
|
|
932
|
+
description: articleDescription(run),
|
|
933
|
+
slug: articleSlug(run),
|
|
934
|
+
metadata: run.article,
|
|
935
|
+
screenshotDirSlug: articleSlug(run)
|
|
936
|
+
}
|
|
937
|
+
];
|
|
938
|
+
}
|
|
939
|
+
const usedSlugs = /* @__PURE__ */ new Set();
|
|
940
|
+
const screenshotDirSlug = slugify2(stripTags(run.title));
|
|
941
|
+
return multiArticles.map((article, index) => {
|
|
942
|
+
const fallbackSlug = `${screenshotDirSlug}-${index + 1}`;
|
|
943
|
+
const baseSlug = slugify2(article.slug?.trim() || fallbackSlug);
|
|
944
|
+
const uniqueSlug = uniqueArticleSlug(baseSlug, usedSlugs);
|
|
945
|
+
if (uniqueSlug !== baseSlug) {
|
|
946
|
+
warn(`Markdown article slug collision for "${article.title ?? run.title}" resolved as "${uniqueSlug}".`);
|
|
947
|
+
}
|
|
948
|
+
usedSlugs.add(uniqueSlug);
|
|
949
|
+
return {
|
|
950
|
+
title: article.title?.trim() || stripTags(run.title),
|
|
951
|
+
description: article.description?.trim() || null,
|
|
952
|
+
slug: uniqueSlug,
|
|
953
|
+
metadata: article,
|
|
954
|
+
stepNames: [...article.steps],
|
|
955
|
+
screenshotDirSlug
|
|
956
|
+
};
|
|
957
|
+
});
|
|
958
|
+
}
|
|
864
959
|
var markdownReporter = {
|
|
865
960
|
name: "markdown",
|
|
866
961
|
description: "Generates one Markdown help article per captured story.",
|
|
@@ -872,37 +967,56 @@ var markdownReporter = {
|
|
|
872
967
|
const stories = groupByStory(context.runs);
|
|
873
968
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
874
969
|
const writtenFiles = /* @__PURE__ */ new Set();
|
|
970
|
+
const usedStorySlugs = /* @__PURE__ */ new Set();
|
|
971
|
+
const screenshotCopies = /* @__PURE__ */ new Map();
|
|
875
972
|
let articleCount = 0;
|
|
876
973
|
for (const [storyTitle, runs] of stories) {
|
|
877
974
|
const primaryRun = choosePrimaryRun(runs, config.preferredProject);
|
|
878
975
|
if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {
|
|
879
976
|
continue;
|
|
880
977
|
}
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
markdownFile,
|
|
895
|
-
renderMarkdown({
|
|
896
|
-
title,
|
|
897
|
-
steps,
|
|
978
|
+
for (const article of resolveArticles(primaryRun)) {
|
|
979
|
+
let storySlug = article.slug;
|
|
980
|
+
if (usedStorySlugs.has(storySlug)) {
|
|
981
|
+
let index = 2;
|
|
982
|
+
while (usedStorySlugs.has(`${article.slug}-${index}`)) {
|
|
983
|
+
index += 1;
|
|
984
|
+
}
|
|
985
|
+
storySlug = `${article.slug}-${index}`;
|
|
986
|
+
warn(`Markdown article slug collision for "${article.title || storyTitle}" resolved as "${storySlug}".`);
|
|
987
|
+
}
|
|
988
|
+
usedStorySlugs.add(storySlug);
|
|
989
|
+
const markdownFile = path2.join(context.outputDir, config.storiesDir ?? ".", `${storySlug}.md`);
|
|
990
|
+
const steps = await buildSteps({
|
|
898
991
|
run: primaryRun,
|
|
992
|
+
stepNames: article.stepNames,
|
|
993
|
+
screenshotDirSlug: article.screenshotDirSlug,
|
|
994
|
+
outputDir: context.outputDir,
|
|
995
|
+
markdownFile,
|
|
899
996
|
config,
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
997
|
+
writtenFiles,
|
|
998
|
+
screenshotCopies
|
|
999
|
+
});
|
|
1000
|
+
if (steps.length === 0) {
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
await fs2.mkdir(path2.dirname(markdownFile), { recursive: true });
|
|
1004
|
+
await fs2.writeFile(
|
|
1005
|
+
markdownFile,
|
|
1006
|
+
renderMarkdown({
|
|
1007
|
+
title: article.title,
|
|
1008
|
+
description: article.description,
|
|
1009
|
+
steps,
|
|
1010
|
+
run: primaryRun,
|
|
1011
|
+
article: article.metadata,
|
|
1012
|
+
config,
|
|
1013
|
+
generatedAt
|
|
1014
|
+
}),
|
|
1015
|
+
"utf8"
|
|
1016
|
+
);
|
|
1017
|
+
writtenFiles.add(markdownFile);
|
|
1018
|
+
articleCount += 1;
|
|
1019
|
+
}
|
|
906
1020
|
}
|
|
907
1021
|
return {
|
|
908
1022
|
files: [...writtenFiles],
|
|
@@ -1370,6 +1484,8 @@ function toRunRecord(manifest, sourceManifestPath) {
|
|
|
1370
1484
|
project: manifest.project,
|
|
1371
1485
|
testId: manifest.testId,
|
|
1372
1486
|
title: manifest.title,
|
|
1487
|
+
...manifest.article ? { article: manifest.article } : {},
|
|
1488
|
+
...manifest.articles ? { articles: manifest.articles } : {},
|
|
1373
1489
|
tags: manifest.tags,
|
|
1374
1490
|
startedAt: manifest.startedAt,
|
|
1375
1491
|
checkpoints: manifest.checkpoints
|
|
@@ -1381,6 +1497,8 @@ function toManifest(run) {
|
|
|
1381
1497
|
project: run.project,
|
|
1382
1498
|
testId: run.testId,
|
|
1383
1499
|
title: run.title,
|
|
1500
|
+
...run.article ? { article: run.article } : {},
|
|
1501
|
+
...run.articles ? { articles: run.articles } : {},
|
|
1384
1502
|
tags: run.tags,
|
|
1385
1503
|
startedAt: run.startedAt,
|
|
1386
1504
|
checkpoints: run.checkpoints
|
|
@@ -1481,4 +1599,4 @@ export {
|
|
|
1481
1599
|
loadRuns,
|
|
1482
1600
|
runReporters
|
|
1483
1601
|
};
|
|
1484
|
-
//# sourceMappingURL=chunk-
|
|
1602
|
+
//# sourceMappingURL=chunk-WXZOP7XI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/report/story-utils.ts","../src/report/html-reporter.ts","../src/report/markdown-reporter.ts","../src/report/mdx-reporter.ts","../src/report/annotate.ts","../src/report/index.ts"],"sourcesContent":["import type { RunRecord } from '../types';\n\nexport function groupByStory(runs: RunRecord[]): Map<string, RunRecord[]> {\n const stories = new Map<string, RunRecord[]>();\n\n for (const run of runs) {\n const existing = stories.get(run.title) ?? [];\n existing.push(run);\n stories.set(run.title, existing);\n }\n\n return stories;\n}\n\nexport function orderedCheckpointNames(runs: RunRecord[]): string[] {\n const names: string[] = [];\n const seen = new Set<string>();\n\n for (const run of runs) {\n for (const checkpoint of run.checkpoints) {\n if (seen.has(checkpoint.name)) {\n continue;\n }\n\n seen.add(checkpoint.name);\n names.push(checkpoint.name);\n }\n }\n\n return names;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { groupByStory, orderedCheckpointNames } from './story-utils';\n\ntype HtmlReporterConfig = {\n title?: string;\n projectOrder?: string[];\n};\n\nconst DEFAULT_PROJECT_ORDER = ['desktop-light', 'desktop-dark', 'mobile-light', 'mobile-dark'];\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction slugify(value: string): string {\n return (\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction formatDateTime(isoDate: string): string {\n const date = new Date(isoDate);\n if (Number.isNaN(date.getTime())) {\n return isoDate;\n }\n\n return new Intl.DateTimeFormat('en-US', {\n dateStyle: 'medium',\n timeStyle: 'short',\n }).format(date);\n}\n\nfunction projectWeight(projectName: string, projectOrder: string[]): number {\n const index = projectOrder.indexOf(projectName);\n return index === -1 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction formatProjectLabel(projectName: string): string {\n const [device, mode] = projectName.split('-');\n if (!device || !mode) {\n return projectName;\n }\n\n const deviceLabel = device === 'desktop' ? 'Desktop' : device === 'mobile' ? 'Mobile' : device;\n const modeLabel = mode === 'light' ? 'Light' : mode === 'dark' ? 'Dark' : mode;\n return `${deviceLabel} / ${modeLabel}`;\n}\n\nfunction sortByProjectAndTime(a: RunRecord, b: RunRecord, projectOrder: string[]): number {\n const byProject = projectWeight(a.project, projectOrder) - projectWeight(b.project, projectOrder);\n if (byProject !== 0) {\n return byProject;\n }\n\n const byProjectName = a.project.localeCompare(b.project);\n if (byProjectName !== 0) {\n return byProjectName;\n }\n\n return new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime();\n}\n\nfunction getCollectorSummaryNumber(checkpoint: CheckpointRecord, collectorName: string, key: string): number | null {\n const value = checkpoint.collectors[collectorName]?.summary[key];\n return typeof value === 'number' ? value : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction highlightOverlayStyle(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const bounds = data?.highlightBounds;\n const imageSize = data?.imageSize;\n\n if (\n !bounds ||\n !imageSize ||\n typeof bounds.x !== 'number' ||\n typeof bounds.y !== 'number' ||\n typeof bounds.width !== 'number' ||\n typeof bounds.height !== 'number' ||\n typeof imageSize.width !== 'number' ||\n typeof imageSize.height !== 'number' ||\n imageSize.width <= 0 ||\n imageSize.height <= 0\n ) {\n return null;\n }\n\n const left = (bounds.x / imageSize.width) * 100;\n const top = (bounds.y / imageSize.height) * 100;\n const width = (bounds.width / imageSize.width) * 100;\n const height = (bounds.height / imageSize.height) * 100;\n\n return [\n `left:${left.toFixed(4)}%`,\n `top:${top.toFixed(4)}%`,\n `width:${width.toFixed(4)}%`,\n `height:${height.toFixed(4)}%`,\n ].join(';');\n}\n\nfunction highlightLabel(checkpoint: CheckpointRecord): string | null {\n const selector = screenshotData(checkpoint)?.highlightSelector;\n return typeof selector === 'string' && selector.trim().length > 0 ? `Focus: ${selector.trim()}` : null;\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction toEncodedHref(outputDir: string, filePath: string | null): string | null {\n if (!filePath) {\n return null;\n }\n\n const relativePath = path.relative(outputDir, filePath);\n return relativePath.split(path.sep).map(encodeURIComponent).join('/');\n}\n\nfunction getArtifactHref(\n run: RunRecord,\n checkpoint: CheckpointRecord,\n outputDir: string,\n collectorName: string,\n artifactName?: string,\n): string | null {\n const artifacts = checkpoint.collectors[collectorName]?.artifacts ?? [];\n const artifact = artifactName\n ? artifacts.find((entry) => entry.name === artifactName)\n : artifacts[0];\n\n if (!artifact?.path) {\n return null;\n }\n\n return toEncodedHref(outputDir, resolveArtifactPath(run, artifact.path));\n}\n\nfunction renderArtifactLinks(run: RunRecord, checkpoint: CheckpointRecord, outputDir: string): string {\n const links: Array<{ label: string; href: string | null }> = [\n { label: 'DOM HTML', href: getArtifactHref(run, checkpoint, outputDir, 'html', 'html') },\n { label: 'Axe', href: getArtifactHref(run, checkpoint, outputDir, 'axe', 'axe') },\n { label: 'Web Vitals', href: getArtifactHref(run, checkpoint, outputDir, 'web-vitals', 'web-vitals') },\n { label: 'Console', href: getArtifactHref(run, checkpoint, outputDir, 'console', 'console-errors') },\n { label: 'Failed Requests', href: getArtifactHref(run, checkpoint, outputDir, 'network', 'failed-requests') },\n ];\n\n return links\n .map((link) => {\n if (!link.href) {\n return `<span class=\"artifact disabled\">${escapeHtml(link.label)}</span>`;\n }\n\n return `<a class=\"artifact\" href=\"${link.href}\" target=\"_blank\" rel=\"noreferrer\">${escapeHtml(link.label)}</a>`;\n })\n .join('');\n}\n\nfunction renderCheckpointCard(run: RunRecord, checkpointName: string, outputDir: string): string {\n const checkpoint = run.checkpoints.find((entry) => entry.name === checkpointName);\n if (!checkpoint) {\n return `\n <article class=\"variant-card missing\">\n <header class=\"variant-card-header\">\n <div>\n <h5>${escapeHtml(formatProjectLabel(run.project))}</h5>\n <p>${escapeHtml(run.project)}</p>\n </div>\n <time>${escapeHtml(formatDateTime(run.startedAt))}</time>\n </header>\n <div class=\"empty-card\">No checkpoint captured for this run.</div>\n </article>\n `;\n }\n\n const screenshotHref = getArtifactHref(run, checkpoint, outputDir, 'screenshot', 'screenshot');\n const overlayStyle = highlightOverlayStyle(checkpoint);\n const focus = highlightLabel(checkpoint);\n const axeViolations = getCollectorSummaryNumber(checkpoint, 'axe', 'violations');\n const consoleErrors = getCollectorSummaryNumber(checkpoint, 'console', 'consoleErrorCount') ?? 0;\n const failedRequests = getCollectorSummaryNumber(checkpoint, 'network', 'failedRequestCount') ?? 0;\n\n return `\n <article class=\"variant-card\">\n <header class=\"variant-card-header\">\n <div>\n <h5>${escapeHtml(formatProjectLabel(run.project))}</h5>\n <p>${escapeHtml(run.project)}</p>\n </div>\n <time>${escapeHtml(formatDateTime(checkpoint.timestamp || run.startedAt))}</time>\n </header>\n <p class=\"page-meta\">\n <span>${escapeHtml(checkpoint.title || 'Untitled page')}</span>\n <span class=\"page-url\">${escapeHtml(checkpoint.url)}</span>\n </p>\n ${\n screenshotHref\n ? `<a class=\"thumbnail-link\" href=\"${screenshotHref}\" target=\"_blank\" rel=\"noreferrer\">\n <img src=\"${screenshotHref}\" alt=\"${escapeHtml(`${run.project} — ${checkpoint.name}`)}\" loading=\"lazy\" />\n ${overlayStyle ? `<span class=\"highlight-overlay\" style=\"${overlayStyle}\" aria-hidden=\"true\"></span>` : ''}\n ${focus ? `<span class=\"highlight-label\">${escapeHtml(focus)}</span>` : ''}\n </a>`\n : '<div class=\"empty-card\">Screenshot unavailable.</div>'\n }\n <div class=\"stats-grid\">\n <span><strong>${axeViolations ?? 'n/a'}</strong><small>Axe violations</small></span>\n <span><strong>${consoleErrors}</strong><small>Console errors</small></span>\n <span><strong>${failedRequests}</strong><small>Failed requests</small></span>\n </div>\n <div class=\"artifact-list\">${renderArtifactLinks(run, checkpoint, outputDir)}</div>\n </article>\n `;\n}\n\nfunction renderStorySection(title: string, runs: RunRecord[], outputDir: string): string {\n const checkpointNames = orderedCheckpointNames(runs);\n const environments = [...new Set(runs.map((run) => run.environment))].sort();\n const tags = [...new Set(runs.flatMap((run) => run.tags))].sort();\n\n const checkpointBlocks = checkpointNames\n .map(\n (checkpointName) => `\n <details class=\"accordion checkpoint-block\">\n <summary class=\"checkpoint-summary\">\n <span>${escapeHtml(checkpointName)}</span>\n <span class=\"checkpoint-meta\">${runs.length} variants</span>\n </summary>\n <div class=\"variant-grid\">\n ${runs.map((run) => renderCheckpointCard(run, checkpointName, outputDir)).join('')}\n </div>\n </details>\n `,\n )\n .join('');\n\n return `\n <details class=\"accordion story-block\" id=\"story-${slugify(title)}\" open>\n <summary class=\"story-summary\">\n <span class=\"story-title\">${escapeHtml(title)}</span>\n <span class=\"story-meta-chip\">${runs.length} run${runs.length === 1 ? '' : 's'}</span>\n </summary>\n <div class=\"story-body\">\n <div class=\"story-meta-row\">\n <span><strong>Projects</strong> ${escapeHtml(runs.map((run) => run.project).join(', '))}</span>\n <span><strong>Environments</strong> ${escapeHtml(environments.join(', ') || 'n/a')}</span>\n <span><strong>Tags</strong> ${escapeHtml(tags.join(', ') || 'none')}</span>\n </div>\n ${checkpointBlocks || '<p class=\"empty-state\">No checkpoints captured for this story.</p>'}\n </div>\n </details>\n `;\n}\n\nfunction buildHtmlReport(runs: RunRecord[], outputDir: string, config: HtmlReporterConfig): string {\n const groupedRuns = groupByStory(runs);\n const storyTitles = [...groupedRuns.keys()].sort((a, b) => a.localeCompare(b));\n const projectOrder = Array.isArray(config.projectOrder)\n ? config.projectOrder.filter((value): value is string => typeof value === 'string')\n : DEFAULT_PROJECT_ORDER;\n const generatedAt = new Date().toISOString();\n const reportTitle = typeof config.title === 'string' && config.title.trim() ? config.title.trim() : 'Playwright Checkpoint Report';\n\n for (const title of storyTitles) {\n groupedRuns.get(title)?.sort((a, b) => sortByProjectAndTime(a, b, projectOrder));\n }\n\n const navLinks = storyTitles\n .map((title) => `<a href=\"#story-${slugify(title)}\">${escapeHtml(title)}</a>`)\n .join('');\n\n const storySections = storyTitles\n .map((title) => renderStorySection(title, groupedRuns.get(title) ?? [], outputDir))\n .join('');\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(reportTitle)}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: #0b1020;\n --panel: rgba(15, 23, 42, 0.88);\n --panel-2: rgba(17, 25, 40, 0.98);\n --text: #e5eefb;\n --muted: #9fb3c8;\n --accent: #60a5fa;\n --accent-2: #22d3ee;\n --border: rgba(148, 163, 184, 0.18);\n --success: #34d399;\n --warning: #fbbf24;\n --danger: #fb7185;\n --shadow: 0 24px 64px rgba(2, 6, 23, 0.45);\n }\n * { box-sizing: border-box; }\n html { scroll-behavior: smooth; }\n body {\n margin: 0;\n min-height: 100vh;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background:\n radial-gradient(circle at top, rgba(96, 165, 250, 0.14), transparent 30%),\n linear-gradient(180deg, #07101f 0%, #0b1020 100%);\n color: var(--text);\n }\n a { color: inherit; }\n .page {\n width: min(1600px, calc(100vw - 32px));\n margin: 0 auto;\n padding: 28px 0 56px;\n }\n .hero {\n background: linear-gradient(180deg, rgba(15, 23, 42, 0.96), rgba(15, 23, 42, 0.84));\n border: 1px solid var(--border);\n border-radius: 24px;\n padding: 24px;\n box-shadow: var(--shadow);\n backdrop-filter: blur(18px);\n }\n .hero h1 {\n margin: 0;\n font-size: clamp(1.9rem, 2.6vw, 3rem);\n line-height: 1.1;\n }\n .hero p {\n margin: 10px 0 0;\n color: var(--muted);\n max-width: 72ch;\n line-height: 1.6;\n }\n .summary-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 18px;\n }\n .summary-pill {\n display: inline-flex;\n gap: 8px;\n align-items: center;\n padding: 9px 12px;\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.72);\n color: var(--muted);\n font-size: 0.92rem;\n }\n .summary-pill strong { color: var(--text); }\n .toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n gap: 16px;\n margin-top: 18px;\n padding-top: 18px;\n border-top: 1px solid var(--border);\n }\n .story-nav {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n }\n .story-nav a,\n .toolbar button,\n .artifact {\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.7);\n color: var(--text);\n text-decoration: none;\n padding: 8px 12px;\n font: inherit;\n font-size: 0.86rem;\n transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;\n }\n .toolbar button:hover,\n .story-nav a:hover,\n .artifact:hover {\n transform: translateY(-1px);\n border-color: rgba(96, 165, 250, 0.55);\n background: rgba(30, 41, 59, 0.96);\n cursor: pointer;\n }\n .content {\n display: grid;\n gap: 18px;\n margin-top: 22px;\n }\n .accordion {\n border: 1px solid var(--border);\n border-radius: 22px;\n background: var(--panel);\n box-shadow: var(--shadow);\n overflow: hidden;\n }\n .accordion summary {\n list-style: none;\n cursor: pointer;\n }\n .accordion summary::-webkit-details-marker { display: none; }\n .story-summary,\n .checkpoint-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n }\n .story-summary {\n padding: 20px 22px;\n background: linear-gradient(180deg, rgba(15, 23, 42, 0.92), rgba(15, 23, 42, 0.74));\n }\n .story-title {\n font-size: 1.1rem;\n font-weight: 700;\n }\n .story-meta-chip,\n .checkpoint-meta {\n color: var(--muted);\n font-size: 0.84rem;\n white-space: nowrap;\n }\n .story-body {\n padding: 0 22px 22px;\n }\n .story-meta-row {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n color: var(--muted);\n font-size: 0.92rem;\n line-height: 1.5;\n margin: 4px 0 18px;\n }\n .story-meta-row strong { color: var(--text); margin-right: 6px; }\n .checkpoint-block {\n margin-top: 14px;\n border-radius: 18px;\n background: var(--panel-2);\n border: 1px solid rgba(148, 163, 184, 0.14);\n }\n .checkpoint-summary {\n padding: 16px 18px;\n font-weight: 600;\n background: rgba(15, 23, 42, 0.68);\n }\n .variant-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 14px;\n padding: 0 18px 18px;\n }\n .variant-card {\n display: grid;\n gap: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 18px;\n background: rgba(15, 23, 42, 0.72);\n padding: 16px;\n min-height: 100%;\n }\n .variant-card.missing {\n opacity: 0.72;\n border-style: dashed;\n }\n .variant-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 10px;\n }\n .variant-card-header h5 {\n margin: 0;\n font-size: 1rem;\n }\n .variant-card-header p,\n .variant-card-header time {\n margin: 4px 0 0;\n color: var(--muted);\n font-size: 0.83rem;\n }\n .page-meta {\n display: grid;\n gap: 4px;\n margin: 0;\n color: var(--muted);\n font-size: 0.9rem;\n }\n .page-url {\n overflow-wrap: anywhere;\n font-size: 0.82rem;\n }\n .thumbnail-link {\n position: relative;\n display: block;\n border-radius: 14px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(2, 6, 23, 0.65);\n }\n .thumbnail-link img {\n display: block;\n width: 100%;\n aspect-ratio: 16 / 10;\n object-fit: cover;\n }\n .highlight-overlay {\n position: absolute;\n border: 2px solid var(--danger);\n border-radius: 12px;\n background: rgba(251, 113, 133, 0.08);\n box-shadow: 0 0 0 999px rgba(251, 113, 133, 0.02);\n pointer-events: none;\n }\n .highlight-label {\n position: absolute;\n left: 12px;\n bottom: 12px;\n max-width: calc(100% - 24px);\n padding: 6px 9px;\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.9);\n border: 1px solid rgba(251, 113, 133, 0.35);\n color: #ffe4e6;\n font-size: 0.74rem;\n line-height: 1.3;\n overflow-wrap: anywhere;\n }\n .stats-grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n }\n .stats-grid span {\n display: grid;\n gap: 6px;\n padding: 10px 12px;\n border-radius: 14px;\n background: rgba(2, 6, 23, 0.42);\n border: 1px solid rgba(148, 163, 184, 0.12);\n }\n .stats-grid strong {\n font-size: 1.15rem;\n line-height: 1;\n }\n .stats-grid small {\n color: var(--muted);\n font-size: 0.76rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n .artifact-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n .artifact.disabled {\n opacity: 0.45;\n pointer-events: none;\n }\n .empty-state,\n .empty-card {\n margin: 0;\n color: var(--muted);\n padding: 12px;\n border: 1px dashed rgba(148, 163, 184, 0.2);\n border-radius: 14px;\n background: rgba(2, 6, 23, 0.24);\n }\n @media (max-width: 720px) {\n .page {\n width: min(100vw - 20px, 1600px);\n padding-top: 18px;\n }\n .hero,\n .story-summary,\n .story-body,\n .checkpoint-summary,\n .variant-grid {\n padding-left: 16px;\n padding-right: 16px;\n }\n .variant-card-header,\n .story-summary,\n .checkpoint-summary,\n .toolbar {\n flex-direction: column;\n align-items: flex-start;\n }\n .stats-grid {\n grid-template-columns: 1fr;\n }\n }\n </style>\n</head>\n<body>\n <main class=\"page\">\n <section class=\"hero\">\n <h1>${escapeHtml(reportTitle)}</h1>\n <p>Explore checkpoint runs by story, inspect every project variant side-by-side, and jump directly to screenshots and generated artifacts.</p>\n <div class=\"summary-bar\">\n <span class=\"summary-pill\"><strong>Generated</strong> ${escapeHtml(formatDateTime(generatedAt))}</span>\n <span class=\"summary-pill\"><strong>Stories</strong> ${storyTitles.length}</span>\n <span class=\"summary-pill\"><strong>Runs</strong> ${runs.length}</span>\n <span class=\"summary-pill\"><strong>Output</strong> ${escapeHtml(outputDir)}</span>\n </div>\n <div class=\"toolbar\">\n <nav class=\"story-nav\">${navLinks || '<span class=\"summary-pill\">No stories found</span>'}</nav>\n <div class=\"toolbar-actions\">\n <button type=\"button\" data-action=\"expand-all\">Expand all</button>\n <button type=\"button\" data-action=\"collapse-all\">Collapse all</button>\n </div>\n </div>\n </section>\n <section class=\"content\">\n ${storySections || '<p class=\"empty-state\">No checkpoint manifests found.</p>'}\n </section>\n </main>\n <script>\n (() => {\n const details = Array.from(document.querySelectorAll('details.accordion'));\n const setAll = (open) => {\n details.forEach((entry) => {\n entry.open = open;\n });\n };\n document.querySelector('[data-action=\"expand-all\"]')?.addEventListener('click', () => setAll(true));\n document.querySelector('[data-action=\"collapse-all\"]')?.addEventListener('click', () => setAll(false));\n const revealHash = () => {\n if (!window.location.hash) {\n return;\n }\n const target = document.querySelector(window.location.hash);\n if (!(target instanceof HTMLElement)) {\n return;\n }\n const parentDetails = target.closest('details');\n if (parentDetails instanceof HTMLDetailsElement) {\n parentDetails.open = true;\n }\n target.scrollIntoView({ block: 'start', behavior: 'smooth' });\n };\n window.addEventListener('hashchange', revealHash);\n revealHash();\n })();\n </script>\n</body>\n</html>\n`;\n}\n\nexport const htmlReporter: ReportGenerator = {\n name: 'html',\n description: 'Responsive HTML report for checkpoint manifests.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const outputFile = path.join(context.outputDir, 'index.html');\n const html = buildHtmlReport(context.runs, context.outputDir, context.config as HtmlReporterConfig);\n\n await fs.mkdir(context.outputDir, { recursive: true });\n await fs.writeFile(outputFile, html, 'utf8');\n\n const storyCount = new Set(context.runs.map((run) => run.title)).size;\n\n return {\n files: [outputFile],\n summary: `Generated HTML report for ${storyCount} stor${storyCount === 1 ? 'y' : 'ies'} (${context.runs.length} run${context.runs.length === 1 ? '' : 's'}).`,\n };\n },\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { ArticleDefinition, ArticleMetadata, CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { warn } from '../core';\nimport { groupByStory } from './story-utils';\n\ntype MarkdownReporterConfig = {\n storiesDir?: string;\n screenshotsDir?: string;\n includeTags?: string[];\n preferredProject?: string;\n header?: string;\n footer?: string;\n frontmatter?: boolean | Record<string, unknown>;\n imagePathPrefix?: string;\n copyScreenshots?: boolean;\n requireExplicitStep?: boolean;\n};\n\ntype MarkdownStep = {\n checkpoint: CheckpointRecord;\n order: number;\n heading: string;\n description: string;\n imagePath: string | null;\n urlLabel: string;\n breadcrumbLabel: string | null;\n focusNote: string | null;\n};\n\ntype MarkdownArticle = {\n title: string;\n description: string | null;\n slug: string;\n metadata?: ArticleMetadata;\n stepNames?: string[];\n screenshotDirSlug: string;\n};\n\nfunction slugify(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction stripTags(value: string): string {\n const stripped = value.replace(/\\s+@[a-z0-9-]+/gi, ' ').replace(/\\s+/g, ' ').trim();\n return stripped || value.trim() || 'Untitled story';\n}\n\nfunction articleTitle(run: RunRecord): string {\n const override = run.article?.title?.trim();\n return override || stripTags(run.title);\n}\n\nfunction articleDescription(run: RunRecord): string | null {\n const description = run.article?.description?.trim();\n return description ? description : null;\n}\n\nfunction articleSlug(run: RunRecord): string {\n const override = run.article?.slug?.trim();\n return slugify(override || stripTags(run.title));\n}\n\nfunction uniqueArticleSlug(baseSlug: string, usedSlugs: Set<string>): string {\n if (!usedSlugs.has(baseSlug)) {\n return baseSlug;\n }\n\n let index = 1;\n while (usedSlugs.has(`${baseSlug}-${index}`)) {\n index += 1;\n }\n\n return `${baseSlug}-${index}`;\n}\n\nfunction normalizeConfig(config: Record<string, unknown>): MarkdownReporterConfig {\n return {\n storiesDir: typeof config.storiesDir === 'string' ? config.storiesDir : '.',\n screenshotsDir: typeof config.screenshotsDir === 'string' ? config.screenshotsDir : 'screenshots',\n includeTags: Array.isArray(config.includeTags)\n ? config.includeTags.filter((value): value is string => typeof value === 'string')\n : undefined,\n preferredProject: typeof config.preferredProject === 'string' ? config.preferredProject : undefined,\n header: typeof config.header === 'string' ? config.header : undefined,\n footer: typeof config.footer === 'string' ? config.footer : undefined,\n frontmatter:\n config.frontmatter === true ||\n config.frontmatter === false ||\n (config.frontmatter != null && typeof config.frontmatter === 'object' && !Array.isArray(config.frontmatter))\n ? (config.frontmatter as MarkdownReporterConfig['frontmatter'])\n : false,\n imagePathPrefix: typeof config.imagePathPrefix === 'string' ? config.imagePathPrefix : undefined,\n copyScreenshots: typeof config.copyScreenshots === 'boolean' ? config.copyScreenshots : true,\n requireExplicitStep: typeof config.requireExplicitStep === 'boolean' ? config.requireExplicitStep : false,\n };\n}\n\nfunction normalizeTags(tags: string[] | undefined): string[] {\n return (tags ?? []).map((tag) => tag.trim().toLowerCase()).filter(Boolean);\n}\n\nfunction shouldIncludeRun(run: RunRecord, config: MarkdownReporterConfig): boolean {\n const includeTags = normalizeTags(config.includeTags);\n if (includeTags.length > 0) {\n const runTags = new Set(normalizeTags(run.tags));\n return includeTags.some((tag) => runTags.has(tag));\n }\n\n if (run.articles) {\n return true;\n }\n\n return run.checkpoints.some((checkpoint) => {\n const hasDescription = typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0;\n return hasDescription || typeof checkpoint.step === 'number';\n });\n}\n\nfunction choosePrimaryRun(runs: RunRecord[], preferredProject?: string): RunRecord | null {\n if (runs.length === 0) {\n return null;\n }\n\n return [...runs].sort((left, right) => {\n const leftPreferred = preferredProject && left.project === preferredProject ? 0 : 1;\n const rightPreferred = preferredProject && right.project === preferredProject ? 0 : 1;\n if (leftPreferred !== rightPreferred) {\n return leftPreferred - rightPreferred;\n }\n\n const rightTime = new Date(right.startedAt).getTime();\n const leftTime = new Date(left.startedAt).getTime();\n if (rightTime !== leftTime) {\n return rightTime - leftTime;\n }\n\n return left.project.localeCompare(right.project);\n })[0] ?? null;\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction screenshotSourcePath(run: RunRecord, checkpoint: CheckpointRecord): string | null {\n const artifacts = checkpoint.collectors.screenshot?.artifacts ?? [];\n const artifact = artifacts.find((entry) => entry.name === 'screenshot') ?? artifacts[0];\n return artifact?.path ? resolveArtifactPath(run, artifact.path) : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction focusNote(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const selector = typeof data?.highlightSelector === 'string' ? data.highlightSelector.trim() : '';\n if (selector) {\n return `Focus: \\`${selector}\\``;\n }\n\n const bounds = data?.highlightBounds;\n if (\n bounds &&\n typeof bounds.x === 'number' &&\n typeof bounds.y === 'number' &&\n typeof bounds.width === 'number' &&\n typeof bounds.height === 'number'\n ) {\n return 'Focus: highlighted UI element.';\n }\n\n return null;\n}\n\nfunction urlLabel(url: string): string {\n try {\n const parsed = new URL(url);\n const value = `${parsed.pathname}${parsed.search}${parsed.hash}`;\n return value || '/';\n } catch {\n return url || '/';\n }\n}\n\nfunction breadcrumbLabel(url: string): string | null {\n const label = urlLabel(url);\n if (!label.startsWith('/')) {\n return null;\n }\n\n const [withoutQuery = label] = label.split('?');\n const [withoutHash = withoutQuery] = withoutQuery.split('#');\n\n const segments = withoutHash\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => decodeURIComponent(segment).replace(/[-_]+/g, ' '));\n\n return segments.length > 0 ? segments.join(' › ') : 'home';\n}\n\nfunction autoDescription(checkpoint: CheckpointRecord): string {\n const pageTitle = checkpoint.title.trim();\n const location = urlLabel(checkpoint.url);\n\n if (pageTitle) {\n return `This step captures **${pageTitle}** at \\`${location}\\`.`;\n }\n\n return `This step captures **${checkpoint.name}** at \\`${location}\\`.`;\n}\n\nfunction markdownRelativePath(fromFile: string, toFile: string): string {\n const relativePath = path.relative(path.dirname(fromFile), toFile).split(path.sep).join('/');\n if (relativePath.startsWith('.')) {\n return relativePath;\n }\n\n return `./${relativePath}`;\n}\n\nfunction rewriteImagePath(markdownFile: string, imageFile: string, outputDir: string, prefix?: string): string {\n const relativePath = path.relative(outputDir, imageFile).split(path.sep).join('/');\n if (prefix) {\n return `${prefix.replace(/\\/+$/g, '')}/${relativePath.replace(/^\\/+/, '')}`;\n }\n\n return markdownRelativePath(markdownFile, imageFile);\n}\n\nfunction yamlScalar(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction serializeFrontmatter(fields: Record<string, unknown>): string {\n const lines = ['---'];\n\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n lines.push(`${key}:`);\n if (value.length === 0) {\n lines.push(' []');\n continue;\n }\n\n for (const item of value) {\n lines.push(` - ${yamlScalar(item)}`);\n }\n continue;\n }\n\n lines.push(`${key}: ${yamlScalar(value)}`);\n }\n\n lines.push('---', '');\n return lines.join('\\n');\n}\n\nasync function materializeScreenshot(args: {\n run: RunRecord;\n checkpoint: CheckpointRecord;\n screenshotDirSlug: string;\n screenshotFileSlug: string;\n outputDir: string;\n markdownFile: string;\n config: MarkdownReporterConfig;\n writtenFiles: Set<string>;\n screenshotCopies?: Map<string, string>;\n}): Promise<string | null> {\n const sourcePath = screenshotSourcePath(args.run, args.checkpoint);\n if (!sourcePath) {\n return null;\n }\n\n const cachedTargetPath = args.screenshotCopies?.get(sourcePath);\n if (cachedTargetPath) {\n return rewriteImagePath(args.markdownFile, cachedTargetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n const extension = path.extname(sourcePath) || '.png';\n const targetPath = path.join(\n args.outputDir,\n args.config.screenshotsDir ?? 'screenshots',\n args.screenshotDirSlug,\n `${args.screenshotFileSlug}${extension}`,\n );\n\n try {\n if (args.config.copyScreenshots !== false) {\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\n await fs.copyFile(sourcePath, targetPath);\n args.screenshotCopies?.set(sourcePath, targetPath);\n args.writtenFiles.add(targetPath);\n return rewriteImagePath(args.markdownFile, targetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n return rewriteImagePath(args.markdownFile, sourcePath, args.outputDir, args.config.imagePathPrefix);\n } catch {\n return null;\n }\n}\n\nfunction orderedCheckpoints(checkpoints: CheckpointRecord[]): CheckpointRecord[] {\n return [...checkpoints].sort((left, right) => {\n const leftOrder = typeof left.step === 'number' ? left.step : Number.MAX_SAFE_INTEGER;\n const rightOrder = typeof right.step === 'number' ? right.step : Number.MAX_SAFE_INTEGER;\n if (leftOrder !== rightOrder) {\n return leftOrder - rightOrder;\n }\n\n return checkpoints.indexOf(left) - checkpoints.indexOf(right);\n });\n}\n\nasync function buildSteps(args: {\n run: RunRecord;\n stepNames?: string[];\n screenshotDirSlug: string;\n outputDir: string;\n markdownFile: string;\n config: MarkdownReporterConfig;\n writtenFiles: Set<string>;\n screenshotCopies?: Map<string, string>;\n}): Promise<MarkdownStep[]> {\n let checkpoints: CheckpointRecord[];\n if (args.stepNames && args.stepNames.length > 0) {\n const byName = new Map<string, CheckpointRecord>();\n\n for (const checkpoint of args.run.checkpoints) {\n if (byName.has(checkpoint.name)) {\n warn(`Duplicate checkpoint name \"${checkpoint.name}\" in \"${args.run.title}\". Using the latest capture for article generation.`);\n }\n\n byName.set(checkpoint.name, checkpoint);\n }\n\n checkpoints = args.stepNames\n .map((stepName) => {\n const checkpoint = byName.get(stepName);\n if (!checkpoint) {\n warn(`Markdown article step \"${stepName}\" was not captured in \"${args.run.title}\". Skipping step.`);\n return null;\n }\n\n return checkpoint;\n })\n .filter((checkpoint): checkpoint is CheckpointRecord => checkpoint !== null);\n } else {\n checkpoints = orderedCheckpoints(args.run.checkpoints).filter(\n (checkpoint) => !args.config.requireExplicitStep || typeof checkpoint.step === 'number',\n );\n }\n\n const steps: MarkdownStep[] = [];\n\n for (const [index, checkpoint] of checkpoints.entries()) {\n const order = args.stepNames ? index + 1 : typeof checkpoint.step === 'number' ? checkpoint.step : index + 1;\n steps.push({\n checkpoint,\n order,\n heading: checkpoint.name,\n description:\n typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0\n ? checkpoint.description.trim()\n : autoDescription(checkpoint),\n imagePath: await materializeScreenshot({\n run: args.run,\n checkpoint,\n screenshotDirSlug: args.screenshotDirSlug,\n screenshotFileSlug: args.stepNames ? checkpoint.slug : `${String(order).padStart(2, '0')}-${slugify(checkpoint.name)}`,\n outputDir: args.outputDir,\n markdownFile: args.markdownFile,\n config: args.config,\n writtenFiles: args.writtenFiles,\n screenshotCopies: args.screenshotCopies,\n }),\n urlLabel: urlLabel(checkpoint.url),\n breadcrumbLabel: breadcrumbLabel(checkpoint.url),\n focusNote: focusNote(checkpoint),\n });\n }\n\n return steps;\n}\n\nfunction renderMarkdown(args: {\n title: string;\n description?: string | null;\n steps: MarkdownStep[];\n run: RunRecord;\n article?: ArticleMetadata;\n config: MarkdownReporterConfig;\n generatedAt: string;\n}): string {\n const frontmatterFields =\n args.config.frontmatter === true || typeof args.config.frontmatter === 'object'\n ? {\n project: args.run.project,\n tags: args.run.tags,\n ...(args.config.frontmatter && typeof args.config.frontmatter === 'object' ? args.config.frontmatter : {}),\n ...(args.article?.frontmatter ?? {}),\n testId: args.run.testId,\n startedAt: args.run.startedAt,\n generatedAt: args.generatedAt,\n title: args.title,\n }\n : null;\n\n const sections = args.steps\n .map((step) => {\n const lines = [`## Step ${step.order}: ${step.heading}`, ''];\n\n if (step.imagePath) {\n lines.push(``, '');\n }\n\n lines.push(`**URL:** \\`${step.urlLabel}\\``);\n if (step.breadcrumbLabel) {\n lines.push('', `**Breadcrumb:** ${step.breadcrumbLabel}`);\n }\n\n if (step.focusNote) {\n lines.push('', `> ${step.focusNote}`);\n }\n\n lines.push('', step.description);\n\n return lines.join('\\n');\n })\n .join('\\n\\n');\n\n const parts = [\n frontmatterFields ? serializeFrontmatter(frontmatterFields) : '',\n `# ${args.title}`,\n args.description?.trim() ?? '',\n args.config.header ? args.config.header.trim() : '',\n sections,\n args.config.footer ? args.config.footer.trim() : '',\n ].filter((value) => value.trim().length > 0);\n\n return `${parts.join('\\n\\n')}\\n`;\n}\n\nfunction resolveArticles(run: RunRecord): MarkdownArticle[] {\n const multiArticles = (run.articles ?? []).filter((article): article is ArticleDefinition => Array.isArray(article.steps));\n if (run.articles && multiArticles.length === 0) {\n warn(`Markdown reporter received an empty articles array for \"${run.title}\". Falling back to the default single-article output.`);\n }\n\n if (multiArticles.length === 0) {\n return [\n {\n title: articleTitle(run),\n description: articleDescription(run),\n slug: articleSlug(run),\n metadata: run.article,\n screenshotDirSlug: articleSlug(run),\n },\n ];\n }\n\n const usedSlugs = new Set<string>();\n const screenshotDirSlug = slugify(stripTags(run.title));\n\n return multiArticles.map((article, index) => {\n const fallbackSlug = `${screenshotDirSlug}-${index + 1}`;\n const baseSlug = slugify(article.slug?.trim() || fallbackSlug);\n const uniqueSlug = uniqueArticleSlug(baseSlug, usedSlugs);\n if (uniqueSlug !== baseSlug) {\n warn(`Markdown article slug collision for \"${article.title ?? run.title}\" resolved as \"${uniqueSlug}\".`);\n }\n usedSlugs.add(uniqueSlug);\n\n return {\n title: article.title?.trim() || stripTags(run.title),\n description: article.description?.trim() || null,\n slug: uniqueSlug,\n metadata: article,\n stepNames: [...article.steps],\n screenshotDirSlug,\n };\n });\n}\n\nexport const markdownReporter: ReportGenerator = {\n name: 'markdown',\n description: 'Generates one Markdown help article per captured story.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const config = normalizeConfig(context.config);\n const stories = groupByStory(context.runs);\n const generatedAt = new Date().toISOString();\n const writtenFiles = new Set<string>();\n const usedStorySlugs = new Set<string>();\n const screenshotCopies = new Map<string, string>();\n let articleCount = 0;\n\n for (const [storyTitle, runs] of stories) {\n const primaryRun = choosePrimaryRun(runs, config.preferredProject);\n if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {\n continue;\n }\n\n for (const article of resolveArticles(primaryRun)) {\n let storySlug = article.slug;\n if (usedStorySlugs.has(storySlug)) {\n let index = 2;\n while (usedStorySlugs.has(`${article.slug}-${index}`)) {\n index += 1;\n }\n storySlug = `${article.slug}-${index}`;\n warn(`Markdown article slug collision for \"${article.title || storyTitle}\" resolved as \"${storySlug}\".`);\n }\n\n usedStorySlugs.add(storySlug);\n const markdownFile = path.join(context.outputDir, config.storiesDir ?? '.', `${storySlug}.md`);\n const steps = await buildSteps({\n run: primaryRun,\n stepNames: article.stepNames,\n screenshotDirSlug: article.screenshotDirSlug,\n outputDir: context.outputDir,\n markdownFile,\n config,\n writtenFiles,\n screenshotCopies,\n });\n if (steps.length === 0) {\n continue;\n }\n\n await fs.mkdir(path.dirname(markdownFile), { recursive: true });\n await fs.writeFile(\n markdownFile,\n renderMarkdown({\n title: article.title,\n description: article.description,\n steps,\n run: primaryRun,\n article: article.metadata,\n config,\n generatedAt,\n }),\n 'utf8',\n );\n\n writtenFiles.add(markdownFile);\n articleCount += 1;\n }\n }\n\n return {\n files: [...writtenFiles],\n summary: `Generated ${articleCount} Markdown article${articleCount === 1 ? '' : 's'}.`,\n };\n },\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { groupByStory } from './story-utils';\n\ntype MdxReporterConfig = {\n storiesDir?: string;\n screenshotsDir?: string;\n includeTags?: string[];\n preferredProject?: string;\n imagePathPrefix?: string;\n copyScreenshots?: boolean;\n componentImportPath?: string;\n};\n\ntype MdxVariant = {\n project: string;\n projectLabel: string;\n imagePath: string | null;\n imageAlt: string;\n};\n\ntype MdxStep = {\n checkpoint: CheckpointRecord;\n order: number;\n title: string;\n description: string;\n focusNote: string | null;\n variants: MdxVariant[];\n};\n\nconst DEFAULT_PROJECT_ORDER = ['desktop-light', 'desktop-dark', 'mobile-light', 'mobile-dark'];\n\nfunction slugify(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction stripTags(value: string): string {\n const stripped = value.replace(/\\s+@[a-z0-9-]+/gi, ' ').replace(/\\s+/g, ' ').trim();\n return stripped || value.trim() || 'Untitled story';\n}\n\nfunction normalizeConfig(config: Record<string, unknown>): MdxReporterConfig {\n return {\n storiesDir: typeof config.storiesDir === 'string' ? config.storiesDir : '.',\n screenshotsDir: typeof config.screenshotsDir === 'string' ? config.screenshotsDir : 'screenshots',\n includeTags: Array.isArray(config.includeTags)\n ? config.includeTags.filter((value): value is string => typeof value === 'string')\n : undefined,\n preferredProject: typeof config.preferredProject === 'string' ? config.preferredProject : undefined,\n imagePathPrefix: typeof config.imagePathPrefix === 'string' ? config.imagePathPrefix : undefined,\n copyScreenshots: typeof config.copyScreenshots === 'boolean' ? config.copyScreenshots : true,\n componentImportPath:\n typeof config.componentImportPath === 'string' ? config.componentImportPath : 'playwright-checkpoint/components',\n };\n}\n\nfunction normalizeTags(tags: string[] | undefined): string[] {\n return (tags ?? []).map((tag) => tag.trim().toLowerCase()).filter(Boolean);\n}\n\nfunction frontmatterTags(tags: string[]): string[] {\n return normalizeTags(tags).map((tag) => tag.replace(/^@+/, '')).filter(Boolean);\n}\n\nfunction shouldIncludeRun(run: RunRecord, config: MdxReporterConfig): boolean {\n const includeTags = normalizeTags(config.includeTags);\n if (includeTags.length > 0) {\n const runTags = new Set(normalizeTags(run.tags));\n return includeTags.some((tag) => runTags.has(tag));\n }\n\n return run.checkpoints.some((checkpoint) => {\n const hasDescription = typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0;\n return hasDescription || typeof checkpoint.step === 'number';\n });\n}\n\nfunction choosePrimaryRun(runs: RunRecord[], preferredProject?: string): RunRecord | null {\n if (runs.length === 0) {\n return null;\n }\n\n return [...runs].sort((left, right) => {\n const leftPreferred = preferredProject && left.project === preferredProject ? 0 : 1;\n const rightPreferred = preferredProject && right.project === preferredProject ? 0 : 1;\n if (leftPreferred !== rightPreferred) {\n return leftPreferred - rightPreferred;\n }\n\n const rightTime = new Date(right.startedAt).getTime();\n const leftTime = new Date(left.startedAt).getTime();\n if (rightTime !== leftTime) {\n return rightTime - leftTime;\n }\n\n return left.project.localeCompare(right.project);\n })[0] ?? null;\n}\n\nfunction orderedCheckpoints(checkpoints: CheckpointRecord[]): CheckpointRecord[] {\n return [...checkpoints].sort((left, right) => {\n const leftOrder = typeof left.step === 'number' ? left.step : Number.MAX_SAFE_INTEGER;\n const rightOrder = typeof right.step === 'number' ? right.step : Number.MAX_SAFE_INTEGER;\n if (leftOrder !== rightOrder) {\n return leftOrder - rightOrder;\n }\n\n return checkpoints.indexOf(left) - checkpoints.indexOf(right);\n });\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction screenshotSourcePath(run: RunRecord, checkpoint: CheckpointRecord): string | null {\n const artifacts = checkpoint.collectors.screenshot?.artifacts ?? [];\n const artifact = artifacts.find((entry) => entry.name === 'screenshot') ?? artifacts[0];\n return artifact?.path ? resolveArtifactPath(run, artifact.path) : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction focusNote(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const selector = typeof data?.highlightSelector === 'string' ? data.highlightSelector.trim() : '';\n if (selector) {\n return `Focus: \\`${selector}\\``;\n }\n\n const bounds = data?.highlightBounds;\n if (\n bounds &&\n typeof bounds.x === 'number' &&\n typeof bounds.y === 'number' &&\n typeof bounds.width === 'number' &&\n typeof bounds.height === 'number'\n ) {\n return 'Focus: highlighted UI element.';\n }\n\n return null;\n}\n\nfunction urlLabel(url: string): string {\n try {\n const parsed = new URL(url);\n const value = `${parsed.pathname}${parsed.search}${parsed.hash}`;\n return value || '/';\n } catch {\n return url || '/';\n }\n}\n\nfunction autoDescription(checkpoint: CheckpointRecord): string {\n const pageTitle = checkpoint.title.trim();\n const location = urlLabel(checkpoint.url);\n\n if (pageTitle) {\n return `This step captures **${pageTitle}** at \\`${location}\\`.`;\n }\n\n return `This step captures **${checkpoint.name}** at \\`${location}\\`.`;\n}\n\nfunction markdownRelativePath(fromFile: string, toFile: string): string {\n const relativePath = path.relative(path.dirname(fromFile), toFile).split(path.sep).join('/');\n if (relativePath.startsWith('.')) {\n return relativePath;\n }\n\n return `./${relativePath}`;\n}\n\nfunction rewriteImagePath(mdxFile: string, imageFile: string, outputDir: string, prefix?: string): string {\n const relativePath = path.relative(outputDir, imageFile).split(path.sep).join('/');\n if (prefix) {\n return `${prefix.replace(/\\/+$/g, '')}/${relativePath.replace(/^\\/+/, '')}`;\n }\n\n return markdownRelativePath(mdxFile, imageFile);\n}\n\nfunction yamlScalar(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction serializeFrontmatter(fields: Record<string, unknown>): string {\n const lines = ['---'];\n\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n lines.push(`${key}:`);\n if (value.length === 0) {\n lines.push(' []');\n continue;\n }\n\n for (const item of value) {\n lines.push(` - ${yamlScalar(item)}`);\n }\n continue;\n }\n\n lines.push(`${key}: ${yamlScalar(value)}`);\n }\n\n lines.push('---', '');\n return lines.join('\\n');\n}\n\nfunction quoteJsx(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction projectWeight(projectName: string): number {\n const index = DEFAULT_PROJECT_ORDER.indexOf(projectName);\n return index === -1 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction formatProjectLabel(projectName: string): string {\n const [device, mode] = projectName.split('-');\n if (!device || !mode) {\n return projectName;\n }\n\n const deviceLabel = device === 'desktop' ? 'Desktop' : device === 'mobile' ? 'Mobile' : device;\n const modeLabel = mode === 'light' ? 'Light' : mode === 'dark' ? 'Dark' : mode;\n return `${deviceLabel} / ${modeLabel}`;\n}\n\nfunction sortRunsForVariants(runs: RunRecord[]): RunRecord[] {\n return [...runs].sort((left, right) => {\n const byWeight = projectWeight(left.project) - projectWeight(right.project);\n if (byWeight !== 0) {\n return byWeight;\n }\n\n return left.project.localeCompare(right.project);\n });\n}\n\nfunction findMatchingCheckpoint(run: RunRecord, baseCheckpoint: CheckpointRecord, fallbackIndex: number): CheckpointRecord | null {\n const checkpoints = orderedCheckpoints(run.checkpoints);\n\n if (typeof baseCheckpoint.step === 'number') {\n const byStep = checkpoints.find((entry) => entry.step === baseCheckpoint.step);\n if (byStep) {\n return byStep;\n }\n }\n\n const byName = checkpoints.find((entry) => entry.name === baseCheckpoint.name);\n if (byName) {\n return byName;\n }\n\n return checkpoints[fallbackIndex] ?? null;\n}\n\nasync function materializeScreenshot(args: {\n run: RunRecord;\n checkpoint: CheckpointRecord;\n storySlug: string;\n stepOrder: number;\n outputDir: string;\n mdxFile: string;\n config: MdxReporterConfig;\n writtenFiles: Set<string>;\n}): Promise<string | null> {\n const sourcePath = screenshotSourcePath(args.run, args.checkpoint);\n if (!sourcePath) {\n return null;\n }\n\n const extension = path.extname(sourcePath) || '.png';\n const targetPath = path.join(\n args.outputDir,\n args.config.screenshotsDir ?? 'screenshots',\n args.storySlug,\n `${String(args.stepOrder).padStart(2, '0')}-${slugify(args.run.project)}-${slugify(args.checkpoint.name)}${extension}`,\n );\n\n try {\n if (args.config.copyScreenshots !== false) {\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\n await fs.copyFile(sourcePath, targetPath);\n args.writtenFiles.add(targetPath);\n return rewriteImagePath(args.mdxFile, targetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n return rewriteImagePath(args.mdxFile, sourcePath, args.outputDir, args.config.imagePathPrefix);\n } catch {\n return null;\n }\n}\n\nasync function buildSteps(args: {\n runs: RunRecord[];\n primaryRun: RunRecord;\n storySlug: string;\n outputDir: string;\n mdxFile: string;\n config: MdxReporterConfig;\n writtenFiles: Set<string>;\n}): Promise<MdxStep[]> {\n const baseCheckpoints = orderedCheckpoints(args.primaryRun.checkpoints);\n const sortedRuns = sortRunsForVariants(args.runs);\n const steps: MdxStep[] = [];\n\n for (const [index, checkpoint] of baseCheckpoints.entries()) {\n const order = typeof checkpoint.step === 'number' ? checkpoint.step : index + 1;\n const variants: MdxVariant[] = [];\n const matchedCheckpoints: CheckpointRecord[] = [];\n\n for (const run of sortedRuns) {\n const variantCheckpoint = findMatchingCheckpoint(run, checkpoint, index);\n if (!variantCheckpoint) {\n continue;\n }\n\n matchedCheckpoints.push(variantCheckpoint);\n variants.push({\n project: run.project,\n projectLabel: formatProjectLabel(run.project),\n imagePath: await materializeScreenshot({\n run,\n checkpoint: variantCheckpoint,\n storySlug: args.storySlug,\n stepOrder: order,\n outputDir: args.outputDir,\n mdxFile: args.mdxFile,\n config: args.config,\n writtenFiles: args.writtenFiles,\n }),\n imageAlt: variantCheckpoint.title || `${checkpoint.name} (${formatProjectLabel(run.project)})`,\n });\n }\n\n const descriptionSource = matchedCheckpoints.find(\n (entry) => typeof entry.description === 'string' && entry.description.trim().length > 0,\n ) ?? checkpoint;\n const stepFocus = matchedCheckpoints.map((entry) => focusNote(entry)).find((value): value is string => Boolean(value)) ?? null;\n\n steps.push({\n checkpoint,\n order,\n title: checkpoint.name,\n description:\n typeof descriptionSource.description === 'string' && descriptionSource.description.trim().length > 0\n ? descriptionSource.description.trim()\n : autoDescription(descriptionSource),\n focusNote: stepFocus,\n variants,\n });\n }\n\n return steps;\n}\n\nfunction renderVariantTabs(variants: MdxVariant[]): string {\n if (variants.length === 0) {\n return '';\n }\n\n if (variants.length === 1) {\n const [variant] = variants;\n if (!variant?.imagePath) {\n return '';\n }\n\n return `<Screenshot src={${quoteJsx(variant.imagePath)}} alt={${quoteJsx(variant.imageAlt)}} />`;\n }\n\n const tabs = variants\n .map((variant) => {\n const lines = [` <DeviceTab label={${quoteJsx(variant.projectLabel)}}>`];\n if (variant.imagePath) {\n lines.push(` <Screenshot src={${quoteJsx(variant.imagePath)}} alt={${quoteJsx(variant.imageAlt)}} />`);\n } else {\n lines.push(` <p>No screenshot captured for ${variant.projectLabel}.</p>`);\n }\n lines.push(' </DeviceTab>');\n return lines.join('\\n');\n })\n .join('\\n');\n\n return `<DeviceTabs>\\n${tabs}\\n</DeviceTabs>`;\n}\n\nfunction renderStep(step: MdxStep): string {\n const lines = [` <Step number={${step.order}} title={${quoteJsx(step.title)}}>`];\n const variantBlock = renderVariantTabs(step.variants);\n if (variantBlock) {\n lines.push(` ${variantBlock.replace(/\\n/g, '\\n ')}`, '');\n }\n\n if (step.focusNote) {\n lines.push(` ${step.focusNote}`, '');\n }\n\n lines.push(` ${step.description}`, ' </Step>');\n return lines.join('\\n');\n}\n\nfunction renderMdx(args: {\n title: string;\n steps: MdxStep[];\n runs: RunRecord[];\n config: MdxReporterConfig;\n generatedAt: string;\n}): string {\n const importNames = new Set(['Screenshot', 'StepList', 'Step']);\n if (args.steps.some((step) => step.variants.length > 1)) {\n importNames.add('DeviceTabs');\n importNames.add('DeviceTab');\n }\n\n const frontmatter = serializeFrontmatter({\n title: args.title,\n tags: frontmatterTags([...new Set(args.runs.flatMap((run) => run.tags))]),\n generatedAt: args.generatedAt,\n projects: [...new Set(args.runs.map((run) => run.project))],\n });\n\n const stepBlocks = args.steps.map(renderStep).join('\\n\\n');\n\n return `${frontmatter}import { ${[...importNames].join(', ')} } from '${args.config.componentImportPath}';\\n\\n<StepList>\\n${stepBlocks}\\n</StepList>\\n`;\n}\n\nexport const mdxReporter: ReportGenerator = {\n name: 'mdx',\n description: 'Generates one MDX help article per captured story.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const config = normalizeConfig(context.config);\n const stories = groupByStory(context.runs);\n const generatedAt = new Date().toISOString();\n const writtenFiles = new Set<string>();\n let articleCount = 0;\n\n for (const [storyTitle, runs] of stories) {\n const primaryRun = choosePrimaryRun(runs, config.preferredProject);\n if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {\n continue;\n }\n\n const title = stripTags(storyTitle);\n const storySlug = slugify(title);\n const mdxFile = path.join(context.outputDir, config.storiesDir ?? '.', `${storySlug}.mdx`);\n const steps = await buildSteps({\n runs,\n primaryRun,\n storySlug,\n outputDir: context.outputDir,\n mdxFile,\n config,\n writtenFiles,\n });\n\n await fs.mkdir(path.dirname(mdxFile), { recursive: true });\n await fs.writeFile(\n mdxFile,\n renderMdx({\n title,\n steps,\n runs,\n config,\n generatedAt,\n }),\n 'utf8',\n );\n\n writtenFiles.add(mdxFile);\n articleCount += 1;\n }\n\n return {\n files: [...writtenFiles],\n summary: `Generated ${articleCount} MDX article${articleCount === 1 ? '' : 's'}.`,\n };\n },\n};\n","import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport type { BoundingBox } from '../types';\n\ntype SharpLike = {\n (input: string | Buffer): {\n metadata(): Promise<{ width?: number; height?: number }>;\n composite(items: Array<{ input: Buffer; top?: number; left?: number }>): {\n png(): {\n toFile(filePath: string): Promise<void>;\n };\n };\n };\n};\n\nconst require = (() => {\n try {\n return Function('return require')() as NodeRequire;\n } catch {\n return createRequire(path.join(process.cwd(), 'playwright-checkpoint-annotate-runtime.cjs'));\n }\n})();\n\nasync function loadSharp(): Promise<SharpLike | null> {\n try {\n const loaded = require('sharp') as SharpLike | { default?: SharpLike };\n return typeof loaded === 'function' ? loaded : loaded.default ?? null;\n } catch {\n return null;\n }\n}\n\nfunction buildOverlaySvg(width: number, height: number, bounds: BoundingBox): Buffer {\n const strokeWidth = Math.max(2, Math.round(Math.min(width, height) * 0.005));\n const radius = Math.max(6, Math.round(Math.min(bounds.width, bounds.height) * 0.08));\n\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n <rect\n x=\"${bounds.x}\"\n y=\"${bounds.y}\"\n width=\"${bounds.width}\"\n height=\"${bounds.height}\"\n rx=\"${radius}\"\n ry=\"${radius}\"\n fill=\"rgba(239, 68, 68, 0.10)\"\n stroke=\"rgba(239, 68, 68, 0.95)\"\n stroke-width=\"${strokeWidth}\"\n />\n </svg>\n `.trim();\n\n return Buffer.from(svg, 'utf8');\n}\n\nexport async function annotateScreenshot(\n imagePath: string,\n bounds: BoundingBox,\n outputPath: string,\n): Promise<string | null> {\n const sharp = await loadSharp();\n if (!sharp) {\n return null;\n }\n\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n if (!metadata.width || !metadata.height) {\n return null;\n }\n\n const overlay = buildOverlaySvg(metadata.width, metadata.height, bounds);\n await image.composite([{ input: overlay, top: 0, left: 0 }]).png().toFile(outputPath);\n return outputPath;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type {\n CheckpointConfig,\n CheckpointManifest,\n ReportGenerationResults,\n ReportGenerator,\n ReporterConfig,\n RunRecord,\n} from '../types';\nimport { htmlReporter } from './html-reporter';\nimport { markdownReporter } from './markdown-reporter';\nimport { mdxReporter } from './mdx-reporter';\nexport { groupByStory, orderedCheckpointNames } from './story-utils';\n\nconst builtinReporters = new Map<string, ReportGenerator>();\nconst builtinReporterDefaults: Partial<Record<string, ReporterConfig>> = {\n html: true,\n markdown: false,\n mdx: false,\n};\n\nasync function walkFiles(directory: string): Promise<string[]> {\n const dirents = await fs.readdir(directory, { withFileTypes: true });\n const files: string[] = [];\n\n for (const dirent of dirents) {\n const absolutePath = path.join(directory, dirent.name);\n if (dirent.isDirectory()) {\n files.push(...(await walkFiles(absolutePath)));\n continue;\n }\n if (dirent.isFile()) {\n files.push(absolutePath);\n }\n }\n\n return files;\n}\n\nfunction isCheckpointManifestFile(filePath: string): boolean {\n const fileName = path.basename(filePath);\n return fileName === 'checkpoint-manifest.json' || (fileName.startsWith('checkpoint-manifest-') && fileName.endsWith('.json'));\n}\n\nfunction isCheckpointManifest(value: unknown): value is CheckpointManifest {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const manifest = value as Partial<CheckpointManifest>;\n return (\n typeof manifest.project === 'string' &&\n typeof manifest.testId === 'string' &&\n typeof manifest.title === 'string' &&\n typeof manifest.startedAt === 'string' &&\n Array.isArray(manifest.tags) &&\n Array.isArray(manifest.checkpoints)\n );\n}\n\nfunction toRunRecord(manifest: CheckpointManifest, sourceManifestPath: string): RunRecord {\n return {\n key: `${manifest.testId}|${manifest.project}|${manifest.startedAt}`,\n sourceManifestPath,\n environment: manifest.environment || 'unknown',\n project: manifest.project,\n testId: manifest.testId,\n title: manifest.title,\n ...(manifest.article ? { article: manifest.article } : {}),\n ...(manifest.articles ? { articles: manifest.articles } : {}),\n tags: manifest.tags,\n startedAt: manifest.startedAt,\n checkpoints: manifest.checkpoints,\n };\n}\n\nfunction toManifest(run: RunRecord): CheckpointManifest {\n return {\n environment: run.environment,\n project: run.project,\n testId: run.testId,\n title: run.title,\n ...(run.article ? { article: run.article } : {}),\n ...(run.articles ? { articles: run.articles } : {}),\n tags: run.tags,\n startedAt: run.startedAt,\n checkpoints: run.checkpoints,\n };\n}\n\nfunction normalizeReporterConfig(config: ReporterConfig | undefined): Record<string, unknown> | null {\n if (config == null || config === false) {\n return null;\n }\n\n if (config === true) {\n return {};\n }\n\n return { ...config };\n}\n\nexport function registerBuiltinReporter(reporter: ReportGenerator): void {\n builtinReporters.set(reporter.name, reporter);\n}\n\nexport function dedupeRuns(runs: RunRecord[]): RunRecord[] {\n const map = new Map<string, RunRecord>();\n\n for (const run of runs) {\n const existing = map.get(run.key);\n if (!existing) {\n map.set(run.key, run);\n continue;\n }\n\n const existingTime = new Date(existing.startedAt).getTime();\n const currentTime = new Date(run.startedAt).getTime();\n if (currentTime >= existingTime) {\n map.set(run.key, run);\n }\n }\n\n return [...map.values()];\n}\n\nexport async function loadRuns(testResultsDir: string): Promise<RunRecord[]> {\n let manifestFiles: string[];\n try {\n manifestFiles = (await walkFiles(testResultsDir)).filter(isCheckpointManifestFile);\n } catch {\n return [];\n }\n\n const runs: RunRecord[] = [];\n for (const manifestPath of manifestFiles) {\n let rawManifest: unknown;\n try {\n rawManifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));\n } catch {\n continue;\n }\n\n if (!isCheckpointManifest(rawManifest)) {\n continue;\n }\n\n runs.push(toRunRecord(rawManifest, manifestPath));\n }\n\n return dedupeRuns(runs);\n}\n\nexport async function runReporters(\n config: CheckpointConfig,\n testResultsDir: string,\n outputDir: string,\n): Promise<ReportGenerationResults> {\n const runs = await loadRuns(testResultsDir);\n const manifests = runs.map(toManifest);\n const results: ReportGenerationResults = {};\n const reporterConfigMap: Partial<Record<string, ReporterConfig>> = {\n ...builtinReporterDefaults,\n ...(config.reporters ?? {}),\n };\n\n for (const [name, value] of Object.entries(reporterConfigMap)) {\n const reporterConfig = normalizeReporterConfig(value);\n if (!reporterConfig) {\n continue;\n }\n\n const reporter = builtinReporters.get(name);\n if (!reporter) {\n throw new Error(`Reporter \"${name}\" is enabled but no implementation is registered.`);\n }\n\n if (reporter.validateConfig && !reporter.validateConfig(reporterConfig)) {\n throw new Error(`Reporter \"${name}\" received invalid configuration.`);\n }\n\n results[name] = await reporter.generate({\n runs,\n outputDir,\n config: reporterConfig,\n manifests,\n });\n }\n\n return results;\n}\n\nregisterBuiltinReporter(htmlReporter);\nregisterBuiltinReporter(markdownReporter);\nregisterBuiltinReporter(mdxReporter);\n\nexport { annotateScreenshot } from './annotate';\nexport { htmlReporter, markdownReporter, mdxReporter };\nexport type { ReportGenerator } from '../types';\n"],"mappings":"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","names":["fs","path","slugify","resolveArtifactPath","path","screenshotData","fs","fs","path","DEFAULT_PROJECT_ORDER","slugify","stripTags","normalizeConfig","normalizeTags","shouldIncludeRun","choosePrimaryRun","orderedCheckpoints","resolveArtifactPath","path","screenshotSourcePath","screenshotData","focusNote","urlLabel","autoDescription","markdownRelativePath","rewriteImagePath","yamlScalar","serializeFrontmatter","projectWeight","formatProjectLabel","materializeScreenshot","fs","buildSteps","path","require","fs","path","fs","path"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runReporters
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WXZOP7XI.js";
|
|
4
4
|
|
|
5
5
|
// src/cli/index.ts
|
|
6
6
|
import path from "path";
|
|
@@ -211,4 +211,4 @@ export {
|
|
|
211
211
|
parseCliArgs,
|
|
212
212
|
runCli
|
|
213
213
|
};
|
|
214
|
-
//# sourceMappingURL=chunk-
|
|
214
|
+
//# sourceMappingURL=chunk-YUFXGGZM.js.map
|