@getcoherent/cli 0.2.0 → 0.2.1
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 +186 -119
- package/package.json +10 -10
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -601,7 +601,7 @@ function installPackages(projectRoot, packages) {
|
|
|
601
601
|
if (packages.length === 0) return Promise.resolve(true);
|
|
602
602
|
const safe = packages.filter((p) => SAFE_PKG_NAME.test(p));
|
|
603
603
|
if (safe.length === 0) return Promise.resolve(true);
|
|
604
|
-
return new Promise((
|
|
604
|
+
return new Promise((resolve17) => {
|
|
605
605
|
try {
|
|
606
606
|
const hasPnpm = existsSync4(join2(projectRoot, "pnpm-lock.yaml"));
|
|
607
607
|
if (hasPnpm) {
|
|
@@ -612,10 +612,10 @@ function installPackages(projectRoot, packages) {
|
|
|
612
612
|
stdio: "pipe"
|
|
613
613
|
});
|
|
614
614
|
}
|
|
615
|
-
|
|
615
|
+
resolve17(true);
|
|
616
616
|
} catch (e) {
|
|
617
617
|
if (process.env.COHERENT_DEBUG === "1") console.error("Failed to install packages:", e);
|
|
618
|
-
|
|
618
|
+
resolve17(false);
|
|
619
619
|
}
|
|
620
620
|
});
|
|
621
621
|
}
|
|
@@ -2425,7 +2425,7 @@ export default config
|
|
|
2425
2425
|
// src/commands/chat.ts
|
|
2426
2426
|
import chalk13 from "chalk";
|
|
2427
2427
|
import ora2 from "ora";
|
|
2428
|
-
import { resolve as
|
|
2428
|
+
import { resolve as resolve10 } from "path";
|
|
2429
2429
|
import { existsSync as existsSync16, readFileSync as readFileSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
2430
2430
|
import {
|
|
2431
2431
|
DesignSystemManager as DesignSystemManager7,
|
|
@@ -6519,6 +6519,53 @@ ${fixed}`;
|
|
|
6519
6519
|
}
|
|
6520
6520
|
}
|
|
6521
6521
|
}
|
|
6522
|
+
const lucideImportMatch2 = fixed.match(/import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/);
|
|
6523
|
+
if (lucideImportMatch2) {
|
|
6524
|
+
let lucideExports2 = null;
|
|
6525
|
+
try {
|
|
6526
|
+
const { createRequire } = await import("module");
|
|
6527
|
+
const req = createRequire(process.cwd() + "/package.json");
|
|
6528
|
+
const lr = req("lucide-react");
|
|
6529
|
+
lucideExports2 = new Set(Object.keys(lr).filter((k) => /^[A-Z]/.test(k)));
|
|
6530
|
+
} catch {
|
|
6531
|
+
}
|
|
6532
|
+
if (lucideExports2) {
|
|
6533
|
+
const importedNames = new Set(
|
|
6534
|
+
lucideImportMatch2[1].split(",").map((s) => s.trim()).filter(Boolean)
|
|
6535
|
+
);
|
|
6536
|
+
const jsxIconRefs = [...fixed.matchAll(/<([A-Z][a-zA-Z]*(?:Icon)?)\s/g)].map((m) => m[1]);
|
|
6537
|
+
const htmlElements = /* @__PURE__ */ new Set(["Link", "Fragment", "Suspense", "Image"]);
|
|
6538
|
+
const missing = [];
|
|
6539
|
+
for (const ref of jsxIconRefs) {
|
|
6540
|
+
if (importedNames.has(ref) || htmlElements.has(ref)) continue;
|
|
6541
|
+
if (fixed.includes(`function ${ref}`) || fixed.includes(`const ${ref}`)) continue;
|
|
6542
|
+
if (/^import\s.*\b${ref}\b/m.test(fixed)) continue;
|
|
6543
|
+
const baseName = ref.replace(/Icon$/, "");
|
|
6544
|
+
if (lucideExports2.has(ref)) {
|
|
6545
|
+
missing.push(ref);
|
|
6546
|
+
importedNames.add(ref);
|
|
6547
|
+
} else if (lucideExports2.has(baseName)) {
|
|
6548
|
+
const re = new RegExp(`\\b${ref}\\b`, "g");
|
|
6549
|
+
fixed = fixed.replace(re, baseName);
|
|
6550
|
+
missing.push(baseName);
|
|
6551
|
+
importedNames.add(baseName);
|
|
6552
|
+
fixes.push(`renamed ${ref} \u2192 ${baseName} (lucide-react)`);
|
|
6553
|
+
} else {
|
|
6554
|
+
const fallback = "Circle";
|
|
6555
|
+
const re = new RegExp(`\\b${ref}\\b`, "g");
|
|
6556
|
+
fixed = fixed.replace(re, fallback);
|
|
6557
|
+
importedNames.add(fallback);
|
|
6558
|
+
fixes.push(`unknown icon ${ref} \u2192 ${fallback}`);
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6561
|
+
if (missing.length > 0) {
|
|
6562
|
+
const allNames = [...importedNames];
|
|
6563
|
+
const origLine = lucideImportMatch2[0];
|
|
6564
|
+
fixed = fixed.replace(origLine, `import { ${allNames.join(", ")} } from "lucide-react"`);
|
|
6565
|
+
fixes.push(`added missing lucide imports: ${missing.join(", ")}`);
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
}
|
|
6522
6569
|
fixed = fixed.replace(/className="([^"]*)"/g, (_match, inner) => {
|
|
6523
6570
|
const cleaned = inner.replace(/\s{2,}/g, " ").trim();
|
|
6524
6571
|
return `className="${cleaned}"`;
|
|
@@ -7542,7 +7589,8 @@ ${pagesCtx}`
|
|
|
7542
7589
|
}
|
|
7543
7590
|
|
|
7544
7591
|
// src/commands/chat/layout-extractor.ts
|
|
7545
|
-
import { readFileSync as readFileSync9 } from "fs";
|
|
7592
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
|
|
7593
|
+
import { join as join9, resolve as resolve8 } from "path";
|
|
7546
7594
|
import chalk11 from "chalk";
|
|
7547
7595
|
import { loadManifest as loadManifest6, generateSharedComponent as generateSharedComponent3, integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3 } from "@getcoherent/core";
|
|
7548
7596
|
|
|
@@ -7645,6 +7693,21 @@ function addActiveNavToHeader(code) {
|
|
|
7645
7693
|
}
|
|
7646
7694
|
|
|
7647
7695
|
// src/commands/chat/layout-extractor.ts
|
|
7696
|
+
function findAllPageFiles(dir) {
|
|
7697
|
+
const results = [];
|
|
7698
|
+
try {
|
|
7699
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
7700
|
+
const full = join9(dir, entry.name);
|
|
7701
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".next" && entry.name !== "design-system") {
|
|
7702
|
+
results.push(...findAllPageFiles(full));
|
|
7703
|
+
} else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
|
|
7704
|
+
results.push(full);
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
} catch {
|
|
7708
|
+
}
|
|
7709
|
+
return results;
|
|
7710
|
+
}
|
|
7648
7711
|
async function extractAndShareLayoutComponents(projectRoot, generatedPageFiles) {
|
|
7649
7712
|
const manifest = await loadManifest6(projectRoot);
|
|
7650
7713
|
const hasSharedHeader = manifest.shared.some((c) => c.type === "layout" && /header|nav/i.test(c.name));
|
|
@@ -7727,7 +7790,11 @@ ${stateBlock}${returnIndent}return (
|
|
|
7727
7790
|
if (!extracted) return false;
|
|
7728
7791
|
await integrateSharedLayoutIntoRootLayout3(projectRoot);
|
|
7729
7792
|
await ensureAuthRouteGroup(projectRoot);
|
|
7730
|
-
|
|
7793
|
+
const allPageFiles = /* @__PURE__ */ new Set([
|
|
7794
|
+
...generatedPageFiles,
|
|
7795
|
+
...findAllPageFiles(resolve8(projectRoot, "app"))
|
|
7796
|
+
]);
|
|
7797
|
+
for (const file of allPageFiles) {
|
|
7731
7798
|
try {
|
|
7732
7799
|
let code = await readFile(file);
|
|
7733
7800
|
const original = code;
|
|
@@ -7752,7 +7819,7 @@ ${stateBlock}${returnIndent}return (
|
|
|
7752
7819
|
|
|
7753
7820
|
// src/commands/chat/interactive.ts
|
|
7754
7821
|
import chalk12 from "chalk";
|
|
7755
|
-
import { resolve as
|
|
7822
|
+
import { resolve as resolve9 } from "path";
|
|
7756
7823
|
import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
7757
7824
|
import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager3, loadManifest as loadManifest7 } from "@getcoherent/core";
|
|
7758
7825
|
var DEBUG3 = process.env.COHERENT_DEBUG === "1";
|
|
@@ -7773,8 +7840,8 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
7773
7840
|
\u274C Invalid provider: ${options.provider}`));
|
|
7774
7841
|
process.exit(1);
|
|
7775
7842
|
}
|
|
7776
|
-
const historyDir =
|
|
7777
|
-
const historyFile =
|
|
7843
|
+
const historyDir = resolve9(homedir2(), ".coherent");
|
|
7844
|
+
const historyFile = resolve9(historyDir, "history");
|
|
7778
7845
|
let history = [];
|
|
7779
7846
|
try {
|
|
7780
7847
|
mkdirSync5(historyDir, { recursive: true });
|
|
@@ -8003,7 +8070,7 @@ async function chatCommand(message, options) {
|
|
|
8003
8070
|
}
|
|
8004
8071
|
if (/switch to light mode|default to light|make.*light.*(default|theme)|light theme/i.test(message)) {
|
|
8005
8072
|
spinner.start("Setting default theme to light...");
|
|
8006
|
-
const layoutPath =
|
|
8073
|
+
const layoutPath = resolve10(projectRoot, "app/layout.tsx");
|
|
8007
8074
|
try {
|
|
8008
8075
|
let layout = await readFile(layoutPath);
|
|
8009
8076
|
layout = layout.replace(/className="dark"/, "");
|
|
@@ -8042,7 +8109,7 @@ async function chatCommand(message, options) {
|
|
|
8042
8109
|
spinner.start("Parsing your request...");
|
|
8043
8110
|
let manifest = await loadManifest8(project.root);
|
|
8044
8111
|
const validShared = manifest.shared.filter((s) => {
|
|
8045
|
-
const fp =
|
|
8112
|
+
const fp = resolve10(project.root, s.file);
|
|
8046
8113
|
return existsSync16(fp);
|
|
8047
8114
|
});
|
|
8048
8115
|
if (validShared.length !== manifest.shared.length) {
|
|
@@ -8443,7 +8510,7 @@ async function chatCommand(message, options) {
|
|
|
8443
8510
|
const isAuth = isAuthRoute(linkedRoute);
|
|
8444
8511
|
const filePath = routeToFsPath(projectRoot, linkedRoute, isAuth);
|
|
8445
8512
|
if (isAuth) await ensureAuthRouteGroup(projectRoot);
|
|
8446
|
-
const dir =
|
|
8513
|
+
const dir = resolve10(filePath, "..");
|
|
8447
8514
|
if (!existsSync16(dir)) {
|
|
8448
8515
|
mkdirSync6(dir, { recursive: true });
|
|
8449
8516
|
}
|
|
@@ -8479,7 +8546,7 @@ async function chatCommand(message, options) {
|
|
|
8479
8546
|
dsm.updateConfig(latestConfig);
|
|
8480
8547
|
if (DEBUG4) console.log(chalk13.dim(` [theme] Set defaultMode to "${targetMode}"`));
|
|
8481
8548
|
}
|
|
8482
|
-
const layoutPath =
|
|
8549
|
+
const layoutPath = resolve10(projectRoot, "app", "layout.tsx");
|
|
8483
8550
|
try {
|
|
8484
8551
|
let layoutCode = await readFile(layoutPath);
|
|
8485
8552
|
if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
|
|
@@ -8534,7 +8601,7 @@ async function chatCommand(message, options) {
|
|
|
8534
8601
|
console.log("");
|
|
8535
8602
|
}
|
|
8536
8603
|
if (uxRecommendations) {
|
|
8537
|
-
const recPath =
|
|
8604
|
+
const recPath = resolve10(projectRoot, "recommendations.md");
|
|
8538
8605
|
const section = `
|
|
8539
8606
|
|
|
8540
8607
|
---
|
|
@@ -8619,18 +8686,18 @@ import chalk14 from "chalk";
|
|
|
8619
8686
|
import ora3 from "ora";
|
|
8620
8687
|
import { spawn } from "child_process";
|
|
8621
8688
|
import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
8622
|
-
import { resolve as
|
|
8689
|
+
import { resolve as resolve11, join as join12 } from "path";
|
|
8623
8690
|
import { readdir as readdir2 } from "fs/promises";
|
|
8624
8691
|
import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
|
|
8625
8692
|
|
|
8626
8693
|
// src/utils/file-watcher.ts
|
|
8627
8694
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
|
|
8628
|
-
import { relative as relative3, join as
|
|
8695
|
+
import { relative as relative3, join as join11 } from "path";
|
|
8629
8696
|
import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
|
|
8630
8697
|
|
|
8631
8698
|
// src/utils/component-integrity.ts
|
|
8632
|
-
import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as
|
|
8633
|
-
import { join as
|
|
8699
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as readdirSync3 } from "fs";
|
|
8700
|
+
import { join as join10, relative as relative2 } from "path";
|
|
8634
8701
|
function extractExportedComponentNames(code) {
|
|
8635
8702
|
const names = [];
|
|
8636
8703
|
let m;
|
|
@@ -8656,7 +8723,7 @@ function arraysEqual(a, b) {
|
|
|
8656
8723
|
}
|
|
8657
8724
|
function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
8658
8725
|
const results = [];
|
|
8659
|
-
const appDir =
|
|
8726
|
+
const appDir = join10(projectRoot, "app");
|
|
8660
8727
|
if (!existsSync17(appDir)) return results;
|
|
8661
8728
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8662
8729
|
const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
@@ -8676,7 +8743,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
8676
8743
|
return results;
|
|
8677
8744
|
}
|
|
8678
8745
|
function isUsedInLayout(projectRoot, componentName) {
|
|
8679
|
-
const layoutPath =
|
|
8746
|
+
const layoutPath = join10(projectRoot, "app", "layout.tsx");
|
|
8680
8747
|
if (!existsSync17(layoutPath)) return false;
|
|
8681
8748
|
try {
|
|
8682
8749
|
const code = readFileSync12(layoutPath, "utf-8");
|
|
@@ -8687,7 +8754,7 @@ function isUsedInLayout(projectRoot, componentName) {
|
|
|
8687
8754
|
}
|
|
8688
8755
|
function findUnregisteredComponents(projectRoot, manifest) {
|
|
8689
8756
|
const results = [];
|
|
8690
|
-
const componentsDir =
|
|
8757
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8691
8758
|
if (!existsSync17(componentsDir)) return results;
|
|
8692
8759
|
const registeredFiles = new Set(manifest.shared.map((s) => s.file));
|
|
8693
8760
|
const registeredNames = new Set(manifest.shared.map((s) => s.name));
|
|
@@ -8715,7 +8782,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
8715
8782
|
}
|
|
8716
8783
|
function findInlineDuplicates(projectRoot, manifest) {
|
|
8717
8784
|
const results = [];
|
|
8718
|
-
const appDir =
|
|
8785
|
+
const appDir = join10(projectRoot, "app");
|
|
8719
8786
|
if (!existsSync17(appDir)) return results;
|
|
8720
8787
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8721
8788
|
for (const absPath of pageFiles) {
|
|
@@ -8745,7 +8812,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
8745
8812
|
return results;
|
|
8746
8813
|
}
|
|
8747
8814
|
function findComponentFileByExportName(projectRoot, componentName) {
|
|
8748
|
-
const componentsDir =
|
|
8815
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8749
8816
|
if (!existsSync17(componentsDir)) return null;
|
|
8750
8817
|
const files = collectFiles(
|
|
8751
8818
|
componentsDir,
|
|
@@ -8767,7 +8834,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
8767
8834
|
function removeOrphanedEntries(projectRoot, manifest) {
|
|
8768
8835
|
const removed = [];
|
|
8769
8836
|
const valid = manifest.shared.filter((entry) => {
|
|
8770
|
-
const filePath =
|
|
8837
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8771
8838
|
if (existsSync17(filePath)) return true;
|
|
8772
8839
|
removed.push({ id: entry.id, name: entry.name });
|
|
8773
8840
|
return false;
|
|
@@ -8786,7 +8853,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8786
8853
|
};
|
|
8787
8854
|
const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
|
|
8788
8855
|
m.shared = m.shared.filter((entry) => {
|
|
8789
|
-
const filePath =
|
|
8856
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8790
8857
|
if (!existsSync17(filePath)) {
|
|
8791
8858
|
const newPath = findComponentFileByExportName(projectRoot, entry.name);
|
|
8792
8859
|
if (newPath) {
|
|
@@ -8799,7 +8866,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8799
8866
|
}
|
|
8800
8867
|
let code;
|
|
8801
8868
|
try {
|
|
8802
|
-
code = readFileSync12(
|
|
8869
|
+
code = readFileSync12(join10(projectRoot, entry.file), "utf-8");
|
|
8803
8870
|
} catch {
|
|
8804
8871
|
return true;
|
|
8805
8872
|
}
|
|
@@ -8876,12 +8943,12 @@ function collectFiles(dir, filter, skipDirs = []) {
|
|
|
8876
8943
|
function walk(d) {
|
|
8877
8944
|
let entries;
|
|
8878
8945
|
try {
|
|
8879
|
-
entries =
|
|
8946
|
+
entries = readdirSync3(d, { withFileTypes: true });
|
|
8880
8947
|
} catch {
|
|
8881
8948
|
return;
|
|
8882
8949
|
}
|
|
8883
8950
|
for (const e of entries) {
|
|
8884
|
-
const full =
|
|
8951
|
+
const full = join10(d, e.name);
|
|
8885
8952
|
if (e.isDirectory()) {
|
|
8886
8953
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
8887
8954
|
walk(full);
|
|
@@ -8920,7 +8987,7 @@ function findInlineDuplicatesOfShared(content, manifest) {
|
|
|
8920
8987
|
}
|
|
8921
8988
|
function getWatcherConfig(projectRoot) {
|
|
8922
8989
|
try {
|
|
8923
|
-
const pkgPath =
|
|
8990
|
+
const pkgPath = join11(projectRoot, "package.json");
|
|
8924
8991
|
if (!existsSync18(pkgPath)) return defaultWatcherConfig();
|
|
8925
8992
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
8926
8993
|
const c = pkg?.coherent?.watcher ?? {};
|
|
@@ -9049,8 +9116,8 @@ function startFileWatcher(projectRoot) {
|
|
|
9049
9116
|
let watcher = null;
|
|
9050
9117
|
let manifestWatcher = null;
|
|
9051
9118
|
import("chokidar").then((chokidar) => {
|
|
9052
|
-
const appGlob =
|
|
9053
|
-
const compGlob =
|
|
9119
|
+
const appGlob = join11(projectRoot, "app", "**", "*.tsx");
|
|
9120
|
+
const compGlob = join11(projectRoot, "components", "**", "*.tsx");
|
|
9054
9121
|
watcher = chokidar.default.watch([appGlob, compGlob], {
|
|
9055
9122
|
ignoreInitial: true,
|
|
9056
9123
|
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
@@ -9062,7 +9129,7 @@ function startFileWatcher(projectRoot) {
|
|
|
9062
9129
|
});
|
|
9063
9130
|
watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
|
|
9064
9131
|
});
|
|
9065
|
-
const manifestPath =
|
|
9132
|
+
const manifestPath = join11(projectRoot, "coherent.components.json");
|
|
9066
9133
|
if (existsSync18(manifestPath)) {
|
|
9067
9134
|
import("chokidar").then((chokidar) => {
|
|
9068
9135
|
manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
|
|
@@ -9077,7 +9144,7 @@ function startFileWatcher(projectRoot) {
|
|
|
9077
9144
|
|
|
9078
9145
|
// src/commands/preview.ts
|
|
9079
9146
|
function getPackageManager(projectRoot) {
|
|
9080
|
-
const hasPnpm = existsSync19(
|
|
9147
|
+
const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
9081
9148
|
return hasPnpm ? "pnpm" : "npm";
|
|
9082
9149
|
}
|
|
9083
9150
|
function runInstall(projectRoot) {
|
|
@@ -9099,8 +9166,8 @@ function runInstall(projectRoot) {
|
|
|
9099
9166
|
});
|
|
9100
9167
|
}
|
|
9101
9168
|
function checkProjectInitialized(projectRoot) {
|
|
9102
|
-
const configPath =
|
|
9103
|
-
const packageJsonPath =
|
|
9169
|
+
const configPath = resolve11(projectRoot, "design-system.config.ts");
|
|
9170
|
+
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
9104
9171
|
if (!existsSync19(configPath)) {
|
|
9105
9172
|
return false;
|
|
9106
9173
|
}
|
|
@@ -9110,11 +9177,11 @@ function checkProjectInitialized(projectRoot) {
|
|
|
9110
9177
|
return true;
|
|
9111
9178
|
}
|
|
9112
9179
|
function checkDependenciesInstalled(projectRoot) {
|
|
9113
|
-
const nodeModulesPath =
|
|
9180
|
+
const nodeModulesPath = resolve11(projectRoot, "node_modules");
|
|
9114
9181
|
return existsSync19(nodeModulesPath);
|
|
9115
9182
|
}
|
|
9116
9183
|
function clearStaleCache(projectRoot) {
|
|
9117
|
-
const nextDir =
|
|
9184
|
+
const nextDir = join12(projectRoot, ".next");
|
|
9118
9185
|
if (existsSync19(nextDir)) {
|
|
9119
9186
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
9120
9187
|
console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
|
|
@@ -9135,7 +9202,7 @@ async function listPageFiles(appDir) {
|
|
|
9135
9202
|
async function walk(dir) {
|
|
9136
9203
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
9137
9204
|
for (const e of entries) {
|
|
9138
|
-
const full =
|
|
9205
|
+
const full = join12(dir, e.name);
|
|
9139
9206
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
|
|
9140
9207
|
else if (e.isFile() && e.name === "page.tsx") out.push(full);
|
|
9141
9208
|
}
|
|
@@ -9144,7 +9211,7 @@ async function listPageFiles(appDir) {
|
|
|
9144
9211
|
return out;
|
|
9145
9212
|
}
|
|
9146
9213
|
async function validateSyntax(projectRoot) {
|
|
9147
|
-
const appDir =
|
|
9214
|
+
const appDir = join12(projectRoot, "app");
|
|
9148
9215
|
const pages = await listPageFiles(appDir);
|
|
9149
9216
|
for (const file of pages) {
|
|
9150
9217
|
const content = readFileSync14(file, "utf-8");
|
|
@@ -9156,8 +9223,8 @@ async function validateSyntax(projectRoot) {
|
|
|
9156
9223
|
}
|
|
9157
9224
|
}
|
|
9158
9225
|
async function fixMissingComponentExports(projectRoot) {
|
|
9159
|
-
const appDir =
|
|
9160
|
-
const uiDir =
|
|
9226
|
+
const appDir = join12(projectRoot, "app");
|
|
9227
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9161
9228
|
if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
|
|
9162
9229
|
const pages = await listPageFiles(appDir);
|
|
9163
9230
|
const neededExports = /* @__PURE__ */ new Map();
|
|
@@ -9172,7 +9239,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9172
9239
|
for (const name of names) neededExports.get(componentId).add(name);
|
|
9173
9240
|
}
|
|
9174
9241
|
}
|
|
9175
|
-
const configPath =
|
|
9242
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9176
9243
|
let config2 = null;
|
|
9177
9244
|
try {
|
|
9178
9245
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9181,7 +9248,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9181
9248
|
}
|
|
9182
9249
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9183
9250
|
for (const [componentId, needed] of neededExports) {
|
|
9184
|
-
const componentFile =
|
|
9251
|
+
const componentFile = join12(uiDir, `${componentId}.tsx`);
|
|
9185
9252
|
const def = getShadcnComponent(componentId);
|
|
9186
9253
|
if (!existsSync19(componentFile)) {
|
|
9187
9254
|
if (!def) continue;
|
|
@@ -9218,7 +9285,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9218
9285
|
}
|
|
9219
9286
|
}
|
|
9220
9287
|
async function backfillPageAnalysis(projectRoot) {
|
|
9221
|
-
const configPath =
|
|
9288
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9222
9289
|
if (!existsSync19(configPath)) return;
|
|
9223
9290
|
try {
|
|
9224
9291
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9230,11 +9297,11 @@ async function backfillPageAnalysis(projectRoot) {
|
|
|
9230
9297
|
const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
|
|
9231
9298
|
let filePath;
|
|
9232
9299
|
if (route === "/") {
|
|
9233
|
-
filePath =
|
|
9300
|
+
filePath = join12(projectRoot, "app", "page.tsx");
|
|
9234
9301
|
} else if (isAuth) {
|
|
9235
|
-
filePath =
|
|
9302
|
+
filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
|
|
9236
9303
|
} else {
|
|
9237
|
-
filePath =
|
|
9304
|
+
filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
|
|
9238
9305
|
}
|
|
9239
9306
|
if (!existsSync19(filePath)) continue;
|
|
9240
9307
|
const code = readFileSync14(filePath, "utf-8");
|
|
@@ -9264,7 +9331,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9264
9331
|
const def = getShadcnComponent(componentId);
|
|
9265
9332
|
if (!def) return false;
|
|
9266
9333
|
try {
|
|
9267
|
-
const configPath =
|
|
9334
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9268
9335
|
let config2 = null;
|
|
9269
9336
|
try {
|
|
9270
9337
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9273,10 +9340,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9273
9340
|
}
|
|
9274
9341
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9275
9342
|
const code = await generator.generate(def);
|
|
9276
|
-
const uiDir =
|
|
9343
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9277
9344
|
const { mkdirSync: mkdirSync9 } = await import("fs");
|
|
9278
9345
|
mkdirSync9(uiDir, { recursive: true });
|
|
9279
|
-
writeFileSync10(
|
|
9346
|
+
writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
|
|
9280
9347
|
return true;
|
|
9281
9348
|
} catch {
|
|
9282
9349
|
return false;
|
|
@@ -9399,12 +9466,12 @@ async function openBrowser(url) {
|
|
|
9399
9466
|
}
|
|
9400
9467
|
}
|
|
9401
9468
|
function startDevServer(projectRoot) {
|
|
9402
|
-
const packageJsonPath =
|
|
9469
|
+
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
9403
9470
|
if (!existsSync19(packageJsonPath)) {
|
|
9404
9471
|
throw new Error('package.json not found. Run "coherent init" first.');
|
|
9405
9472
|
}
|
|
9406
|
-
const hasPnpm = existsSync19(
|
|
9407
|
-
const hasNpm = existsSync19(
|
|
9473
|
+
const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
9474
|
+
const hasNpm = existsSync19(resolve11(projectRoot, "package-lock.json"));
|
|
9408
9475
|
const command = hasPnpm ? "pnpm" : hasNpm ? "npm" : "npx";
|
|
9409
9476
|
const args = hasPnpm ? ["dev", "--turbo"] : hasNpm ? ["run", "dev", "--", "--turbo"] : ["next", "dev", "--turbo"];
|
|
9410
9477
|
const child = spawn(command, args, {
|
|
@@ -9451,7 +9518,7 @@ async function previewCommand() {
|
|
|
9451
9518
|
if (needsGlobalsFix(projectRoot)) {
|
|
9452
9519
|
spinner.text = "Fixing globals.css...";
|
|
9453
9520
|
try {
|
|
9454
|
-
const dsm = new DesignSystemManager8(
|
|
9521
|
+
const dsm = new DesignSystemManager8(resolve11(projectRoot, "design-system.config.ts"));
|
|
9455
9522
|
await dsm.load();
|
|
9456
9523
|
const config2 = dsm.getConfig();
|
|
9457
9524
|
fixGlobalsCss(projectRoot, config2);
|
|
@@ -9490,8 +9557,8 @@ async function previewCommand() {
|
|
|
9490
9557
|
import chalk15 from "chalk";
|
|
9491
9558
|
import ora4 from "ora";
|
|
9492
9559
|
import { spawn as spawn2 } from "child_process";
|
|
9493
|
-
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as
|
|
9494
|
-
import { resolve as
|
|
9560
|
+
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
|
|
9561
|
+
import { resolve as resolve12, join as join13, dirname as dirname7 } from "path";
|
|
9495
9562
|
import { readdir as readdir3, readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, copyFile as copyFile2 } from "fs/promises";
|
|
9496
9563
|
var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
9497
9564
|
"node_modules",
|
|
@@ -9513,8 +9580,8 @@ async function copyDir(src, dest) {
|
|
|
9513
9580
|
await mkdir4(dest, { recursive: true });
|
|
9514
9581
|
const entries = await readdir3(src, { withFileTypes: true });
|
|
9515
9582
|
for (const e of entries) {
|
|
9516
|
-
const srcPath =
|
|
9517
|
-
const destPath =
|
|
9583
|
+
const srcPath = join13(src, e.name);
|
|
9584
|
+
const destPath = join13(dest, e.name);
|
|
9518
9585
|
if (COPY_EXCLUDE.has(e.name)) continue;
|
|
9519
9586
|
if (e.isDirectory()) {
|
|
9520
9587
|
await copyDir(srcPath, destPath);
|
|
@@ -9525,16 +9592,16 @@ async function copyDir(src, dest) {
|
|
|
9525
9592
|
}
|
|
9526
9593
|
}
|
|
9527
9594
|
function checkProjectInitialized2(projectRoot) {
|
|
9528
|
-
return existsSync20(
|
|
9595
|
+
return existsSync20(resolve12(projectRoot, "design-system.config.ts")) && existsSync20(resolve12(projectRoot, "package.json"));
|
|
9529
9596
|
}
|
|
9530
9597
|
function getPackageManager2(projectRoot) {
|
|
9531
|
-
if (existsSync20(
|
|
9532
|
-
if (existsSync20(
|
|
9598
|
+
if (existsSync20(resolve12(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
9599
|
+
if (existsSync20(resolve12(projectRoot, "package-lock.json"))) return "npm";
|
|
9533
9600
|
return "npx";
|
|
9534
9601
|
}
|
|
9535
9602
|
async function patchNextConfigForExport(outRoot) {
|
|
9536
9603
|
for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
|
|
9537
|
-
const p =
|
|
9604
|
+
const p = join13(outRoot, name);
|
|
9538
9605
|
if (!existsSync20(p)) continue;
|
|
9539
9606
|
let content = await readFile5(p, "utf-8");
|
|
9540
9607
|
if (content.includes("ignoreDuringBuilds")) return;
|
|
@@ -9550,9 +9617,9 @@ async function buildProduction(projectRoot) {
|
|
|
9550
9617
|
const pm = getPackageManager2(projectRoot);
|
|
9551
9618
|
const command = pm === "pnpm" ? "pnpm" : pm === "npm" ? "npm" : "npx";
|
|
9552
9619
|
const args = pm === "npx" ? ["next", "build"] : ["run", "build"];
|
|
9553
|
-
return new Promise((
|
|
9620
|
+
return new Promise((resolve17, reject) => {
|
|
9554
9621
|
const child = spawn2(command, args, { cwd: projectRoot, stdio: "inherit", shell: true });
|
|
9555
|
-
child.on("exit", (code) => code === 0 ?
|
|
9622
|
+
child.on("exit", (code) => code === 0 ? resolve17() : reject(new Error(`Build failed with exit code ${code}`)));
|
|
9556
9623
|
child.on("error", (error) => reject(new Error(`Failed to start build: ${error.message}`)));
|
|
9557
9624
|
});
|
|
9558
9625
|
}
|
|
@@ -9576,7 +9643,7 @@ EXPOSE 3000
|
|
|
9576
9643
|
\`\`\`
|
|
9577
9644
|
`;
|
|
9578
9645
|
async function ensureReadmeDeploySection(outRoot) {
|
|
9579
|
-
const readmePath =
|
|
9646
|
+
const readmePath = join13(outRoot, "README.md");
|
|
9580
9647
|
if (!existsSync20(readmePath)) return;
|
|
9581
9648
|
try {
|
|
9582
9649
|
let content = await readFile5(readmePath, "utf-8");
|
|
@@ -9596,22 +9663,22 @@ async function countPages(outRoot) {
|
|
|
9596
9663
|
return;
|
|
9597
9664
|
}
|
|
9598
9665
|
for (const e of entries) {
|
|
9599
|
-
const full =
|
|
9666
|
+
const full = join13(dir, e.name);
|
|
9600
9667
|
if (e.isFile() && e.name === "page.tsx") n++;
|
|
9601
9668
|
else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
|
|
9602
9669
|
}
|
|
9603
9670
|
}
|
|
9604
|
-
const appDir =
|
|
9671
|
+
const appDir = join13(outRoot, "app");
|
|
9605
9672
|
if (existsSync20(appDir)) await walk(appDir);
|
|
9606
9673
|
return n;
|
|
9607
9674
|
}
|
|
9608
9675
|
function countComponents(outRoot) {
|
|
9609
9676
|
let n = 0;
|
|
9610
9677
|
for (const sub of ["ui", "shared"]) {
|
|
9611
|
-
const dir =
|
|
9678
|
+
const dir = join13(outRoot, "components", sub);
|
|
9612
9679
|
if (!existsSync20(dir)) continue;
|
|
9613
9680
|
try {
|
|
9614
|
-
n +=
|
|
9681
|
+
n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
|
|
9615
9682
|
} catch {
|
|
9616
9683
|
}
|
|
9617
9684
|
}
|
|
@@ -9624,7 +9691,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9624
9691
|
async function walk(d) {
|
|
9625
9692
|
const entries = await readdir3(d, { withFileTypes: true });
|
|
9626
9693
|
for (const e of entries) {
|
|
9627
|
-
const full =
|
|
9694
|
+
const full = join13(d, e.name);
|
|
9628
9695
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
9629
9696
|
await walk(full);
|
|
9630
9697
|
continue;
|
|
@@ -9647,7 +9714,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9647
9714
|
return packages;
|
|
9648
9715
|
}
|
|
9649
9716
|
async function findMissingDepsInExport(outRoot) {
|
|
9650
|
-
const pkgPath =
|
|
9717
|
+
const pkgPath = join13(outRoot, "package.json");
|
|
9651
9718
|
if (!existsSync20(pkgPath)) return [];
|
|
9652
9719
|
let pkg;
|
|
9653
9720
|
try {
|
|
@@ -9656,7 +9723,7 @@ async function findMissingDepsInExport(outRoot) {
|
|
|
9656
9723
|
return [];
|
|
9657
9724
|
}
|
|
9658
9725
|
const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
9659
|
-
const codeDirs = [
|
|
9726
|
+
const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
|
|
9660
9727
|
const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
|
|
9661
9728
|
const imported = /* @__PURE__ */ new Set();
|
|
9662
9729
|
for (const dir of codeDirs) {
|
|
@@ -9668,25 +9735,25 @@ async function findMissingDepsInExport(outRoot) {
|
|
|
9668
9735
|
async function stripCoherentArtifacts(outputDir) {
|
|
9669
9736
|
const removed = [];
|
|
9670
9737
|
for (const p of ["app/design-system", "app/api/design-system"]) {
|
|
9671
|
-
const full =
|
|
9738
|
+
const full = join13(outputDir, p);
|
|
9672
9739
|
if (existsSync20(full)) {
|
|
9673
9740
|
rmSync4(full, { recursive: true, force: true });
|
|
9674
9741
|
removed.push(p);
|
|
9675
9742
|
}
|
|
9676
9743
|
}
|
|
9677
|
-
const appNavPath =
|
|
9744
|
+
const appNavPath = join13(outputDir, "app", "AppNav.tsx");
|
|
9678
9745
|
if (existsSync20(appNavPath)) {
|
|
9679
9746
|
rmSync4(appNavPath, { force: true });
|
|
9680
9747
|
removed.push("app/AppNav.tsx");
|
|
9681
9748
|
}
|
|
9682
|
-
const layoutPath =
|
|
9749
|
+
const layoutPath = join13(outputDir, "app", "layout.tsx");
|
|
9683
9750
|
if (existsSync20(layoutPath)) {
|
|
9684
9751
|
let layout = await readFile5(layoutPath, "utf-8");
|
|
9685
9752
|
layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
|
|
9686
9753
|
layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
|
|
9687
9754
|
await writeFile4(layoutPath, layout, "utf-8");
|
|
9688
9755
|
}
|
|
9689
|
-
const guardPath =
|
|
9756
|
+
const guardPath = join13(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
|
|
9690
9757
|
if (existsSync20(guardPath)) {
|
|
9691
9758
|
let guard = await readFile5(guardPath, "utf-8");
|
|
9692
9759
|
guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
|
|
@@ -9715,14 +9782,14 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9715
9782
|
".env.local",
|
|
9716
9783
|
"recommendations.md"
|
|
9717
9784
|
]) {
|
|
9718
|
-
const full =
|
|
9785
|
+
const full = join13(outputDir, name);
|
|
9719
9786
|
if (existsSync20(full)) {
|
|
9720
9787
|
rmSync4(full, { force: true });
|
|
9721
9788
|
removed.push(name);
|
|
9722
9789
|
}
|
|
9723
9790
|
}
|
|
9724
9791
|
for (const dir of [".claude", ".coherent"]) {
|
|
9725
|
-
const full =
|
|
9792
|
+
const full = join13(outputDir, dir);
|
|
9726
9793
|
if (existsSync20(full)) {
|
|
9727
9794
|
rmSync4(full, { recursive: true, force: true });
|
|
9728
9795
|
removed.push(dir + "/");
|
|
@@ -9731,7 +9798,7 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9731
9798
|
return removed;
|
|
9732
9799
|
}
|
|
9733
9800
|
async function exportCommand(options = {}) {
|
|
9734
|
-
const outputDir =
|
|
9801
|
+
const outputDir = resolve12(process.cwd(), options.output ?? "./export");
|
|
9735
9802
|
const doBuild = options.build !== false;
|
|
9736
9803
|
const keepDs = options.keepDs === true;
|
|
9737
9804
|
const spinner = ora4("Preparing export...").start();
|
|
@@ -9922,8 +9989,8 @@ async function regenerateDocsCommand() {
|
|
|
9922
9989
|
|
|
9923
9990
|
// src/commands/fix.ts
|
|
9924
9991
|
import chalk18 from "chalk";
|
|
9925
|
-
import { readdirSync as
|
|
9926
|
-
import { resolve as
|
|
9992
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
9993
|
+
import { resolve as resolve13, join as join14 } from "path";
|
|
9927
9994
|
import {
|
|
9928
9995
|
DesignSystemManager as DesignSystemManager11,
|
|
9929
9996
|
ComponentManager as ComponentManager5,
|
|
@@ -9947,9 +10014,9 @@ function extractComponentIdsFromCode2(code) {
|
|
|
9947
10014
|
function listTsxFiles(dir) {
|
|
9948
10015
|
const files = [];
|
|
9949
10016
|
try {
|
|
9950
|
-
const entries =
|
|
10017
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
9951
10018
|
for (const e of entries) {
|
|
9952
|
-
const full =
|
|
10019
|
+
const full = join14(dir, e.name);
|
|
9953
10020
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
9954
10021
|
files.push(...listTsxFiles(full));
|
|
9955
10022
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -9978,7 +10045,7 @@ async function fixCommand(opts = {}) {
|
|
|
9978
10045
|
console.log(chalk18.cyan("\ncoherent fix\n"));
|
|
9979
10046
|
}
|
|
9980
10047
|
if (!skipCache) {
|
|
9981
|
-
const nextDir =
|
|
10048
|
+
const nextDir = join14(projectRoot, ".next");
|
|
9982
10049
|
if (existsSync21(nextDir)) {
|
|
9983
10050
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
9984
10051
|
fixes.push("Cleared build cache");
|
|
@@ -10001,9 +10068,9 @@ async function fixCommand(opts = {}) {
|
|
|
10001
10068
|
}
|
|
10002
10069
|
}
|
|
10003
10070
|
}
|
|
10004
|
-
const appDir =
|
|
10071
|
+
const appDir = resolve13(projectRoot, "app");
|
|
10005
10072
|
const allTsxFiles = listTsxFiles(appDir);
|
|
10006
|
-
const componentsTsxFiles = listTsxFiles(
|
|
10073
|
+
const componentsTsxFiles = listTsxFiles(resolve13(projectRoot, "components"));
|
|
10007
10074
|
const allComponentIds = /* @__PURE__ */ new Set();
|
|
10008
10075
|
for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
|
|
10009
10076
|
const content = readFileSync15(file, "utf-8");
|
|
@@ -10025,7 +10092,7 @@ async function fixCommand(opts = {}) {
|
|
|
10025
10092
|
missingComponents.push(id);
|
|
10026
10093
|
} else {
|
|
10027
10094
|
const fileName = toKebabCase(id) + ".tsx";
|
|
10028
|
-
const filePath =
|
|
10095
|
+
const filePath = resolve13(projectRoot, "components", "ui", fileName);
|
|
10029
10096
|
if (!existsSync21(filePath)) missingFiles.push(id);
|
|
10030
10097
|
}
|
|
10031
10098
|
}
|
|
@@ -10053,8 +10120,8 @@ async function fixCommand(opts = {}) {
|
|
|
10053
10120
|
const generator = new ComponentGenerator4(updatedConfig);
|
|
10054
10121
|
const code = await generator.generate(component);
|
|
10055
10122
|
const fileName = toKebabCase(component.name) + ".tsx";
|
|
10056
|
-
const filePath =
|
|
10057
|
-
mkdirSync7(
|
|
10123
|
+
const filePath = resolve13(projectRoot, "components", "ui", fileName);
|
|
10124
|
+
mkdirSync7(resolve13(projectRoot, "components", "ui"), { recursive: true });
|
|
10058
10125
|
await writeFile(filePath, code);
|
|
10059
10126
|
installed++;
|
|
10060
10127
|
}
|
|
@@ -10234,16 +10301,16 @@ async function fixCommand(opts = {}) {
|
|
|
10234
10301
|
|
|
10235
10302
|
// src/commands/check.ts
|
|
10236
10303
|
import chalk19 from "chalk";
|
|
10237
|
-
import { resolve as
|
|
10238
|
-
import { readdirSync as
|
|
10304
|
+
import { resolve as resolve14 } from "path";
|
|
10305
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync16, statSync as statSync2, existsSync as existsSync22 } from "fs";
|
|
10239
10306
|
import { loadManifest as loadManifest11 } from "@getcoherent/core";
|
|
10240
10307
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
10241
10308
|
function findTsxFiles(dir) {
|
|
10242
10309
|
const results = [];
|
|
10243
10310
|
try {
|
|
10244
|
-
const entries =
|
|
10311
|
+
const entries = readdirSync6(dir);
|
|
10245
10312
|
for (const entry of entries) {
|
|
10246
|
-
const full =
|
|
10313
|
+
const full = resolve14(dir, entry);
|
|
10247
10314
|
const stat = statSync2(full);
|
|
10248
10315
|
if (stat.isDirectory() && !entry.startsWith(".") && !EXCLUDED_DIRS.has(entry)) {
|
|
10249
10316
|
results.push(...findTsxFiles(full));
|
|
@@ -10278,7 +10345,7 @@ async function checkCommand(opts = {}) {
|
|
|
10278
10345
|
} catch {
|
|
10279
10346
|
}
|
|
10280
10347
|
if (!skipPages) {
|
|
10281
|
-
const appDir =
|
|
10348
|
+
const appDir = resolve14(projectRoot, "app");
|
|
10282
10349
|
const files = findTsxFiles(appDir);
|
|
10283
10350
|
result.pages.total = files.length;
|
|
10284
10351
|
if (!opts.json) console.log(chalk19.cyan("\n \u{1F4C4} Pages") + chalk19.dim(` (${files.length} scanned)
|
|
@@ -10366,7 +10433,7 @@ async function checkCommand(opts = {}) {
|
|
|
10366
10433
|
const manifest = await loadManifest11(project.root);
|
|
10367
10434
|
if (manifest.shared.length > 0) {
|
|
10368
10435
|
for (const entry of manifest.shared) {
|
|
10369
|
-
const fullPath =
|
|
10436
|
+
const fullPath = resolve14(project.root, entry.file);
|
|
10370
10437
|
if (!existsSync22(fullPath)) {
|
|
10371
10438
|
result.pages.withErrors++;
|
|
10372
10439
|
if (!opts.json) console.log(chalk19.red(`
|
|
@@ -10391,7 +10458,7 @@ async function checkCommand(opts = {}) {
|
|
|
10391
10458
|
let _staleUsedIn = 0;
|
|
10392
10459
|
let _nameMismatch = 0;
|
|
10393
10460
|
for (const entry of manifest.shared) {
|
|
10394
|
-
const filePath =
|
|
10461
|
+
const filePath = resolve14(projectRoot, entry.file);
|
|
10395
10462
|
const fileExists = existsSync22(filePath);
|
|
10396
10463
|
if (!fileExists) {
|
|
10397
10464
|
_orphaned++;
|
|
@@ -10470,7 +10537,7 @@ async function checkCommand(opts = {}) {
|
|
|
10470
10537
|
id: e.id,
|
|
10471
10538
|
name: e.name,
|
|
10472
10539
|
type: e.type,
|
|
10473
|
-
status: existsSync22(
|
|
10540
|
+
status: existsSync22(resolve14(projectRoot, e.file)) ? "ok" : "unused",
|
|
10474
10541
|
message: "",
|
|
10475
10542
|
suggestions: void 0
|
|
10476
10543
|
}))
|
|
@@ -10566,11 +10633,11 @@ import {
|
|
|
10566
10633
|
integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4
|
|
10567
10634
|
} from "@getcoherent/core";
|
|
10568
10635
|
import { existsSync as existsSync23 } from "fs";
|
|
10569
|
-
import { resolve as
|
|
10636
|
+
import { resolve as resolve15 } from "path";
|
|
10570
10637
|
|
|
10571
10638
|
// src/utils/ds-files.ts
|
|
10572
10639
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
|
|
10573
|
-
import { join as
|
|
10640
|
+
import { join as join15, dirname as dirname8 } from "path";
|
|
10574
10641
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
10575
10642
|
var SHARED_DS_KEYS = [
|
|
10576
10643
|
"app/design-system/shared/page.tsx",
|
|
@@ -10584,7 +10651,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
10584
10651
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
|
|
10585
10652
|
const written = [];
|
|
10586
10653
|
for (const [relativePath, content] of toWrite) {
|
|
10587
|
-
const fullPath =
|
|
10654
|
+
const fullPath = join15(projectRoot, relativePath);
|
|
10588
10655
|
await mkdir5(dirname8(fullPath), { recursive: true });
|
|
10589
10656
|
await writeFile5(fullPath, content, "utf-8");
|
|
10590
10657
|
written.push(relativePath);
|
|
@@ -10696,7 +10763,7 @@ function createComponentsCommand() {
|
|
|
10696
10763
|
const updated = await integrateSharedLayoutIntoRootLayout4(project.root);
|
|
10697
10764
|
if (updated) console.log(chalk25.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
10698
10765
|
}
|
|
10699
|
-
const sharedPagePath =
|
|
10766
|
+
const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
|
|
10700
10767
|
if (!existsSync23(sharedPagePath)) {
|
|
10701
10768
|
try {
|
|
10702
10769
|
const dsm = new DesignSystemManager12(project.configPath);
|
|
@@ -10723,7 +10790,7 @@ function createComponentsCommand() {
|
|
|
10723
10790
|
import chalk26 from "chalk";
|
|
10724
10791
|
import ora6 from "ora";
|
|
10725
10792
|
import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
10726
|
-
import { resolve as
|
|
10793
|
+
import { resolve as resolve16, join as join16, dirname as dirname9 } from "path";
|
|
10727
10794
|
import { existsSync as existsSync24 } from "fs";
|
|
10728
10795
|
import {
|
|
10729
10796
|
FigmaClient,
|
|
@@ -10871,7 +10938,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
10871
10938
|
stats.filesWritten.push(filePath);
|
|
10872
10939
|
return;
|
|
10873
10940
|
}
|
|
10874
|
-
const fullPath =
|
|
10941
|
+
const fullPath = join16(projectRoot, filePath);
|
|
10875
10942
|
await mkdir6(dirname9(fullPath), { recursive: true });
|
|
10876
10943
|
await writeFile6(fullPath, content, "utf-8");
|
|
10877
10944
|
stats.filesWritten.push(filePath);
|
|
@@ -10948,7 +11015,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
10948
11015
|
if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
|
|
10949
11016
|
else
|
|
10950
11017
|
await writeFile6(
|
|
10951
|
-
|
|
11018
|
+
resolve16(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
|
|
10952
11019
|
JSON.stringify(componentMapObj, null, 2),
|
|
10953
11020
|
"utf-8"
|
|
10954
11021
|
);
|
|
@@ -10971,7 +11038,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
10971
11038
|
const fullConfig = buildFigmaImportConfig(mergedConfig, pageDefs, intermediate.fileName);
|
|
10972
11039
|
if (!dryRun) {
|
|
10973
11040
|
spinner.start("Updating design-system.config.ts...");
|
|
10974
|
-
const configPath =
|
|
11041
|
+
const configPath = resolve16(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
|
|
10975
11042
|
const dsm = new DesignSystemManager13(configPath);
|
|
10976
11043
|
if (existsSync24(configPath)) {
|
|
10977
11044
|
await dsm.load();
|
|
@@ -11002,7 +11069,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
11002
11069
|
stats.configUpdated = true;
|
|
11003
11070
|
spinner.succeed("design-system.config.ts updated");
|
|
11004
11071
|
spinner.start("Ensuring root layout...");
|
|
11005
|
-
const layoutPath =
|
|
11072
|
+
const layoutPath = join16(projectRoot, "app/layout.tsx");
|
|
11006
11073
|
if (!existsSync24(layoutPath)) {
|
|
11007
11074
|
await mkdir6(dirname9(layoutPath), { recursive: true });
|
|
11008
11075
|
await writeFile6(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
@@ -11094,7 +11161,7 @@ async function dsRegenerateCommand() {
|
|
|
11094
11161
|
import chalk28 from "chalk";
|
|
11095
11162
|
import ora8 from "ora";
|
|
11096
11163
|
import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
|
|
11097
|
-
import { join as
|
|
11164
|
+
import { join as join17 } from "path";
|
|
11098
11165
|
import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
11099
11166
|
|
|
11100
11167
|
// src/utils/migrations.ts
|
|
@@ -11263,7 +11330,7 @@ var EXPECTED_CSS_VARS = [
|
|
|
11263
11330
|
"--sidebar-ring"
|
|
11264
11331
|
];
|
|
11265
11332
|
function checkMissingCssVars(projectRoot) {
|
|
11266
|
-
const globalsPath =
|
|
11333
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11267
11334
|
if (!existsSync25(globalsPath)) return [];
|
|
11268
11335
|
try {
|
|
11269
11336
|
const content = readFileSync17(globalsPath, "utf-8");
|
|
@@ -11273,7 +11340,7 @@ function checkMissingCssVars(projectRoot) {
|
|
|
11273
11340
|
}
|
|
11274
11341
|
}
|
|
11275
11342
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
11276
|
-
const globalsPath =
|
|
11343
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11277
11344
|
if (!existsSync25(globalsPath) || missingVars.length === 0) return;
|
|
11278
11345
|
const { writeFileSync: writeFileSync13 } = __require("fs");
|
|
11279
11346
|
let content = readFileSync17(globalsPath, "utf-8");
|
|
@@ -11355,14 +11422,14 @@ async function undoCommand(options) {
|
|
|
11355
11422
|
import chalk30 from "chalk";
|
|
11356
11423
|
import ora9 from "ora";
|
|
11357
11424
|
import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
|
|
11358
|
-
import { join as
|
|
11425
|
+
import { join as join18, relative as relative4, dirname as dirname10 } from "path";
|
|
11359
11426
|
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
11360
11427
|
import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
|
|
11361
11428
|
import { loadManifest as loadManifest13, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
|
|
11362
11429
|
function extractTokensFromProject(projectRoot) {
|
|
11363
11430
|
const lightColors = {};
|
|
11364
11431
|
const darkColors = {};
|
|
11365
|
-
const globalsPath =
|
|
11432
|
+
const globalsPath = join18(projectRoot, "app", "globals.css");
|
|
11366
11433
|
if (existsSync26(globalsPath)) {
|
|
11367
11434
|
const css = readFileSync18(globalsPath, "utf-8");
|
|
11368
11435
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
@@ -11370,7 +11437,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
11370
11437
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
11371
11438
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
11372
11439
|
}
|
|
11373
|
-
const layoutPath =
|
|
11440
|
+
const layoutPath = join18(projectRoot, "app", "layout.tsx");
|
|
11374
11441
|
let layoutCode = "";
|
|
11375
11442
|
if (existsSync26(layoutPath)) {
|
|
11376
11443
|
layoutCode = readFileSync18(layoutPath, "utf-8");
|
|
@@ -11414,7 +11481,7 @@ function parseVarsInto(block, target) {
|
|
|
11414
11481
|
}
|
|
11415
11482
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
11416
11483
|
const results = [];
|
|
11417
|
-
const componentsDir =
|
|
11484
|
+
const componentsDir = join18(projectRoot, "components");
|
|
11418
11485
|
if (!existsSync26(componentsDir)) return results;
|
|
11419
11486
|
const files = [];
|
|
11420
11487
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
@@ -11442,7 +11509,7 @@ async function walkForTsx(dir, files, skipDirs) {
|
|
|
11442
11509
|
return;
|
|
11443
11510
|
}
|
|
11444
11511
|
for (const e of entries) {
|
|
11445
|
-
const full =
|
|
11512
|
+
const full = join18(dir, e.name);
|
|
11446
11513
|
if (e.isDirectory()) {
|
|
11447
11514
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
11448
11515
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -11516,7 +11583,7 @@ async function discoverPages(appDir) {
|
|
|
11516
11583
|
return;
|
|
11517
11584
|
}
|
|
11518
11585
|
for (const entry of entries) {
|
|
11519
|
-
const full =
|
|
11586
|
+
const full = join18(dir, entry.name);
|
|
11520
11587
|
if (entry.isDirectory()) {
|
|
11521
11588
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
11522
11589
|
if (entry.name.startsWith(".")) continue;
|
|
@@ -11593,7 +11660,7 @@ async function syncCommand(options = {}) {
|
|
|
11593
11660
|
if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
|
|
11594
11661
|
const spinner = ora9("Scanning project files...").start();
|
|
11595
11662
|
try {
|
|
11596
|
-
const appDir =
|
|
11663
|
+
const appDir = join18(project.root, "app");
|
|
11597
11664
|
if (!existsSync26(appDir)) {
|
|
11598
11665
|
spinner.fail("No app/ directory found");
|
|
11599
11666
|
process.exit(1);
|
|
@@ -11820,14 +11887,14 @@ async function syncCommand(options = {}) {
|
|
|
11820
11887
|
|
|
11821
11888
|
// src/utils/update-notifier.ts
|
|
11822
11889
|
import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
|
|
11823
|
-
import { join as
|
|
11890
|
+
import { join as join19 } from "path";
|
|
11824
11891
|
import { homedir } from "os";
|
|
11825
11892
|
import chalk31 from "chalk";
|
|
11826
11893
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
11827
11894
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
11828
11895
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
11829
|
-
var CACHE_DIR =
|
|
11830
|
-
var CACHE_FILE =
|
|
11896
|
+
var CACHE_DIR = join19(homedir(), ".coherent");
|
|
11897
|
+
var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
|
|
11831
11898
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
11832
11899
|
function readCache() {
|
|
11833
11900
|
try {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.1",
|
|
7
7
|
"description": "CLI interface for Coherent Design Method",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.js",
|
|
@@ -33,8 +33,15 @@
|
|
|
33
33
|
],
|
|
34
34
|
"author": "Coherent Design Method",
|
|
35
35
|
"license": "MIT",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "tsup --watch",
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest"
|
|
41
|
+
},
|
|
36
42
|
"dependencies": {
|
|
37
43
|
"@anthropic-ai/sdk": "^0.32.0",
|
|
44
|
+
"@getcoherent/core": "workspace:*",
|
|
38
45
|
"chalk": "^5.3.0",
|
|
39
46
|
"chokidar": "^4.0.1",
|
|
40
47
|
"commander": "^11.1.0",
|
|
@@ -42,19 +49,12 @@
|
|
|
42
49
|
"open": "^10.1.0",
|
|
43
50
|
"ora": "^7.0.1",
|
|
44
51
|
"prompts": "^2.4.2",
|
|
45
|
-
"zod": "^3.22.4"
|
|
46
|
-
"@getcoherent/core": "0.2.0"
|
|
52
|
+
"zod": "^3.22.4"
|
|
47
53
|
},
|
|
48
54
|
"devDependencies": {
|
|
49
55
|
"@types/node": "^20.11.0",
|
|
50
56
|
"@types/prompts": "^2.4.9",
|
|
51
57
|
"tsup": "^8.0.1",
|
|
52
58
|
"typescript": "^5.3.3"
|
|
53
|
-
},
|
|
54
|
-
"scripts": {
|
|
55
|
-
"dev": "tsup --watch",
|
|
56
|
-
"build": "tsup",
|
|
57
|
-
"typecheck": "tsc --noEmit",
|
|
58
|
-
"test": "vitest"
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Sergei Kovtun
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|