@getcoherent/cli 0.6.10 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CORE_CONSTRAINTS,
|
|
3
3
|
DESIGN_QUALITY,
|
|
4
|
+
DESIGN_QUALITY_COMMON,
|
|
4
5
|
DESIGN_THINKING,
|
|
5
6
|
INTERACTION_PATTERNS,
|
|
6
7
|
VISUAL_DEPTH,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
routeToKey,
|
|
14
15
|
savePlan,
|
|
15
16
|
selectContextualRules
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-CLPILU3Z.js";
|
|
17
18
|
import {
|
|
18
19
|
__require
|
|
19
20
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -1369,10 +1370,10 @@ var ShadcnProvider = class {
|
|
|
1369
1370
|
const componentPath = path.join(projectRoot, "components", "ui", `${name}.tsx`);
|
|
1370
1371
|
if (!force && deps.existsSync(componentPath)) return;
|
|
1371
1372
|
try {
|
|
1372
|
-
await new Promise((
|
|
1373
|
+
await new Promise((resolve17, reject) => {
|
|
1373
1374
|
deps.exec(`npx shadcn@latest add ${name} --yes --overwrite`, { cwd: projectRoot, timeout: 15e3 }, (err) => {
|
|
1374
1375
|
if (err) reject(err);
|
|
1375
|
-
else
|
|
1376
|
+
else resolve17();
|
|
1376
1377
|
});
|
|
1377
1378
|
});
|
|
1378
1379
|
} catch {
|
|
@@ -1413,13 +1414,13 @@ var ShadcnProvider = class {
|
|
|
1413
1414
|
}
|
|
1414
1415
|
if (toInstall.length === 0) return results;
|
|
1415
1416
|
try {
|
|
1416
|
-
await new Promise((
|
|
1417
|
+
await new Promise((resolve17, reject) => {
|
|
1417
1418
|
deps.exec(
|
|
1418
1419
|
`npx shadcn@latest add ${toInstall.join(" ")} --yes --overwrite`,
|
|
1419
1420
|
{ cwd: projectRoot, timeout: 3e4 },
|
|
1420
1421
|
(err) => {
|
|
1421
1422
|
if (err) reject(err);
|
|
1422
|
-
else
|
|
1423
|
+
else resolve17();
|
|
1423
1424
|
}
|
|
1424
1425
|
);
|
|
1425
1426
|
});
|
|
@@ -1931,7 +1932,7 @@ function installPackages(projectRoot, packages) {
|
|
|
1931
1932
|
if (packages.length === 0) return Promise.resolve(true);
|
|
1932
1933
|
const safe = packages.filter((p) => SAFE_PKG_NAME.test(p));
|
|
1933
1934
|
if (safe.length === 0) return Promise.resolve(true);
|
|
1934
|
-
return new Promise((
|
|
1935
|
+
return new Promise((resolve17) => {
|
|
1935
1936
|
try {
|
|
1936
1937
|
const hasPnpm = existsSync4(join3(projectRoot, "pnpm-lock.yaml"));
|
|
1937
1938
|
if (hasPnpm) {
|
|
@@ -1942,10 +1943,10 @@ function installPackages(projectRoot, packages) {
|
|
|
1942
1943
|
stdio: "pipe"
|
|
1943
1944
|
});
|
|
1944
1945
|
}
|
|
1945
|
-
|
|
1946
|
+
resolve17(true);
|
|
1946
1947
|
} catch (e) {
|
|
1947
1948
|
if (process.env.COHERENT_DEBUG === "1") console.error("Failed to install packages:", e);
|
|
1948
|
-
|
|
1949
|
+
resolve17(false);
|
|
1949
1950
|
}
|
|
1950
1951
|
});
|
|
1951
1952
|
}
|
|
@@ -3372,6 +3373,7 @@ function generateV4GlobalsCss(config2) {
|
|
|
3372
3373
|
--color-accent-foreground: var(--accent-foreground);
|
|
3373
3374
|
--color-destructive: var(--destructive);
|
|
3374
3375
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
3376
|
+
--color-transparent: transparent;
|
|
3375
3377
|
--color-border: var(--border);
|
|
3376
3378
|
--color-input: var(--input);
|
|
3377
3379
|
--color-ring: var(--ring);
|
|
@@ -3763,8 +3765,8 @@ async function createAppRouteGroupLayout(projectPath) {
|
|
|
3763
3765
|
// src/commands/chat.ts
|
|
3764
3766
|
import chalk14 from "chalk";
|
|
3765
3767
|
import ora2 from "ora";
|
|
3766
|
-
import { resolve as
|
|
3767
|
-
import { existsSync as
|
|
3768
|
+
import { resolve as resolve10, relative as relative2, join as join11 } from "path";
|
|
3769
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
|
|
3768
3770
|
import {
|
|
3769
3771
|
DesignSystemManager as DesignSystemManager7,
|
|
3770
3772
|
ComponentManager as ComponentManager5,
|
|
@@ -4919,6 +4921,7 @@ function needsGlobalsFix(projectRoot) {
|
|
|
4919
4921
|
if (isTailwindV4(projectRoot)) {
|
|
4920
4922
|
if (!content.includes("@theme inline")) return true;
|
|
4921
4923
|
if (content.includes("@tailwind base")) return true;
|
|
4924
|
+
if (!content.includes("--color-transparent")) return true;
|
|
4922
4925
|
return false;
|
|
4923
4926
|
}
|
|
4924
4927
|
if (content.includes(":root {") || content.includes(".dark {")) return true;
|
|
@@ -6077,19 +6080,12 @@ ${selectImport}`
|
|
|
6077
6080
|
/(<TabsTrigger\b[^>]*className=")([^"]*)(")/g,
|
|
6078
6081
|
(_m, pre, classes, post) => {
|
|
6079
6082
|
const cleaned = classes.replace(/\b(border-input|border\b|outline\b)\s*/g, "").trim();
|
|
6080
|
-
|
|
6081
|
-
if (withBorder0 !== classes.trim()) return `${pre}${withBorder0}${post}`;
|
|
6083
|
+
if (cleaned !== classes.trim()) return `${pre}${cleaned}${post}`;
|
|
6082
6084
|
return _m;
|
|
6083
6085
|
}
|
|
6084
6086
|
);
|
|
6085
|
-
fixed = fixed.replace(/<TabsTrigger\b(?![^>]*className=)(?![^>]*border-0)/g, '<TabsTrigger className="border-0"');
|
|
6086
6087
|
if (fixed !== beforeTabsFix) {
|
|
6087
|
-
fixes.push("
|
|
6088
|
-
}
|
|
6089
|
-
const beforeTabsListFix = fixed;
|
|
6090
|
-
fixed = fixed.replace(/<TabsList\b(?![^>]*variant=)/g, '<TabsList variant="line"');
|
|
6091
|
-
if (fixed !== beforeTabsListFix) {
|
|
6092
|
-
fixes.push('added variant="line" to TabsList (clean underline style)');
|
|
6088
|
+
fixes.push("stripped border from TabsTrigger (shadcn handles active state)");
|
|
6093
6089
|
}
|
|
6094
6090
|
fixed = fixed.replace(/className="([^"]*)"/g, (_match, inner) => {
|
|
6095
6091
|
const cleaned = inner.replace(/\s{2,}/g, " ").trim();
|
|
@@ -6810,6 +6806,8 @@ function applyDefaults(request) {
|
|
|
6810
6806
|
}
|
|
6811
6807
|
|
|
6812
6808
|
// src/commands/chat/split-generator.ts
|
|
6809
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
6810
|
+
import { resolve as resolve6 } from "path";
|
|
6813
6811
|
import { z } from "zod";
|
|
6814
6812
|
import {
|
|
6815
6813
|
loadManifest as loadManifest5,
|
|
@@ -7041,9 +7039,55 @@ ${groupLines.join("\n")}`];
|
|
|
7041
7039
|
if (compLines.length > 0) {
|
|
7042
7040
|
parts.push(`Shared Components:
|
|
7043
7041
|
${compLines.join("\n")}`);
|
|
7042
|
+
}
|
|
7043
|
+
const noteEntries = Object.entries(plan.pageNotes || {}).filter(
|
|
7044
|
+
([, note]) => note.sections && note.sections.length > 0
|
|
7045
|
+
);
|
|
7046
|
+
if (noteEntries.length > 0) {
|
|
7047
|
+
const noteLines = noteEntries.map(([key, note]) => ` ${key}: ${note.sections.join(", ")}`);
|
|
7048
|
+
parts.push(`Page Sections:
|
|
7049
|
+
${noteLines.join("\n")}`);
|
|
7044
7050
|
}
|
|
7045
7051
|
return parts.join("\n");
|
|
7046
7052
|
}
|
|
7053
|
+
function readExistingAppPageForReference(projectRoot, plan) {
|
|
7054
|
+
if (!projectRoot) return null;
|
|
7055
|
+
if (plan?.pageNotes) {
|
|
7056
|
+
for (const [key, note] of Object.entries(plan.pageNotes)) {
|
|
7057
|
+
if (note.type !== "app") continue;
|
|
7058
|
+
for (const group of ["(app)", "(admin)", "(dashboard)"]) {
|
|
7059
|
+
const filePath = resolve6(projectRoot, "app", group, key, "page.tsx");
|
|
7060
|
+
if (existsSync14(filePath)) {
|
|
7061
|
+
const code = readFileSync9(filePath, "utf-8");
|
|
7062
|
+
const lines = code.split("\n");
|
|
7063
|
+
return lines.slice(0, 200).join("\n");
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
const appDir = resolve6(projectRoot, "app");
|
|
7069
|
+
if (!existsSync14(appDir)) return null;
|
|
7070
|
+
try {
|
|
7071
|
+
const entries = readdirSync2(appDir);
|
|
7072
|
+
for (const entry of entries) {
|
|
7073
|
+
if (!entry.startsWith("(") || entry === "(auth)") continue;
|
|
7074
|
+
const groupDir = resolve6(appDir, entry);
|
|
7075
|
+
if (!statSync2(groupDir).isDirectory()) continue;
|
|
7076
|
+
const subDirs = readdirSync2(groupDir);
|
|
7077
|
+
for (const sub of subDirs) {
|
|
7078
|
+
const pagePath = resolve6(groupDir, sub, "page.tsx");
|
|
7079
|
+
if (existsSync14(pagePath)) {
|
|
7080
|
+
const code = readFileSync9(pagePath, "utf-8");
|
|
7081
|
+
const lines = code.split("\n");
|
|
7082
|
+
return lines.slice(0, 200).join("\n");
|
|
7083
|
+
}
|
|
7084
|
+
}
|
|
7085
|
+
}
|
|
7086
|
+
} catch {
|
|
7087
|
+
return null;
|
|
7088
|
+
}
|
|
7089
|
+
return null;
|
|
7090
|
+
}
|
|
7047
7091
|
async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts) {
|
|
7048
7092
|
let pageNames = [];
|
|
7049
7093
|
spinner.start("Phase 1/6 \u2014 Planning pages...");
|
|
@@ -7198,7 +7242,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7198
7242
|
if (plan && plan.sharedComponents.length > 0) {
|
|
7199
7243
|
spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
|
|
7200
7244
|
try {
|
|
7201
|
-
const { generateSharedComponentsFromPlan } = await import("./plan-generator-
|
|
7245
|
+
const { generateSharedComponentsFromPlan } = await import("./plan-generator-QUESV7GS.js");
|
|
7202
7246
|
const generated = await generateSharedComponentsFromPlan(
|
|
7203
7247
|
plan,
|
|
7204
7248
|
styleContext,
|
|
@@ -7245,6 +7289,13 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7245
7289
|
const routeNote = `EXISTING ROUTES in this project: ${allRoutes}. All internal links MUST point to one of these routes. If a target doesn't exist, use href="#".`;
|
|
7246
7290
|
const alignmentNote = 'CRITICAL LAYOUT RULE: Every <section> must wrap its content in a container div matching the header width. Use the EXACT same container classes as shown in the style context (e.g. className="container max-w-6xl px-4" or className="max-w-6xl mx-auto px-4"). Inner content can use narrower max-w for text centering, but the outer section container MUST match.';
|
|
7247
7291
|
const planSummaryNote = plan ? formatPlanSummary(plan) : "";
|
|
7292
|
+
const existingAppPageCode = readExistingAppPageForReference(parseOpts?.projectRoot ?? null, plan);
|
|
7293
|
+
const existingAppPageNote = existingAppPageCode ? `
|
|
7294
|
+
EXISTING APP PAGE (match these UI patterns for consistency):
|
|
7295
|
+
\`\`\`
|
|
7296
|
+
${existingAppPageCode}
|
|
7297
|
+
\`\`\`
|
|
7298
|
+
` : "";
|
|
7248
7299
|
const existingPagesContext = buildExistingPagesContext(modCtx.config);
|
|
7249
7300
|
const AI_CONCURRENCY = 3;
|
|
7250
7301
|
let phase5Done = 0;
|
|
@@ -7267,6 +7318,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7267
7318
|
alignmentNote,
|
|
7268
7319
|
authNote,
|
|
7269
7320
|
planSummaryNote,
|
|
7321
|
+
pageType !== "auth" ? existingAppPageNote : void 0,
|
|
7270
7322
|
existingPagesContext,
|
|
7271
7323
|
styleContext
|
|
7272
7324
|
].filter(Boolean).join("\n\n");
|
|
@@ -7427,7 +7479,7 @@ function extractAppNameFromPrompt(prompt) {
|
|
|
7427
7479
|
}
|
|
7428
7480
|
|
|
7429
7481
|
// src/commands/chat/modification-handler.ts
|
|
7430
|
-
import { resolve as
|
|
7482
|
+
import { resolve as resolve8 } from "path";
|
|
7431
7483
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
7432
7484
|
import { dirname as dirname6 } from "path";
|
|
7433
7485
|
import chalk12 from "chalk";
|
|
@@ -7441,8 +7493,8 @@ import {
|
|
|
7441
7493
|
} from "@getcoherent/core";
|
|
7442
7494
|
|
|
7443
7495
|
// src/commands/chat/code-generator.ts
|
|
7444
|
-
import { resolve as
|
|
7445
|
-
import { existsSync as
|
|
7496
|
+
import { resolve as resolve7 } from "path";
|
|
7497
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync10 } from "fs";
|
|
7446
7498
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
7447
7499
|
import { dirname as dirname5 } from "path";
|
|
7448
7500
|
import {
|
|
@@ -7507,8 +7559,8 @@ async function ensureComponentsInstalled(componentIds, cm, dsm, pm, projectRoot)
|
|
|
7507
7559
|
for (const componentId of ids) {
|
|
7508
7560
|
const isRegistered = !!cm.read(componentId);
|
|
7509
7561
|
const fileName = toKebabCase(componentId) + ".tsx";
|
|
7510
|
-
const filePath =
|
|
7511
|
-
const fileExists =
|
|
7562
|
+
const filePath = resolve7(projectRoot, "components", "ui", fileName);
|
|
7563
|
+
const fileExists = existsSync15(filePath);
|
|
7512
7564
|
if (isRegistered && fileExists) continue;
|
|
7513
7565
|
const result = await provider.installComponent(componentId, projectRoot);
|
|
7514
7566
|
if (result.success && result.componentDef) {
|
|
@@ -7532,7 +7584,7 @@ async function regenerateComponent(componentId, config2, projectRoot) {
|
|
|
7532
7584
|
const generator = new ComponentGenerator2(config2);
|
|
7533
7585
|
const code = await generator.generate(component);
|
|
7534
7586
|
const fileName = toKebabCase(component.name) + ".tsx";
|
|
7535
|
-
const filePath =
|
|
7587
|
+
const filePath = resolve7(projectRoot, "components", "ui", fileName);
|
|
7536
7588
|
await writeFile(filePath, code);
|
|
7537
7589
|
}
|
|
7538
7590
|
async function regeneratePage(pageId, config2, projectRoot) {
|
|
@@ -7549,8 +7601,8 @@ async function regeneratePage(pageId, config2, projectRoot) {
|
|
|
7549
7601
|
await writeFile(filePath, code);
|
|
7550
7602
|
}
|
|
7551
7603
|
async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
|
|
7552
|
-
const filePath =
|
|
7553
|
-
if (!
|
|
7604
|
+
const filePath = resolve7(projectRoot, componentFile);
|
|
7605
|
+
if (!existsSync15(filePath)) return true;
|
|
7554
7606
|
const storedHash = storedHashes[componentFile];
|
|
7555
7607
|
if (!storedHash) return true;
|
|
7556
7608
|
const edited = await isManuallyEdited(filePath, storedHash);
|
|
@@ -7567,7 +7619,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
|
|
|
7567
7619
|
if (!initialized) {
|
|
7568
7620
|
const layout = config2.pages[0]?.layout || "centered";
|
|
7569
7621
|
const code = await generator.generateLayout(layout, appType, { skipNav: true });
|
|
7570
|
-
await writeFile(
|
|
7622
|
+
await writeFile(resolve7(projectRoot, "app", "layout.tsx"), code);
|
|
7571
7623
|
}
|
|
7572
7624
|
if (config2.navigation?.enabled && appType === "multi-page") {
|
|
7573
7625
|
const navType = config2.navigation.type || "header";
|
|
@@ -7623,17 +7675,17 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
|
|
|
7623
7675
|
}
|
|
7624
7676
|
}
|
|
7625
7677
|
async function scanAndInstallSharedDeps(projectRoot) {
|
|
7626
|
-
const sharedDir =
|
|
7627
|
-
if (!
|
|
7628
|
-
const files =
|
|
7678
|
+
const sharedDir = resolve7(projectRoot, "components", "shared");
|
|
7679
|
+
if (!existsSync15(sharedDir)) return [];
|
|
7680
|
+
const files = readdirSync3(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
7629
7681
|
const installed = [];
|
|
7630
7682
|
const provider = getComponentProvider();
|
|
7631
7683
|
for (const file of files) {
|
|
7632
|
-
const code =
|
|
7684
|
+
const code = readFileSync10(resolve7(sharedDir, file), "utf-8");
|
|
7633
7685
|
const importMatches = [...code.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g)];
|
|
7634
7686
|
for (const [, componentId] of importMatches) {
|
|
7635
|
-
const uiPath =
|
|
7636
|
-
if (!
|
|
7687
|
+
const uiPath = resolve7(projectRoot, "components", "ui", `${componentId}.tsx`);
|
|
7688
|
+
if (!existsSync15(uiPath) && provider.has(componentId)) {
|
|
7637
7689
|
try {
|
|
7638
7690
|
await provider.installComponent(componentId, projectRoot);
|
|
7639
7691
|
installed.push(componentId);
|
|
@@ -7645,10 +7697,10 @@ async function scanAndInstallSharedDeps(projectRoot) {
|
|
|
7645
7697
|
return [...new Set(installed)];
|
|
7646
7698
|
}
|
|
7647
7699
|
async function ensureAppRouteGroupLayout(projectRoot, navType, forceUpdate = false) {
|
|
7648
|
-
const layoutPath =
|
|
7649
|
-
if (
|
|
7700
|
+
const layoutPath = resolve7(projectRoot, "app", "(app)", "layout.tsx");
|
|
7701
|
+
if (existsSync15(layoutPath) && !forceUpdate) return;
|
|
7650
7702
|
const { mkdir: mkdirAsync } = await import("fs/promises");
|
|
7651
|
-
await mkdirAsync(
|
|
7703
|
+
await mkdirAsync(resolve7(projectRoot, "app", "(app)"), { recursive: true });
|
|
7652
7704
|
const code = buildAppLayoutCode(navType);
|
|
7653
7705
|
await writeFile(layoutPath, code);
|
|
7654
7706
|
}
|
|
@@ -7736,9 +7788,9 @@ export default function GroupLayout({
|
|
|
7736
7788
|
async function ensurePlanGroupLayouts(projectRoot, plan) {
|
|
7737
7789
|
const { mkdir: mkdirAsync } = await import("fs/promises");
|
|
7738
7790
|
for (const group of plan.groups) {
|
|
7739
|
-
const groupDir =
|
|
7791
|
+
const groupDir = resolve7(projectRoot, "app", `(${group.id})`);
|
|
7740
7792
|
await mkdirAsync(groupDir, { recursive: true });
|
|
7741
|
-
const layoutPath =
|
|
7793
|
+
const layoutPath = resolve7(groupDir, "layout.tsx");
|
|
7742
7794
|
const code = buildGroupLayoutCode(group.layout, group.pages);
|
|
7743
7795
|
await writeFile(layoutPath, code);
|
|
7744
7796
|
}
|
|
@@ -7765,11 +7817,11 @@ async function regenerateFiles(modified, config2, projectRoot, options = { navCh
|
|
|
7765
7817
|
}
|
|
7766
7818
|
if (componentIds.size > 0) {
|
|
7767
7819
|
const twGen = new TailwindConfigGenerator(config2);
|
|
7768
|
-
const twPath =
|
|
7769
|
-
const twCjsPath =
|
|
7770
|
-
if (
|
|
7820
|
+
const twPath = resolve7(projectRoot, "tailwind.config.ts");
|
|
7821
|
+
const twCjsPath = resolve7(projectRoot, "tailwind.config.cjs");
|
|
7822
|
+
if (existsSync15(twPath)) {
|
|
7771
7823
|
await writeFile(twPath, await twGen.generate());
|
|
7772
|
-
} else if (
|
|
7824
|
+
} else if (existsSync15(twCjsPath)) {
|
|
7773
7825
|
await writeFile(twCjsPath, await twGen.generateCjs());
|
|
7774
7826
|
}
|
|
7775
7827
|
}
|
|
@@ -8141,7 +8193,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8141
8193
|
modified: []
|
|
8142
8194
|
};
|
|
8143
8195
|
}
|
|
8144
|
-
const fullPath =
|
|
8196
|
+
const fullPath = resolve8(projectRoot, resolved.file);
|
|
8145
8197
|
let currentCode;
|
|
8146
8198
|
try {
|
|
8147
8199
|
currentCode = await readFile(fullPath);
|
|
@@ -8215,7 +8267,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8215
8267
|
} catch {
|
|
8216
8268
|
return { success: false, message: `Could not read ${pageFilePath}`, modified: [] };
|
|
8217
8269
|
}
|
|
8218
|
-
const sharedPath =
|
|
8270
|
+
const sharedPath = resolve8(projectRoot, resolved.file);
|
|
8219
8271
|
let sharedCode;
|
|
8220
8272
|
try {
|
|
8221
8273
|
sharedCode = await readFile(sharedPath);
|
|
@@ -8294,7 +8346,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8294
8346
|
}
|
|
8295
8347
|
let sourceCode;
|
|
8296
8348
|
try {
|
|
8297
|
-
sourceCode = await readFile(
|
|
8349
|
+
sourceCode = await readFile(resolve8(projectRoot, sourcePath));
|
|
8298
8350
|
} catch {
|
|
8299
8351
|
return { success: false, message: `Could not read ${sourcePath}`, modified: [] };
|
|
8300
8352
|
}
|
|
@@ -8314,7 +8366,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8314
8366
|
description: `Extracted from ${sourcePageName}: ${blockHint}`,
|
|
8315
8367
|
usedIn: []
|
|
8316
8368
|
});
|
|
8317
|
-
const sharedPath =
|
|
8369
|
+
const sharedPath = resolve8(projectRoot, created.file);
|
|
8318
8370
|
let sharedCode;
|
|
8319
8371
|
try {
|
|
8320
8372
|
sharedCode = await readFile(sharedPath);
|
|
@@ -8325,7 +8377,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8325
8377
|
for (const pageName of allPagesToLink) {
|
|
8326
8378
|
const relPath = routeToPath(pageName);
|
|
8327
8379
|
if (!relPath) continue;
|
|
8328
|
-
const fullPath =
|
|
8380
|
+
const fullPath = resolve8(projectRoot, relPath);
|
|
8329
8381
|
let linkPageCode;
|
|
8330
8382
|
try {
|
|
8331
8383
|
linkPageCode = await readFile(fullPath);
|
|
@@ -8659,7 +8711,10 @@ Rules:
|
|
|
8659
8711
|
if (ai.editPageCode) {
|
|
8660
8712
|
console.log(chalk12.dim(" \u270F\uFE0F Applying changes to existing page..."));
|
|
8661
8713
|
const coreRules = CORE_CONSTRAINTS;
|
|
8662
|
-
const
|
|
8714
|
+
const pageRoute = pageDef.route || `/${pageDef.id}`;
|
|
8715
|
+
const pageType = inferPageTypeFromRoute(pageRoute);
|
|
8716
|
+
const qualityRules = `${DESIGN_QUALITY_COMMON}
|
|
8717
|
+
${getDesignQualityForType(pageType)}`;
|
|
8663
8718
|
const contextualRules = selectContextualRules(instruction);
|
|
8664
8719
|
const existingRoutes = dsm.getConfig().pages.map((p) => p.route).join(", ");
|
|
8665
8720
|
const routeRules = `
|
|
@@ -8949,8 +9004,8 @@ function hasNavChanged(before, after) {
|
|
|
8949
9004
|
|
|
8950
9005
|
// src/commands/chat/interactive.ts
|
|
8951
9006
|
import chalk13 from "chalk";
|
|
8952
|
-
import { resolve as
|
|
8953
|
-
import { existsSync as
|
|
9007
|
+
import { resolve as resolve9 } from "path";
|
|
9008
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
8954
9009
|
import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager4, loadManifest as loadManifest7 } from "@getcoherent/core";
|
|
8955
9010
|
var DEBUG3 = process.env.COHERENT_DEBUG === "1";
|
|
8956
9011
|
async function interactiveChat(options, chatCommandFn) {
|
|
@@ -8970,13 +9025,13 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
8970
9025
|
\u274C Invalid provider: ${options.provider}`));
|
|
8971
9026
|
process.exit(1);
|
|
8972
9027
|
}
|
|
8973
|
-
const historyDir =
|
|
8974
|
-
const historyFile =
|
|
9028
|
+
const historyDir = resolve9(homedir2(), ".coherent");
|
|
9029
|
+
const historyFile = resolve9(historyDir, "history");
|
|
8975
9030
|
let history = [];
|
|
8976
9031
|
try {
|
|
8977
9032
|
mkdirSync5(historyDir, { recursive: true });
|
|
8978
|
-
if (
|
|
8979
|
-
history =
|
|
9033
|
+
if (existsSync16(historyFile)) {
|
|
9034
|
+
history = readFileSync11(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
|
|
8980
9035
|
}
|
|
8981
9036
|
} catch (e) {
|
|
8982
9037
|
if (DEBUG3) console.error("Failed to load REPL history:", e);
|
|
@@ -9147,7 +9202,7 @@ async function chatCommand(message, options) {
|
|
|
9147
9202
|
const projectRoot = project.root;
|
|
9148
9203
|
const configPath = project.configPath;
|
|
9149
9204
|
const migrationGuard = join11(projectRoot, ".coherent", "migration-in-progress");
|
|
9150
|
-
if (
|
|
9205
|
+
if (existsSync17(migrationGuard)) {
|
|
9151
9206
|
spinner.fail("Migration in progress");
|
|
9152
9207
|
console.error(chalk14.red("\n\u274C A migration is in progress. Run `coherent migrate --rollback` to undo first."));
|
|
9153
9208
|
bail("Migration in progress");
|
|
@@ -9207,7 +9262,7 @@ async function chatCommand(message, options) {
|
|
|
9207
9262
|
}
|
|
9208
9263
|
if (/switch to light mode|default to light|make.*light.*(default|theme)|light theme/i.test(message)) {
|
|
9209
9264
|
spinner.start("Setting default theme to light...");
|
|
9210
|
-
const layoutPath =
|
|
9265
|
+
const layoutPath = resolve10(projectRoot, "app/layout.tsx");
|
|
9211
9266
|
try {
|
|
9212
9267
|
let layout = await readFile(layoutPath);
|
|
9213
9268
|
layout = layout.replace(/className="dark"/, "");
|
|
@@ -9246,8 +9301,8 @@ async function chatCommand(message, options) {
|
|
|
9246
9301
|
spinner.start("Parsing your request...");
|
|
9247
9302
|
let manifest = await loadManifest8(project.root);
|
|
9248
9303
|
const validShared = manifest.shared.filter((s) => {
|
|
9249
|
-
const fp =
|
|
9250
|
-
return
|
|
9304
|
+
const fp = resolve10(project.root, s.file);
|
|
9305
|
+
return existsSync17(fp);
|
|
9251
9306
|
});
|
|
9252
9307
|
if (validShared.length !== manifest.shared.length) {
|
|
9253
9308
|
const cleaned = manifest.shared.length - validShared.length;
|
|
@@ -9451,9 +9506,9 @@ async function chatCommand(message, options) {
|
|
|
9451
9506
|
if (manifest.shared.length > 0) {
|
|
9452
9507
|
for (const entry of manifest.shared) {
|
|
9453
9508
|
try {
|
|
9454
|
-
const sharedPath =
|
|
9455
|
-
if (
|
|
9456
|
-
const sharedCode =
|
|
9509
|
+
const sharedPath = resolve10(projectRoot, entry.file);
|
|
9510
|
+
if (existsSync17(sharedPath)) {
|
|
9511
|
+
const sharedCode = readFileSync12(sharedPath, "utf-8");
|
|
9457
9512
|
const sharedImports = sharedCode.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g);
|
|
9458
9513
|
for (const m of sharedImports) {
|
|
9459
9514
|
if (m[1]) allNeededComponentIds.add(m[1]);
|
|
@@ -9474,7 +9529,7 @@ async function chatCommand(message, options) {
|
|
|
9474
9529
|
for (const componentId of allNeededComponentIds) {
|
|
9475
9530
|
const isRegistered = !!cm.read(componentId);
|
|
9476
9531
|
const filePath = join11(projectRoot, "components", "ui", `${componentId}.tsx`);
|
|
9477
|
-
const fileExists =
|
|
9532
|
+
const fileExists = existsSync17(filePath);
|
|
9478
9533
|
if (DEBUG4) console.log(chalk14.gray(` Checking ${componentId}: registered=${isRegistered} file=${fileExists}`));
|
|
9479
9534
|
if (!isRegistered || !fileExists) {
|
|
9480
9535
|
missingComponents.push(componentId);
|
|
@@ -9600,9 +9655,9 @@ async function chatCommand(message, options) {
|
|
|
9600
9655
|
const route = page.route || `/${page.id || "page"}`;
|
|
9601
9656
|
const pageFilePath = routeToFsPath(projectRoot, route, false);
|
|
9602
9657
|
let pageCode = "";
|
|
9603
|
-
if (
|
|
9658
|
+
if (existsSync17(pageFilePath)) {
|
|
9604
9659
|
try {
|
|
9605
|
-
pageCode =
|
|
9660
|
+
pageCode = readFileSync12(pageFilePath, "utf-8");
|
|
9606
9661
|
} catch {
|
|
9607
9662
|
}
|
|
9608
9663
|
}
|
|
@@ -9622,8 +9677,8 @@ async function chatCommand(message, options) {
|
|
|
9622
9677
|
}
|
|
9623
9678
|
const missingRoutes = [...allLinkedRoutes].filter((route) => {
|
|
9624
9679
|
if (expandedExisting.has(route)) return false;
|
|
9625
|
-
if (
|
|
9626
|
-
if (
|
|
9680
|
+
if (existsSync17(routeToFsPath(projectRoot, route, false))) return false;
|
|
9681
|
+
if (existsSync17(routeToFsPath(projectRoot, route, true))) return false;
|
|
9627
9682
|
return true;
|
|
9628
9683
|
});
|
|
9629
9684
|
const SCAFFOLD_AI_LIMIT = 10;
|
|
@@ -9687,8 +9742,8 @@ async function chatCommand(message, options) {
|
|
|
9687
9742
|
const isAuth = isAuthRoute(linkedRoute);
|
|
9688
9743
|
const filePath = routeToFsPath(projectRoot, linkedRoute, isAuth);
|
|
9689
9744
|
if (isAuth) await ensureAuthRouteGroup(projectRoot);
|
|
9690
|
-
const dir =
|
|
9691
|
-
if (!
|
|
9745
|
+
const dir = resolve10(filePath, "..");
|
|
9746
|
+
if (!existsSync17(dir)) {
|
|
9692
9747
|
mkdirSync6(dir, { recursive: true });
|
|
9693
9748
|
}
|
|
9694
9749
|
const placeholderCode = `export default function ${pageName.replace(/\s/g, "")}Page() {
|
|
@@ -9721,7 +9776,7 @@ async function chatCommand(message, options) {
|
|
|
9721
9776
|
for (const mod of result.modified) {
|
|
9722
9777
|
if (mod.startsWith("app/") && mod.endsWith("/page.tsx")) {
|
|
9723
9778
|
try {
|
|
9724
|
-
const code =
|
|
9779
|
+
const code = readFileSync12(resolve10(projectRoot, mod), "utf-8");
|
|
9725
9780
|
const issues = validatePageQuality(code, allRoutes).filter(
|
|
9726
9781
|
(i) => i.type === "BROKEN_INTERNAL_LINK"
|
|
9727
9782
|
);
|
|
@@ -9751,7 +9806,7 @@ async function chatCommand(message, options) {
|
|
|
9751
9806
|
dsm.updateConfig(latestConfig);
|
|
9752
9807
|
if (DEBUG4) console.log(chalk14.dim(` [theme] Set defaultMode to "${targetMode}"`));
|
|
9753
9808
|
}
|
|
9754
|
-
const layoutPath =
|
|
9809
|
+
const layoutPath = resolve10(projectRoot, "app", "layout.tsx");
|
|
9755
9810
|
try {
|
|
9756
9811
|
let layoutCode = await readFile(layoutPath);
|
|
9757
9812
|
if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
|
|
@@ -9801,16 +9856,16 @@ async function chatCommand(message, options) {
|
|
|
9801
9856
|
}
|
|
9802
9857
|
try {
|
|
9803
9858
|
const updatedHashes = { ...storedHashes };
|
|
9804
|
-
const sharedDir =
|
|
9805
|
-
const layoutFile =
|
|
9859
|
+
const sharedDir = resolve10(projectRoot, "components", "shared");
|
|
9860
|
+
const layoutFile = resolve10(projectRoot, "app", "layout.tsx");
|
|
9806
9861
|
const filesToHash = [layoutFile];
|
|
9807
|
-
if (
|
|
9808
|
-
for (const f of
|
|
9809
|
-
if (f.endsWith(".tsx")) filesToHash.push(
|
|
9862
|
+
if (existsSync17(sharedDir)) {
|
|
9863
|
+
for (const f of readdirSync4(sharedDir)) {
|
|
9864
|
+
if (f.endsWith(".tsx")) filesToHash.push(resolve10(sharedDir, f));
|
|
9810
9865
|
}
|
|
9811
9866
|
}
|
|
9812
9867
|
for (const filePath of filesToHash) {
|
|
9813
|
-
if (
|
|
9868
|
+
if (existsSync17(filePath)) {
|
|
9814
9869
|
const rel = relative2(projectRoot, filePath);
|
|
9815
9870
|
updatedHashes[rel] = await computeFileHash(filePath);
|
|
9816
9871
|
}
|
|
@@ -9842,7 +9897,7 @@ async function chatCommand(message, options) {
|
|
|
9842
9897
|
console.log("");
|
|
9843
9898
|
}
|
|
9844
9899
|
if (uxRecommendations) {
|
|
9845
|
-
const recPath =
|
|
9900
|
+
const recPath = resolve10(projectRoot, "recommendations.md");
|
|
9846
9901
|
const section = `
|
|
9847
9902
|
|
|
9848
9903
|
---
|
|
@@ -9852,7 +9907,7 @@ async function chatCommand(message, options) {
|
|
|
9852
9907
|
${uxRecommendations}
|
|
9853
9908
|
`;
|
|
9854
9909
|
try {
|
|
9855
|
-
if (!
|
|
9910
|
+
if (!existsSync17(recPath)) {
|
|
9856
9911
|
await writeFile(
|
|
9857
9912
|
recPath,
|
|
9858
9913
|
"# UX/UI Recommendations\n\nRecommendations are added here when you use `coherent chat` and the AI suggests improvements.\n"
|
|
@@ -9926,18 +9981,18 @@ ${uxRecommendations}
|
|
|
9926
9981
|
import chalk15 from "chalk";
|
|
9927
9982
|
import ora3 from "ora";
|
|
9928
9983
|
import { spawn } from "child_process";
|
|
9929
|
-
import { existsSync as
|
|
9930
|
-
import { resolve as
|
|
9984
|
+
import { existsSync as existsSync20, rmSync as rmSync3, readFileSync as readFileSync15, writeFileSync as writeFileSync10, readdirSync as readdirSync6 } from "fs";
|
|
9985
|
+
import { resolve as resolve11, join as join14 } from "path";
|
|
9931
9986
|
import { readdir as readdir2 } from "fs/promises";
|
|
9932
9987
|
import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
|
|
9933
9988
|
|
|
9934
9989
|
// src/utils/file-watcher.ts
|
|
9935
|
-
import { readFileSync as
|
|
9990
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync19 } from "fs";
|
|
9936
9991
|
import { relative as relative4, join as join13 } from "path";
|
|
9937
9992
|
import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
|
|
9938
9993
|
|
|
9939
9994
|
// src/utils/component-integrity.ts
|
|
9940
|
-
import { existsSync as
|
|
9995
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13, readdirSync as readdirSync5 } from "fs";
|
|
9941
9996
|
import { join as join12, relative as relative3 } from "path";
|
|
9942
9997
|
function extractExportedComponentNames(code) {
|
|
9943
9998
|
const names = [];
|
|
@@ -9965,13 +10020,13 @@ function arraysEqual(a, b) {
|
|
|
9965
10020
|
function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
9966
10021
|
const results = [];
|
|
9967
10022
|
const appDir = join12(projectRoot, "app");
|
|
9968
|
-
if (!
|
|
10023
|
+
if (!existsSync18(appDir)) return results;
|
|
9969
10024
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
9970
10025
|
const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
9971
10026
|
for (const absPath of pageFiles) {
|
|
9972
10027
|
if (absPath.includes("design-system")) continue;
|
|
9973
10028
|
try {
|
|
9974
|
-
const code =
|
|
10029
|
+
const code = readFileSync13(absPath, "utf-8");
|
|
9975
10030
|
const hasNamedImport = new RegExp(`import\\s+\\{[^}]*\\b${componentName}\\b[^}]*\\}\\s+from\\s+['"]`).test(code);
|
|
9976
10031
|
const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
|
|
9977
10032
|
const hasPathImport = code.includes(`@/${componentImportPath}`);
|
|
@@ -9985,9 +10040,9 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
9985
10040
|
}
|
|
9986
10041
|
function isUsedInLayout(projectRoot, componentName) {
|
|
9987
10042
|
const layoutPath = join12(projectRoot, "app", "layout.tsx");
|
|
9988
|
-
if (!
|
|
10043
|
+
if (!existsSync18(layoutPath)) return false;
|
|
9989
10044
|
try {
|
|
9990
|
-
const code =
|
|
10045
|
+
const code = readFileSync13(layoutPath, "utf-8");
|
|
9991
10046
|
return code.includes(componentName);
|
|
9992
10047
|
} catch {
|
|
9993
10048
|
return false;
|
|
@@ -9996,7 +10051,7 @@ function isUsedInLayout(projectRoot, componentName) {
|
|
|
9996
10051
|
function findUnregisteredComponents(projectRoot, manifest) {
|
|
9997
10052
|
const results = [];
|
|
9998
10053
|
const componentsDir = join12(projectRoot, "components");
|
|
9999
|
-
if (!
|
|
10054
|
+
if (!existsSync18(componentsDir)) return results;
|
|
10000
10055
|
const registeredFiles = new Set(manifest.shared.map((s) => s.file));
|
|
10001
10056
|
const registeredNames = new Set(manifest.shared.map((s) => s.name));
|
|
10002
10057
|
const files = collectFiles(
|
|
@@ -10008,7 +10063,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
10008
10063
|
const relFile = relative3(projectRoot, absPath);
|
|
10009
10064
|
if (registeredFiles.has(relFile)) continue;
|
|
10010
10065
|
try {
|
|
10011
|
-
const code =
|
|
10066
|
+
const code = readFileSync13(absPath, "utf-8");
|
|
10012
10067
|
const exports = extractExportedComponentNames(code);
|
|
10013
10068
|
for (const name of exports) {
|
|
10014
10069
|
if (registeredNames.has(name)) continue;
|
|
@@ -10024,13 +10079,13 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
10024
10079
|
function findInlineDuplicates(projectRoot, manifest) {
|
|
10025
10080
|
const results = [];
|
|
10026
10081
|
const appDir = join12(projectRoot, "app");
|
|
10027
|
-
if (!
|
|
10082
|
+
if (!existsSync18(appDir)) return results;
|
|
10028
10083
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
10029
10084
|
for (const absPath of pageFiles) {
|
|
10030
10085
|
if (absPath.includes("design-system")) continue;
|
|
10031
10086
|
let code;
|
|
10032
10087
|
try {
|
|
10033
|
-
code =
|
|
10088
|
+
code = readFileSync13(absPath, "utf-8");
|
|
10034
10089
|
} catch {
|
|
10035
10090
|
continue;
|
|
10036
10091
|
}
|
|
@@ -10054,7 +10109,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
10054
10109
|
}
|
|
10055
10110
|
function findComponentFileByExportName(projectRoot, componentName) {
|
|
10056
10111
|
const componentsDir = join12(projectRoot, "components");
|
|
10057
|
-
if (!
|
|
10112
|
+
if (!existsSync18(componentsDir)) return null;
|
|
10058
10113
|
const files = collectFiles(
|
|
10059
10114
|
componentsDir,
|
|
10060
10115
|
(name) => (name.endsWith(".tsx") || name.endsWith(".jsx")) && !name.startsWith("."),
|
|
@@ -10062,7 +10117,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
10062
10117
|
);
|
|
10063
10118
|
for (const absPath of files) {
|
|
10064
10119
|
try {
|
|
10065
|
-
const code =
|
|
10120
|
+
const code = readFileSync13(absPath, "utf-8");
|
|
10066
10121
|
const exports = extractExportedComponentNames(code);
|
|
10067
10122
|
if (exports.includes(componentName)) {
|
|
10068
10123
|
return relative3(projectRoot, absPath);
|
|
@@ -10076,7 +10131,7 @@ function removeOrphanedEntries(projectRoot, manifest) {
|
|
|
10076
10131
|
const removed = [];
|
|
10077
10132
|
const valid = manifest.shared.filter((entry) => {
|
|
10078
10133
|
const filePath = join12(projectRoot, entry.file);
|
|
10079
|
-
if (
|
|
10134
|
+
if (existsSync18(filePath)) return true;
|
|
10080
10135
|
removed.push({ id: entry.id, name: entry.name });
|
|
10081
10136
|
return false;
|
|
10082
10137
|
});
|
|
@@ -10095,7 +10150,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
10095
10150
|
const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
|
|
10096
10151
|
m.shared = m.shared.filter((entry) => {
|
|
10097
10152
|
const filePath = join12(projectRoot, entry.file);
|
|
10098
|
-
if (!
|
|
10153
|
+
if (!existsSync18(filePath)) {
|
|
10099
10154
|
const newPath = findComponentFileByExportName(projectRoot, entry.name);
|
|
10100
10155
|
if (newPath) {
|
|
10101
10156
|
result.updated.push({ id: entry.id, field: "file", from: entry.file, to: newPath });
|
|
@@ -10107,7 +10162,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
10107
10162
|
}
|
|
10108
10163
|
let code;
|
|
10109
10164
|
try {
|
|
10110
|
-
code =
|
|
10165
|
+
code = readFileSync13(join12(projectRoot, entry.file), "utf-8");
|
|
10111
10166
|
} catch {
|
|
10112
10167
|
return true;
|
|
10113
10168
|
}
|
|
@@ -10184,7 +10239,7 @@ function collectFiles(dir, filter, skipDirs = []) {
|
|
|
10184
10239
|
function walk(d) {
|
|
10185
10240
|
let entries;
|
|
10186
10241
|
try {
|
|
10187
|
-
entries =
|
|
10242
|
+
entries = readdirSync5(d, { withFileTypes: true });
|
|
10188
10243
|
} catch {
|
|
10189
10244
|
return;
|
|
10190
10245
|
}
|
|
@@ -10229,8 +10284,8 @@ function findInlineDuplicatesOfShared(content, manifest) {
|
|
|
10229
10284
|
function getWatcherConfig(projectRoot) {
|
|
10230
10285
|
try {
|
|
10231
10286
|
const pkgPath = join13(projectRoot, "package.json");
|
|
10232
|
-
if (!
|
|
10233
|
-
const pkg = JSON.parse(
|
|
10287
|
+
if (!existsSync19(pkgPath)) return defaultWatcherConfig();
|
|
10288
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
10234
10289
|
const c = pkg?.coherent?.watcher ?? {};
|
|
10235
10290
|
return {
|
|
10236
10291
|
enabled: c.enabled !== false,
|
|
@@ -10258,7 +10313,7 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
10258
10313
|
if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
|
|
10259
10314
|
let content;
|
|
10260
10315
|
try {
|
|
10261
|
-
content =
|
|
10316
|
+
content = readFileSync14(filePath, "utf-8");
|
|
10262
10317
|
} catch {
|
|
10263
10318
|
return;
|
|
10264
10319
|
}
|
|
@@ -10331,7 +10386,7 @@ async function detectNewComponent(projectRoot, filePath) {
|
|
|
10331
10386
|
const manifest = await loadManifest9(projectRoot);
|
|
10332
10387
|
const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
|
|
10333
10388
|
if (alreadyRegistered) return;
|
|
10334
|
-
const code =
|
|
10389
|
+
const code = readFileSync14(filePath, "utf-8");
|
|
10335
10390
|
const exports = extractExportedComponentNames(code);
|
|
10336
10391
|
if (exports.length > 0) {
|
|
10337
10392
|
const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
|
|
@@ -10371,7 +10426,7 @@ function startFileWatcher(projectRoot) {
|
|
|
10371
10426
|
watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
|
|
10372
10427
|
});
|
|
10373
10428
|
const manifestPath = join13(projectRoot, "coherent.components.json");
|
|
10374
|
-
if (
|
|
10429
|
+
if (existsSync19(manifestPath)) {
|
|
10375
10430
|
import("chokidar").then((chokidar) => {
|
|
10376
10431
|
manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
|
|
10377
10432
|
manifestWatcher.on("change", () => handleManifestChange(projectRoot));
|
|
@@ -10385,7 +10440,7 @@ function startFileWatcher(projectRoot) {
|
|
|
10385
10440
|
|
|
10386
10441
|
// src/commands/preview.ts
|
|
10387
10442
|
function getPackageManager(projectRoot) {
|
|
10388
|
-
const hasPnpm =
|
|
10443
|
+
const hasPnpm = existsSync20(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
10389
10444
|
return hasPnpm ? "pnpm" : "npm";
|
|
10390
10445
|
}
|
|
10391
10446
|
function runInstall(projectRoot) {
|
|
@@ -10407,23 +10462,23 @@ function runInstall(projectRoot) {
|
|
|
10407
10462
|
});
|
|
10408
10463
|
}
|
|
10409
10464
|
function checkProjectInitialized(projectRoot) {
|
|
10410
|
-
const configPath =
|
|
10411
|
-
const packageJsonPath =
|
|
10412
|
-
if (!
|
|
10465
|
+
const configPath = resolve11(projectRoot, "design-system.config.ts");
|
|
10466
|
+
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
10467
|
+
if (!existsSync20(configPath)) {
|
|
10413
10468
|
return false;
|
|
10414
10469
|
}
|
|
10415
|
-
if (!
|
|
10470
|
+
if (!existsSync20(packageJsonPath)) {
|
|
10416
10471
|
return false;
|
|
10417
10472
|
}
|
|
10418
10473
|
return true;
|
|
10419
10474
|
}
|
|
10420
10475
|
function checkDependenciesInstalled(projectRoot) {
|
|
10421
|
-
const nodeModulesPath =
|
|
10422
|
-
return
|
|
10476
|
+
const nodeModulesPath = resolve11(projectRoot, "node_modules");
|
|
10477
|
+
return existsSync20(nodeModulesPath);
|
|
10423
10478
|
}
|
|
10424
10479
|
function clearStaleCache(projectRoot) {
|
|
10425
10480
|
const nextDir = join14(projectRoot, ".next");
|
|
10426
|
-
if (
|
|
10481
|
+
if (existsSync20(nextDir)) {
|
|
10427
10482
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
10428
10483
|
console.log(chalk15.dim(" \u2714 Cleared stale build cache"));
|
|
10429
10484
|
}
|
|
@@ -10439,7 +10494,7 @@ async function preflightDependencyCheck(projectRoot) {
|
|
|
10439
10494
|
}
|
|
10440
10495
|
async function listPageFiles(appDir) {
|
|
10441
10496
|
const out = [];
|
|
10442
|
-
if (!
|
|
10497
|
+
if (!existsSync20(appDir)) return out;
|
|
10443
10498
|
async function walk(dir) {
|
|
10444
10499
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
10445
10500
|
for (const e of entries) {
|
|
@@ -10455,7 +10510,7 @@ async function validateSyntax(projectRoot) {
|
|
|
10455
10510
|
const appDir = join14(projectRoot, "app");
|
|
10456
10511
|
const pages = await listPageFiles(appDir);
|
|
10457
10512
|
for (const file of pages) {
|
|
10458
|
-
const content =
|
|
10513
|
+
const content = readFileSync15(file, "utf-8");
|
|
10459
10514
|
const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
|
|
10460
10515
|
if (fixed !== content) {
|
|
10461
10516
|
writeFileSync10(file, fixed, "utf-8");
|
|
@@ -10466,16 +10521,16 @@ async function validateSyntax(projectRoot) {
|
|
|
10466
10521
|
async function fixMissingComponentExports(projectRoot) {
|
|
10467
10522
|
const appDir = join14(projectRoot, "app");
|
|
10468
10523
|
const uiDir = join14(projectRoot, "components", "ui");
|
|
10469
|
-
if (!
|
|
10524
|
+
if (!existsSync20(appDir) || !existsSync20(uiDir)) return;
|
|
10470
10525
|
const pages = await listPageFiles(appDir);
|
|
10471
10526
|
const sharedDir = join14(projectRoot, "components", "shared");
|
|
10472
|
-
if (
|
|
10473
|
-
const sharedFiles =
|
|
10527
|
+
if (existsSync20(sharedDir)) {
|
|
10528
|
+
const sharedFiles = readdirSync6(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => join14(sharedDir, f));
|
|
10474
10529
|
pages.push(...sharedFiles);
|
|
10475
10530
|
}
|
|
10476
10531
|
const neededExports = /* @__PURE__ */ new Map();
|
|
10477
10532
|
for (const file of pages) {
|
|
10478
|
-
const content =
|
|
10533
|
+
const content = readFileSync15(file, "utf-8");
|
|
10479
10534
|
const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/ui\/([^'"]+)['"]/g;
|
|
10480
10535
|
let m;
|
|
10481
10536
|
while ((m = importRe.exec(content)) !== null) {
|
|
@@ -10496,7 +10551,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10496
10551
|
const provider = getComponentProvider();
|
|
10497
10552
|
for (const [componentId, needed] of neededExports) {
|
|
10498
10553
|
const componentFile = join14(uiDir, `${componentId}.tsx`);
|
|
10499
|
-
if (!
|
|
10554
|
+
if (!existsSync20(componentFile)) {
|
|
10500
10555
|
if (provider.has(componentId)) {
|
|
10501
10556
|
try {
|
|
10502
10557
|
const result = await provider.installComponent(componentId, projectRoot);
|
|
@@ -10519,7 +10574,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10519
10574
|
}
|
|
10520
10575
|
continue;
|
|
10521
10576
|
}
|
|
10522
|
-
const content =
|
|
10577
|
+
const content = readFileSync15(componentFile, "utf-8");
|
|
10523
10578
|
const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
|
|
10524
10579
|
const existingExports = /* @__PURE__ */ new Set();
|
|
10525
10580
|
let em;
|
|
@@ -10554,7 +10609,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10554
10609
|
}
|
|
10555
10610
|
async function backfillPageAnalysis(projectRoot) {
|
|
10556
10611
|
const configPath = join14(projectRoot, "design-system.config.ts");
|
|
10557
|
-
if (!
|
|
10612
|
+
if (!existsSync20(configPath)) return;
|
|
10558
10613
|
try {
|
|
10559
10614
|
const mgr = new DesignSystemManager8(configPath);
|
|
10560
10615
|
const config2 = mgr.getConfig();
|
|
@@ -10571,8 +10626,8 @@ async function backfillPageAnalysis(projectRoot) {
|
|
|
10571
10626
|
} else {
|
|
10572
10627
|
filePath = join14(projectRoot, "app", route.slice(1), "page.tsx");
|
|
10573
10628
|
}
|
|
10574
|
-
if (!
|
|
10575
|
-
const code =
|
|
10629
|
+
if (!existsSync20(filePath)) continue;
|
|
10630
|
+
const code = readFileSync15(filePath, "utf-8");
|
|
10576
10631
|
if (code.length < 50) continue;
|
|
10577
10632
|
page.pageAnalysis = analyzePageCode(code);
|
|
10578
10633
|
changed = true;
|
|
@@ -10722,12 +10777,12 @@ async function openBrowser(url) {
|
|
|
10722
10777
|
}
|
|
10723
10778
|
}
|
|
10724
10779
|
function startDevServer(projectRoot) {
|
|
10725
|
-
const packageJsonPath =
|
|
10726
|
-
if (!
|
|
10780
|
+
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
10781
|
+
if (!existsSync20(packageJsonPath)) {
|
|
10727
10782
|
throw new Error('package.json not found. Run "coherent init" first.');
|
|
10728
10783
|
}
|
|
10729
|
-
const hasPnpm =
|
|
10730
|
-
const hasNpm =
|
|
10784
|
+
const hasPnpm = existsSync20(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
10785
|
+
const hasNpm = existsSync20(resolve11(projectRoot, "package-lock.json"));
|
|
10731
10786
|
const command = hasPnpm ? "pnpm" : hasNpm ? "npm" : "npx";
|
|
10732
10787
|
const args = hasPnpm ? ["dev", "--turbo"] : hasNpm ? ["run", "dev", "--", "--turbo"] : ["next", "dev", "--turbo"];
|
|
10733
10788
|
const child = spawn(command, args, {
|
|
@@ -10774,7 +10829,7 @@ async function previewCommand() {
|
|
|
10774
10829
|
if (needsGlobalsFix(projectRoot)) {
|
|
10775
10830
|
spinner.text = "Fixing globals.css...";
|
|
10776
10831
|
try {
|
|
10777
|
-
const dsm = new DesignSystemManager8(
|
|
10832
|
+
const dsm = new DesignSystemManager8(resolve11(projectRoot, "design-system.config.ts"));
|
|
10778
10833
|
await dsm.load();
|
|
10779
10834
|
const config2 = dsm.getConfig();
|
|
10780
10835
|
fixGlobalsCss(projectRoot, config2);
|
|
@@ -10813,8 +10868,8 @@ async function previewCommand() {
|
|
|
10813
10868
|
import chalk16 from "chalk";
|
|
10814
10869
|
import ora4 from "ora";
|
|
10815
10870
|
import { spawn as spawn2 } from "child_process";
|
|
10816
|
-
import { existsSync as
|
|
10817
|
-
import { resolve as
|
|
10871
|
+
import { existsSync as existsSync21, rmSync as rmSync4, readdirSync as readdirSync7 } from "fs";
|
|
10872
|
+
import { resolve as resolve12, join as join15, dirname as dirname7 } from "path";
|
|
10818
10873
|
import { readdir as readdir3, readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
|
|
10819
10874
|
var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
10820
10875
|
"node_modules",
|
|
@@ -10848,17 +10903,17 @@ async function copyDir(src, dest) {
|
|
|
10848
10903
|
}
|
|
10849
10904
|
}
|
|
10850
10905
|
function checkProjectInitialized2(projectRoot) {
|
|
10851
|
-
return
|
|
10906
|
+
return existsSync21(resolve12(projectRoot, "design-system.config.ts")) && existsSync21(resolve12(projectRoot, "package.json"));
|
|
10852
10907
|
}
|
|
10853
10908
|
function getPackageManager2(projectRoot) {
|
|
10854
|
-
if (
|
|
10855
|
-
if (
|
|
10909
|
+
if (existsSync21(resolve12(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
10910
|
+
if (existsSync21(resolve12(projectRoot, "package-lock.json"))) return "npm";
|
|
10856
10911
|
return "npx";
|
|
10857
10912
|
}
|
|
10858
10913
|
async function patchNextConfigForExport(outRoot) {
|
|
10859
10914
|
for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
|
|
10860
10915
|
const p = join15(outRoot, name);
|
|
10861
|
-
if (!
|
|
10916
|
+
if (!existsSync21(p)) continue;
|
|
10862
10917
|
let content = await readFile6(p, "utf-8");
|
|
10863
10918
|
if (content.includes("ignoreDuringBuilds")) return;
|
|
10864
10919
|
content = content.replace(
|
|
@@ -10873,9 +10928,9 @@ async function buildProduction(projectRoot) {
|
|
|
10873
10928
|
const pm = getPackageManager2(projectRoot);
|
|
10874
10929
|
const command = pm === "pnpm" ? "pnpm" : pm === "npm" ? "npm" : "npx";
|
|
10875
10930
|
const args = pm === "npx" ? ["next", "build"] : ["run", "build"];
|
|
10876
|
-
return new Promise((
|
|
10931
|
+
return new Promise((resolve17, reject) => {
|
|
10877
10932
|
const child = spawn2(command, args, { cwd: projectRoot, stdio: "inherit", shell: true });
|
|
10878
|
-
child.on("exit", (code) => code === 0 ?
|
|
10933
|
+
child.on("exit", (code) => code === 0 ? resolve17() : reject(new Error(`Build failed with exit code ${code}`)));
|
|
10879
10934
|
child.on("error", (error) => reject(new Error(`Failed to start build: ${error.message}`)));
|
|
10880
10935
|
});
|
|
10881
10936
|
}
|
|
@@ -10900,7 +10955,7 @@ EXPOSE 3000
|
|
|
10900
10955
|
`;
|
|
10901
10956
|
async function ensureReadmeDeploySection(outRoot) {
|
|
10902
10957
|
const readmePath = join15(outRoot, "README.md");
|
|
10903
|
-
if (!
|
|
10958
|
+
if (!existsSync21(readmePath)) return;
|
|
10904
10959
|
try {
|
|
10905
10960
|
let content = await readFile6(readmePath, "utf-8");
|
|
10906
10961
|
if (/##\s+Deploy\b/m.test(content)) return;
|
|
@@ -10925,16 +10980,16 @@ async function countPages(outRoot) {
|
|
|
10925
10980
|
}
|
|
10926
10981
|
}
|
|
10927
10982
|
const appDir = join15(outRoot, "app");
|
|
10928
|
-
if (
|
|
10983
|
+
if (existsSync21(appDir)) await walk(appDir);
|
|
10929
10984
|
return n;
|
|
10930
10985
|
}
|
|
10931
10986
|
function countComponents(outRoot) {
|
|
10932
10987
|
let n = 0;
|
|
10933
10988
|
for (const sub of ["ui", "shared"]) {
|
|
10934
10989
|
const dir = join15(outRoot, "components", sub);
|
|
10935
|
-
if (!
|
|
10990
|
+
if (!existsSync21(dir)) continue;
|
|
10936
10991
|
try {
|
|
10937
|
-
n +=
|
|
10992
|
+
n += readdirSync7(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
|
|
10938
10993
|
} catch {
|
|
10939
10994
|
}
|
|
10940
10995
|
}
|
|
@@ -10943,7 +10998,7 @@ function countComponents(outRoot) {
|
|
|
10943
10998
|
var IMPORT_FROM_REGEX2 = /from\s+['"]([^'"]+)['"]/g;
|
|
10944
10999
|
async function collectImportedPackages2(dir, extensions) {
|
|
10945
11000
|
const packages = /* @__PURE__ */ new Set();
|
|
10946
|
-
if (!
|
|
11001
|
+
if (!existsSync21(dir)) return packages;
|
|
10947
11002
|
async function walk(d) {
|
|
10948
11003
|
const entries = await readdir3(d, { withFileTypes: true });
|
|
10949
11004
|
for (const e of entries) {
|
|
@@ -10971,7 +11026,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
10971
11026
|
}
|
|
10972
11027
|
async function findMissingDepsInExport(outRoot) {
|
|
10973
11028
|
const pkgPath = join15(outRoot, "package.json");
|
|
10974
|
-
if (!
|
|
11029
|
+
if (!existsSync21(pkgPath)) return [];
|
|
10975
11030
|
let pkg;
|
|
10976
11031
|
try {
|
|
10977
11032
|
pkg = JSON.parse(await readFile6(pkgPath, "utf-8"));
|
|
@@ -10992,25 +11047,25 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
10992
11047
|
const removed = [];
|
|
10993
11048
|
for (const p of ["app/design-system", "app/api/design-system"]) {
|
|
10994
11049
|
const full = join15(outputDir, p);
|
|
10995
|
-
if (
|
|
11050
|
+
if (existsSync21(full)) {
|
|
10996
11051
|
rmSync4(full, { recursive: true, force: true });
|
|
10997
11052
|
removed.push(p);
|
|
10998
11053
|
}
|
|
10999
11054
|
}
|
|
11000
11055
|
const appNavPath = join15(outputDir, "app", "AppNav.tsx");
|
|
11001
|
-
if (
|
|
11056
|
+
if (existsSync21(appNavPath)) {
|
|
11002
11057
|
rmSync4(appNavPath, { force: true });
|
|
11003
11058
|
removed.push("app/AppNav.tsx");
|
|
11004
11059
|
}
|
|
11005
11060
|
const layoutPath = join15(outputDir, "app", "layout.tsx");
|
|
11006
|
-
if (
|
|
11061
|
+
if (existsSync21(layoutPath)) {
|
|
11007
11062
|
let layout = await readFile6(layoutPath, "utf-8");
|
|
11008
11063
|
layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
|
|
11009
11064
|
layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
|
|
11010
11065
|
await writeFile5(layoutPath, layout, "utf-8");
|
|
11011
11066
|
}
|
|
11012
11067
|
const sharedHeaderPath = join15(outputDir, "components", "shared", "header.tsx");
|
|
11013
|
-
if (
|
|
11068
|
+
if (existsSync21(sharedHeaderPath)) {
|
|
11014
11069
|
let header = await readFile6(sharedHeaderPath, "utf-8");
|
|
11015
11070
|
header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
|
|
11016
11071
|
header = header.replace(/\n\s*<>\s*\n/, "\n");
|
|
@@ -11018,7 +11073,7 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
11018
11073
|
await writeFile5(sharedHeaderPath, header, "utf-8");
|
|
11019
11074
|
}
|
|
11020
11075
|
const guardPath2 = join15(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
|
|
11021
|
-
if (
|
|
11076
|
+
if (existsSync21(guardPath2)) {
|
|
11022
11077
|
let guard = await readFile6(guardPath2, "utf-8");
|
|
11023
11078
|
guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
|
|
11024
11079
|
const pathsMatch = guard.match(/HIDDEN_PATHS\s*=\s*\[([^\]]*)\]/);
|
|
@@ -11026,7 +11081,7 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
11026
11081
|
if (remaining.length === 0) {
|
|
11027
11082
|
rmSync4(guardPath2, { force: true });
|
|
11028
11083
|
removed.push("app/ShowWhenNotAuthRoute.tsx");
|
|
11029
|
-
if (
|
|
11084
|
+
if (existsSync21(layoutPath)) {
|
|
11030
11085
|
let layout = await readFile6(layoutPath, "utf-8");
|
|
11031
11086
|
layout = layout.replace(/import\s+\w+\s+from\s*['"]\.\/ShowWhenNotAuthRoute['"]\s*\n?/g, "");
|
|
11032
11087
|
layout = layout.replace(/\s*<ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
@@ -11047,14 +11102,14 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
11047
11102
|
"recommendations.md"
|
|
11048
11103
|
]) {
|
|
11049
11104
|
const full = join15(outputDir, name);
|
|
11050
|
-
if (
|
|
11105
|
+
if (existsSync21(full)) {
|
|
11051
11106
|
rmSync4(full, { force: true });
|
|
11052
11107
|
removed.push(name);
|
|
11053
11108
|
}
|
|
11054
11109
|
}
|
|
11055
11110
|
for (const dir of [".claude", ".coherent"]) {
|
|
11056
11111
|
const full = join15(outputDir, dir);
|
|
11057
|
-
if (
|
|
11112
|
+
if (existsSync21(full)) {
|
|
11058
11113
|
rmSync4(full, { recursive: true, force: true });
|
|
11059
11114
|
removed.push(dir + "/");
|
|
11060
11115
|
}
|
|
@@ -11062,7 +11117,7 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
11062
11117
|
return removed;
|
|
11063
11118
|
}
|
|
11064
11119
|
async function exportCommand(options = {}) {
|
|
11065
|
-
const outputDir =
|
|
11120
|
+
const outputDir = resolve12(process.cwd(), options.output ?? "./export");
|
|
11066
11121
|
const doBuild = options.build !== false;
|
|
11067
11122
|
const keepDs = options.keepDs === true;
|
|
11068
11123
|
const spinner = ora4("Preparing export...").start();
|
|
@@ -11078,7 +11133,7 @@ async function exportCommand(options = {}) {
|
|
|
11078
11133
|
process.exit(1);
|
|
11079
11134
|
}
|
|
11080
11135
|
spinner.text = "Copying project...";
|
|
11081
|
-
if (
|
|
11136
|
+
if (existsSync21(outputDir)) rmSync4(outputDir, { recursive: true, force: true });
|
|
11082
11137
|
await copyDir(projectRoot, outputDir);
|
|
11083
11138
|
spinner.succeed("Project copied");
|
|
11084
11139
|
if (!keepDs) {
|
|
@@ -11253,8 +11308,8 @@ async function regenerateDocsCommand() {
|
|
|
11253
11308
|
|
|
11254
11309
|
// src/commands/fix.ts
|
|
11255
11310
|
import chalk19 from "chalk";
|
|
11256
|
-
import { readdirSync as
|
|
11257
|
-
import { resolve as
|
|
11311
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync16, existsSync as existsSync22, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
11312
|
+
import { resolve as resolve13, join as join16 } from "path";
|
|
11258
11313
|
import {
|
|
11259
11314
|
DesignSystemManager as DesignSystemManager11,
|
|
11260
11315
|
ComponentManager as ComponentManager6,
|
|
@@ -11278,7 +11333,7 @@ function extractComponentIdsFromCode2(code) {
|
|
|
11278
11333
|
function listTsxFiles(dir) {
|
|
11279
11334
|
const files = [];
|
|
11280
11335
|
try {
|
|
11281
|
-
const entries =
|
|
11336
|
+
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
11282
11337
|
for (const e of entries) {
|
|
11283
11338
|
const full = join16(dir, e.name);
|
|
11284
11339
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
@@ -11310,7 +11365,7 @@ async function fixCommand(opts = {}) {
|
|
|
11310
11365
|
}
|
|
11311
11366
|
if (!skipCache) {
|
|
11312
11367
|
const nextDir = join16(projectRoot, ".next");
|
|
11313
|
-
if (
|
|
11368
|
+
if (existsSync22(nextDir)) {
|
|
11314
11369
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
11315
11370
|
fixes.push("Cleared build cache");
|
|
11316
11371
|
console.log(chalk19.green(" \u2714 Cleared build cache"));
|
|
@@ -11332,12 +11387,12 @@ async function fixCommand(opts = {}) {
|
|
|
11332
11387
|
}
|
|
11333
11388
|
}
|
|
11334
11389
|
}
|
|
11335
|
-
const appDir =
|
|
11390
|
+
const appDir = resolve13(projectRoot, "app");
|
|
11336
11391
|
const allTsxFiles = listTsxFiles(appDir);
|
|
11337
|
-
const componentsTsxFiles = listTsxFiles(
|
|
11392
|
+
const componentsTsxFiles = listTsxFiles(resolve13(projectRoot, "components"));
|
|
11338
11393
|
const allComponentIds = /* @__PURE__ */ new Set();
|
|
11339
11394
|
for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
|
|
11340
|
-
const content =
|
|
11395
|
+
const content = readFileSync16(file, "utf-8");
|
|
11341
11396
|
extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
|
|
11342
11397
|
}
|
|
11343
11398
|
let dsm = null;
|
|
@@ -11356,8 +11411,8 @@ async function fixCommand(opts = {}) {
|
|
|
11356
11411
|
missingComponents.push(id);
|
|
11357
11412
|
} else {
|
|
11358
11413
|
const fileName = toKebabCase(id) + ".tsx";
|
|
11359
|
-
const filePath =
|
|
11360
|
-
if (!
|
|
11414
|
+
const filePath = resolve13(projectRoot, "components", "ui", fileName);
|
|
11415
|
+
if (!existsSync22(filePath)) missingFiles.push(id);
|
|
11361
11416
|
}
|
|
11362
11417
|
}
|
|
11363
11418
|
const provider = getComponentProvider();
|
|
@@ -11386,8 +11441,8 @@ async function fixCommand(opts = {}) {
|
|
|
11386
11441
|
const generator = new ComponentGenerator4(updatedConfig);
|
|
11387
11442
|
const code = await generator.generate(component);
|
|
11388
11443
|
const fileName = toKebabCase(component.name) + ".tsx";
|
|
11389
|
-
const filePath =
|
|
11390
|
-
mkdirSync7(
|
|
11444
|
+
const filePath = resolve13(projectRoot, "components", "ui", fileName);
|
|
11445
|
+
mkdirSync7(resolve13(projectRoot, "components", "ui"), { recursive: true });
|
|
11391
11446
|
await writeFile(filePath, code);
|
|
11392
11447
|
}
|
|
11393
11448
|
}
|
|
@@ -11409,7 +11464,7 @@ async function fixCommand(opts = {}) {
|
|
|
11409
11464
|
const userTsxFiles = allTsxFiles.filter((f) => !f.includes("/design-system/"));
|
|
11410
11465
|
let syntaxFixed = 0;
|
|
11411
11466
|
for (const file of userTsxFiles) {
|
|
11412
|
-
const content =
|
|
11467
|
+
const content = readFileSync16(file, "utf-8");
|
|
11413
11468
|
const fixed = fixUnescapedLtInJsx(
|
|
11414
11469
|
fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
|
|
11415
11470
|
);
|
|
@@ -11427,7 +11482,7 @@ async function fixCommand(opts = {}) {
|
|
|
11427
11482
|
let qualityFixCount = 0;
|
|
11428
11483
|
const qualityFixDetails = [];
|
|
11429
11484
|
for (const file of userTsxFiles) {
|
|
11430
|
-
const content =
|
|
11485
|
+
const content = readFileSync16(file, "utf-8");
|
|
11431
11486
|
const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
|
|
11432
11487
|
if (autoFixed !== content) {
|
|
11433
11488
|
if (!dryRun) writeFileSync11(file, autoFixed, "utf-8");
|
|
@@ -11446,7 +11501,7 @@ async function fixCommand(opts = {}) {
|
|
|
11446
11501
|
let totalWarnings = 0;
|
|
11447
11502
|
const fileIssues = [];
|
|
11448
11503
|
for (const file of allTsxFiles) {
|
|
11449
|
-
const code = dryRun ?
|
|
11504
|
+
const code = dryRun ? readFileSync16(file, "utf-8") : readFileSync16(file, "utf-8");
|
|
11450
11505
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11451
11506
|
const baseName = file.split("/").pop() || "";
|
|
11452
11507
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -11568,17 +11623,17 @@ async function fixCommand(opts = {}) {
|
|
|
11568
11623
|
|
|
11569
11624
|
// src/commands/check.ts
|
|
11570
11625
|
import chalk20 from "chalk";
|
|
11571
|
-
import { resolve as
|
|
11572
|
-
import { readdirSync as
|
|
11626
|
+
import { resolve as resolve14 } from "path";
|
|
11627
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync17, statSync as statSync3, existsSync as existsSync23 } from "fs";
|
|
11573
11628
|
import { loadManifest as loadManifest11 } from "@getcoherent/core";
|
|
11574
11629
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
11575
11630
|
function findTsxFiles(dir) {
|
|
11576
11631
|
const results = [];
|
|
11577
11632
|
try {
|
|
11578
|
-
const entries =
|
|
11633
|
+
const entries = readdirSync9(dir);
|
|
11579
11634
|
for (const entry of entries) {
|
|
11580
|
-
const full =
|
|
11581
|
-
const stat =
|
|
11635
|
+
const full = resolve14(dir, entry);
|
|
11636
|
+
const stat = statSync3(full);
|
|
11582
11637
|
if (stat.isDirectory() && !entry.startsWith(".") && !EXCLUDED_DIRS.has(entry)) {
|
|
11583
11638
|
results.push(...findTsxFiles(full));
|
|
11584
11639
|
} else if (entry.endsWith(".tsx")) {
|
|
@@ -11612,7 +11667,7 @@ async function checkCommand(opts = {}) {
|
|
|
11612
11667
|
} catch {
|
|
11613
11668
|
}
|
|
11614
11669
|
if (!skipPages) {
|
|
11615
|
-
const appDir =
|
|
11670
|
+
const appDir = resolve14(projectRoot, "app");
|
|
11616
11671
|
const files = findTsxFiles(appDir);
|
|
11617
11672
|
result.pages.total = files.length;
|
|
11618
11673
|
if (!opts.json) console.log(chalk20.cyan("\n \u{1F4C4} Pages") + chalk20.dim(` (${files.length} scanned)
|
|
@@ -11626,7 +11681,7 @@ async function checkCommand(opts = {}) {
|
|
|
11626
11681
|
"NATIVE_TABLE"
|
|
11627
11682
|
]);
|
|
11628
11683
|
for (const file of files) {
|
|
11629
|
-
const code =
|
|
11684
|
+
const code = readFileSync17(file, "utf-8");
|
|
11630
11685
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11631
11686
|
const baseName = file.split("/").pop() || "";
|
|
11632
11687
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -11668,7 +11723,7 @@ async function checkCommand(opts = {}) {
|
|
|
11668
11723
|
routeSet.add("/");
|
|
11669
11724
|
routeSet.add("#");
|
|
11670
11725
|
for (const file of files) {
|
|
11671
|
-
const code =
|
|
11726
|
+
const code = readFileSync17(file, "utf-8");
|
|
11672
11727
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11673
11728
|
const lines = code.split("\n");
|
|
11674
11729
|
const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
|
|
@@ -11700,8 +11755,8 @@ async function checkCommand(opts = {}) {
|
|
|
11700
11755
|
const manifest = await loadManifest11(project.root);
|
|
11701
11756
|
if (manifest.shared.length > 0) {
|
|
11702
11757
|
for (const entry of manifest.shared) {
|
|
11703
|
-
const fullPath =
|
|
11704
|
-
if (!
|
|
11758
|
+
const fullPath = resolve14(project.root, entry.file);
|
|
11759
|
+
if (!existsSync23(fullPath)) {
|
|
11705
11760
|
result.pages.withErrors++;
|
|
11706
11761
|
if (!opts.json) console.log(chalk20.red(`
|
|
11707
11762
|
\u2717 Missing shared component file: ${entry.id} (${entry.file})`));
|
|
@@ -11725,8 +11780,8 @@ async function checkCommand(opts = {}) {
|
|
|
11725
11780
|
let _staleUsedIn = 0;
|
|
11726
11781
|
let _nameMismatch = 0;
|
|
11727
11782
|
for (const entry of manifest.shared) {
|
|
11728
|
-
const filePath =
|
|
11729
|
-
const fileExists =
|
|
11783
|
+
const filePath = resolve14(projectRoot, entry.file);
|
|
11784
|
+
const fileExists = existsSync23(filePath);
|
|
11730
11785
|
if (!fileExists) {
|
|
11731
11786
|
_orphaned++;
|
|
11732
11787
|
if (!opts.json) {
|
|
@@ -11736,7 +11791,7 @@ async function checkCommand(opts = {}) {
|
|
|
11736
11791
|
continue;
|
|
11737
11792
|
}
|
|
11738
11793
|
try {
|
|
11739
|
-
const code =
|
|
11794
|
+
const code = readFileSync17(filePath, "utf-8");
|
|
11740
11795
|
const actualExports = extractExportedComponentNames(code);
|
|
11741
11796
|
if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
|
|
11742
11797
|
_nameMismatch++;
|
|
@@ -11804,7 +11859,7 @@ async function checkCommand(opts = {}) {
|
|
|
11804
11859
|
id: e.id,
|
|
11805
11860
|
name: e.name,
|
|
11806
11861
|
type: e.type,
|
|
11807
|
-
status:
|
|
11862
|
+
status: existsSync23(resolve14(projectRoot, e.file)) ? "ok" : "unused",
|
|
11808
11863
|
message: "",
|
|
11809
11864
|
suggestions: void 0
|
|
11810
11865
|
}))
|
|
@@ -11899,8 +11954,8 @@ import {
|
|
|
11899
11954
|
generateSharedComponent as generateSharedComponent5,
|
|
11900
11955
|
integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3
|
|
11901
11956
|
} from "@getcoherent/core";
|
|
11902
|
-
import { existsSync as
|
|
11903
|
-
import { resolve as
|
|
11957
|
+
import { existsSync as existsSync24 } from "fs";
|
|
11958
|
+
import { resolve as resolve15 } from "path";
|
|
11904
11959
|
|
|
11905
11960
|
// src/utils/ds-files.ts
|
|
11906
11961
|
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
@@ -12030,8 +12085,8 @@ function createComponentsCommand() {
|
|
|
12030
12085
|
const updated = await integrateSharedLayoutIntoRootLayout3(project.root);
|
|
12031
12086
|
if (updated) console.log(chalk26.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
12032
12087
|
}
|
|
12033
|
-
const sharedPagePath =
|
|
12034
|
-
if (!
|
|
12088
|
+
const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
|
|
12089
|
+
if (!existsSync24(sharedPagePath)) {
|
|
12035
12090
|
try {
|
|
12036
12091
|
const dsm = new DesignSystemManager12(project.configPath);
|
|
12037
12092
|
await dsm.load();
|
|
@@ -12057,8 +12112,8 @@ function createComponentsCommand() {
|
|
|
12057
12112
|
import chalk27 from "chalk";
|
|
12058
12113
|
import ora6 from "ora";
|
|
12059
12114
|
import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
12060
|
-
import { resolve as
|
|
12061
|
-
import { existsSync as
|
|
12115
|
+
import { resolve as resolve16, join as join18, dirname as dirname9 } from "path";
|
|
12116
|
+
import { existsSync as existsSync25 } from "fs";
|
|
12062
12117
|
import {
|
|
12063
12118
|
FigmaClient,
|
|
12064
12119
|
parseFigmaFileResponse,
|
|
@@ -12282,7 +12337,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
12282
12337
|
if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
|
|
12283
12338
|
else
|
|
12284
12339
|
await writeFile7(
|
|
12285
|
-
|
|
12340
|
+
resolve16(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
|
|
12286
12341
|
JSON.stringify(componentMapObj, null, 2),
|
|
12287
12342
|
"utf-8"
|
|
12288
12343
|
);
|
|
@@ -12305,9 +12360,9 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
12305
12360
|
const fullConfig = buildFigmaImportConfig(mergedConfig, pageDefs, intermediate.fileName);
|
|
12306
12361
|
if (!dryRun) {
|
|
12307
12362
|
spinner.start("Updating design-system.config.ts...");
|
|
12308
|
-
const configPath =
|
|
12363
|
+
const configPath = resolve16(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
|
|
12309
12364
|
const dsm = new DesignSystemManager13(configPath);
|
|
12310
|
-
if (
|
|
12365
|
+
if (existsSync25(configPath)) {
|
|
12311
12366
|
await dsm.load();
|
|
12312
12367
|
const existing = dsm.getConfig();
|
|
12313
12368
|
dsm.updateConfig({
|
|
@@ -12337,7 +12392,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
12337
12392
|
spinner.succeed("design-system.config.ts updated");
|
|
12338
12393
|
spinner.start("Ensuring root layout...");
|
|
12339
12394
|
const layoutPath = join18(projectRoot, "app/layout.tsx");
|
|
12340
|
-
if (!
|
|
12395
|
+
if (!existsSync25(layoutPath)) {
|
|
12341
12396
|
await mkdir7(dirname9(layoutPath), { recursive: true });
|
|
12342
12397
|
await writeFile7(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
12343
12398
|
stats.filesWritten.push("app/layout.tsx");
|
|
@@ -12427,7 +12482,7 @@ async function dsRegenerateCommand() {
|
|
|
12427
12482
|
// src/commands/update.ts
|
|
12428
12483
|
import chalk29 from "chalk";
|
|
12429
12484
|
import ora8 from "ora";
|
|
12430
|
-
import { readFileSync as
|
|
12485
|
+
import { readFileSync as readFileSync18, existsSync as existsSync26 } from "fs";
|
|
12431
12486
|
import { join as join19 } from "path";
|
|
12432
12487
|
import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
12433
12488
|
|
|
@@ -12598,9 +12653,9 @@ var EXPECTED_CSS_VARS = [
|
|
|
12598
12653
|
];
|
|
12599
12654
|
function checkMissingCssVars(projectRoot) {
|
|
12600
12655
|
const globalsPath = join19(projectRoot, "app", "globals.css");
|
|
12601
|
-
if (!
|
|
12656
|
+
if (!existsSync26(globalsPath)) return [];
|
|
12602
12657
|
try {
|
|
12603
|
-
const content =
|
|
12658
|
+
const content = readFileSync18(globalsPath, "utf-8");
|
|
12604
12659
|
return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
|
|
12605
12660
|
} catch {
|
|
12606
12661
|
return [];
|
|
@@ -12608,9 +12663,9 @@ function checkMissingCssVars(projectRoot) {
|
|
|
12608
12663
|
}
|
|
12609
12664
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
12610
12665
|
const globalsPath = join19(projectRoot, "app", "globals.css");
|
|
12611
|
-
if (!
|
|
12666
|
+
if (!existsSync26(globalsPath) || missingVars.length === 0) return;
|
|
12612
12667
|
const { writeFileSync: writeFileSync14 } = __require("fs");
|
|
12613
|
-
let content =
|
|
12668
|
+
let content = readFileSync18(globalsPath, "utf-8");
|
|
12614
12669
|
const defaultValues = {
|
|
12615
12670
|
"--chart-1": "220 70% 50%",
|
|
12616
12671
|
"--chart-2": "160 60% 45%",
|
|
@@ -12688,7 +12743,7 @@ async function undoCommand(options) {
|
|
|
12688
12743
|
// src/commands/sync.ts
|
|
12689
12744
|
import chalk31 from "chalk";
|
|
12690
12745
|
import ora9 from "ora";
|
|
12691
|
-
import { existsSync as
|
|
12746
|
+
import { existsSync as existsSync27, readFileSync as readFileSync19 } from "fs";
|
|
12692
12747
|
import { join as join20, relative as relative5, dirname as dirname10 } from "path";
|
|
12693
12748
|
import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
12694
12749
|
import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
|
|
@@ -12697,8 +12752,8 @@ function extractTokensFromProject(projectRoot) {
|
|
|
12697
12752
|
const lightColors = {};
|
|
12698
12753
|
const darkColors = {};
|
|
12699
12754
|
const globalsPath = join20(projectRoot, "app", "globals.css");
|
|
12700
|
-
if (
|
|
12701
|
-
const css =
|
|
12755
|
+
if (existsSync27(globalsPath)) {
|
|
12756
|
+
const css = readFileSync19(globalsPath, "utf-8");
|
|
12702
12757
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
12703
12758
|
if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
|
|
12704
12759
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
@@ -12706,8 +12761,8 @@ function extractTokensFromProject(projectRoot) {
|
|
|
12706
12761
|
}
|
|
12707
12762
|
const layoutPath = join20(projectRoot, "app", "layout.tsx");
|
|
12708
12763
|
let layoutCode = "";
|
|
12709
|
-
if (
|
|
12710
|
-
layoutCode =
|
|
12764
|
+
if (existsSync27(layoutPath)) {
|
|
12765
|
+
layoutCode = readFileSync19(layoutPath, "utf-8");
|
|
12711
12766
|
const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
|
|
12712
12767
|
if (rootInline && Object.keys(lightColors).length === 0) {
|
|
12713
12768
|
parseVarsInto(rootInline[1], lightColors);
|
|
@@ -12725,7 +12780,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
12725
12780
|
defaultMode = "dark";
|
|
12726
12781
|
}
|
|
12727
12782
|
let radius;
|
|
12728
|
-
const allCss = [
|
|
12783
|
+
const allCss = [existsSync27(globalsPath) ? readFileSync19(globalsPath, "utf-8") : "", layoutCode].join("\n");
|
|
12729
12784
|
const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
|
|
12730
12785
|
if (radiusMatch) radius = radiusMatch[1].trim();
|
|
12731
12786
|
return {
|
|
@@ -12749,7 +12804,7 @@ function parseVarsInto(block, target) {
|
|
|
12749
12804
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
12750
12805
|
const results = [];
|
|
12751
12806
|
const componentsDir = join20(projectRoot, "components");
|
|
12752
|
-
if (!
|
|
12807
|
+
if (!existsSync27(componentsDir)) return results;
|
|
12753
12808
|
const files = [];
|
|
12754
12809
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
12755
12810
|
const fileResults = await Promise.all(
|
|
@@ -12928,7 +12983,7 @@ async function syncCommand(options = {}) {
|
|
|
12928
12983
|
const spinner = ora9("Scanning project files...").start();
|
|
12929
12984
|
try {
|
|
12930
12985
|
const appDir = join20(project.root, "app");
|
|
12931
|
-
if (!
|
|
12986
|
+
if (!existsSync27(appDir)) {
|
|
12932
12987
|
spinner.fail("No app/ directory found");
|
|
12933
12988
|
process.exit(1);
|
|
12934
12989
|
}
|
|
@@ -13155,7 +13210,7 @@ async function syncCommand(options = {}) {
|
|
|
13155
13210
|
// src/commands/migrate.ts
|
|
13156
13211
|
import chalk32 from "chalk";
|
|
13157
13212
|
import ora10 from "ora";
|
|
13158
|
-
import { existsSync as
|
|
13213
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync20, readdirSync as readdirSync10 } from "fs";
|
|
13159
13214
|
import { join as join21 } from "path";
|
|
13160
13215
|
function backupDir(projectRoot) {
|
|
13161
13216
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -13168,11 +13223,11 @@ function createBackup2(projectRoot) {
|
|
|
13168
13223
|
const uiDir = join21(projectRoot, "components", "ui");
|
|
13169
13224
|
const dest = backupDir(projectRoot);
|
|
13170
13225
|
mkdirSync8(dest, { recursive: true });
|
|
13171
|
-
if (
|
|
13226
|
+
if (existsSync28(uiDir)) {
|
|
13172
13227
|
cpSync(uiDir, join21(dest, "components-ui"), { recursive: true });
|
|
13173
13228
|
}
|
|
13174
13229
|
const configPath = join21(projectRoot, "design-system.config.ts");
|
|
13175
|
-
if (
|
|
13230
|
+
if (existsSync28(configPath)) {
|
|
13176
13231
|
cpSync(configPath, join21(dest, "design-system.config.ts"));
|
|
13177
13232
|
}
|
|
13178
13233
|
return dest;
|
|
@@ -13184,24 +13239,24 @@ function setGuard(projectRoot, backupPath) {
|
|
|
13184
13239
|
}
|
|
13185
13240
|
function clearGuard(projectRoot) {
|
|
13186
13241
|
const guard = guardPath(projectRoot);
|
|
13187
|
-
if (
|
|
13242
|
+
if (existsSync28(guard)) rmSync6(guard);
|
|
13188
13243
|
}
|
|
13189
13244
|
function rollback(projectRoot) {
|
|
13190
13245
|
const guard = guardPath(projectRoot);
|
|
13191
|
-
if (!
|
|
13246
|
+
if (!existsSync28(guard)) return false;
|
|
13192
13247
|
try {
|
|
13193
|
-
const data = JSON.parse(
|
|
13248
|
+
const data = JSON.parse(readFileSync20(guard, "utf-8"));
|
|
13194
13249
|
const backup = data.backup;
|
|
13195
|
-
if (!
|
|
13250
|
+
if (!existsSync28(backup)) return false;
|
|
13196
13251
|
const uiBackup = join21(backup, "components-ui");
|
|
13197
13252
|
const uiDir = join21(projectRoot, "components", "ui");
|
|
13198
|
-
if (
|
|
13199
|
-
if (
|
|
13253
|
+
if (existsSync28(uiBackup)) {
|
|
13254
|
+
if (existsSync28(uiDir)) rmSync6(uiDir, { recursive: true });
|
|
13200
13255
|
cpSync(uiBackup, uiDir, { recursive: true });
|
|
13201
13256
|
}
|
|
13202
13257
|
const configBackup = join21(backup, "design-system.config.ts");
|
|
13203
13258
|
const configDest = join21(projectRoot, "design-system.config.ts");
|
|
13204
|
-
if (
|
|
13259
|
+
if (existsSync28(configBackup)) {
|
|
13205
13260
|
cpSync(configBackup, configDest);
|
|
13206
13261
|
}
|
|
13207
13262
|
clearGuard(projectRoot);
|
|
@@ -13229,19 +13284,19 @@ async function migrateAction(options) {
|
|
|
13229
13284
|
return;
|
|
13230
13285
|
}
|
|
13231
13286
|
const guard = guardPath(projectRoot);
|
|
13232
|
-
if (
|
|
13287
|
+
if (existsSync28(guard)) {
|
|
13233
13288
|
console.log(chalk32.yellow("A migration is already in progress."));
|
|
13234
13289
|
console.log(chalk32.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
|
|
13235
13290
|
return;
|
|
13236
13291
|
}
|
|
13237
13292
|
const uiDir = join21(projectRoot, "components", "ui");
|
|
13238
|
-
if (!
|
|
13293
|
+
if (!existsSync28(uiDir)) {
|
|
13239
13294
|
console.log(chalk32.yellow("No components/ui directory found. Nothing to migrate."));
|
|
13240
13295
|
return;
|
|
13241
13296
|
}
|
|
13242
13297
|
const provider = getComponentProvider();
|
|
13243
13298
|
const managedIds = new Set(provider.listNames());
|
|
13244
|
-
const files =
|
|
13299
|
+
const files = readdirSync10(uiDir).filter((f) => f.endsWith(".tsx"));
|
|
13245
13300
|
const migratable = files.map((f) => f.replace(".tsx", "")).filter((id) => managedIds.has(id));
|
|
13246
13301
|
if (migratable.length === 0) {
|
|
13247
13302
|
console.log(chalk32.green("All components are already up to date."));
|
|
@@ -13262,7 +13317,7 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
13262
13317
|
try {
|
|
13263
13318
|
for (const id of migratable) {
|
|
13264
13319
|
const filePath = join21(uiDir, `${id}.tsx`);
|
|
13265
|
-
if (
|
|
13320
|
+
if (existsSync28(filePath)) rmSync6(filePath);
|
|
13266
13321
|
}
|
|
13267
13322
|
const results = await provider.installBatch(migratable, projectRoot, { force: true });
|
|
13268
13323
|
let migrated = 0;
|
|
@@ -13284,7 +13339,7 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
13284
13339
|
}
|
|
13285
13340
|
|
|
13286
13341
|
// src/utils/update-notifier.ts
|
|
13287
|
-
import { existsSync as
|
|
13342
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
|
|
13288
13343
|
import { join as join22 } from "path";
|
|
13289
13344
|
import { homedir } from "os";
|
|
13290
13345
|
import chalk33 from "chalk";
|
|
@@ -13296,8 +13351,8 @@ var CACHE_FILE = join22(CACHE_DIR, "update-check.json");
|
|
|
13296
13351
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
13297
13352
|
function readCache() {
|
|
13298
13353
|
try {
|
|
13299
|
-
if (!
|
|
13300
|
-
const raw =
|
|
13354
|
+
if (!existsSync29(CACHE_FILE)) return null;
|
|
13355
|
+
const raw = readFileSync21(CACHE_FILE, "utf-8");
|
|
13301
13356
|
return JSON.parse(raw);
|
|
13302
13357
|
} catch (e) {
|
|
13303
13358
|
if (DEBUG5) console.error("Failed to read update cache:", e);
|
|
@@ -13306,7 +13361,7 @@ function readCache() {
|
|
|
13306
13361
|
}
|
|
13307
13362
|
function writeCache(data) {
|
|
13308
13363
|
try {
|
|
13309
|
-
if (!
|
|
13364
|
+
if (!existsSync29(CACHE_DIR)) mkdirSync9(CACHE_DIR, { recursive: true });
|
|
13310
13365
|
writeFileSync13(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
13311
13366
|
} catch (e) {
|
|
13312
13367
|
if (DEBUG5) console.error("Failed to write update cache:", e);
|