executable-stories-formatters 0.13.0 → 0.14.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/index.cjs CHANGED
@@ -15269,7 +15269,9 @@ var MarkdownFormatter = class {
15269
15269
  ticketUrlTemplate: options.ticketUrlTemplate,
15270
15270
  traceUrlTemplate: options.traceUrlTemplate,
15271
15271
  includeSourceLinks: options.includeSourceLinks ?? true,
15272
- customRenderers: options.customRenderers
15272
+ customRenderers: options.customRenderers,
15273
+ scenarioAnchor: options.scenarioAnchor,
15274
+ scenarioBadge: options.scenarioBadge
15273
15275
  };
15274
15276
  }
15275
15277
  /**
@@ -15456,6 +15458,11 @@ var MarkdownFormatter = class {
15456
15458
  * Render a single scenario.
15457
15459
  */
15458
15460
  renderScenario(lines, tc) {
15461
+ const anchorId = this.options.scenarioAnchor?.(tc);
15462
+ if (anchorId) {
15463
+ lines.push(`<a id="${anchorId}"></a>`);
15464
+ lines.push("");
15465
+ }
15459
15466
  if (this.options.customRenderers?.renderScenarioHeader) {
15460
15467
  const custom = this.options.customRenderers.renderScenarioHeader(tc);
15461
15468
  if (custom !== null) {
@@ -15471,6 +15478,10 @@ var MarkdownFormatter = class {
15471
15478
  icon = this.getStatusIcon(tc.status) + " ";
15472
15479
  }
15473
15480
  lines.push(`${headingPrefix} ${icon}${tc.story.scenario}`);
15481
+ const badge = this.options.scenarioBadge?.(tc);
15482
+ if (badge) {
15483
+ lines.push(badge);
15484
+ }
15474
15485
  if (this.options.includeSourceLinks && this.options.permalinkBaseUrl && tc.sourceFile !== "unknown") {
15475
15486
  const permalink = this.buildPermalink(tc);
15476
15487
  lines.push(`Source: [${tc.sourceFile}](${permalink})`);
@@ -18287,12 +18298,16 @@ function groupBy7(items, keyFn) {
18287
18298
  var fs5 = __toESM(require("fs"), 1);
18288
18299
  var path6 = __toESM(require("path"), 1);
18289
18300
  var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
18290
- function isLocalPath(src) {
18301
+ function isRemoteRef(src) {
18291
18302
  const trimmed = src.trim();
18292
- if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
18293
- return false;
18294
- }
18295
- return !path6.posix.isAbsolute(trimmed) && !path6.win32.isAbsolute(trimmed);
18303
+ return SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix));
18304
+ }
18305
+ function isAbsoluteRef(src) {
18306
+ const trimmed = src.trim();
18307
+ return path6.posix.isAbsolute(trimmed) || path6.win32.isAbsolute(trimmed);
18308
+ }
18309
+ function isRelativeLocalPath(src) {
18310
+ return !isRemoteRef(src) && !isAbsoluteRef(src);
18296
18311
  }
18297
18312
  function stripCodeContent(markdown) {
18298
18313
  let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
@@ -18308,21 +18323,21 @@ function scanMarkdownAssets(markdown) {
18308
18323
  let match;
18309
18324
  while ((match = mdImageRe.exec(stripped)) !== null) {
18310
18325
  const src = match[1].trim();
18311
- if (isLocalPath(src)) {
18326
+ if (!isRemoteRef(src)) {
18312
18327
  found.add(src);
18313
18328
  }
18314
18329
  }
18315
18330
  const htmlSrcRe = /<(?:img|source|video)[^>]+\bsrc=["']([^"']+)["'][^>]*>/gi;
18316
18331
  while ((match = htmlSrcRe.exec(stripped)) !== null) {
18317
18332
  const src = match[1].trim();
18318
- if (isLocalPath(src)) {
18333
+ if (!isRemoteRef(src)) {
18319
18334
  found.add(src);
18320
18335
  }
18321
18336
  }
18322
18337
  const posterRe = /<video[^>]+\bposter=["']([^"']+)["'][^>]*>/gi;
18323
18338
  while ((match = posterRe.exec(stripped)) !== null) {
18324
18339
  const src = match[1].trim();
18325
- if (isLocalPath(src)) {
18340
+ if (!isRemoteRef(src)) {
18326
18341
  found.add(src);
18327
18342
  }
18328
18343
  }
@@ -18348,48 +18363,21 @@ function isCode(segment) {
18348
18363
  const trimmed = segment.trimStart();
18349
18364
  return trimmed.startsWith("`") || trimmed.startsWith("~") || trimmed.startsWith("<pre") || trimmed.startsWith("<code");
18350
18365
  }
18366
+ function resolveRewrite(trimmed, assetsBaseUrl, pathMap) {
18367
+ if (isRemoteRef(trimmed)) return null;
18368
+ if (pathMap) {
18369
+ const mapped = pathMap.get(trimmed);
18370
+ return mapped === void 0 ? null : `${assetsBaseUrl}/${mapped}`;
18371
+ }
18372
+ if (!isRelativeLocalPath(trimmed)) return null;
18373
+ return `${assetsBaseUrl}/${trimmed}`;
18374
+ }
18351
18375
  function rewriteProseSegment(prose, assetsBaseUrl, pathMap) {
18352
- let result = prose;
18353
- result = result.replace(
18354
- /(!\[[^\]]*\]\()([^)"'\s]+)((?:\s+["'][^"']*["'])?\s*\))/g,
18355
- (full, pre, src, post) => {
18356
- const trimmed = src.trim();
18357
- if (!isLocalPath(trimmed)) return full;
18358
- if (pathMap) {
18359
- const mapped = pathMap.get(trimmed);
18360
- if (mapped === void 0) return full;
18361
- return `${pre}${assetsBaseUrl}/${mapped}${post}`;
18362
- }
18363
- return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
18364
- }
18365
- );
18366
- result = result.replace(
18367
- /(<(?:img|source|video)[^>]+\bsrc=["'])([^"']+)(["'][^>]*>)/gi,
18368
- (full, pre, src, post) => {
18369
- const trimmed = src.trim();
18370
- if (!isLocalPath(trimmed)) return full;
18371
- if (pathMap) {
18372
- const mapped = pathMap.get(trimmed);
18373
- if (mapped === void 0) return full;
18374
- return `${pre}${assetsBaseUrl}/${mapped}${post}`;
18375
- }
18376
- return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
18377
- }
18378
- );
18379
- result = result.replace(
18380
- /(<video[^>]+\bposter=["'])([^"']+)(["'][^>]*>)/gi,
18381
- (full, pre, src, post) => {
18382
- const trimmed = src.trim();
18383
- if (!isLocalPath(trimmed)) return full;
18384
- if (pathMap) {
18385
- const mapped = pathMap.get(trimmed);
18386
- if (mapped === void 0) return full;
18387
- return `${pre}${assetsBaseUrl}/${mapped}${post}`;
18388
- }
18389
- return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
18390
- }
18391
- );
18392
- return result;
18376
+ const rewrite = (full, pre, src, post) => {
18377
+ const target = resolveRewrite(src.trim(), assetsBaseUrl, pathMap);
18378
+ return target === null ? full : `${pre}${target}${post}`;
18379
+ };
18380
+ return prose.replace(/(!\[[^\]]*\]\()([^)"'\s]+)((?:\s+["'][^"']*["'])?\s*\))/g, rewrite).replace(/(<(?:img|source|video)[^>]+\bsrc=["'])([^"']+)(["'][^>]*>)/gi, rewrite).replace(/(<video[^>]+\bposter=["'])([^"']+)(["'][^>]*>)/gi, rewrite);
18393
18381
  }
18394
18382
  function rewriteAssetPaths(markdown, assetsBaseUrl, pathMap) {
18395
18383
  return splitByCode(markdown).map((seg) => isCode(seg) ? seg : rewriteProseSegment(seg, assetsBaseUrl, pathMap)).join("");
@@ -18406,8 +18394,9 @@ function copyMarkdownAssets(options) {
18406
18394
  const pathMap = /* @__PURE__ */ new Map();
18407
18395
  const missing = [];
18408
18396
  for (const ref of refs) {
18409
- const absPath = path6.resolve(markdownDir, ref);
18397
+ const absPath = isAbsoluteRef(ref) ? ref : path6.resolve(markdownDir, ref);
18410
18398
  if (!fs5.existsSync(absPath)) {
18399
+ if (isAbsoluteRef(ref)) continue;
18411
18400
  if (!allowMissing) {
18412
18401
  throw new Error(`Asset not found: ${absPath}`);
18413
18402
  }
@@ -22034,7 +22023,9 @@ var ReportGenerator = class {
22034
22023
  permalinkBaseUrl: options.astro?.markdown?.permalinkBaseUrl,
22035
22024
  ticketUrlTemplate: options.astro?.markdown?.ticketUrlTemplate,
22036
22025
  traceUrlTemplate: options.astro?.markdown?.traceUrlTemplate,
22037
- customRenderers: options.astro?.markdown?.customRenderers
22026
+ customRenderers: options.astro?.markdown?.customRenderers,
22027
+ scenarioAnchor: options.astro?.markdown?.scenarioAnchor,
22028
+ scenarioBadge: options.astro?.markdown?.scenarioBadge
22038
22029
  }
22039
22030
  },
22040
22031
  assetMode: options.assetMode ?? "none",