@bridge-ds/visual 0.3.5 → 0.3.6

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.
Files changed (2) hide show
  1. package/dist/index.js +80 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -21,8 +21,8 @@ function parseOwnerEntry(raw) {
21
21
  }
22
22
  function ownerEntryProblems(raw) {
23
23
  if (raw === void 0) return [];
24
- const isObj2 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
25
- if (!isObj2(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
24
+ const isObj3 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
25
+ if (!isObj3(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
26
26
  const out = [];
27
27
  for (const [k, v] of Object.entries(raw)) {
28
28
  if (k !== "design" && k !== "tech") {
@@ -989,6 +989,81 @@ function planReferences(file) {
989
989
  );
990
990
  }
991
991
 
992
+ // ../core/src/storybook.ts
993
+ var StorybookIndexError = class extends Error {
994
+ constructor(code, message, status) {
995
+ super(message);
996
+ this.code = code;
997
+ this.status = status;
998
+ this.name = "StorybookIndexError";
999
+ }
1000
+ code;
1001
+ status;
1002
+ };
1003
+ var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1004
+ var str = (v) => typeof v === "string" && v.trim() ? v : void 0;
1005
+ function entryFrom(key, raw, v3) {
1006
+ if (!isObj2(raw)) return null;
1007
+ const id = str(raw.id) ?? key;
1008
+ const title = str(raw.title) ?? str(raw.kind);
1009
+ const name = str(raw.name) ?? str(raw.story);
1010
+ if (!id || !title || !name) return null;
1011
+ const params = isObj2(raw.parameters) ? raw.parameters : {};
1012
+ const type = raw.type === "docs" || raw.type === "story" ? raw.type : v3 && params.docsOnly === true ? "docs" : "story";
1013
+ const e = {
1014
+ id,
1015
+ title,
1016
+ name,
1017
+ type,
1018
+ importPath: str(raw.importPath) ?? str(params.fileName) ?? "",
1019
+ tags: Array.isArray(raw.tags) ? raw.tags.filter((t) => typeof t === "string") : []
1020
+ };
1021
+ const subtype = str(raw.subtype);
1022
+ if (subtype) e.subtype = subtype;
1023
+ const exportName2 = str(raw.exportName);
1024
+ if (exportName2) e.exportName = exportName2;
1025
+ const componentPath = str(raw.componentPath);
1026
+ if (componentPath) e.componentPath = componentPath;
1027
+ return e;
1028
+ }
1029
+ function groupStorybookComponents(entries) {
1030
+ const byTitle = /* @__PURE__ */ new Map();
1031
+ for (const e of entries) {
1032
+ let c = byTitle.get(e.title);
1033
+ if (!c) {
1034
+ const parts = e.title.split("/").map((p) => p.trim()).filter(Boolean);
1035
+ c = { title: e.title, name: parts[parts.length - 1] ?? e.title, group: parts.slice(0, -1), stories: [], docs: [], tags: [] };
1036
+ byTitle.set(e.title, c);
1037
+ }
1038
+ if (e.type === "docs") c.docs.push(e);
1039
+ else if (e.subtype !== "test") c.stories.push(e);
1040
+ for (const t of e.tags) if (!c.tags.includes(t)) c.tags.push(t);
1041
+ }
1042
+ const out = [...byTitle.values()].filter((c) => c.stories.length || c.docs.length);
1043
+ for (const c of out) c.tags.sort();
1044
+ return out.sort((a, b) => a.title.localeCompare(b.title));
1045
+ }
1046
+ function parseStorybookIndex(json, source) {
1047
+ if (!isObj2(json)) throw new StorybookIndexError("not-json", "The Storybook index is not a JSON object.");
1048
+ if (typeof json.loginUrl === "string" && !("entries" in json) && !("stories" in json)) {
1049
+ throw new StorybookIndexError("private", "This Storybook is private: it asks for a sign-in before showing its index.");
1050
+ }
1051
+ const map = isObj2(json.entries) ? json.entries : isObj2(json.stories) ? json.stories : null;
1052
+ if (!map) throw new StorybookIndexError("empty", "This file has no Storybook entries.");
1053
+ const v3 = !isObj2(json.entries);
1054
+ const entries = Object.entries(map).map(([k, raw]) => entryFrom(k, raw, v3)).filter((e) => e !== null);
1055
+ if (!entries.length) throw new StorybookIndexError("empty", "This Storybook index lists no stories or docs.");
1056
+ const version = typeof json.v === "number" ? json.v : v3 ? 3 : 4;
1057
+ return { version, source: source ?? (v3 ? "stories.json" : "index.json"), entries, components: groupStorybookComponents(entries) };
1058
+ }
1059
+ var pairs = (o) => Object.entries(o).map(([k, v]) => `${encodeURIComponent(k)}:${encodeURIComponent(v)}`).join(";");
1060
+ function storybookIframeUrl(base, id, o = {}) {
1061
+ let url = `${base.replace(/\/+$/, "")}/iframe.html?id=${encodeURIComponent(id)}&viewMode=${o.viewMode ?? "story"}`;
1062
+ if (o.globals && Object.keys(o.globals).length) url += `&globals=${pairs(o.globals)}`;
1063
+ if (o.args && Object.keys(o.args).length) url += `&args=${pairs(o.args)}`;
1064
+ return url;
1065
+ }
1066
+
992
1067
  // ../core/src/node/git-local.ts
993
1068
  import { execFile } from "node:child_process";
994
1069
  function git(root, args, opts = {}) {
@@ -1405,7 +1480,7 @@ function routeUrl(base, route, tenant, theme) {
1405
1480
  return `${base.replace(/\/$/, "")}/?tenant=${encodeURIComponent(tenant)}&theme=${encodeURIComponent(theme)}&density=comfortable&lang=en#${route.pathByTenant?.[tenant] ?? route.path}`;
1406
1481
  }
1407
1482
  function storyUrl(base, id, brand, theme) {
1408
- return `${base.replace(/\/$/, "")}/iframe.html?id=${encodeURIComponent(id)}&viewMode=story&globals=brand:${brand};theme:${theme};density:comfortable`;
1483
+ return storybookIframeUrl(base, id, { globals: { brand, theme, density: "comfortable" } });
1409
1484
  }
1410
1485
  var shotKey = (...parts) => parts.map((p) => p.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "")).join("__");
1411
1486
 
@@ -1696,8 +1771,8 @@ function changedTokens(before, after) {
1696
1771
  return [...modes.entries()].map(([path, m]) => ({ path, kind: direct.has(path) ? "direct" : "alias", modes: m })).sort((a, b) => Number(a.kind === "alias") - Number(b.kind === "alias") || a.path.localeCompare(b.path));
1697
1772
  }
1698
1773
  async function readIndexJson(dir) {
1699
- const raw = JSON.parse(await readFile3(join3(dir, "index.json"), "utf8"));
1700
- return Object.values(raw.entries).filter((e) => e.type === "story").map(({ id, title, name, importPath }) => ({ id, title, name, importPath }));
1774
+ const index = parseStorybookIndex(JSON.parse(await readFile3(join3(dir, "index.json"), "utf8")), "index.json");
1775
+ return index.entries.filter((e) => e.type === "story").map(({ id, title, name, importPath }) => ({ id, title, name, importPath }));
1701
1776
  }
1702
1777
  async function planImpact(o) {
1703
1778
  const root = resolve(o.root);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge-ds/visual",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "type": "module",
5
5
  "description": "Bridge visual impact analysis: screenshots the stories and app screens a design system change touches, at the base and head refs, and pixel-diffs them into before/after/diff images and a JSON manifest.",
6
6
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  "build:package": "node ../../scripts/build-packages.mjs visual"
48
48
  },
49
49
  "dependencies": {
50
- "@bridge-ds/core": "^0.11.0",
50
+ "@bridge-ds/core": "^0.12.0",
51
51
  "pixelmatch": "7.1.0",
52
52
  "playwright-core": "1.63.0",
53
53
  "pngjs": "7.0.0"