@bridge-ds/core 0.9.0 → 0.10.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 +27 -1
- package/bridge.config.schema.json +23 -0
- package/dist/audit/index.js +3 -3
- package/dist/config.d.ts +28 -0
- package/dist/docs-proposal.d.ts +58 -10
- package/dist/figma-push-github.d.ts +143 -0
- package/dist/figma-push.d.ts +204 -0
- package/dist/github-client.d.ts +6 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +998 -2861
- package/dist/index.js.map +1 -1
- package/dist/node/git-local.d.ts +15 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +201 -10
- package/dist/node/index.js.map +1 -1
- package/dist/node/suggestion-apply-node.d.ts +69 -0
- package/dist/{pr-composer-D2SZm7cg.js → prod-branch-BdMPKffE.js} +491 -173
- package/dist/prod-branch-BdMPKffE.js.map +1 -0
- package/dist/prod-branch-github.d.ts +28 -7
- package/dist/prod-branch.d.ts +17 -7
- package/dist/{report-DY_peRy4.js → report-DOnKUw8G.js} +2 -2
- package/dist/{report-DY_peRy4.js.map → report-DOnKUw8G.js.map} +1 -1
- package/dist/suggestion-apply-ByBEYtct.js +4802 -0
- package/dist/suggestion-apply-ByBEYtct.js.map +1 -0
- package/dist/suggestion-apply.d.ts +150 -0
- package/dist/suggestion-review.d.ts +70 -0
- package/dist/suggestions-github.d.ts +131 -0
- package/dist/suggestions.d.ts +236 -0
- package/dist/token-proposal.d.ts +51 -0
- package/package.json +1 -1
- package/dist/component-spec-BzfBkYRD.js +0 -1122
- package/dist/component-spec-BzfBkYRD.js.map +0 -1
- package/dist/pr-composer-D2SZm7cg.js.map +0 -1
- package/dist/prod-branch-D5Tl87Wf.js +0 -266
- package/dist/prod-branch-D5Tl87Wf.js.map +0 -1
package/dist/node/git-local.d.ts
CHANGED
|
@@ -29,3 +29,18 @@ export declare function commitToNewLocalBranch(root: string, args: LocalCommitIn
|
|
|
29
29
|
sha: string;
|
|
30
30
|
diff: string;
|
|
31
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* A commit with `files` on top of `base`, built in a throwaway index and pointed to by no branch: nothing in the working
|
|
34
|
+
* tree, the index or the refs changes. commitToNewLocalBranch names it; a patch (git format-patch) can be read from it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function commitTree(root: string, args: {
|
|
37
|
+
base: string;
|
|
38
|
+
message: string;
|
|
39
|
+
files: {
|
|
40
|
+
path: string;
|
|
41
|
+
content: string | null;
|
|
42
|
+
}[];
|
|
43
|
+
}): Promise<{
|
|
44
|
+
sha: string;
|
|
45
|
+
parent: string;
|
|
46
|
+
}>;
|
package/dist/node/index.d.ts
CHANGED
package/dist/node/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, rm, mkdir, writeFile, lstat, readFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join, resolve, relative, dirname, sep, posix } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { aT as assertSafeBranch, c as collectHistory, p as packageDirCandidates, a as parseChangelog, m as mergeReleases, r as releasesFromTags, b as buildReport, cq as prNumberFromSubject, cN as specHash, d9 as validateComponentSpec, bM as hashSpec, bN as iconComponentName, C as variantKeyOf, d5 as tokenCssVar, ai as COMPONENT_SPECS_VERSION, ah as COLOR_FIELDS, cQ as suggestionAppliedComment, X as gitBlobSha, co as planSuggestionApply } from "../suggestion-apply-ByBEYtct.js";
|
|
6
|
+
import { K as selectTokenFiles, bl as globToRegExp, N as loadConfig, L as LayeredTokenSet, V as CONFIG_FILE, $ as layoutOptions, a0 as buildTokenSet, b3 as defaultContext, B as resolve$1, ae as slugify, bX as protectedBranchesOf, aM as baseBranchOf } from "../prod-branch-BdMPKffE.js";
|
|
7
7
|
import { statSync, readFileSync, existsSync, writeFileSync, readdirSync } from "node:fs";
|
|
8
|
-
import { i as isAuditable, Q as planCssFixes, O as parseAllowlist, d as DesignSystemIndex, l as buildAuditReport, o as compareWithBaseline, b as AUDIT_IGNORE_DIRS } from "../report-
|
|
8
|
+
import { i as isAuditable, Q as planCssFixes, O as parseAllowlist, d as DesignSystemIndex, l as buildAuditReport, o as compareWithBaseline, b as AUDIT_IGNORE_DIRS } from "../report-DOnKUw8G.js";
|
|
9
9
|
import ts from "typescript";
|
|
10
10
|
function git(root, args, opts = {}) {
|
|
11
11
|
return new Promise((resolve2, reject) => {
|
|
@@ -59,11 +59,17 @@ async function commitToNewLocalBranch(root, args) {
|
|
|
59
59
|
throw new Error(`Refusing to write branch "${args.branch}": ${err.message}`);
|
|
60
60
|
}
|
|
61
61
|
if (await branchExists(root, args.branch)) throw new Error(`Branch "${args.branch}" already exists. Pick another title or delete it first.`);
|
|
62
|
-
const
|
|
62
|
+
const { sha, parent } = await commitTree(root, args);
|
|
63
|
+
await git(root, ["update-ref", `refs/heads/${args.branch}`, sha, ""]);
|
|
64
|
+
const diff = await git(root, ["diff", parent, sha]);
|
|
65
|
+
return { sha, diff };
|
|
66
|
+
}
|
|
67
|
+
async function commitTree(root, args) {
|
|
68
|
+
const parent = (await git(root, ["rev-parse", "--verify", `${args.base}^{commit}`])).trim();
|
|
63
69
|
const dir = await mkdtemp(join(tmpdir(), "bridge-index-"));
|
|
64
70
|
const env = { GIT_INDEX_FILE: join(dir, "index") };
|
|
65
71
|
try {
|
|
66
|
-
await git(root, ["read-tree",
|
|
72
|
+
await git(root, ["read-tree", parent], { env });
|
|
67
73
|
for (const f of args.files) {
|
|
68
74
|
if (f.content === null) {
|
|
69
75
|
await git(root, ["update-index", "--force-remove", f.path], { env });
|
|
@@ -74,10 +80,8 @@ async function commitToNewLocalBranch(root, args) {
|
|
|
74
80
|
}
|
|
75
81
|
const tree = (await git(root, ["write-tree"], { env })).trim();
|
|
76
82
|
const ident = await identityEnv(root);
|
|
77
|
-
const sha = (await git(root, ["commit-tree", tree, "-p",
|
|
78
|
-
|
|
79
|
-
const diff = await git(root, ["diff", baseSha, sha]);
|
|
80
|
-
return { sha, diff };
|
|
83
|
+
const sha = (await git(root, ["commit-tree", tree, "-p", parent, "-F", "-"], { input: args.message, env: ident })).trim();
|
|
84
|
+
return { sha, parent };
|
|
81
85
|
} finally {
|
|
82
86
|
await rm(dir, { recursive: true, force: true });
|
|
83
87
|
}
|
|
@@ -3611,6 +3615,188 @@ function generateRepoComponentSpecs(root, names) {
|
|
|
3611
3615
|
}
|
|
3612
3616
|
const serializeComponentSpecs = (f) => `${JSON.stringify(f, null, 2)}
|
|
3613
3617
|
`;
|
|
3618
|
+
function gitCode(root, args) {
|
|
3619
|
+
return new Promise((resolve2) => {
|
|
3620
|
+
execFile("git", args, { cwd: root, maxBuffer: 32 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
3621
|
+
const code = err ? typeof err.code === "number" ? err.code : -1 : 0;
|
|
3622
|
+
resolve2({ code, stdout, stderr });
|
|
3623
|
+
});
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
async function treeEntry(root, ref, path) {
|
|
3627
|
+
const r = await gitCode(root, ["ls-tree", ref, "--", path]);
|
|
3628
|
+
const m = /^(\d+) (\w+) ([0-9a-f]+)\t/.exec(r.stdout);
|
|
3629
|
+
return r.code === 0 && m ? { mode: m[1], type: m[2], sha: m[3] } : null;
|
|
3630
|
+
}
|
|
3631
|
+
const prefixes = (path) => path.split("/").map((_, i, segs) => segs.slice(0, i + 1).join("/"));
|
|
3632
|
+
async function mergeText(root, input) {
|
|
3633
|
+
const dir = await mkdtemp(join(tmpdir(), "bridge-merge-"));
|
|
3634
|
+
try {
|
|
3635
|
+
const [o, b, t] = ["current", "base", "suggested"].map((n) => join(dir, n));
|
|
3636
|
+
await Promise.all([writeFile(o, input.ours), writeFile(b, input.base), writeFile(t, input.theirs)]);
|
|
3637
|
+
const r = await gitCode(root, ["merge-file", "-p", "-L", "current", "-L", "base", "-L", "suggested", o, b, t]);
|
|
3638
|
+
if (r.code < 0 || r.code > 127) throw new Error(`git merge-file failed: ${r.stderr.trim() || `exit ${r.code}`}`);
|
|
3639
|
+
return { clean: r.code === 0, text: r.stdout, conflicts: r.code };
|
|
3640
|
+
} finally {
|
|
3641
|
+
await rm(dir, { recursive: true, force: true });
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
async function fileDiff(path, before, after) {
|
|
3645
|
+
if (before === after) return "";
|
|
3646
|
+
const dir = await mkdtemp(join(tmpdir(), "bridge-diff-"));
|
|
3647
|
+
try {
|
|
3648
|
+
const side = async (name, text) => {
|
|
3649
|
+
if (text === null) return "/dev/null";
|
|
3650
|
+
const p = join(dir, name, path);
|
|
3651
|
+
await mkdir(dirname(p), { recursive: true });
|
|
3652
|
+
await writeFile(p, text);
|
|
3653
|
+
return `${name}/${path}`;
|
|
3654
|
+
};
|
|
3655
|
+
const a = await side("a", before);
|
|
3656
|
+
const b = await side("b", after);
|
|
3657
|
+
const r = await new Promise((resolve2) => execFile("git", ["diff", "--no-index", "--no-color", "--src-prefix=", "--dst-prefix=", "--", a, b], { cwd: dir, maxBuffer: 32 * 1024 * 1024 }, (_e, stdout) => resolve2({ stdout })));
|
|
3658
|
+
return r.stdout;
|
|
3659
|
+
} finally {
|
|
3660
|
+
await rm(dir, { recursive: true, force: true });
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
function gitWorkspace(root, opts = {}) {
|
|
3664
|
+
const { ref } = opts;
|
|
3665
|
+
let fetched = false;
|
|
3666
|
+
return {
|
|
3667
|
+
async read(path) {
|
|
3668
|
+
if (ref) {
|
|
3669
|
+
const e = await treeEntry(root, ref, path);
|
|
3670
|
+
if (!e || e.type !== "blob" || e.mode === "120000") return null;
|
|
3671
|
+
return { text: await git(root, ["cat-file", "blob", e.sha]), sha: e.sha };
|
|
3672
|
+
}
|
|
3673
|
+
try {
|
|
3674
|
+
const st = await lstat(join(root, path));
|
|
3675
|
+
if (!st.isFile()) return null;
|
|
3676
|
+
const text = await readFile(join(root, path), "utf8");
|
|
3677
|
+
return { text, sha: gitBlobSha(text) };
|
|
3678
|
+
} catch {
|
|
3679
|
+
return null;
|
|
3680
|
+
}
|
|
3681
|
+
},
|
|
3682
|
+
async readAt(at, path) {
|
|
3683
|
+
if ((await gitCode(root, ["cat-file", "-e", `${at}^{commit}`])).code !== 0 && !fetched) {
|
|
3684
|
+
fetched = true;
|
|
3685
|
+
await gitCode(root, ["fetch", "--no-tags", "--quiet", "origin", at]);
|
|
3686
|
+
}
|
|
3687
|
+
const e = await treeEntry(root, at, path);
|
|
3688
|
+
if (e?.type === "blob") return git(root, ["cat-file", "blob", e.sha]);
|
|
3689
|
+
return opts.readBase ? opts.readBase(path, at) : null;
|
|
3690
|
+
},
|
|
3691
|
+
async list() {
|
|
3692
|
+
if (ref) return listFilesAt(root, ref);
|
|
3693
|
+
const out = await git(root, ["ls-files", "-z", "--cached", "--others", "--exclude-standard"]);
|
|
3694
|
+
const all = [...new Set(out.split("\0").filter(Boolean))];
|
|
3695
|
+
const present = await Promise.all(all.map((p) => lstat(join(root, p)).then((s) => s.isFile(), () => false)));
|
|
3696
|
+
return all.filter((_, i) => present[i]);
|
|
3697
|
+
},
|
|
3698
|
+
async isSymlink(path) {
|
|
3699
|
+
for (const p of prefixes(path)) {
|
|
3700
|
+
if (ref) {
|
|
3701
|
+
const e = await treeEntry(root, ref, p);
|
|
3702
|
+
if (!e) return false;
|
|
3703
|
+
if (e.mode === "120000") return true;
|
|
3704
|
+
} else {
|
|
3705
|
+
try {
|
|
3706
|
+
if ((await lstat(join(root, p))).isSymbolicLink()) return true;
|
|
3707
|
+
} catch {
|
|
3708
|
+
return false;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
return false;
|
|
3713
|
+
},
|
|
3714
|
+
merge: (input) => mergeText(root, input)
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
const COMMIT_TYPE = { "token-change": "feat(tokens)", docs: "docs", "component-spec": "feat", brand: "feat(tokens)", revert: "revert", config: "chore(config)" };
|
|
3718
|
+
function commitMessage(plan, n) {
|
|
3719
|
+
const p = plan.payload;
|
|
3720
|
+
const type = `${COMMIT_TYPE[p.kind] ?? "chore"}${p.tier.bump === "major" ? "!" : ""}`;
|
|
3721
|
+
return [`${type}: ${p.title.trim()}`, "", p.summary.trim(), "", `Applies Bridge suggestion #${n} (version ${plan.payloadSha.slice(0, 7)}).`].filter((l, i, a) => !(l === "" && a[i - 1] === "")).join("\n");
|
|
3722
|
+
}
|
|
3723
|
+
async function readConfigAt(ws) {
|
|
3724
|
+
const text = (await ws.read(CONFIG_FILE))?.text;
|
|
3725
|
+
if (!text) throw new Error(`No ${CONFIG_FILE} here: run \`bridge init\` first, or apply from the repository root.`);
|
|
3726
|
+
return loadConfig(text);
|
|
3727
|
+
}
|
|
3728
|
+
async function applySuggestionLocally(input) {
|
|
3729
|
+
const mode = input.mode ?? "branch";
|
|
3730
|
+
const base = input.base ?? "HEAD";
|
|
3731
|
+
const { root } = input;
|
|
3732
|
+
const wsOpts = { readBase: input.readBase };
|
|
3733
|
+
const ws = gitWorkspace(root, mode === "write" ? wsOpts : { ...wsOpts, ref: base });
|
|
3734
|
+
const config = input.config ?? await readConfigAt(ws);
|
|
3735
|
+
const planFor = (workspace) => planSuggestionApply({ ...input, config, workspace });
|
|
3736
|
+
const plan = await planFor(ws);
|
|
3737
|
+
const at = (input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
3738
|
+
const result = (status, rest = {}) => ({ status, exitCode: plan.exitCode, message: plan.message, plan, ...rest });
|
|
3739
|
+
if (plan.outcome !== "ready") return result(plan.outcome);
|
|
3740
|
+
const readNow = async (path) => (await ws.read(path))?.text ?? null;
|
|
3741
|
+
const diffOf = async () => (await Promise.all(plan.writes.map(async (w) => fileDiff(w.path, await readNow(w.path), w.content)))).join("");
|
|
3742
|
+
if (input.dryRun) return result("dry-run", { message: `Dry run: ${plan.message.replace(/^Ready: /, "")} Nothing written.`, diff: await diffOf() });
|
|
3743
|
+
if (mode === "patch") {
|
|
3744
|
+
const { sha } = await commitTree(root, { base, message: commitMessage(plan, input.number), files: plan.writes });
|
|
3745
|
+
const patch = await git(root, ["format-patch", "-1", "--stdout", sha]);
|
|
3746
|
+
return result("patch", { message: "Patch ready: apply it with `git am`.", patch, commit: sha });
|
|
3747
|
+
}
|
|
3748
|
+
if (mode === "write") {
|
|
3749
|
+
const paths = plan.writes.map((w) => w.path);
|
|
3750
|
+
const dirty = (await git(root, ["status", "--porcelain", "-z", "--", ...paths])).split("\0").filter((l) => l.length > 3).map((l) => l.slice(3));
|
|
3751
|
+
if (dirty.length) {
|
|
3752
|
+
return result("refused", {
|
|
3753
|
+
exitCode: 1,
|
|
3754
|
+
message: `Not written: these files have uncommitted changes: ${dirty.join(", ")}. Commit or stash them, or apply on a branch instead.`
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
const diff = await diffOf();
|
|
3758
|
+
for (const w of plan.writes) {
|
|
3759
|
+
const full = join(root, w.path);
|
|
3760
|
+
if (w.content === null) await rm(full, { force: true });
|
|
3761
|
+
else {
|
|
3762
|
+
await mkdir(dirname(full), { recursive: true });
|
|
3763
|
+
await writeFile(full, w.content);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
return result("written", {
|
|
3767
|
+
message: `Written: ${paths.length} file(s) in the working tree. Review them, commit, and say "Closes #${input.number}" in the pull request.`,
|
|
3768
|
+
written: paths,
|
|
3769
|
+
diff,
|
|
3770
|
+
appliedComment: suggestionAppliedComment(plan, { by: input.by, at })
|
|
3771
|
+
});
|
|
3772
|
+
}
|
|
3773
|
+
const prefix = config.governance.branchPrefix;
|
|
3774
|
+
const wanted = input.branch ?? `${prefix}suggestion-${input.number}-${slugify(plan.payload.title, 40)}`;
|
|
3775
|
+
let branch = wanted;
|
|
3776
|
+
for (let i = 2; await branchExists(root, branch); i++) {
|
|
3777
|
+
const there = await planFor(gitWorkspace(root, { ...wsOpts, ref: branch }));
|
|
3778
|
+
if (there.outcome === "already-applied") {
|
|
3779
|
+
const commit = (await git(root, ["rev-parse", `refs/heads/${branch}`])).trim();
|
|
3780
|
+
return result("branch-exists", {
|
|
3781
|
+
exitCode: 0,
|
|
3782
|
+
message: `Already on ${branch}: that branch has every change of this suggestion. Push it and open a pull request that says "Closes #${input.number}".`,
|
|
3783
|
+
branch,
|
|
3784
|
+
commit,
|
|
3785
|
+
appliedComment: suggestionAppliedComment(plan, { by: input.by, branch, commit, at })
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
branch = `${wanted}-${i}`;
|
|
3789
|
+
}
|
|
3790
|
+
const protectedBranches = [.../* @__PURE__ */ new Set([...protectedBranchesOf(config), baseBranchOf(config)])];
|
|
3791
|
+
const res = await commitToNewLocalBranch(root, { base, branch, message: commitMessage(plan, input.number), files: plan.writes, protectedBranches, branchPrefix: prefix });
|
|
3792
|
+
return result("branch-created", {
|
|
3793
|
+
message: `Applied on ${branch}, your working tree is untouched. Push it and open a pull request that says "Closes #${input.number}".`,
|
|
3794
|
+
branch,
|
|
3795
|
+
commit: res.sha,
|
|
3796
|
+
diff: res.diff,
|
|
3797
|
+
appliedComment: suggestionAppliedComment(plan, { by: input.by, branch, commit: res.sha, at })
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3614
3800
|
export {
|
|
3615
3801
|
COMMON_ICONS,
|
|
3616
3802
|
DEFAULT_STATE,
|
|
@@ -3618,10 +3804,12 @@ export {
|
|
|
3618
3804
|
INHERITED,
|
|
3619
3805
|
MEDIA_SLOT,
|
|
3620
3806
|
UnsupportedComponentError,
|
|
3807
|
+
applySuggestionLocally,
|
|
3621
3808
|
branchExists,
|
|
3622
3809
|
changedProperties,
|
|
3623
3810
|
collectAuditFiles,
|
|
3624
3811
|
commitToNewLocalBranch,
|
|
3812
|
+
commitTree,
|
|
3625
3813
|
computeValues,
|
|
3626
3814
|
currentBranch,
|
|
3627
3815
|
declaredFor,
|
|
@@ -3629,6 +3817,7 @@ export {
|
|
|
3629
3817
|
draftedSpec,
|
|
3630
3818
|
entryFor,
|
|
3631
3819
|
expandDecl,
|
|
3820
|
+
fileDiff,
|
|
3632
3821
|
findDesignSystemRoot,
|
|
3633
3822
|
fixCss,
|
|
3634
3823
|
generateAllComponentSpecs,
|
|
@@ -3638,6 +3827,7 @@ export {
|
|
|
3638
3827
|
generateRepoComponentSpecs,
|
|
3639
3828
|
generateSlots,
|
|
3640
3829
|
git,
|
|
3830
|
+
gitWorkspace,
|
|
3641
3831
|
isGitRepo,
|
|
3642
3832
|
jsxText,
|
|
3643
3833
|
listFilesAt,
|
|
@@ -3648,6 +3838,7 @@ export {
|
|
|
3648
3838
|
localTags,
|
|
3649
3839
|
matchComplex,
|
|
3650
3840
|
mediaApplies,
|
|
3841
|
+
mergeText,
|
|
3651
3842
|
motionNotes,
|
|
3652
3843
|
openingTransitions,
|
|
3653
3844
|
originRepo,
|