@bridge-ds/cli 0.8.0 → 0.9.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.js +95 -15
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1813,10 +1813,10 @@ ${" ".repeat(column - 1)}${expr}${suffix}` : `${prefix}${expr}${suffix}`;
|
|
|
1813
1813
|
out.themeRefs += r.themeRefs;
|
|
1814
1814
|
return;
|
|
1815
1815
|
}
|
|
1816
|
-
const
|
|
1817
|
-
if (
|
|
1818
|
-
const body =
|
|
1819
|
-
const r = auditCssText(body, "css", ds, { pos, offset: a.valueStart +
|
|
1816
|
+
const str2 = /^(\s*)([`'"])([\s\S]*)\2\s*$/.exec(a.value);
|
|
1817
|
+
if (str2) {
|
|
1818
|
+
const body = str2[3].replace(/\$\{[^}]*\}/g, (m) => " ".repeat(m.length));
|
|
1819
|
+
const r = auditCssText(body, "css", ds, { pos, offset: a.valueStart + str2[1].length + 1, source: "style" });
|
|
1820
1820
|
out.findings.push(...r.findings);
|
|
1821
1821
|
out.tokenUsages += r.tokenUsages;
|
|
1822
1822
|
out.themeRefs += r.themeRefs;
|
|
@@ -2176,8 +2176,8 @@ function parseOwnerEntry(raw) {
|
|
|
2176
2176
|
var teamKey = (org, slug) => `@${org.toLowerCase()}/${slug.toLowerCase()}`;
|
|
2177
2177
|
function ownerEntryProblems(raw) {
|
|
2178
2178
|
if (raw === void 0) return [];
|
|
2179
|
-
const
|
|
2180
|
-
if (!
|
|
2179
|
+
const isObj3 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
|
|
2180
|
+
if (!isObj3(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
|
|
2181
2181
|
const out = [];
|
|
2182
2182
|
for (const [k, v] of Object.entries(raw)) {
|
|
2183
2183
|
if (k !== "design" && k !== "tech") {
|
|
@@ -2562,7 +2562,8 @@ function defineConfig(input) {
|
|
|
2562
2562
|
minReasonLength: g.override.minReasonLength ?? DEFAULT_OVERRIDE.minReasonLength
|
|
2563
2563
|
}
|
|
2564
2564
|
} : {},
|
|
2565
|
-
...g.rights ? { rights: normalizeRights(g.rights) } : {}
|
|
2565
|
+
...g.rights ? { rights: normalizeRights(g.rights) } : {},
|
|
2566
|
+
...g.soloOwnerCanApproveBoth === true ? { soloOwnerCanApproveBoth: true } : {}
|
|
2566
2567
|
},
|
|
2567
2568
|
products: input.products?.length ? input.products.map((p) => ({ ...p })) : DEFAULT_PRODUCTS.map((p) => ({ ...p })),
|
|
2568
2569
|
changes: input.changes === "direct" ? "direct" : DEFAULT_CHANGES_MODE,
|
|
@@ -2686,6 +2687,7 @@ function loadConfig(source) {
|
|
|
2686
2687
|
}
|
|
2687
2688
|
}
|
|
2688
2689
|
problems.push(...rightsProblems(gov.rights));
|
|
2690
|
+
if (gov.soloOwnerCanApproveBoth !== void 0 && typeof gov.soloOwnerCanApproveBoth !== "boolean") problems.push('"governance.soloOwnerCanApproveBoth" must be true or false');
|
|
2689
2691
|
}
|
|
2690
2692
|
}
|
|
2691
2693
|
problems.push(...visualImpactProblems(d.visualImpact));
|
|
@@ -4251,6 +4253,81 @@ function planReferences(file) {
|
|
|
4251
4253
|
);
|
|
4252
4254
|
}
|
|
4253
4255
|
|
|
4256
|
+
// ../core/src/storybook.ts
|
|
4257
|
+
var StorybookIndexError = class extends Error {
|
|
4258
|
+
constructor(code, message, status) {
|
|
4259
|
+
super(message);
|
|
4260
|
+
this.code = code;
|
|
4261
|
+
this.status = status;
|
|
4262
|
+
this.name = "StorybookIndexError";
|
|
4263
|
+
}
|
|
4264
|
+
code;
|
|
4265
|
+
status;
|
|
4266
|
+
};
|
|
4267
|
+
var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4268
|
+
var str = (v) => typeof v === "string" && v.trim() ? v : void 0;
|
|
4269
|
+
function entryFrom(key, raw, v3) {
|
|
4270
|
+
if (!isObj2(raw)) return null;
|
|
4271
|
+
const id = str(raw.id) ?? key;
|
|
4272
|
+
const title = str(raw.title) ?? str(raw.kind);
|
|
4273
|
+
const name = str(raw.name) ?? str(raw.story);
|
|
4274
|
+
if (!id || !title || !name) return null;
|
|
4275
|
+
const params = isObj2(raw.parameters) ? raw.parameters : {};
|
|
4276
|
+
const type = raw.type === "docs" || raw.type === "story" ? raw.type : v3 && params.docsOnly === true ? "docs" : "story";
|
|
4277
|
+
const e = {
|
|
4278
|
+
id,
|
|
4279
|
+
title,
|
|
4280
|
+
name,
|
|
4281
|
+
type,
|
|
4282
|
+
importPath: str(raw.importPath) ?? str(params.fileName) ?? "",
|
|
4283
|
+
tags: Array.isArray(raw.tags) ? raw.tags.filter((t) => typeof t === "string") : []
|
|
4284
|
+
};
|
|
4285
|
+
const subtype = str(raw.subtype);
|
|
4286
|
+
if (subtype) e.subtype = subtype;
|
|
4287
|
+
const exportName2 = str(raw.exportName);
|
|
4288
|
+
if (exportName2) e.exportName = exportName2;
|
|
4289
|
+
const componentPath = str(raw.componentPath);
|
|
4290
|
+
if (componentPath) e.componentPath = componentPath;
|
|
4291
|
+
return e;
|
|
4292
|
+
}
|
|
4293
|
+
function groupStorybookComponents(entries) {
|
|
4294
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
4295
|
+
for (const e of entries) {
|
|
4296
|
+
let c = byTitle.get(e.title);
|
|
4297
|
+
if (!c) {
|
|
4298
|
+
const parts = e.title.split("/").map((p) => p.trim()).filter(Boolean);
|
|
4299
|
+
c = { title: e.title, name: parts[parts.length - 1] ?? e.title, group: parts.slice(0, -1), stories: [], docs: [], tags: [] };
|
|
4300
|
+
byTitle.set(e.title, c);
|
|
4301
|
+
}
|
|
4302
|
+
if (e.type === "docs") c.docs.push(e);
|
|
4303
|
+
else if (e.subtype !== "test") c.stories.push(e);
|
|
4304
|
+
for (const t of e.tags) if (!c.tags.includes(t)) c.tags.push(t);
|
|
4305
|
+
}
|
|
4306
|
+
const out = [...byTitle.values()].filter((c) => c.stories.length || c.docs.length);
|
|
4307
|
+
for (const c of out) c.tags.sort();
|
|
4308
|
+
return out.sort((a, b) => a.title.localeCompare(b.title));
|
|
4309
|
+
}
|
|
4310
|
+
function parseStorybookIndex(json, source) {
|
|
4311
|
+
if (!isObj2(json)) throw new StorybookIndexError("not-json", "The Storybook index is not a JSON object.");
|
|
4312
|
+
if (typeof json.loginUrl === "string" && !("entries" in json) && !("stories" in json)) {
|
|
4313
|
+
throw new StorybookIndexError("private", "This Storybook is private: it asks for a sign-in before showing its index.");
|
|
4314
|
+
}
|
|
4315
|
+
const map = isObj2(json.entries) ? json.entries : isObj2(json.stories) ? json.stories : null;
|
|
4316
|
+
if (!map) throw new StorybookIndexError("empty", "This file has no Storybook entries.");
|
|
4317
|
+
const v3 = !isObj2(json.entries);
|
|
4318
|
+
const entries = Object.entries(map).map(([k, raw]) => entryFrom(k, raw, v3)).filter((e) => e !== null);
|
|
4319
|
+
if (!entries.length) throw new StorybookIndexError("empty", "This Storybook index lists no stories or docs.");
|
|
4320
|
+
const version = typeof json.v === "number" ? json.v : v3 ? 3 : 4;
|
|
4321
|
+
return { version, source: source ?? (v3 ? "stories.json" : "index.json"), entries, components: groupStorybookComponents(entries) };
|
|
4322
|
+
}
|
|
4323
|
+
var pairs = (o) => Object.entries(o).map(([k, v]) => `${encodeURIComponent(k)}:${encodeURIComponent(v)}`).join(";");
|
|
4324
|
+
function storybookIframeUrl(base, id, o = {}) {
|
|
4325
|
+
let url = `${base.replace(/\/+$/, "")}/iframe.html?id=${encodeURIComponent(id)}&viewMode=${o.viewMode ?? "story"}`;
|
|
4326
|
+
if (o.globals && Object.keys(o.globals).length) url += `&globals=${pairs(o.globals)}`;
|
|
4327
|
+
if (o.args && Object.keys(o.args).length) url += `&args=${pairs(o.args)}`;
|
|
4328
|
+
return url;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4254
4331
|
// ../visual/src/capture.ts
|
|
4255
4332
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
4256
4333
|
import { dirname as dirname2 } from "node:path";
|
|
@@ -4645,7 +4722,7 @@ function routeUrl(base, route, tenant, theme) {
|
|
|
4645
4722
|
return `${base.replace(/\/$/, "")}/?tenant=${encodeURIComponent(tenant)}&theme=${encodeURIComponent(theme)}&density=comfortable&lang=en#${route.pathByTenant?.[tenant] ?? route.path}`;
|
|
4646
4723
|
}
|
|
4647
4724
|
function storyUrl(base, id, brand2, theme) {
|
|
4648
|
-
return
|
|
4725
|
+
return storybookIframeUrl(base, id, { globals: { brand: brand2, theme, density: "comfortable" } });
|
|
4649
4726
|
}
|
|
4650
4727
|
var shotKey = (...parts) => parts.map((p) => p.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "")).join("__");
|
|
4651
4728
|
|
|
@@ -4936,8 +5013,8 @@ function changedTokens(before, after) {
|
|
|
4936
5013
|
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));
|
|
4937
5014
|
}
|
|
4938
5015
|
async function readIndexJson(dir) {
|
|
4939
|
-
const
|
|
4940
|
-
return
|
|
5016
|
+
const index = parseStorybookIndex(JSON.parse(await readFile3(join4(dir, "index.json"), "utf8")), "index.json");
|
|
5017
|
+
return index.entries.filter((e) => e.type === "story").map(({ id, title, name, importPath }) => ({ id, title, name, importPath }));
|
|
4941
5018
|
}
|
|
4942
5019
|
async function planImpact(o) {
|
|
4943
5020
|
const root = resolve2(o.root);
|
|
@@ -5824,6 +5901,7 @@ ${r.summary}`);
|
|
|
5824
5901
|
Version ${r.version}${r.edited ? ` (${r.edited})` : ""}`);
|
|
5825
5902
|
io.print(`Approved by: ${r.approvals.approvedBy.map((l) => `@${l}`).join(", ") || "nobody yet"}`);
|
|
5826
5903
|
if (r.approvals.missing.length) io.print(`Missing: ${r.approvals.missing.join(" and ")}`);
|
|
5904
|
+
if (r.approvals.soloOwner) io.print(r.approvals.soloOwner);
|
|
5827
5905
|
if (r.approvals.changesRequestedBy.length) io.print(`Changes requested by: ${r.approvals.changesRequestedBy.map((l) => `@${l}`).join(", ")}`);
|
|
5828
5906
|
for (const p of r.linkedPrs ?? []) io.print(`Pull request: #${p.number} ${p.merged ? "merged" : p.state} ${p.url}`);
|
|
5829
5907
|
io.print(`
|
|
@@ -5960,7 +6038,7 @@ function number(a, at, usage) {
|
|
|
5960
6038
|
if (!raw) throw new RequestUsageError(`Usage: ${usage}`);
|
|
5961
6039
|
return branches.branchNumber(raw);
|
|
5962
6040
|
}
|
|
5963
|
-
function
|
|
6041
|
+
function pairs2(list2, what) {
|
|
5964
6042
|
const out = {};
|
|
5965
6043
|
for (const s of list2 ?? []) {
|
|
5966
6044
|
const i = s.lastIndexOf("=");
|
|
@@ -5993,6 +6071,7 @@ async function runBranch(a, root, io = defaultIO()) {
|
|
|
5993
6071
|
return out(r, () => {
|
|
5994
6072
|
io.print(`#${r.pr} ${r.title} (${r.product.name})`);
|
|
5995
6073
|
io.print(`${r.status.label}. ${r.approvalNeeded} needed; approved by ${r.approvals.approvedBy.map((x) => `@${x}`).join(", ") || "nobody yet"}.`);
|
|
6074
|
+
if (r.approvals.soloOwner) io.print(r.approvals.soloOwner);
|
|
5996
6075
|
if (r.merge.blockedBy?.length) for (const x of r.merge.blockedBy) io.print(` ${x}`);
|
|
5997
6076
|
io.print(`Next: ${r.next}`);
|
|
5998
6077
|
io.print(r.url);
|
|
@@ -6049,7 +6128,7 @@ async function runBranch(a, root, io = defaultIO()) {
|
|
|
6049
6128
|
}
|
|
6050
6129
|
case "update": {
|
|
6051
6130
|
const n = number(a, 2, "bridge branch update <n> [--choice key=mine|theirs] [--dry-run]");
|
|
6052
|
-
const choices =
|
|
6131
|
+
const choices = pairs2(a.flags.choice, "choice");
|
|
6053
6132
|
for (const [k, v] of Object.entries(choices)) if (v !== "mine" && v !== "theirs") throw new RequestUsageError(`--choice ${k}= takes mine or theirs, got "${v}".`);
|
|
6054
6133
|
const r = await branches.updateFromMain(ctx, { pr: n, ...Object.keys(choices).length ? { choices } : {}, dryRun: has4(a, "dry-run") });
|
|
6055
6134
|
return out(r, () => {
|
|
@@ -6067,7 +6146,7 @@ Share with a product team: ${r.share.install ? r.share.install : r.share.sentenc
|
|
|
6067
6146
|
}
|
|
6068
6147
|
case "combine": {
|
|
6069
6148
|
const prs = a._.slice(2);
|
|
6070
|
-
const picks = Object.fromEntries(Object.entries(
|
|
6149
|
+
const picks = Object.fromEntries(Object.entries(pairs2(a.flags.pick, "pick")).map(([k, v]) => [k, branches.branchNumber(v)]));
|
|
6071
6150
|
const r = await branches.combineBranches(ctx, { prs, ...Object.keys(picks).length ? { picks } : {} });
|
|
6072
6151
|
return out(r, () => {
|
|
6073
6152
|
io.print(`${r.tokens} tokens change together.`);
|
|
@@ -6098,6 +6177,7 @@ This folder: ${r2.current.name}. Pick another with --product <key>.`);
|
|
|
6098
6177
|
if (json) return io.print(JSON.stringify(r2, null, 2)), 0;
|
|
6099
6178
|
io.print(`Design approval: ${r2.roles.designApproval.join(", ") || "any reviewer"}`);
|
|
6100
6179
|
io.print(`Tech approval: ${r2.roles.techApproval.join(", ") || "any reviewer"}`);
|
|
6180
|
+
if (r2.roles.soloOwner) io.print(r2.roles.soloOwner);
|
|
6101
6181
|
io.print(`Merge: ${r2.roles.ship}`);
|
|
6102
6182
|
io.print(`Owner override: ${r2.ownerOverride.on ? `${r2.ownerOverride.who.join(", ")}. ${r2.ownerOverride.reason}` : r2.ownerOverride.reason}`);
|
|
6103
6183
|
io.print(`Protection: ${r2.protection}`);
|
|
@@ -6590,8 +6670,8 @@ async function diff(a, root) {
|
|
|
6590
6670
|
const before = await core.loadTokenSet(new GitRefSource(root, base), config);
|
|
6591
6671
|
const after = await ctx.tokens();
|
|
6592
6672
|
const changes = core.diff(before, after);
|
|
6593
|
-
const
|
|
6594
|
-
const delta2 =
|
|
6673
|
+
const pairs3 = await core.readContrastPairs(ctx.source, config).catch(() => []);
|
|
6674
|
+
const delta2 = pairs3.length ? core.contrastDelta(core.runContrast(before, pairs3, config.contrast?.modes), core.runContrast(after, pairs3, config.contrast?.modes)) : [];
|
|
6595
6675
|
const tier = core.classify(changes, [], config);
|
|
6596
6676
|
const imp = core.impact(before, after, changes, 30);
|
|
6597
6677
|
if (has7(a, "json")) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bridge-ds/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "bridge CLI: set up Bridge in a design system repo, check tokens in CI, search and diff tokens, propose changes, start the MCP server.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"build:package": "node ../../scripts/build-packages.mjs cli"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bridge-ds/mcp": "^0.
|
|
47
|
-
"@bridge-ds/visual": "^0.3.
|
|
46
|
+
"@bridge-ds/mcp": "^0.8.0",
|
|
47
|
+
"@bridge-ds/visual": "^0.3.7",
|
|
48
48
|
"typescript": "5.9.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|