youiyoux 0.1.8 → 0.1.10
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/chunks/{chunk-F33PKGES.js → chunk-3RP664IP.js} +1 -1
- package/dist/chunks/{chunk-BARSW3JO.js → chunk-75DBKEAH.js} +301 -93
- package/dist/chunks/{chunk-Y4IBE6WS.js → chunk-OCZJ3JGH.js} +1 -1
- package/dist/chunks/{chunk-DCBF7QQY.js → chunk-T3KNPVQG.js} +1 -1
- package/dist/chunks/{chunk-3HON5Q5K.js → chunk-X2YYMHR4.js} +8 -2
- package/dist/chunks/{src-EUVBYTKY.js → src-6SNQVIZS.js} +2 -2
- package/dist/chunks/{src-YKPJ3PII.js → src-75ODGHE3.js} +2 -2
- package/dist/chunks/{src-M3OJMCUE.js → src-XTNHB65M.js} +1 -1
- package/dist/hub-previews.js +2 -2
- package/dist/hub-server.js +2 -2
- package/dist/hub-snapshot.js +1 -1
- package/dist/youi.js +2 -2
- package/dist/youiyoux.js +2 -2
- package/package.json +4 -4
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
vendoredPath,
|
|
54
54
|
whereUsed,
|
|
55
55
|
withFile
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-X2YYMHR4.js";
|
|
57
57
|
import {
|
|
58
58
|
escapingLinks,
|
|
59
59
|
within
|
|
@@ -137,6 +137,7 @@ import {
|
|
|
137
137
|
sha256,
|
|
138
138
|
shortHash,
|
|
139
139
|
toPosix,
|
|
140
|
+
unifiedDiff,
|
|
140
141
|
writeFileAtomic
|
|
141
142
|
} from "./chunk-DWYDHVLZ.js";
|
|
142
143
|
import {
|
|
@@ -147,8 +148,8 @@ import {
|
|
|
147
148
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
148
149
|
import { execFile as execFile5 } from "node:child_process";
|
|
149
150
|
import { existsSync as existsSync5 } from "node:fs";
|
|
150
|
-
import { appendFile as appendFile2, readFile as readFile10, rm as
|
|
151
|
-
import { basename as basename5, dirname as dirname13, join as
|
|
151
|
+
import { appendFile as appendFile2, readFile as readFile10, rm as rm7, rmdir as rmdir3 } from "node:fs/promises";
|
|
152
|
+
import { basename as basename5, dirname as dirname13, join as join26, relative as relative11, resolve as resolve15 } from "node:path";
|
|
152
153
|
import { parseArgs, promisify as promisify5 } from "node:util";
|
|
153
154
|
|
|
154
155
|
// ../cli/src/check.ts
|
|
@@ -166,7 +167,7 @@ import { join as join2, relative, resolve } from "node:path";
|
|
|
166
167
|
|
|
167
168
|
// ../cli/src/command.ts
|
|
168
169
|
var COMMAND = "youiyoux";
|
|
169
|
-
var SUBCOMMANDS = ["show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "init", "adopt", "status", "system", "sync", "link", "unlink", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "discover", "index", "report", "render", "screens", "requests", "detect", "build", "parity", "schema", "inventory", "gaps", "help"];
|
|
170
|
+
var SUBCOMMANDS = ["show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "upgrade", "init", "adopt", "status", "system", "sync", "link", "unlink", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "discover", "index", "report", "render", "screens", "requests", "detect", "build", "parity", "schema", "inventory", "gaps", "help"];
|
|
170
171
|
|
|
171
172
|
// ../cli/src/agents.ts
|
|
172
173
|
var import_yaml = __toESM(require_dist(), 1);
|
|
@@ -801,6 +802,7 @@ async function readLock(root) {
|
|
|
801
802
|
var Writer = class {
|
|
802
803
|
actions = [];
|
|
803
804
|
files = {};
|
|
805
|
+
contents = /* @__PURE__ */ new Map();
|
|
804
806
|
project;
|
|
805
807
|
options;
|
|
806
808
|
previous;
|
|
@@ -813,6 +815,7 @@ var Writer = class {
|
|
|
813
815
|
return join6(this.project.root, file);
|
|
814
816
|
}
|
|
815
817
|
async write(file, content) {
|
|
818
|
+
this.contents.set(file, content);
|
|
816
819
|
if (!this.options.dryRun) await writeFileAtomic(this.path(file), content);
|
|
817
820
|
}
|
|
818
821
|
record(action) {
|
|
@@ -985,7 +988,7 @@ async function syncProject(project2, options = {}) {
|
|
|
985
988
|
};
|
|
986
989
|
if (!options.dryRun) await writeFileAtomic(join6(project2.root, LOCK_FILE), `${JSON.stringify(lock, null, 2)}
|
|
987
990
|
`);
|
|
988
|
-
return { actions: writer.actions, lock, unknownTools: unknownToolMentions(project2) };
|
|
991
|
+
return { actions: writer.actions, lock, unknownTools: unknownToolMentions(project2), contents: writer.contents };
|
|
989
992
|
}
|
|
990
993
|
async function sameFile(a, b) {
|
|
991
994
|
const [x, y] = await Promise.all([realpath(a).catch(() => null), realpath(b).catch(() => null)]);
|
|
@@ -2106,7 +2109,7 @@ async function libraryLicence(id) {
|
|
|
2106
2109
|
}
|
|
2107
2110
|
}
|
|
2108
2111
|
async function startRuleEditor(options) {
|
|
2109
|
-
const { FileStore, RuleWorkflow, serveWorkflow } = await import("./src-
|
|
2112
|
+
const { FileStore, RuleWorkflow, serveWorkflow } = await import("./src-6SNQVIZS.js");
|
|
2110
2113
|
const projectRoot = resolve6(options.project);
|
|
2111
2114
|
const load2 = { ...options.config ? { configPath: resolve6(options.config) } : {}, ...options.design ? { designRoot: resolve6(options.design) } : {}, ...options.root ? { productRoot: resolve6(options.root) } : {} };
|
|
2112
2115
|
const project2 = await loadProject(projectRoot, load2);
|
|
@@ -2357,14 +2360,26 @@ function moveDeclarations(css, blocks, moved, importPath) {
|
|
|
2357
2360
|
const rules = readRules(css).filter((r) => blocks.some((b) => r.preludes.at(-1) === b.selector && (b.outer ? r.preludes.length === 2 && r.preludes[0] === b.outer : r.preludes.length === 1)));
|
|
2358
2361
|
if (rules.length === 0) return null;
|
|
2359
2362
|
const lines = css.split("\n");
|
|
2363
|
+
const byLine = /* @__PURE__ */ new Map();
|
|
2364
|
+
for (const rule2 of rules) for (const d of rule2.declarations) if (moved.has(d.property.slice(2))) byLine.set(d.line - 1, (byLine.get(d.line - 1) ?? /* @__PURE__ */ new Set()).add(d.property));
|
|
2365
|
+
let count = 0;
|
|
2366
|
+
const kept = [];
|
|
2360
2367
|
const drop = /* @__PURE__ */ new Set();
|
|
2361
|
-
for (const
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2368
|
+
for (const [index, properties] of byLine) {
|
|
2369
|
+
const segments = lines[index].split(";");
|
|
2370
|
+
const left = segments.filter((segment) => {
|
|
2371
|
+
const property = /^\s*(--[\w-]+)\s*:/.exec(segment)?.[1];
|
|
2372
|
+
if (property === void 0 || !properties.has(property)) return true;
|
|
2373
|
+
properties.delete(property);
|
|
2374
|
+
count++;
|
|
2375
|
+
return false;
|
|
2376
|
+
});
|
|
2377
|
+
kept.push(...[...properties].map((p) => p.slice(2)));
|
|
2378
|
+
const text = left.join(";");
|
|
2379
|
+
if (text.trim() === "") drop.add(index);
|
|
2380
|
+
else lines[index] = /^\s/.test(text) || index === 0 ? text : `${lines[index].match(/^\s*/)[0]}${text.trimStart()}`;
|
|
2366
2381
|
}
|
|
2367
|
-
if (
|
|
2382
|
+
if (count === 0) return null;
|
|
2368
2383
|
let content = lines.filter((_, i) => !drop.has(i)).join("\n");
|
|
2369
2384
|
for (const block of blocks) content = content.replace(new RegExp(`(^|\\n)[ \\t]*${block.selector.replace(/[.[\]()"=:]/g, "\\$&")}\\s*\\{\\s*\\}[ \\t]*(?=\\n|$)`, "g"), "");
|
|
2370
2385
|
for (const outer of new Set(blocks.flatMap((b) => b.outer ? [b.outer] : []))) content = content.replace(new RegExp(`(^|\\n)[ \\t]*${outer.replace(/[.[\]()"=:]/g, "\\$&")}\\s*\\{\\s*\\}[ \\t]*(?=\\n|$)`, "g"), "");
|
|
@@ -2374,7 +2389,7 @@ function moveDeclarations(css, blocks, moved, importPath) {
|
|
|
2374
2389
|
content = at > 0 ? `${content.slice(0, at)}
|
|
2375
2390
|
${statement}${content.slice(at)}` : `${statement}
|
|
2376
2391
|
${content}`;
|
|
2377
|
-
return { content: content.replace(/\n{3,}/g, "\n\n"), moved:
|
|
2392
|
+
return { content: content.replace(/\n{3,}/g, "\n\n"), moved: count, kept: [...new Set(kept)].sort() };
|
|
2378
2393
|
}
|
|
2379
2394
|
async function planAdoption(options) {
|
|
2380
2395
|
const root = options.root;
|
|
@@ -2470,6 +2485,10 @@ async function planAdoption(options) {
|
|
|
2470
2485
|
const rewritten = moving && found.forked ? { ...moving, content: moving.content.split("\n").filter((line) => !line.includes(GENERATED_MARK) && !/^\/\* Built with Terrazzo/.test(line)).join("\n") } : moving;
|
|
2471
2486
|
if (found.forked) notes.push(`forked from ${found.forked.system ?? "the System"}${found.forked.version ? ` v${found.forked.version}` : ""}: this product's System starts from the ${found.forked.count} variables it generated into ${found.forked.file}, and the product no longer follows ${found.forked.system ?? "it"}`);
|
|
2472
2487
|
if (css && moved.size > 0 && !rewritten) notes.push(`${stylesheet}'s variables were copied, not moved (their blocks are nested or share lines): import ${OUT_DIR}/tokens.css from it and remove them by hand`);
|
|
2488
|
+
const read = [":root", ...darkBlocks.map((b) => [b.outer, b.selector].filter(Boolean).join(" > "))];
|
|
2489
|
+
const others = css ? [...new Set(readRules(css).filter((r) => !read.includes(r.preludes.join(" > ")) && !r.preludes.at(-1).startsWith("@") && r.declarations.some((d) => d.property.startsWith("--"))).map((r) => r.preludes.join(" > ").replace(/\s+/g, " ")))] : [];
|
|
2490
|
+
if (others.length > 0) notes.push(`${plural(others.length, "other block")} in ${stylesheet} set${others.length === 1 ? "s" : ""} variables too and stay${others.length === 1 ? "s" : ""} there, as ${others.length === 1 ? "it was" : "they were"} (${others.slice(0, 4).join(" \xB7 ")}${others.length > 4 ? " \xB7 \u2026" : ""}): the System holds the :root${darkBlocks.length > 0 ? " and dark" : ""} values only`);
|
|
2491
|
+
if (rewritten && rewritten.kept.length > 0) notes.push(`${plural(rewritten.kept.length, "variable")} ${rewritten.kept.length === 1 ? "stays" : "stay"} in ${stylesheet} too, written where adopt cannot take them out alone (beside a selector): ${rewritten.kept.slice(0, 4).map((n) => `--${n}`).join(", ")}${rewritten.kept.length > 4 ? ", \u2026" : ""}; they hold the same values, so remove them by hand`);
|
|
2473
2492
|
for (const [rule2, content] of starterRules(id, componentsDir)) out.push({ path: `design/rules/${rule2}.rule.yaml`, content });
|
|
2474
2493
|
out.push({
|
|
2475
2494
|
path: "design/design.md",
|
|
@@ -2634,14 +2653,186 @@ async function extractSystem(project2, dir, options = {}) {
|
|
|
2634
2653
|
}
|
|
2635
2654
|
}
|
|
2636
2655
|
|
|
2656
|
+
// ../cli/src/upgrade.ts
|
|
2657
|
+
import { lstat as lstat2, rm as rm5 } from "node:fs/promises";
|
|
2658
|
+
import { join as join16 } from "node:path";
|
|
2659
|
+
var LOCK_FILE2 = "youi.lock.json";
|
|
2660
|
+
var WRITES = /* @__PURE__ */ new Set(["created", "updated", "merged", "adopted", "removed"]);
|
|
2661
|
+
function compareVersions(a, b) {
|
|
2662
|
+
const parse = (v) => {
|
|
2663
|
+
const [core = "", pre = ""] = v.replace(/^v/, "").split("-", 2);
|
|
2664
|
+
return [core.split(".").slice(0, 3).map((n) => Number(n) || 0), pre];
|
|
2665
|
+
};
|
|
2666
|
+
const [[x, xp], [y, yp]] = [parse(a), parse(b)];
|
|
2667
|
+
for (let i = 0; i < 3; i++) if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) - (y[i] ?? 0);
|
|
2668
|
+
if (xp === yp) return 0;
|
|
2669
|
+
return xp === "" ? 1 : yp === "" ? -1 : xp < yp ? -1 : 1;
|
|
2670
|
+
}
|
|
2671
|
+
function admits(range, version) {
|
|
2672
|
+
const parts = range.trim().split(/\s+/);
|
|
2673
|
+
const results = parts.map((part) => {
|
|
2674
|
+
if (part === "*" || part === "x") return true;
|
|
2675
|
+
const xr = /^v?(\d+)(?:\.(\d+|x))?(?:\.(x))?$/.exec(part);
|
|
2676
|
+
if (xr && (xr[2] === "x" || xr[3] === "x" || xr[2] === void 0)) {
|
|
2677
|
+
const [major, minor] = version.split(".").map(Number);
|
|
2678
|
+
return major === Number(xr[1]) && (xr[2] === void 0 || xr[2] === "x" || minor === Number(xr[2]));
|
|
2679
|
+
}
|
|
2680
|
+
const op = /^(\^|~|>=|<=|>|<|=)?v?(\d+\.\d+\.\d+(?:-[\w.]+)?)$/.exec(part);
|
|
2681
|
+
if (!op) return null;
|
|
2682
|
+
const [, kind = "=", base = ""] = op;
|
|
2683
|
+
const cmp = compareVersions(version, base);
|
|
2684
|
+
const [bMajor, bMinor] = base.split(".").map(Number);
|
|
2685
|
+
const [vMajor, vMinor] = version.split(".").map(Number);
|
|
2686
|
+
if (kind === "^") return cmp >= 0 && (bMajor === 0 ? vMajor === 0 && vMinor === bMinor : vMajor === bMajor);
|
|
2687
|
+
if (kind === "~") return cmp >= 0 && vMajor === bMajor && vMinor === bMinor;
|
|
2688
|
+
return { "=": cmp === 0, ">=": cmp >= 0, "<=": cmp <= 0, ">": cmp > 0, "<": cmp < 0 }[kind] ?? null;
|
|
2689
|
+
});
|
|
2690
|
+
return results.includes(null) ? null : results.every(Boolean);
|
|
2691
|
+
}
|
|
2692
|
+
function pendingMigrations(project2, lock) {
|
|
2693
|
+
const entries = Object.entries(lock.files).flatMap(([file, entry]) => entry.kind === "vendored" ? [{ file, ...entry }] : []);
|
|
2694
|
+
const planned = [];
|
|
2695
|
+
for (const { contract, migrations } of project2.system.components) {
|
|
2696
|
+
const entry = entries.find((e) => e.file === vendoredPath(project2, contract.source.file) && e.elements.includes(contract.id)) ?? entries.filter((e) => e.elements.includes(contract.id)).sort((a, b) => a.elements.length - b.elements.length)[0];
|
|
2697
|
+
if (!entry) continue;
|
|
2698
|
+
let at = entry.version;
|
|
2699
|
+
for (const spec of [...migrations].filter((m) => compareVersions(m.to, entry.version) > 0 && compareVersions(m.to, contract.version) <= 0).sort((a, b) => compareVersions(a.to, b.to) || a.id.localeCompare(b.id))) {
|
|
2700
|
+
planned.push({ spec, at, mismatch: admits(spec.from, at) === false });
|
|
2701
|
+
at = spec.to;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
return planned.sort((a, b) => compareVersions(a.spec.to, b.spec.to) || a.spec.id.localeCompare(b.spec.id));
|
|
2705
|
+
}
|
|
2706
|
+
var Snapshot = class {
|
|
2707
|
+
saved = /* @__PURE__ */ new Map();
|
|
2708
|
+
root;
|
|
2709
|
+
constructor(root) {
|
|
2710
|
+
this.root = root;
|
|
2711
|
+
}
|
|
2712
|
+
async keep(files) {
|
|
2713
|
+
for (const file of files) if (!this.saved.has(file)) this.saved.set(file, await readTextOrNull(join16(this.root, file)));
|
|
2714
|
+
}
|
|
2715
|
+
/** A file's content before a step wrote it, unless it was kept already. */
|
|
2716
|
+
remember(file, content) {
|
|
2717
|
+
if (!this.saved.has(file)) this.saved.set(file, content);
|
|
2718
|
+
}
|
|
2719
|
+
/** Put back each file that changed; those that could not be are returned, to name them. */
|
|
2720
|
+
async restore() {
|
|
2721
|
+
const stuck = [];
|
|
2722
|
+
for (const [file, content] of this.saved) {
|
|
2723
|
+
const path = join16(this.root, file);
|
|
2724
|
+
try {
|
|
2725
|
+
if (content !== null) {
|
|
2726
|
+
if (await readTextOrNull(path) !== content) await writeFileAtomic(path, content);
|
|
2727
|
+
} else if (await lstat2(path).then((s) => s.isFile(), () => false)) await rm5(path);
|
|
2728
|
+
} catch {
|
|
2729
|
+
stuck.push(file);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
return stuck;
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
var gateFailures = (result) => result.gates.filter((g) => !g.ok).map((g) => `${g.name}: ${g.detail}`).join("; ");
|
|
2736
|
+
async function withRef(configFile, ref) {
|
|
2737
|
+
const text = await readTextOrNull(configFile) ?? "{}";
|
|
2738
|
+
const config = JSON.parse(text);
|
|
2739
|
+
return `${JSON.stringify({ ...config, source: { ...config.source, ref } }, null, 2)}
|
|
2740
|
+
`;
|
|
2741
|
+
}
|
|
2742
|
+
async function upgradeProject(project2, options = {}) {
|
|
2743
|
+
const lock = await readLock(project2.root);
|
|
2744
|
+
const source = project2.config.source;
|
|
2745
|
+
const system = { id: project2.system.config.id, name: project2.system.config.name };
|
|
2746
|
+
const to = { version: project2.system.config.version, commit: project2.designCommit ?? null, ref: options.to ?? ("git" in source ? source.ref : null) };
|
|
2747
|
+
const from = { version: lock?.systemVersion ?? null, commit: lock?.sourceCommit ?? null };
|
|
2748
|
+
const result = (status, parts, next2) => ({ status, system, from, to, sync: parts.sync ?? [], migrations: parts.migrations ?? [], blocked: parts.blocked ?? [], next: next2 });
|
|
2749
|
+
if (!lock) return result("blocked", { blocked: ["this product has never been synced with its System, so there is no earlier version to upgrade from"] }, "youiyoux sync");
|
|
2750
|
+
if (lock.system !== system.id) return result("blocked", { blocked: [`youi.lock.json is for System ${lock.system}, and the config names ${system.id}: an upgrade moves within one System`] }, "youiyoux sync, to switch Systems");
|
|
2751
|
+
const dry = await syncProject(project2, { dryRun: true });
|
|
2752
|
+
const conflicts = dry.actions.filter((a) => a.action === "conflict");
|
|
2753
|
+
const sync = dry.actions.filter((a) => WRITES.has(a.action) || a.action === "conflict" || a.action === "kept-local");
|
|
2754
|
+
const planned = pendingMigrations(project2, lock);
|
|
2755
|
+
const blocked = [
|
|
2756
|
+
...conflicts.map((a) => `${a.file}: ${a.detail ?? "conflicts with upstream"}`),
|
|
2757
|
+
...planned.filter((p) => p.mismatch).map((p) => `${p.spec.id} migrates from ${p.spec.from}, but ${p.spec.element} would be at ${p.at} here: no migration takes it from ${p.at} to ${p.spec.from}`)
|
|
2758
|
+
];
|
|
2759
|
+
if (blocked.length > 0) return result("blocked", { sync, blocked }, conflicts.length > 0 ? "youiyoux sync: resolve the conflicts it reports, then youiyoux sync --adopt records them, then youiyoux upgrade" : "ask the System's owners for the missing migration, or migrate by hand, then youiyoux sync");
|
|
2760
|
+
let staged = Object.fromEntries(dry.actions.filter((a) => a.role === "component" && dry.contents.has(a.file)).map((a) => [a.file, dry.contents.get(a.file)]));
|
|
2761
|
+
const previews = [];
|
|
2762
|
+
for (const { spec } of planned) {
|
|
2763
|
+
const preview = await runMigration({ ...project2, staged }, spec);
|
|
2764
|
+
previews.push(preview);
|
|
2765
|
+
if (preview.status === "rejected") return result("blocked", { sync, migrations: previews, blocked: [`${spec.id} would not hold: ${gateFailures(preview)}`] }, "fix what the failing gate names, or ask the System's owners; nothing was written");
|
|
2766
|
+
staged = { ...staged, ...Object.fromEntries(preview.changes.map((c) => [c.file, c.after])) };
|
|
2767
|
+
}
|
|
2768
|
+
const ref = options.to !== void 0 && "git" in source && options.to !== source.ref ? options.to : null;
|
|
2769
|
+
const changes = sync.some((a) => WRITES.has(a.action)) || previews.some((m) => m.status !== "noop") || ref !== null || lock.systemVersion !== to.version;
|
|
2770
|
+
if (!changes) return result("current", { sync, migrations: previews }, "nothing to do");
|
|
2771
|
+
if (!options.write) return result("preview", { sync, migrations: previews }, `youiyoux upgrade${options.to !== void 0 ? ` --to ${options.to}` : ""} --write`);
|
|
2772
|
+
const configFile = project2.configFile ?? join16(project2.root, PROJECT_FILE);
|
|
2773
|
+
const snapshot = new Snapshot(project2.root);
|
|
2774
|
+
const configBefore = await readTextOrNull(configFile);
|
|
2775
|
+
await snapshot.keep([LOCK_FILE2, ...dry.contents.keys(), ...dry.actions.filter((a) => a.action === "removed").map((a) => a.file), ...previews.flatMap((m) => m.changes.map((c) => c.file))]);
|
|
2776
|
+
const fail = async (why, done) => {
|
|
2777
|
+
const stuck = await snapshot.restore();
|
|
2778
|
+
if (ref !== null && configBefore !== null) await writeFileAtomic(configFile, configBefore).catch(() => stuck.push(PROJECT_FILE));
|
|
2779
|
+
const back = stuck.length === 0 ? "every file the upgrade wrote was put back" : `the files it wrote were put back except ${stuck.join(", ")}: restore those from git`;
|
|
2780
|
+
return result("blocked", { sync, migrations: previews, ...done, blocked: [`${why}; ${back}`] }, "run youiyoux upgrade again to see what changed");
|
|
2781
|
+
};
|
|
2782
|
+
try {
|
|
2783
|
+
if (ref !== null) await writeFileAtomic(configFile, await withRef(configFile, ref));
|
|
2784
|
+
const synced = await syncProject(project2);
|
|
2785
|
+
const late = synced.actions.filter((a) => a.action === "conflict");
|
|
2786
|
+
if (late.length > 0) return await fail(`the sync conflicted in ${late.map((a) => a.file).join(", ")}`, {});
|
|
2787
|
+
const applied = [];
|
|
2788
|
+
for (const { spec } of planned) {
|
|
2789
|
+
const run7 = await runMigration(project2, spec, { write: true });
|
|
2790
|
+
applied.push(run7);
|
|
2791
|
+
for (const change of run7.changes) snapshot.remember(change.file, change.before);
|
|
2792
|
+
if (run7.status === "rejected") return await fail(`${spec.id} did not hold on the written files: ${gateFailures(run7)}`, { migrations: applied });
|
|
2793
|
+
}
|
|
2794
|
+
const residuals = applied.reduce((n, m) => n + m.residuals.length, 0);
|
|
2795
|
+
return result("upgraded", { sync: synced.actions.filter((a) => WRITES.has(a.action) || a.action === "kept-local"), migrations: applied }, residuals > 0 ? `fix the ${residuals === 1 ? "site" : `${residuals} sites`} listed for a person, then youiyoux check` : "youiyoux check");
|
|
2796
|
+
} catch (error) {
|
|
2797
|
+
return fail(error instanceof Error ? error.message : String(error), {});
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
var ACTION = { created: style.ok, updated: style.live, merged: style.live, adopted: style.ok, removed: style.live, "kept-local": style.warn, conflict: style.error };
|
|
2801
|
+
function renderUpgrade(r, showDiff) {
|
|
2802
|
+
const version = (v) => v === null ? "never synced" : `v${v}`;
|
|
2803
|
+
const move = r.from.version === r.to.version ? `${version(r.to.version)}${r.from.commit && r.to.commit && r.from.commit !== r.to.commit ? style.dim(` (${r.from.commit.slice(0, 7)} \u2192 ${r.to.commit.slice(0, 7)})`) : ""}` : `${version(r.from.version)} \u2192 v${r.to.version}`;
|
|
2804
|
+
const verdict = { current: style.ok("up to date"), preview: style.live("preview: nothing written"), upgraded: style.ok("upgraded"), blocked: style.error("blocked: nothing written") }[r.status];
|
|
2805
|
+
const lines = [`${style.accent("upgrade")} ${r.system.name} ${move}${r.to.ref ? style.dim(` \xB7 ref ${r.to.ref}`) : ""} ${verdict}`, ""];
|
|
2806
|
+
const row = (a) => ` ${padEnd((ACTION[a.action] ?? style.dim)(a.action), 11)} ${a.file}${a.detail ? style.dim(` ${a.detail}`) : ""}`;
|
|
2807
|
+
const generated = r.sync.filter((a) => a.role !== "component" && a.action !== "conflict");
|
|
2808
|
+
lines.push(style.bold("sync"), ...r.sync.length === 0 ? [style.dim(" nothing to bring")] : r.sync.filter((a) => !generated.includes(a)).map(row));
|
|
2809
|
+
if (generated.length === 1) lines.push(row(generated[0]));
|
|
2810
|
+
else if (generated.length > 1) lines.push(` ${padEnd(style.live("updated"), 11)} ${plural(generated.length, "generated file")}${style.dim(` ${generated.slice(0, 3).map((a) => a.file).join(", ")}${generated.length > 3 ? ", \u2026" : ""}`)}`);
|
|
2811
|
+
lines.push("", style.bold("migrations"));
|
|
2812
|
+
if (r.migrations.length === 0) lines.push(style.dim(r.status === "blocked" && r.sync.length > 0 ? " not run" : r.status === "current" ? " none pending" : " none: no component this product has changed its API in this release"));
|
|
2813
|
+
for (const m of r.migrations) {
|
|
2814
|
+
const mark = m.status === "rejected" ? style.error("\u2716") : m.status === "noop" ? style.dim("\xB7") : style.ok("\u2713");
|
|
2815
|
+
const what = m.status === "noop" ? style.dim("nothing here uses what it changes") : `${plural(m.edited, "site")} rewritten in ${plural(m.changes.length, "file")}${m.residuals.length > 0 ? ` \xB7 ${style.warn(`${plural(m.residuals.length, "site")} for a person`)}` : ""}`;
|
|
2816
|
+
lines.push(` ${mark} ${m.migration} ${what}`);
|
|
2817
|
+
for (const gate of m.gates.filter((g) => !g.ok)) lines.push(` ${style.error(gate.name)} ${style.dim(gate.detail)}`);
|
|
2818
|
+
for (const residual of m.residuals) lines.push(` ${style.dim(`${residual.file}:${residual.line}`)} ${residual.reason}`);
|
|
2819
|
+
}
|
|
2820
|
+
const files = /* @__PURE__ */ new Map();
|
|
2821
|
+
for (const change of r.migrations.flatMap((m) => m.changes)) files.set(change.file, { before: files.get(change.file)?.before ?? change.before, after: change.after });
|
|
2822
|
+
if (showDiff) for (const [file, { before, after }] of files) lines.push("", ...unifiedDiff(file, before, after).trimEnd().split("\n").map((l) => l.startsWith("+") && !l.startsWith("+++") ? style.ok(l) : l.startsWith("-") && !l.startsWith("---") ? style.error(l) : style.dim(l)));
|
|
2823
|
+
if (r.blocked.length > 0) lines.push("", ...r.blocked.map((b) => `${style.error("\u2716")} ${b}`));
|
|
2824
|
+
lines.push("", `${style.bold("Next:")} ${r.next}`);
|
|
2825
|
+
return lines.join("\n");
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2637
2828
|
// ../cli/src/status.ts
|
|
2638
2829
|
import { realpath as realpath5 } from "node:fs/promises";
|
|
2639
|
-
import { dirname as dirname9, join as
|
|
2830
|
+
import { dirname as dirname9, join as join17, relative as relative8, resolve as resolve10 } from "node:path";
|
|
2640
2831
|
var placeOf = (cwd, root) => {
|
|
2641
2832
|
const rel = relative8(cwd, root) || ".";
|
|
2642
2833
|
return rel.split(/[\\/]/).filter((part) => part === "..").length > 1 ? resolve10(root) : rel;
|
|
2643
2834
|
};
|
|
2644
|
-
var
|
|
2835
|
+
var WRITES2 = /* @__PURE__ */ new Set(["created", "updated", "merged", "adopted", "conflict", "removed"]);
|
|
2645
2836
|
function sourceOf(project2) {
|
|
2646
2837
|
if (project2.linked) return `your checkout ${project2.linked}`;
|
|
2647
2838
|
const source = project2.config.source;
|
|
@@ -2649,9 +2840,10 @@ function sourceOf(project2) {
|
|
|
2649
2840
|
if ("git" in source) return `git ${source.git} at ${source.ref}`;
|
|
2650
2841
|
return resolve10(project2.root) === resolve10(project2.repo.root) ? "this repo" : `path ${source.path}`;
|
|
2651
2842
|
}
|
|
2652
|
-
function nextStep(s) {
|
|
2843
|
+
function nextStep(s, migrations) {
|
|
2653
2844
|
if (s.sync.state === "never") return "youiyoux sync: this product has never been synced with its System";
|
|
2654
2845
|
if (s.sync.state === "conflict") return "youiyoux sync: resolve the conflicts it reports, then youiyoux sync --adopt records them";
|
|
2846
|
+
if (migrations > 0) return `youiyoux upgrade: ${s.system.name} v${s.system.version} comes with ${plural(migrations, "migration")} for this product's code, which upgrade runs with the sync`;
|
|
2655
2847
|
if (s.sync.state === "behind") return `youiyoux sync: ${plural(s.sync.changes.length, "file")} would change`;
|
|
2656
2848
|
if (s.check.incomplete) return "youiyoux check: some files or rules could not be checked, and it says which";
|
|
2657
2849
|
if (s.check.errors > 0) return `youiyoux check: fix ${plural(s.check.errors, "error")}`;
|
|
@@ -2662,13 +2854,13 @@ function nextStep(s) {
|
|
|
2662
2854
|
async function projectStatus(project2, cwd = process.cwd()) {
|
|
2663
2855
|
const lock = await readLock(project2.root);
|
|
2664
2856
|
const { actions } = await syncProject(project2, { dryRun: true });
|
|
2665
|
-
const changes = actions.filter((a) =>
|
|
2857
|
+
const changes = actions.filter((a) => WRITES2.has(a.action)).map((a) => ({ file: a.file, action: a.action }));
|
|
2666
2858
|
const baselineFile2 = baselinePath(project2);
|
|
2667
2859
|
const check = await runCheck(project2, { baseline: await readTextOrNull(baselineFile2) === null ? null : baselineFile2 });
|
|
2668
2860
|
const comparison = check.baseline?.comparison;
|
|
2669
2861
|
const counted = comparison ? [...comparison.new, ...comparison.regressed] : check.violations;
|
|
2670
2862
|
const usages = check.scan.report.usages;
|
|
2671
|
-
const { RequestStore } = await import("./src-
|
|
2863
|
+
const { RequestStore } = await import("./src-6SNQVIZS.js");
|
|
2672
2864
|
const requests = await new RequestStore(project2.root, project2.config.id).list();
|
|
2673
2865
|
const status = {
|
|
2674
2866
|
id: project2.config.id,
|
|
@@ -2686,7 +2878,7 @@ async function projectStatus(project2, cwd = process.cwd()) {
|
|
|
2686
2878
|
components: { system: project2.system.components.length, used: new Set(usages.map((u) => u.element)).size, sites: usages.length },
|
|
2687
2879
|
requests: { open: requests.requests.filter((r) => r.status === "open").length, addressed: requests.requests.filter((r) => r.status === "addressed").length, unreadable: requests.unreadable.length }
|
|
2688
2880
|
};
|
|
2689
|
-
return { ...status, next: nextStep(status) };
|
|
2881
|
+
return { ...status, next: nextStep(status, lock ? pendingMigrations(project2, lock).length : 0) };
|
|
2690
2882
|
}
|
|
2691
2883
|
async function statusTargets(cwd, named2) {
|
|
2692
2884
|
if (named2) return { roots: [resolve10(named2)], workspace: null };
|
|
@@ -2694,7 +2886,7 @@ async function statusTargets(cwd, named2) {
|
|
|
2694
2886
|
if (product2) return { roots: [product2], workspace: null };
|
|
2695
2887
|
const at = findUp(cwd, WORKSPACE_FILE);
|
|
2696
2888
|
if (at) {
|
|
2697
|
-
const file =
|
|
2889
|
+
const file = join17(at, WORKSPACE_FILE);
|
|
2698
2890
|
const workspace = await readDefinition(file, WorkspaceConfig);
|
|
2699
2891
|
return { roots: workspace.projects.map((p) => resolve10(at, p)), workspace: file };
|
|
2700
2892
|
}
|
|
@@ -2716,7 +2908,7 @@ async function workspaceStatus(cwd, named2) {
|
|
|
2716
2908
|
projects.push(await projectStatus(await loadProject(root), cwd));
|
|
2717
2909
|
} catch (error) {
|
|
2718
2910
|
const label = placeOf(cwd, root);
|
|
2719
|
-
projects.push({ root: label, error: error instanceof Error ? error.message : String(error), next: await readTextOrNull(
|
|
2911
|
+
projects.push({ root: label, error: error instanceof Error ? error.message : String(error), next: await readTextOrNull(join17(root, PROJECT_FILE)) === null ? `youiyoux adopt or youiyoux init --design <dir> in ${label}` : `fix ${join17(label, PROJECT_FILE)}` });
|
|
2720
2912
|
}
|
|
2721
2913
|
}
|
|
2722
2914
|
return { schema: "youi.status/v1", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), workspace: workspace ? relative8(cwd, workspace) || WORKSPACE_FILE : null, projects };
|
|
@@ -2813,14 +3005,14 @@ async function startWorkspaceHub(options) {
|
|
|
2813
3005
|
}
|
|
2814
3006
|
|
|
2815
3007
|
// ../cli/src/index-store.ts
|
|
2816
|
-
import { appendFile, mkdir as mkdir4, readFile as readFile6, stat as stat2, rename as rename2, mkdtemp as mkdtemp4, rm as
|
|
2817
|
-
import { dirname as dirname10, join as
|
|
3008
|
+
import { appendFile, mkdir as mkdir4, readFile as readFile6, stat as stat2, rename as rename2, mkdtemp as mkdtemp4, rm as rm6 } from "node:fs/promises";
|
|
3009
|
+
import { dirname as dirname10, join as join18, resolve as resolve11 } from "node:path";
|
|
2818
3010
|
var SOURCES_FILE = "index-sources.json";
|
|
2819
3011
|
var SCANS_LOG = "scans.jsonl";
|
|
2820
3012
|
var EMPTY = { discover: null, inventories: [], projects: [] };
|
|
2821
3013
|
async function readIndexSources(home) {
|
|
2822
3014
|
try {
|
|
2823
|
-
const raw = JSON.parse(await readFile6(
|
|
3015
|
+
const raw = JSON.parse(await readFile6(join18(home, SOURCES_FILE), "utf8"));
|
|
2824
3016
|
return { discover: raw.discover ?? null, inventories: raw.inventories ?? [], projects: raw.projects ?? [] };
|
|
2825
3017
|
} catch {
|
|
2826
3018
|
return EMPTY;
|
|
@@ -2833,14 +3025,14 @@ async function noteSource(home, change) {
|
|
|
2833
3025
|
inventories: change.inventory && !sources.inventories.includes(change.inventory) ? [...sources.inventories, change.inventory] : sources.inventories,
|
|
2834
3026
|
projects: change.project ? [...sources.projects.filter((p) => p.root !== change.project.root), change.project] : sources.projects
|
|
2835
3027
|
};
|
|
2836
|
-
await writeFileAtomic(
|
|
3028
|
+
await writeFileAtomic(join18(home, SOURCES_FILE), `${JSON.stringify(next2, null, 2)}
|
|
2837
3029
|
`);
|
|
2838
3030
|
}
|
|
2839
3031
|
async function recordInventory(store, home, report, file) {
|
|
2840
3032
|
const run7 = inventoryRun(report, resolve11(file));
|
|
2841
3033
|
const logged = { run: run7.run, source: run7.source, scannedAt: run7.scannedAt, projects: run7.projects, families: run7.families };
|
|
2842
3034
|
await mkdir4(home, { recursive: true });
|
|
2843
|
-
await appendFile(
|
|
3035
|
+
await appendFile(join18(home, SCANS_LOG), `${JSON.stringify({ schema: "youi.scan-log/v1", ...logged })}
|
|
2844
3036
|
`);
|
|
2845
3037
|
await store.recordInventory(run7);
|
|
2846
3038
|
await noteSource(home, { inventory: run7.source });
|
|
@@ -2851,7 +3043,7 @@ async function recordProject(store, project2, usage) {
|
|
|
2851
3043
|
await store.replaceGraph(`project:${project2.root}`, projectGraph({ project: project2.config.id, rules: project2.system.rules, components: project2.system.components.map((c) => ({ id: c.contract.id, name: c.contract.name })), usage }));
|
|
2852
3044
|
}
|
|
2853
3045
|
async function readLog(home) {
|
|
2854
|
-
const text = await readFile6(
|
|
3046
|
+
const text = await readFile6(join18(home, SCANS_LOG), "utf8").catch(() => "");
|
|
2855
3047
|
return text.split("\n").flatMap((line) => {
|
|
2856
3048
|
if (!line.trim()) return [];
|
|
2857
3049
|
try {
|
|
@@ -2868,7 +3060,7 @@ async function rebuildIndex(path, options = {}) {
|
|
|
2868
3060
|
const home = indexHome(path, env);
|
|
2869
3061
|
await mkdir4(dirname10(path), { recursive: true });
|
|
2870
3062
|
const scratch = !env.DATABASE_URL ? await mkdtemp4(`${path}.rebuild-`) : null;
|
|
2871
|
-
const target = scratch ?
|
|
3063
|
+
const target = scratch ? join18(scratch, "index.db") : path;
|
|
2872
3064
|
const store = scratch ? await openSqlite(target) : await sharedStore(path, env);
|
|
2873
3065
|
try {
|
|
2874
3066
|
if (env.DATABASE_URL) await store.reset();
|
|
@@ -2876,7 +3068,7 @@ async function rebuildIndex(path, options = {}) {
|
|
|
2876
3068
|
const sources = await readIndexSources(home);
|
|
2877
3069
|
const cache = options.discoverCache ?? sources.discover;
|
|
2878
3070
|
let discover = { cache, chosen: 0, all: 0 };
|
|
2879
|
-
if (cache && await stat2(
|
|
3071
|
+
if (cache && await stat2(join18(cache, "index.json")).then(() => true, () => false)) {
|
|
2880
3072
|
const loaded = await ingestCatalog(store, cache);
|
|
2881
3073
|
discover = { cache, chosen: loaded.chosen, all: loaded.all };
|
|
2882
3074
|
}
|
|
@@ -2904,7 +3096,7 @@ async function rebuildIndex(path, options = {}) {
|
|
|
2904
3096
|
}
|
|
2905
3097
|
try {
|
|
2906
3098
|
const project2 = await loadProject(source.root, source.options);
|
|
2907
|
-
const usage = ScanReport.safeParse(await readJson(source.usage ??
|
|
3099
|
+
const usage = ScanReport.safeParse(await readJson(source.usage ?? join18(project2.root, ".youi/usage.json")));
|
|
2908
3100
|
await recordProject(store, project2, usage.success ? usage.data : null);
|
|
2909
3101
|
projects++;
|
|
2910
3102
|
} catch (error) {
|
|
@@ -2939,7 +3131,7 @@ async function rebuildIndex(path, options = {}) {
|
|
|
2939
3131
|
} finally {
|
|
2940
3132
|
if (scratch) {
|
|
2941
3133
|
await store.close();
|
|
2942
|
-
await
|
|
3134
|
+
await rm6(scratch, { recursive: true, force: true });
|
|
2943
3135
|
}
|
|
2944
3136
|
}
|
|
2945
3137
|
}
|
|
@@ -3123,7 +3315,7 @@ function renderAdoptionRequest(request) {
|
|
|
3123
3315
|
// ../cli/src/report/run.ts
|
|
3124
3316
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
3125
3317
|
import { mkdir as mkdir5, rename as rename3, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
3126
|
-
import { join as
|
|
3318
|
+
import { join as join25, relative as relative10, resolve as resolve14 } from "node:path";
|
|
3127
3319
|
|
|
3128
3320
|
// ../cli/src/report/card.ts
|
|
3129
3321
|
import { deflateSync } from "node:zlib";
|
|
@@ -3359,7 +3551,7 @@ function drawCard(input) {
|
|
|
3359
3551
|
|
|
3360
3552
|
// ../cli/src/report/model.ts
|
|
3361
3553
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
3362
|
-
import { join as
|
|
3554
|
+
import { join as join22 } from "node:path";
|
|
3363
3555
|
|
|
3364
3556
|
// ../cli/src/report/filters.ts
|
|
3365
3557
|
var SHOWCASE_GLOBS = ["**/{__stories__,.storybook,storybook,storybook-static,example,examples,demo,demos,playground,showcase,showcases}/**", "**/*.stories.*", "**/*.story.*"];
|
|
@@ -3486,11 +3678,11 @@ function publicLook(caption) {
|
|
|
3486
3678
|
|
|
3487
3679
|
// ../cli/src/report/groups.ts
|
|
3488
3680
|
import { existsSync as existsSync3 } from "node:fs";
|
|
3489
|
-
import { join as
|
|
3681
|
+
import { join as join20 } from "node:path";
|
|
3490
3682
|
|
|
3491
3683
|
// ../cli/src/report/imports.ts
|
|
3492
3684
|
import { realpathSync } from "node:fs";
|
|
3493
|
-
import { dirname as dirname11, join as
|
|
3685
|
+
import { dirname as dirname11, join as join19, relative as relative9, resolve as resolve12 } from "node:path";
|
|
3494
3686
|
import ts from "typescript";
|
|
3495
3687
|
var resolvers = /* @__PURE__ */ new Map();
|
|
3496
3688
|
function resolverOf(root) {
|
|
@@ -3509,13 +3701,13 @@ function clearImportCache() {
|
|
|
3509
3701
|
}
|
|
3510
3702
|
async function verifiedImport(root, keeper, from, suggested) {
|
|
3511
3703
|
const { options, cache } = resolverOf(root);
|
|
3512
|
-
let local = relative9(dirname11(
|
|
3704
|
+
let local = relative9(dirname11(join19(root, from)), join19(root, keeper)).replaceAll("\\", "/").replace(/\.(tsx?|jsx?|mjs)$/, "");
|
|
3513
3705
|
if (!local.startsWith(".")) local = `./${local}`;
|
|
3514
3706
|
for (const specifier of /* @__PURE__ */ new Set([suggested, local])) {
|
|
3515
3707
|
const found = ts.resolveModuleName(specifier, resolve12(root, from), options, ts.sys, cache).resolvedModule?.resolvedFileName;
|
|
3516
3708
|
if (found) {
|
|
3517
3709
|
try {
|
|
3518
|
-
if (realpathSync(found) === realpathSync(
|
|
3710
|
+
if (realpathSync(found) === realpathSync(join19(root, keeper))) return specifier;
|
|
3519
3711
|
} catch {
|
|
3520
3712
|
}
|
|
3521
3713
|
}
|
|
@@ -3923,7 +4115,7 @@ function factsOf(project2, id, modules, eligible, workspaceNames, dependencies,
|
|
|
3923
4115
|
if (dirs.has(file.slice(0, at))) break;
|
|
3924
4116
|
dirs.add(file.slice(0, at));
|
|
3925
4117
|
}
|
|
3926
|
-
const roots = [...dirs].filter((dir) => existsSync3(
|
|
4118
|
+
const roots = [...dirs].filter((dir) => existsSync3(join20(project2.root, dir, "package.json"))).sort((a, b) => b.length - a.length);
|
|
3927
4119
|
const nested = (file) => roots.find((dir) => file.startsWith(`${dir}/`)) ?? "";
|
|
3928
4120
|
return { project: project2, id, dependencies, packageImports, jsxCounts, eligible, twins, dirs, importer, packageOf: nested };
|
|
3929
4121
|
}
|
|
@@ -3931,11 +4123,11 @@ function factsOf(project2, id, modules, eligible, workspaceNames, dependencies,
|
|
|
3931
4123
|
// ../cli/src/report/projects.ts
|
|
3932
4124
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
3933
4125
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
3934
|
-
import { basename as basename4, join as
|
|
4126
|
+
import { basename as basename4, join as join21, resolve as resolve13 } from "node:path";
|
|
3935
4127
|
var UI_FILES = ["**/*.{tsx,jsx,astro,vue,svelte}"];
|
|
3936
4128
|
var strings = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
3937
4129
|
async function workspaceGlobs(root, pkg) {
|
|
3938
|
-
const pnpm = await readFile7(
|
|
4130
|
+
const pnpm = await readFile7(join21(root, "pnpm-workspace.yaml"), "utf8").catch(() => null);
|
|
3939
4131
|
if (pnpm !== null) {
|
|
3940
4132
|
try {
|
|
3941
4133
|
const globs2 = strings((0, import_yaml3.parse)(pnpm)?.packages);
|
|
@@ -3950,7 +4142,7 @@ async function workspaceGlobs(root, pkg) {
|
|
|
3950
4142
|
var hasUi = async (dir) => (await findFiles(dir, UI_FILES, DEFAULT_EXCLUDE)).length > 0;
|
|
3951
4143
|
async function findProjects(dir) {
|
|
3952
4144
|
const root = resolve13(dir);
|
|
3953
|
-
const pkg = await readJson(
|
|
4145
|
+
const pkg = await readJson(join21(root, "package.json"));
|
|
3954
4146
|
const name = typeof pkg?.name === "string" && pkg.name.trim() ? pkg.name.trim() : basename4(root);
|
|
3955
4147
|
const single = { dir: ".", root, name: typeof pkg?.name === "string" ? pkg.name : null };
|
|
3956
4148
|
const workspace = await workspaceGlobs(root, pkg);
|
|
@@ -3961,12 +4153,12 @@ async function findProjects(dir) {
|
|
|
3961
4153
|
const projects = [];
|
|
3962
4154
|
const skipped = [];
|
|
3963
4155
|
for (const member of members) {
|
|
3964
|
-
const memberRoot =
|
|
4156
|
+
const memberRoot = join21(root, member);
|
|
3965
4157
|
if (!await hasUi(memberRoot)) {
|
|
3966
4158
|
skipped.push(member);
|
|
3967
4159
|
continue;
|
|
3968
4160
|
}
|
|
3969
|
-
const memberPkg = await readJson(
|
|
4161
|
+
const memberPkg = await readJson(join21(memberRoot, "package.json"));
|
|
3970
4162
|
projects.push({ dir: member, root: memberRoot, name: typeof memberPkg?.name === "string" ? memberPkg.name : null });
|
|
3971
4163
|
}
|
|
3972
4164
|
if (projects.length === 0) return { root, name, layout: `one project (${workspace.source} names no package with UI files)`, projects: [single], skipped };
|
|
@@ -3993,7 +4185,7 @@ var Lines = class {
|
|
|
3993
4185
|
if (!this.enabled) return { at };
|
|
3994
4186
|
let lines = this.files.get(file);
|
|
3995
4187
|
if (!lines) {
|
|
3996
|
-
lines = (await readFile8(
|
|
4188
|
+
lines = (await readFile8(join22(this.root, file), "utf8").catch(() => "")).split("\n");
|
|
3997
4189
|
this.files.set(file, lines);
|
|
3998
4190
|
}
|
|
3999
4191
|
const code = lines[line - 1]?.trim();
|
|
@@ -4013,7 +4205,7 @@ async function modulesOf(project2, target, failed2) {
|
|
|
4013
4205
|
for (const file of target.files) {
|
|
4014
4206
|
if (isShowcase(file)) continue;
|
|
4015
4207
|
try {
|
|
4016
|
-
const module = await parseModule(file, await readFile8(
|
|
4208
|
+
const module = await parseModule(file, await readFile8(join22(project2.root, file), "utf8"));
|
|
4017
4209
|
if (module) modules.push(module);
|
|
4018
4210
|
} catch (error) {
|
|
4019
4211
|
failed2.push(`${repoPath(project2.dir, file)}: ${errorText(error)}`);
|
|
@@ -4117,7 +4309,7 @@ async function analyseProject(project2, index, inventory, inventoryError, worksp
|
|
|
4117
4309
|
incomplete.push(`Library choices were not compared (${errorText(error)}); run ${NPX} detect ${project2.dir} to see why`);
|
|
4118
4310
|
}
|
|
4119
4311
|
}
|
|
4120
|
-
const pkg = await readFile8(
|
|
4312
|
+
const pkg = await readFile8(join22(project2.root, "package.json"), "utf8").then((text) => JSON.parse(text), () => null);
|
|
4121
4313
|
const dependencies = new Set(["dependencies", "devDependencies", "peerDependencies"].flatMap((key2) => Object.keys(pkg?.[key2] ?? {})));
|
|
4122
4314
|
const importer = changeProject ? await importOf(changeProject) : () => "";
|
|
4123
4315
|
const facts = factsOf(project2, projectId ?? null, modules, eligible, workspaceNames, dependencies, target?.files ?? [], importer);
|
|
@@ -4561,7 +4753,7 @@ ${model.projects.map((p) => projectSection(p, many, looks)).join("\n")}
|
|
|
4561
4753
|
import { spawn as spawn2 } from "node:child_process";
|
|
4562
4754
|
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "node:fs";
|
|
4563
4755
|
import { homedir as homedir2 } from "node:os";
|
|
4564
|
-
import { dirname as dirname12, join as
|
|
4756
|
+
import { dirname as dirname12, join as join23 } from "node:path";
|
|
4565
4757
|
var LOOKS_PER_PROJECT = 4;
|
|
4566
4758
|
var DOWNLOAD_SIZE = "~100 MB";
|
|
4567
4759
|
var DEPENDENCIES = "Install the repo's dependencies to see rendered looks";
|
|
@@ -4569,26 +4761,26 @@ var UNDRAWABLE = /not installed|loads no stylesheet|drawn from (?:its|their) (?:
|
|
|
4569
4761
|
var playwrightDir = () => dirname12(playwrightCli());
|
|
4570
4762
|
function browsersPath(env) {
|
|
4571
4763
|
const set = env.PLAYWRIGHT_BROWSERS_PATH;
|
|
4572
|
-
if (set === "0") return
|
|
4764
|
+
if (set === "0") return join23(playwrightDir(), ".local-browsers");
|
|
4573
4765
|
if (set) return set;
|
|
4574
|
-
if (process.platform === "darwin") return
|
|
4575
|
-
if (process.platform === "win32") return
|
|
4576
|
-
return
|
|
4766
|
+
if (process.platform === "darwin") return join23(homedir2(), "Library/Caches/ms-playwright");
|
|
4767
|
+
if (process.platform === "win32") return join23(env.LOCALAPPDATA ?? join23(homedir2(), "AppData/Local"), "ms-playwright");
|
|
4768
|
+
return join23(env.XDG_CACHE_HOME ?? join23(homedir2(), ".cache"), "ms-playwright");
|
|
4577
4769
|
}
|
|
4578
4770
|
function chromiumStatus(env = process.env) {
|
|
4579
4771
|
if (env.YOUI_CHROMIUM) return { available: existsSync4(env.YOUI_CHROMIUM), where: env.YOUI_CHROMIUM };
|
|
4580
4772
|
try {
|
|
4581
|
-
const browsers = JSON.parse(readFileSync2(
|
|
4773
|
+
const browsers = JSON.parse(readFileSync2(join23(playwrightDir(), "browsers.json"), "utf8"));
|
|
4582
4774
|
const revision2 = browsers.browsers.find((b) => b.name === "chromium-headless-shell")?.revision;
|
|
4583
|
-
const dir =
|
|
4584
|
-
return { available: revision2 !== void 0 && existsSync4(
|
|
4775
|
+
const dir = join23(browsersPath(env), `chromium_headless_shell-${revision2}`);
|
|
4776
|
+
return { available: revision2 !== void 0 && existsSync4(join23(dir, "INSTALLATION_COMPLETE")), where: dir };
|
|
4585
4777
|
} catch {
|
|
4586
4778
|
return { available: false, where: "playwright-core is not installed" };
|
|
4587
4779
|
}
|
|
4588
4780
|
}
|
|
4589
4781
|
function downloadChromium() {
|
|
4590
4782
|
return new Promise((resolve16) => {
|
|
4591
|
-
const child = spawn2(process.execPath, [
|
|
4783
|
+
const child = spawn2(process.execPath, [join23(playwrightDir(), "cli.js"), "install", "chromium-headless-shell"], { stdio: ["ignore", 2, 2] });
|
|
4592
4784
|
child.on("error", () => resolve16(false));
|
|
4593
4785
|
child.on("close", (code) => resolve16(code === 0));
|
|
4594
4786
|
});
|
|
@@ -4604,9 +4796,9 @@ async function addLooks(model, repoRoot, options = {}) {
|
|
|
4604
4796
|
const where2 = section.dir === "." ? "this repo" : section.dir;
|
|
4605
4797
|
try {
|
|
4606
4798
|
const installed = (name) => {
|
|
4607
|
-
let dir =
|
|
4799
|
+
let dir = join23(repoRoot, section.dir);
|
|
4608
4800
|
while (true) {
|
|
4609
|
-
if (existsSync4(
|
|
4801
|
+
if (existsSync4(join23(dir, "node_modules", name, "package.json"))) return true;
|
|
4610
4802
|
if (dir === repoRoot || dirname12(dir) === dir) return false;
|
|
4611
4803
|
dir = dirname12(dir);
|
|
4612
4804
|
}
|
|
@@ -4615,13 +4807,13 @@ async function addLooks(model, repoRoot, options = {}) {
|
|
|
4615
4807
|
missing.push(where2);
|
|
4616
4808
|
continue;
|
|
4617
4809
|
}
|
|
4618
|
-
const projectRoot =
|
|
4810
|
+
const projectRoot = join23(repoRoot, section.dir);
|
|
4619
4811
|
const target = await loadInventoryTarget(projectRoot);
|
|
4620
4812
|
if (findStylesheets(projectRoot, target.files, { tsconfig: target.tsconfig, override: target.specimenCss, templates: target.templates }).found.length === 0) {
|
|
4621
4813
|
missing.push(where2);
|
|
4622
4814
|
continue;
|
|
4623
4815
|
}
|
|
4624
|
-
const report = await renderShots(
|
|
4816
|
+
const report = await renderShots(join23(repoRoot, section.dir), names, { download: false, base: "HEAD", ...options.executablePath ? { executablePath: options.executablePath } : {}, ...options.cache ? { cache: options.cache } : {}, ...options.log ? { log: options.log } : {} });
|
|
4625
4817
|
const unavailable = report.notes.some((note) => UNDRAWABLE.test(note));
|
|
4626
4818
|
if (unavailable) {
|
|
4627
4819
|
missing.push(where2);
|
|
@@ -4693,7 +4885,7 @@ function renderMarkdown(model, reportFile) {
|
|
|
4693
4885
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
4694
4886
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
4695
4887
|
import { homedir as homedir3, userInfo as userInfo2 } from "node:os";
|
|
4696
|
-
import { join as
|
|
4888
|
+
import { join as join24 } from "node:path";
|
|
4697
4889
|
var REDACTED = "[redacted]";
|
|
4698
4890
|
var OUTSIDE = "[path outside the repo]";
|
|
4699
4891
|
var MIN_VALUE = 6;
|
|
@@ -4705,7 +4897,7 @@ async function dotenvValues(root) {
|
|
|
4705
4897
|
const files = await findFiles(root, [".env", ".env.*", "**/.env", "**/.env.*"]).catch(() => []);
|
|
4706
4898
|
const values = [];
|
|
4707
4899
|
for (const file of files) {
|
|
4708
|
-
const text = await readFile9(
|
|
4900
|
+
const text = await readFile9(join24(root, file), "utf8").catch(() => "");
|
|
4709
4901
|
for (const line of text.split(/\r?\n/)) {
|
|
4710
4902
|
const match = /^\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_.-]*\s*=\s*(.*)$/.exec(line);
|
|
4711
4903
|
if (!match) continue;
|
|
@@ -4878,7 +5070,7 @@ async function runReport(options = {}, io = {}) {
|
|
|
4878
5070
|
const clean = redactor.model(model);
|
|
4879
5071
|
const outDir = resolve14(options.out ?? root);
|
|
4880
5072
|
await mkdir5(outDir, { recursive: true });
|
|
4881
|
-
const files = { report:
|
|
5073
|
+
const files = { report: join25(outDir, REPORT_FILE), card: join25(outDir, CARD_FILE), summary: join25(outDir, SUMMARY_FILE) };
|
|
4882
5074
|
const t = clean.totals;
|
|
4883
5075
|
const card = {
|
|
4884
5076
|
repo: redactor.scrub(clean.repo),
|
|
@@ -5028,6 +5220,12 @@ ${style.bold("System")} ${style.dim("approved elements: build only with these")
|
|
|
5028
5220
|
plan <component> --patch <json|file> classify a contract change and show its impact
|
|
5029
5221
|
plan --token <id> [--value v] impact of a token change
|
|
5030
5222
|
migrate <migration.yaml> [--write] [--diff] gated codemod for props and renamed or removed exports
|
|
5223
|
+
upgrade [--to ref] [--write] [--diff]
|
|
5224
|
+
move to the System's new release in one step: the sync, and each component's
|
|
5225
|
+
migrations past the version youi.lock.json records, run as gated dry runs on
|
|
5226
|
+
the components as the sync would bring them; a preview unless --write, which
|
|
5227
|
+
writes all of it or, if a step fails, puts every file back. --to moves a git
|
|
5228
|
+
source to another tag or branch (written to the config's source.ref)
|
|
5031
5229
|
(imports, re-exports, uses); a dry run that prints the diff and the sites
|
|
5032
5230
|
left for a person (file:line, why) unless --write; --diff shows it after --write too
|
|
5033
5231
|
rules [--preview <rule.yaml>] whether each rule is checked; preview a draft rule on the current scan
|
|
@@ -5143,6 +5341,7 @@ var OPTIONS = {
|
|
|
5143
5341
|
inventory: { type: "string" },
|
|
5144
5342
|
package: { type: "string" },
|
|
5145
5343
|
fork: { type: "boolean", default: false },
|
|
5344
|
+
to: { type: "string" },
|
|
5146
5345
|
cache: { type: "string" },
|
|
5147
5346
|
role: { type: "string" },
|
|
5148
5347
|
license: { type: "string" },
|
|
@@ -5180,7 +5379,7 @@ async function readJsonOrFile(value) {
|
|
|
5180
5379
|
async function cmdScan(values) {
|
|
5181
5380
|
const p = await project(values);
|
|
5182
5381
|
const { report } = await scanProject(p);
|
|
5183
|
-
const out = values.out ??
|
|
5382
|
+
const out = values.out ?? join26(p.root, ".youi/usage.json");
|
|
5184
5383
|
await writeFileAtomic(resolve15(out), `${JSON.stringify(report)}
|
|
5185
5384
|
`);
|
|
5186
5385
|
const recorded = await recordUsage(values, p, report, resolve15(out));
|
|
@@ -5227,7 +5426,7 @@ async function cmdInventory(values) {
|
|
|
5227
5426
|
if (values.history) return cmdHistory(values, []);
|
|
5228
5427
|
const dirs = inventoryDirs(values);
|
|
5229
5428
|
const report = await scanInventory(values, dirs);
|
|
5230
|
-
const out = resolve15(values.out ??
|
|
5429
|
+
const out = resolve15(values.out ?? join26(dirs[0], ".youi/inventory.json"));
|
|
5231
5430
|
await writeFileAtomic(out, `${JSON.stringify(report, null, 2)}
|
|
5232
5431
|
`);
|
|
5233
5432
|
const recorded = await recordScan(values, report, out);
|
|
@@ -5257,7 +5456,7 @@ async function cmdIndex(values, positionals) {
|
|
|
5257
5456
|
const [action, ...rest] = positionals;
|
|
5258
5457
|
const { path, home } = indexOf(values);
|
|
5259
5458
|
if (action === "path") {
|
|
5260
|
-
if (values.json) json3({ store: storeLocation({ path }), sources:
|
|
5459
|
+
if (values.json) json3({ store: storeLocation({ path }), sources: join26(home, SOURCES_FILE), scans: join26(home, SCANS_LOG) });
|
|
5261
5460
|
else print(storeLocation({ path }));
|
|
5262
5461
|
return 0;
|
|
5263
5462
|
}
|
|
@@ -5428,15 +5627,23 @@ async function cmdLibrary(values, positionals) {
|
|
|
5428
5627
|
async function cmdSync(values) {
|
|
5429
5628
|
const p = await loadProject(values.project ?? process.cwd(), { ...loadOptions(values), refreshDesign: true });
|
|
5430
5629
|
const result = await syncProject(p, { dryRun: values["dry-run"], adopt: values.adopt });
|
|
5431
|
-
if (values.json) json3(result);
|
|
5630
|
+
if (values.json) json3({ actions: result.actions, lock: result.lock, unknownTools: result.unknownTools });
|
|
5432
5631
|
else print([renderSync(result, values["dry-run"]), ...p.linked ? [style.warn(`! linked: the design system is your checkout ${p.linked}, not the config's source (youiyoux unlink to undo)`)] : []].join("\n"));
|
|
5433
5632
|
return result.actions.some((a) => a.action === "conflict") ? 1 : 0;
|
|
5434
5633
|
}
|
|
5634
|
+
async function cmdUpgrade(values) {
|
|
5635
|
+
if (values.to !== void 0 && !/^[\w][\w./-]*$/.test(values.to)) throw new UsageError(`--to ${JSON.stringify(values.to)}: expected a git tag, branch or commit`);
|
|
5636
|
+
const p = await loadProject(values.project ?? process.cwd(), { ...loadOptions(values), refreshDesign: true, ...values.to !== void 0 ? { ref: values.to } : {} });
|
|
5637
|
+
const result = await upgradeProject(p, { write: values.write, ...values.to !== void 0 ? { to: values.to } : {} });
|
|
5638
|
+
if (values.json) json3(result);
|
|
5639
|
+
else print([renderUpgrade(result, values.diff || result.status === "preview"), ...p.linked ? [style.warn(`! linked: the design system is your checkout ${p.linked}, not the config's source (youiyoux unlink to undo)`)] : []].join("\n"));
|
|
5640
|
+
return result.status === "blocked" ? 1 : 0;
|
|
5641
|
+
}
|
|
5435
5642
|
async function cmdInit(values) {
|
|
5436
5643
|
if (!values.design) throw new UsageError("youiyoux init --design <dir|package> (a design repo checkout, the directory holding youi.config.json, or the npm package that holds it)");
|
|
5437
5644
|
const root = values.project ?? process.cwd();
|
|
5438
5645
|
const design = await designArg(values.design, root);
|
|
5439
|
-
const result = await initProject({ root, ...design, schemaDir:
|
|
5646
|
+
const result = await initProject({ root, ...design, schemaDir: join26(TOOL_ROOT, "schema"), ...values.system ? { system: values.system } : {}, ...values.components ? { componentsDir: values.components } : {}, force: values.force });
|
|
5440
5647
|
if (!values.json) {
|
|
5441
5648
|
print(`${style.ok("created")} ${result.file}`);
|
|
5442
5649
|
for (const note of result.notes) print(style.dim(` \xB7 ${note}`));
|
|
@@ -5447,27 +5654,27 @@ async function cmdInit(values) {
|
|
|
5447
5654
|
}
|
|
5448
5655
|
async function cmdAdopt(values) {
|
|
5449
5656
|
const root = resolve15(values.project ?? process.cwd());
|
|
5450
|
-
if (await exists(
|
|
5657
|
+
if (await exists(join26(root, PROJECT_FILE))) throw new Error(`${root} already has ${PROJECT_FILE}: it follows a design system already (youiyoux sync updates it)`);
|
|
5451
5658
|
const plan = await planAdoption({ root, ...values.components ? { componentsDir: values.components } : {}, ...values.css ? { css: values.css } : {}, ...values.system ? { system: values.system } : {}, fork: values.fork });
|
|
5452
|
-
const taken = (await Promise.all(plan.files.map(async (f) => await exists(
|
|
5659
|
+
const taken = (await Promise.all(plan.files.map(async (f) => await exists(join26(root, f.path)) ? f.path : null))).filter((f) => f !== null);
|
|
5453
5660
|
if (taken.length > 0) throw new Error(`nothing was written: ${taken.slice(0, 5).join(", ")}${taken.length > 5 ? ` and ${taken.length - 5} more` : ""} already exist${taken.length === 1 ? "s" : ""}`);
|
|
5454
5661
|
if (values["dry-run"]) {
|
|
5455
5662
|
if (values.json) json3(plan);
|
|
5456
5663
|
else print([...adoptLines(plan, true), "", style.dim(`dry run: nothing written; would write ${plan.files.map((f) => f.path).join(", ")}${plan.stylesheet ? ` and change ${plan.stylesheet.path}` : ""}`)].join("\n"));
|
|
5457
5664
|
return 0;
|
|
5458
5665
|
}
|
|
5459
|
-
const stylesheet = plan.stylesheet ? await readFile10(
|
|
5666
|
+
const stylesheet = plan.stylesheet ? await readFile10(join26(root, plan.stylesheet.path), "utf8") : null;
|
|
5460
5667
|
try {
|
|
5461
|
-
for (const file of plan.files) await writeFileAtomic(
|
|
5462
|
-
if (plan.stylesheet) await writeFileAtomic(
|
|
5463
|
-
await initProject({ root, design: root, schemaDir:
|
|
5668
|
+
for (const file of plan.files) await writeFileAtomic(join26(root, file.path), file.content);
|
|
5669
|
+
if (plan.stylesheet) await writeFileAtomic(join26(root, plan.stylesheet.path), plan.stylesheet.content);
|
|
5670
|
+
await initProject({ root, design: root, schemaDir: join26(TOOL_ROOT, "schema"), system: plan.system.id, componentsDir: plan.componentsDir, outputs: { ...plan.outputs, targets: [...plan.outputs.targets], dark: [...plan.outputs.dark] }, tokenSources: plan.tokenSources });
|
|
5464
5671
|
await syncProject(await loadProject(root), { dryRun: true });
|
|
5465
5672
|
} catch (error) {
|
|
5466
|
-
for (const file of [...plan.files.map((f) => f.path), PROJECT_FILE]) await
|
|
5467
|
-
for (const folder of [...new Set(plan.files.map((f) => dirname13(f.path)))].sort((a, b) => b.length - a.length)) await rmdir3(
|
|
5468
|
-
await rmdir3(
|
|
5469
|
-
await rmdir3(
|
|
5470
|
-
if (plan.stylesheet && stylesheet !== null) await writeFileAtomic(
|
|
5673
|
+
for (const file of [...plan.files.map((f) => f.path), PROJECT_FILE]) await rm7(join26(root, file), { force: true });
|
|
5674
|
+
for (const folder of [...new Set(plan.files.map((f) => dirname13(f.path)))].sort((a, b) => b.length - a.length)) await rmdir3(join26(root, folder)).catch(() => void 0);
|
|
5675
|
+
await rmdir3(join26(root, "design/components")).catch(() => void 0);
|
|
5676
|
+
await rmdir3(join26(root, "design")).catch(() => void 0);
|
|
5677
|
+
if (plan.stylesheet && stylesheet !== null) await writeFileAtomic(join26(root, plan.stylesheet.path), stylesheet);
|
|
5471
5678
|
throw new Error(`adopt was undone, nothing kept: ${error instanceof Error ? error.message : String(error)}`);
|
|
5472
5679
|
}
|
|
5473
5680
|
if (!values.json) print([...adoptLines(plan), ""].join("\n"));
|
|
@@ -5541,7 +5748,7 @@ async function cmdUnlink(values) {
|
|
|
5541
5748
|
}
|
|
5542
5749
|
async function designArg(value, root) {
|
|
5543
5750
|
if (existsSync5(resolve15(value)) || !PACKAGE_NAME.test(value)) return { design: value };
|
|
5544
|
-
return { design: await designRootOf({ package: value },
|
|
5751
|
+
return { design: await designRootOf({ package: value }, join26(resolve15(root), PROJECT_FILE), root), package: value };
|
|
5545
5752
|
}
|
|
5546
5753
|
async function cmdBuild(values) {
|
|
5547
5754
|
const root = resolve15(values.design ?? process.cwd());
|
|
@@ -5550,10 +5757,10 @@ async function cmdBuild(values) {
|
|
|
5550
5757
|
const output = OutputConfig.parse({ cssVarPrefix: values.prefix ?? "youi", targets: ["css-vars", "tailwind-v4", "ts", "react-native"] });
|
|
5551
5758
|
const started = performance.now();
|
|
5552
5759
|
const built = await buildTokens(root, system.config, output, { contracts: system.components.map((c) => c.contract) });
|
|
5553
|
-
const out = resolve15(values.out ??
|
|
5554
|
-
const boundary = values.out ? out :
|
|
5555
|
-
if (!within(boundary, out) || built.files.some((file) => !within(out,
|
|
5556
|
-
for (const file of built.files) await writeFileAtomic(
|
|
5760
|
+
const out = resolve15(values.out ?? join26(root, "build", system.config.id));
|
|
5761
|
+
const boundary = values.out ? out : join26(root, "build");
|
|
5762
|
+
if (!within(boundary, out) || built.files.some((file) => !within(out, join26(out, file.path)))) throw new Error(`Cannot build system ${JSON.stringify(system.config.id)}: an output path leaves the output directory.`);
|
|
5763
|
+
for (const file of built.files) await writeFileAtomic(join26(out, file.path), file.content);
|
|
5557
5764
|
if (values.json) json3({ out, files: built.files.map((f) => f.path), tokens: built.tokens.length });
|
|
5558
5765
|
else print(`${style.ok("built")} ${plural(built.tokens.length, "token")} \u2192 ${built.files.map((f) => f.path).join(", ")} ${style.dim(`in ${out} (${ms(performance.now() - started)})`)}`);
|
|
5559
5766
|
return 0;
|
|
@@ -5578,16 +5785,16 @@ async function cmdParity(values) {
|
|
|
5578
5785
|
async function cmdSchema(values) {
|
|
5579
5786
|
const schemas = renderJsonSchemas();
|
|
5580
5787
|
if (values.check) {
|
|
5581
|
-
const dir =
|
|
5788
|
+
const dir = join26(values.design ? resolve15(values.design) : TOOL_ROOT, "schema");
|
|
5582
5789
|
const stale = [];
|
|
5583
|
-
for (const { file, content } of schemas) if (await readFile10(
|
|
5790
|
+
for (const { file, content } of schemas) if (await readFile10(join26(dir, file), "utf8").catch(() => null) !== content) stale.push(file);
|
|
5584
5791
|
if (values.json) json3({ dir, stale });
|
|
5585
5792
|
else print(stale.length === 0 ? `${style.ok("\u2713")} ${dir} matches this youi's schemas` : `${style.error("\u2716")} out of date in ${dir}: ${stale.join(", ")}; run youiyoux schema --write${values.design ? " --design <dir>" : ""}`);
|
|
5586
5793
|
return stale.length === 0 ? 0 : 1;
|
|
5587
5794
|
}
|
|
5588
5795
|
if (values.write) {
|
|
5589
5796
|
const root = values.design ? resolve15(values.design) : TOOL_ROOT;
|
|
5590
|
-
for (const { file, content } of schemas) await writeFileAtomic(
|
|
5797
|
+
for (const { file, content } of schemas) await writeFileAtomic(join26(root, "schema", file), content);
|
|
5591
5798
|
print(`${style.ok("wrote")} ${schemas.map((s) => s.file).join(", ")}`);
|
|
5592
5799
|
} else json3(Object.fromEntries(schemas.map((s) => [s.file, JSON.parse(s.content)])));
|
|
5593
5800
|
return 0;
|
|
@@ -5610,7 +5817,7 @@ async function cmdHooks(values, positionals) {
|
|
|
5610
5817
|
if (action !== "install") throw new UsageError("youiyoux hooks install [--claude] [--git] [--ci [--diff]] [--strict] or youiyoux hooks run claude|pre-commit");
|
|
5611
5818
|
const p = await loadCheckProject(values.project ?? process.cwd(), loadOptions(values), !values.project);
|
|
5612
5819
|
if (values.ci && !configured(p)) throw new UsageError("Run youiyoux init --design <dir> before installing the pinned CI workflow; repo-mode CI can run youiyoux check --diff <base> --strict directly");
|
|
5613
|
-
const target = { ...hookTarget(values), project: p.root, ...configured(p) && p.configFile && p.configFile !==
|
|
5820
|
+
const target = { ...hookTarget(values), project: p.root, ...configured(p) && p.configFile && p.configFile !== join26(p.root, PROJECT_FILE) ? { config: p.configFile } : {}, ...values.root !== void 0 ? { root: p.root } : {} };
|
|
5614
5821
|
const all = !values.claude && !values.git && !values.ci;
|
|
5615
5822
|
const results = [];
|
|
5616
5823
|
if (values.claude || all) results.push({ kind: "Claude Code", ...await installClaudeHook(await repoRootOf(p.root), hookCommand(TOOL_ROOT, "claude", target)) });
|
|
@@ -5629,7 +5836,7 @@ async function cmdAgents(values, positionals) {
|
|
|
5629
5836
|
const repoRoot = await repoRootOf(projectRoot);
|
|
5630
5837
|
const target = { project: projectRoot, ...values.config ? { config: resolve15(values.config) } : {}, ...values.design ? { design: resolve15(values.design) } : {}, ...values.root !== void 0 ? { root: resolve15(values.root || ".") } : {} };
|
|
5631
5838
|
const server = mcpCommand(TOOL_ROOT, target);
|
|
5632
|
-
const system = existsSync5(resolve15(values.config ??
|
|
5839
|
+
const system = existsSync5(resolve15(values.config ?? join26(projectRoot, PROJECT_FILE))) ? await project(values) : null;
|
|
5633
5840
|
const claudeServer = system && system.root === repoRoot ? { command: "node", args: mcpArgs(system) } : relativeTo(repoRoot, server);
|
|
5634
5841
|
const both = !values.claude && !values.codex;
|
|
5635
5842
|
const results = [];
|
|
@@ -5771,7 +5978,7 @@ async function cmdRequests(values, positionals) {
|
|
|
5771
5978
|
const usage = "youiyoux requests [--status open|addressed|closed|all] [--route r] or youiyoux requests show <id> | reply <id> --message <text> | addressed <id> --message <what you changed>";
|
|
5772
5979
|
const [action, id, ...rest] = positionals;
|
|
5773
5980
|
if (rest.length > 0) throw new UsageError(usage);
|
|
5774
|
-
const { RequestStore, requestBrief } = await import("./src-
|
|
5981
|
+
const { RequestStore, requestBrief } = await import("./src-6SNQVIZS.js");
|
|
5775
5982
|
const { project: project2, root } = await product(values);
|
|
5776
5983
|
const store = new RequestStore(root, project2?.config.id ?? basename5(root));
|
|
5777
5984
|
const agent = { kind: "agent", name: "youiyoux CLI" };
|
|
@@ -5804,8 +6011,8 @@ async function cmdScreens(values, positionals, io) {
|
|
|
5804
6011
|
if (!["capture", "login"].includes(positionals[0] ?? "") || positionals.length > 1 || !values.base) throw new UsageError(usage);
|
|
5805
6012
|
const { root } = await product(values);
|
|
5806
6013
|
if (positionals[0] === "login") return screensLogin(values.base, values["storage-state"] ? resolve15(values["storage-state"]) : sessionFile(root), io);
|
|
5807
|
-
const dir = resolve15(values.out ??
|
|
5808
|
-
if (!existsSync5(
|
|
6014
|
+
const dir = resolve15(values.out ?? join26(root, ".youi/screens"));
|
|
6015
|
+
if (!existsSync5(join26(dir, "screens.json"))) {
|
|
5809
6016
|
process.stderr.write(`${style.error("\u2716")} no screens.json in ${dir}: list the routes to capture there, as {"schema":"youiyoux.screens/v1","project":"<name>","source":"<what the app is>","capturedAt":"","viewport":{"width":1440,"height":900},"screens":[{"route":"/","path":"/"}]}
|
|
5810
6017
|
`);
|
|
5811
6018
|
return 1;
|
|
@@ -5870,7 +6077,7 @@ async function cmdReport(values, positionals, io) {
|
|
|
5870
6077
|
return result.code;
|
|
5871
6078
|
}
|
|
5872
6079
|
async function cmdMcp(values) {
|
|
5873
|
-
const { startStdioServer } = await import("./src-
|
|
6080
|
+
const { startStdioServer } = await import("./src-75ODGHE3.js");
|
|
5874
6081
|
await startStdioServer({ projectRoot: values.project ?? process.cwd(), ...loadOptions(values), ...values.cache ? { discoverCache: values.cache } : {} });
|
|
5875
6082
|
return -1;
|
|
5876
6083
|
}
|
|
@@ -5945,6 +6152,7 @@ var COMMANDS = {
|
|
|
5945
6152
|
rules: cmdRules,
|
|
5946
6153
|
plan: cmdPlan,
|
|
5947
6154
|
migrate: cmdMigrate,
|
|
6155
|
+
upgrade: cmdUpgrade,
|
|
5948
6156
|
init: cmdInit,
|
|
5949
6157
|
sync: cmdSync,
|
|
5950
6158
|
scan: cmdScan,
|
|
@@ -5996,7 +6204,7 @@ ${HELP}
|
|
|
5996
6204
|
}
|
|
5997
6205
|
return guarded(() => handler(located(command2, parsed.values), rest, io));
|
|
5998
6206
|
}
|
|
5999
|
-
var IN_A_PRODUCT = /* @__PURE__ */ new Set(["system", "link", "unlink", "show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "sync", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "render", "screens", "requests"]);
|
|
6207
|
+
var IN_A_PRODUCT = /* @__PURE__ */ new Set(["system", "link", "unlink", "show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "upgrade", "sync", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "render", "screens", "requests"]);
|
|
6000
6208
|
var IN_A_DESIGN_REPO = /* @__PURE__ */ new Set(["build", "parity"]);
|
|
6001
6209
|
function located(command2, values, cwd = process.cwd()) {
|
|
6002
6210
|
if (IN_A_PRODUCT.has(command2) && values.project === void 0 && values.config === void 0 && values.root === void 0) {
|
|
@@ -3,7 +3,7 @@ import { createRequire as __youiRequire } from "node:module"; import { dirname a
|
|
|
3
3
|
// src/hub-packages.js
|
|
4
4
|
var PACKAGES = {
|
|
5
5
|
core: () => import("./src-BYNWPNRT.js"),
|
|
6
|
-
scanner: () => import("./src-
|
|
6
|
+
scanner: () => import("./src-XTNHB65M.js"),
|
|
7
7
|
discover: () => import("./src-DPFJ4S2N.js"),
|
|
8
8
|
tokens: () => import("./src-PURAHRCR.js")
|
|
9
9
|
};
|
|
@@ -751,7 +751,13 @@ async function loadProject(root, options = {}) {
|
|
|
751
751
|
const productRoot = options.productRoot !== void 0 ? resolve2(options.productRoot || ".") : config.root ? fromConfig(config.root) : resolve2(root);
|
|
752
752
|
if (!await isDirectory2(productRoot)) throw new ProjectRootError(`product root ${productRoot} is not a directory (config ${configFile}); ${ROOT_HINT}`);
|
|
753
753
|
const linked = options.designRoot ? null : await linkedDesign(productRoot);
|
|
754
|
-
|
|
754
|
+
if (options.ref !== void 0) {
|
|
755
|
+
if (options.designRoot || linked) throw new Error(`the design system is ${options.designRoot ? "the --design folder" : `your checkout ${linked}`}, not the config's source: check out ${options.ref} there${linked ? ", or youiyoux unlink" : ""}`);
|
|
756
|
+
if ("package" in config.source) throw new Error(`${config.source.package} is an npm package: install the version you want (npm install ${config.source.package}@<version>), then run this again without --to`);
|
|
757
|
+
if ("path" in config.source) throw new Error(`the design system is the folder ${config.source.path}: check out ${options.ref} there, then run this again without --to`);
|
|
758
|
+
}
|
|
759
|
+
const source = options.ref !== void 0 && "git" in config.source ? { ...config.source, ref: options.ref } : config.source;
|
|
760
|
+
const located = options.designRoot || linked ? { root: resolve2(options.designRoot || linked), commit: null } : await locateDesign(source, configFile, productRoot, { refresh: options.refreshDesign || options.ref !== void 0 });
|
|
755
761
|
const repo = await loadDesignRepo(located.root);
|
|
756
762
|
return {
|
|
757
763
|
root: productRoot,
|
|
@@ -2408,7 +2414,7 @@ async function parseAll(project, files, overrides, parse3) {
|
|
|
2408
2414
|
}
|
|
2409
2415
|
async function scanProject(project, options = {}) {
|
|
2410
2416
|
const started = performance.now();
|
|
2411
|
-
const overrides = options.overrides
|
|
2417
|
+
const overrides = { ...project.staged, ...options.overrides };
|
|
2412
2418
|
const { cache } = options;
|
|
2413
2419
|
cache?.beginScan();
|
|
2414
2420
|
const parse3 = cache?.parse ?? parseModule;
|
|
@@ -57,8 +57,8 @@ import {
|
|
|
57
57
|
validateContent,
|
|
58
58
|
validateLibraryContent,
|
|
59
59
|
validateStyleContent
|
|
60
|
-
} from "./chunk-
|
|
61
|
-
import "./chunk-
|
|
60
|
+
} from "./chunk-OCZJ3JGH.js";
|
|
61
|
+
import "./chunk-X2YYMHR4.js";
|
|
62
62
|
import "./chunk-42YJX3W5.js";
|
|
63
63
|
import "./chunk-FLBRX6Y3.js";
|
|
64
64
|
import "./chunk-DWYDHVLZ.js";
|
|
@@ -2,7 +2,7 @@ import { createRequire as __youiRequire } from "node:module"; import { dirname a
|
|
|
2
2
|
import {
|
|
3
3
|
RequestStore,
|
|
4
4
|
requestBrief
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OCZJ3JGH.js";
|
|
6
6
|
import {
|
|
7
7
|
PROJECT_FILE,
|
|
8
8
|
ScanCache,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
shotsText,
|
|
31
31
|
tokenRows,
|
|
32
32
|
whereUsed
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-X2YYMHR4.js";
|
|
34
34
|
import "./chunk-42YJX3W5.js";
|
|
35
35
|
import {
|
|
36
36
|
SHADCN_SLOTS,
|
package/dist/hub-previews.js
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
selectCandidates,
|
|
17
17
|
thumbsDir,
|
|
18
18
|
writeThumbIndex
|
|
19
|
-
} from "./chunks/chunk-
|
|
20
|
-
import "./chunks/chunk-
|
|
19
|
+
} from "./chunks/chunk-3RP664IP.js";
|
|
20
|
+
import "./chunks/chunk-T3KNPVQG.js";
|
|
21
21
|
import "./chunks/chunk-FVVL5JCY.js";
|
|
22
22
|
import "./chunks/chunk-DWYDHVLZ.js";
|
|
23
23
|
import "./chunks/chunk-WNTS7ZO4.js";
|
package/dist/hub-server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire as __youiRequire } from "node:module"; import { dirname as __youiDir } from "node:path"; import { fileURLToPath as __youiPath } from "node:url"; const require = __youiRequire(import.meta.url); const __youiDirname = __youiDir(__youiPath(import.meta.url));
|
|
3
3
|
import {
|
|
4
4
|
discoverThumbsMiddleware
|
|
5
|
-
} from "./chunks/chunk-
|
|
5
|
+
} from "./chunks/chunk-3RP664IP.js";
|
|
6
6
|
import {
|
|
7
7
|
isLocalHost,
|
|
8
8
|
loadPackage,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
readTryIndex,
|
|
11
11
|
send,
|
|
12
12
|
tryMiddleware
|
|
13
|
-
} from "./chunks/chunk-
|
|
13
|
+
} from "./chunks/chunk-T3KNPVQG.js";
|
|
14
14
|
import "./chunks/chunk-FVVL5JCY.js";
|
|
15
15
|
import "./chunks/chunk-DWYDHVLZ.js";
|
|
16
16
|
import "./chunks/chunk-WNTS7ZO4.js";
|
package/dist/hub-snapshot.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire as __youiRequire } from "node:module"; import { dirname a
|
|
|
3
3
|
import {
|
|
4
4
|
loadPackage,
|
|
5
5
|
tryProject
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-T3KNPVQG.js";
|
|
7
7
|
import "./chunks/chunk-FVVL5JCY.js";
|
|
8
8
|
import "./chunks/chunk-WNTS7ZO4.js";
|
|
9
9
|
|
package/dist/youi.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { createRequire as __youiRequire } from "node:module"; import { dirname as __youiDir } from "node:path"; import { fileURLToPath as __youiPath } from "node:url"; const require = __youiRequire(import.meta.url); const __youiDirname = __youiDir(__youiPath(import.meta.url));
|
|
3
3
|
import {
|
|
4
4
|
main
|
|
5
|
-
} from "./chunks/chunk-
|
|
6
|
-
import "./chunks/chunk-
|
|
5
|
+
} from "./chunks/chunk-75DBKEAH.js";
|
|
6
|
+
import "./chunks/chunk-X2YYMHR4.js";
|
|
7
7
|
import "./chunks/chunk-42YJX3W5.js";
|
|
8
8
|
import "./chunks/chunk-HDJ2TOWZ.js";
|
|
9
9
|
import "./chunks/chunk-6GITMASZ.js";
|
package/dist/youiyoux.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createRequire as __youiRequire } from "node:module"; import { dirname a
|
|
|
3
3
|
import {
|
|
4
4
|
COMMAND_NAMES,
|
|
5
5
|
main
|
|
6
|
-
} from "./chunks/chunk-
|
|
7
|
-
import "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-75DBKEAH.js";
|
|
7
|
+
import "./chunks/chunk-X2YYMHR4.js";
|
|
8
8
|
import "./chunks/chunk-42YJX3W5.js";
|
|
9
9
|
import "./chunks/chunk-HDJ2TOWZ.js";
|
|
10
10
|
import "./chunks/chunk-6GITMASZ.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "youiyoux",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Your agent rebuilds components you already have. youiyoux catches it: npx youiyoux turns any repo into a shareable report of near-duplicate components, hardcoded token values and off-library imports, with the fixes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"typescript": "6.0.3"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@youiyoux/cli": "0.1.
|
|
50
|
-
"@youiyoux/core": "0.1.
|
|
51
|
-
"@youiyoux/scanner": "0.1.
|
|
49
|
+
"@youiyoux/cli": "0.1.10",
|
|
50
|
+
"@youiyoux/core": "0.1.10",
|
|
51
|
+
"@youiyoux/scanner": "0.1.10"
|
|
52
52
|
},
|
|
53
53
|
"exports": {
|
|
54
54
|
"./package.json": "./package.json"
|