@getcoherent/cli 0.6.24 → 0.6.26
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/{chunk-IYLHC4RC.js → chunk-CC3ARMGS.js} +240 -6
- package/dist/{chunk-2ZL4X4QD.js → chunk-H644LLXJ.js} +30 -0
- package/dist/index.js +416 -620
- package/dist/{plan-generator-BHDEJGMY.js → plan-generator-WPYWUA7U.js} +2 -1
- package/dist/{quality-validator-G5AE4337.js → quality-validator-VDQXXDAV.js} +1 -1
- package/dist/{reuse-validator-HC4LZEKF.js → reuse-validator-XR2ZEYC4.js} +2 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,21 +2,24 @@ import {
|
|
|
2
2
|
createAIProvider
|
|
3
3
|
} from "./chunk-SPQZBQYY.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
COHERENT_REQUIRED_PACKAGES,
|
|
6
|
+
ensureUseClientIfNeeded,
|
|
7
|
+
extractNpmPackagesFromCode,
|
|
8
|
+
findMissingPackages,
|
|
9
|
+
findMissingPackagesInCode,
|
|
10
|
+
fixEscapedClosingQuotes,
|
|
11
|
+
fixUnescapedLtInJsx,
|
|
12
12
|
generateArchitecturePlan,
|
|
13
|
+
getInstalledPackages,
|
|
13
14
|
getPageGroup,
|
|
14
15
|
getPageType,
|
|
16
|
+
installPackages,
|
|
15
17
|
loadPlan,
|
|
16
18
|
routeToKey,
|
|
19
|
+
sanitizeMetadataStrings,
|
|
17
20
|
savePlan,
|
|
18
21
|
updateArchitecturePlan
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-CC3ARMGS.js";
|
|
20
23
|
import {
|
|
21
24
|
CORE_CONSTRAINTS,
|
|
22
25
|
DESIGN_QUALITY,
|
|
@@ -28,6 +31,13 @@ import {
|
|
|
28
31
|
inferPageTypeFromRoute,
|
|
29
32
|
selectContextualRules
|
|
30
33
|
} from "./chunk-5AHG4NNX.js";
|
|
34
|
+
import {
|
|
35
|
+
autoFixCode,
|
|
36
|
+
checkDesignConsistency,
|
|
37
|
+
formatIssues,
|
|
38
|
+
validatePageQuality,
|
|
39
|
+
verifyIncrementalEdit
|
|
40
|
+
} from "./chunk-H644LLXJ.js";
|
|
31
41
|
import {
|
|
32
42
|
__require
|
|
33
43
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -41,9 +51,9 @@ import { CLI_VERSION as CLI_VERSION6 } from "@getcoherent/core";
|
|
|
41
51
|
import chalk4 from "chalk";
|
|
42
52
|
import ora from "ora";
|
|
43
53
|
import prompts2 from "prompts";
|
|
44
|
-
import { existsSync as
|
|
45
|
-
import { join as
|
|
46
|
-
import { execSync
|
|
54
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
55
|
+
import { join as join5 } from "path";
|
|
56
|
+
import { execSync } from "child_process";
|
|
47
57
|
|
|
48
58
|
// src/utils/find-config.ts
|
|
49
59
|
import { existsSync } from "fs";
|
|
@@ -1803,222 +1813,6 @@ function formatTimeAgo(iso) {
|
|
|
1803
1813
|
return date.toLocaleDateString();
|
|
1804
1814
|
}
|
|
1805
1815
|
|
|
1806
|
-
// src/utils/self-heal.ts
|
|
1807
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
1808
|
-
import { join as join3 } from "path";
|
|
1809
|
-
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
1810
|
-
import { execSync } from "child_process";
|
|
1811
|
-
var COHERENT_REQUIRED_PACKAGES = [
|
|
1812
|
-
"lucide-react",
|
|
1813
|
-
"class-variance-authority",
|
|
1814
|
-
"clsx",
|
|
1815
|
-
"tailwind-merge",
|
|
1816
|
-
"@radix-ui/react-slot"
|
|
1817
|
-
];
|
|
1818
|
-
var IMPORT_FROM_REGEX = /from\s+['"]([^'"]+)['"]/g;
|
|
1819
|
-
var NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
1820
|
-
"assert",
|
|
1821
|
-
"async_hooks",
|
|
1822
|
-
"buffer",
|
|
1823
|
-
"child_process",
|
|
1824
|
-
"cluster",
|
|
1825
|
-
"console",
|
|
1826
|
-
"constants",
|
|
1827
|
-
"crypto",
|
|
1828
|
-
"dgram",
|
|
1829
|
-
"diagnostics_channel",
|
|
1830
|
-
"dns",
|
|
1831
|
-
"domain",
|
|
1832
|
-
"events",
|
|
1833
|
-
"fs",
|
|
1834
|
-
"http",
|
|
1835
|
-
"http2",
|
|
1836
|
-
"https",
|
|
1837
|
-
"inspector",
|
|
1838
|
-
"module",
|
|
1839
|
-
"net",
|
|
1840
|
-
"os",
|
|
1841
|
-
"path",
|
|
1842
|
-
"perf_hooks",
|
|
1843
|
-
"process",
|
|
1844
|
-
"punycode",
|
|
1845
|
-
"querystring",
|
|
1846
|
-
"readline",
|
|
1847
|
-
"repl",
|
|
1848
|
-
"stream",
|
|
1849
|
-
"string_decoder",
|
|
1850
|
-
"sys",
|
|
1851
|
-
"test",
|
|
1852
|
-
"timers",
|
|
1853
|
-
"tls",
|
|
1854
|
-
"trace_events",
|
|
1855
|
-
"tty",
|
|
1856
|
-
"url",
|
|
1857
|
-
"util",
|
|
1858
|
-
"v8",
|
|
1859
|
-
"vm",
|
|
1860
|
-
"wasi",
|
|
1861
|
-
"worker_threads",
|
|
1862
|
-
"zlib",
|
|
1863
|
-
"fs/promises",
|
|
1864
|
-
"path/posix",
|
|
1865
|
-
"path/win32",
|
|
1866
|
-
"stream/promises",
|
|
1867
|
-
"stream/web",
|
|
1868
|
-
"timers/promises",
|
|
1869
|
-
"util/types"
|
|
1870
|
-
]);
|
|
1871
|
-
function extractNpmPackagesFromCode(code) {
|
|
1872
|
-
if (typeof code !== "string") return /* @__PURE__ */ new Set();
|
|
1873
|
-
const pkgs = /* @__PURE__ */ new Set();
|
|
1874
|
-
let m;
|
|
1875
|
-
IMPORT_FROM_REGEX.lastIndex = 0;
|
|
1876
|
-
while ((m = IMPORT_FROM_REGEX.exec(code)) !== null) {
|
|
1877
|
-
const spec = m[1];
|
|
1878
|
-
if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("@/") || spec === "next") continue;
|
|
1879
|
-
if (spec.startsWith("node:") || NODE_BUILTINS.has(spec)) continue;
|
|
1880
|
-
const pkg = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
|
|
1881
|
-
if (pkg && !NODE_BUILTINS.has(pkg)) pkgs.add(pkg);
|
|
1882
|
-
}
|
|
1883
|
-
return pkgs;
|
|
1884
|
-
}
|
|
1885
|
-
function getInstalledPackages(projectRoot) {
|
|
1886
|
-
const pkgPath = join3(projectRoot, "package.json");
|
|
1887
|
-
if (!existsSync4(pkgPath)) return /* @__PURE__ */ new Set();
|
|
1888
|
-
try {
|
|
1889
|
-
const json = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1890
|
-
const deps = { ...json.dependencies ?? {}, ...json.devDependencies ?? {} };
|
|
1891
|
-
return new Set(Object.keys(deps));
|
|
1892
|
-
} catch (e) {
|
|
1893
|
-
if (process.env.COHERENT_DEBUG === "1") console.error("Failed to read package.json:", e);
|
|
1894
|
-
return /* @__PURE__ */ new Set();
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
async function collectImportedPackages(dir, extensions) {
|
|
1898
|
-
const packages = /* @__PURE__ */ new Set();
|
|
1899
|
-
if (!existsSync4(dir)) return packages;
|
|
1900
|
-
async function walk(d) {
|
|
1901
|
-
let entries;
|
|
1902
|
-
try {
|
|
1903
|
-
entries = await readdir(d, { withFileTypes: true });
|
|
1904
|
-
} catch {
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
for (const e of entries) {
|
|
1908
|
-
const full = join3(d, e.name);
|
|
1909
|
-
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
1910
|
-
await walk(full);
|
|
1911
|
-
continue;
|
|
1912
|
-
}
|
|
1913
|
-
if (!e.isFile()) continue;
|
|
1914
|
-
const ext = e.name.replace(/^.*\./, "");
|
|
1915
|
-
if (!extensions.has(ext)) continue;
|
|
1916
|
-
const content = await readFile2(full, "utf-8").catch(() => "");
|
|
1917
|
-
extractNpmPackagesFromCode(content).forEach((p) => packages.add(p));
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
await walk(dir);
|
|
1921
|
-
return packages;
|
|
1922
|
-
}
|
|
1923
|
-
async function findMissingPackages(projectRoot, dirs = ["app", "components"]) {
|
|
1924
|
-
const installed = getInstalledPackages(projectRoot);
|
|
1925
|
-
const required = new Set(COHERENT_REQUIRED_PACKAGES);
|
|
1926
|
-
const imported = /* @__PURE__ */ new Set();
|
|
1927
|
-
const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
|
|
1928
|
-
for (const d of dirs) {
|
|
1929
|
-
const abs = join3(projectRoot, d);
|
|
1930
|
-
const pkgs = await collectImportedPackages(abs, extensions);
|
|
1931
|
-
pkgs.forEach((p) => imported.add(p));
|
|
1932
|
-
}
|
|
1933
|
-
const needed = /* @__PURE__ */ new Set([...required, ...imported]);
|
|
1934
|
-
return [...needed].filter((p) => !installed.has(p)).sort();
|
|
1935
|
-
}
|
|
1936
|
-
function findMissingPackagesInCode(code, projectRoot) {
|
|
1937
|
-
const installed = getInstalledPackages(projectRoot);
|
|
1938
|
-
const required = new Set(COHERENT_REQUIRED_PACKAGES);
|
|
1939
|
-
const fromCode = extractNpmPackagesFromCode(code);
|
|
1940
|
-
const needed = /* @__PURE__ */ new Set([...required, ...fromCode]);
|
|
1941
|
-
return [...needed].filter((p) => !installed.has(p)).sort();
|
|
1942
|
-
}
|
|
1943
|
-
var SAFE_PKG_NAME = /^(@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
|
|
1944
|
-
function installPackages(projectRoot, packages) {
|
|
1945
|
-
if (packages.length === 0) return Promise.resolve(true);
|
|
1946
|
-
const safe = packages.filter((p) => SAFE_PKG_NAME.test(p));
|
|
1947
|
-
if (safe.length === 0) return Promise.resolve(true);
|
|
1948
|
-
return new Promise((resolve17) => {
|
|
1949
|
-
try {
|
|
1950
|
-
const hasPnpm = existsSync4(join3(projectRoot, "pnpm-lock.yaml"));
|
|
1951
|
-
if (hasPnpm) {
|
|
1952
|
-
execSync(`pnpm add ${safe.join(" ")}`, { cwd: projectRoot, stdio: "pipe" });
|
|
1953
|
-
} else {
|
|
1954
|
-
execSync(`npm install --legacy-peer-deps ${safe.join(" ")}`, {
|
|
1955
|
-
cwd: projectRoot,
|
|
1956
|
-
stdio: "pipe"
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
1959
|
-
resolve17(true);
|
|
1960
|
-
} catch (e) {
|
|
1961
|
-
if (process.env.COHERENT_DEBUG === "1") console.error("Failed to install packages:", e);
|
|
1962
|
-
resolve17(false);
|
|
1963
|
-
}
|
|
1964
|
-
});
|
|
1965
|
-
}
|
|
1966
|
-
var CLIENT_HOOKS = /\b(useState|useEffect|useRef|useContext|useReducer|useCallback|useMemo|useId|useTransition|useDeferredValue)\s*\(/;
|
|
1967
|
-
var CLIENT_EVENTS = /\b(onClick|onChange|onSubmit|onBlur|onFocus|onKeyDown|onKeyUp|onMouseEnter|onMouseLeave|onScroll|onInput)\s*[={]/;
|
|
1968
|
-
function stripMetadataFromCode(code) {
|
|
1969
|
-
const match = code.match(/\bexport\s+const\s+metadata\s*:\s*Metadata\s*=\s*\{/);
|
|
1970
|
-
if (!match) return code;
|
|
1971
|
-
const start = code.indexOf(match[0]);
|
|
1972
|
-
const open = code.indexOf("{", start);
|
|
1973
|
-
if (open === -1) return code;
|
|
1974
|
-
let depth = 1;
|
|
1975
|
-
let i = open + 1;
|
|
1976
|
-
while (i < code.length && depth > 0) {
|
|
1977
|
-
const c = code[i];
|
|
1978
|
-
if (c === "{") depth++;
|
|
1979
|
-
else if (c === "}") depth--;
|
|
1980
|
-
i++;
|
|
1981
|
-
}
|
|
1982
|
-
const end = i;
|
|
1983
|
-
const tail = code.slice(end);
|
|
1984
|
-
const semicolon = tail.match(/^\s*;/);
|
|
1985
|
-
const removeEnd = semicolon ? end + (semicolon.index + semicolon[0].length) : end;
|
|
1986
|
-
return (code.slice(0, start) + code.slice(removeEnd)).replace(/\n{3,}/g, "\n\n").trim();
|
|
1987
|
-
}
|
|
1988
|
-
function ensureUseClientIfNeeded(code) {
|
|
1989
|
-
const trimmed = code.trimStart();
|
|
1990
|
-
const hasUseClient = trimmed.startsWith("'use client'") || trimmed.startsWith('"use client"');
|
|
1991
|
-
const needsUseClient = CLIENT_HOOKS.test(code) || CLIENT_EVENTS.test(code);
|
|
1992
|
-
let out = code;
|
|
1993
|
-
if (hasUseClient || needsUseClient) {
|
|
1994
|
-
out = stripMetadataFromCode(out);
|
|
1995
|
-
if (needsUseClient && !hasUseClient) out = `'use client'
|
|
1996
|
-
|
|
1997
|
-
${out}`;
|
|
1998
|
-
}
|
|
1999
|
-
return out;
|
|
2000
|
-
}
|
|
2001
|
-
function sanitizeMetadataStrings(code) {
|
|
2002
|
-
let out = code.replace(/\\'(\s*[}\],])/g, "'$1");
|
|
2003
|
-
out = out.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
|
|
2004
|
-
for (const key of ["description", "title"]) {
|
|
2005
|
-
const re = new RegExp(`\\b${key}:\\s*'((?:[^'\\\\]|'(?![,}]))*)'`, "gs");
|
|
2006
|
-
out = out.replace(re, (_, inner) => `${key}: '${inner.replace(/'/g, "\\'")}'`);
|
|
2007
|
-
}
|
|
2008
|
-
return out;
|
|
2009
|
-
}
|
|
2010
|
-
function fixEscapedClosingQuotes(code) {
|
|
2011
|
-
let out = code.replace(/\\'(\s*[}\],])/g, "'$1");
|
|
2012
|
-
out = out.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
|
|
2013
|
-
return out;
|
|
2014
|
-
}
|
|
2015
|
-
function fixUnescapedLtInJsx(code) {
|
|
2016
|
-
let out = code;
|
|
2017
|
-
out = out.replace(/>([^<\n]*)<(\d)/g, ">$1<$2");
|
|
2018
|
-
out = out.replace(/>([^<\n]*)<([^/a-zA-Z!{>\n])/g, ">$1<$2");
|
|
2019
|
-
return out;
|
|
2020
|
-
}
|
|
2021
|
-
|
|
2022
1816
|
// src/utils/welcome-content.ts
|
|
2023
1817
|
import fs from "fs";
|
|
2024
1818
|
import path2 from "path";
|
|
@@ -2453,14 +2247,14 @@ function hasApiKey() {
|
|
|
2453
2247
|
}
|
|
2454
2248
|
|
|
2455
2249
|
// src/utils/cursor-rules.ts
|
|
2456
|
-
import { writeFileSync as writeFileSync4, existsSync as
|
|
2457
|
-
import { join as
|
|
2250
|
+
import { writeFileSync as writeFileSync4, existsSync as existsSync5 } from "fs";
|
|
2251
|
+
import { join as join4 } from "path";
|
|
2458
2252
|
import { loadManifest as loadManifest2 } from "@getcoherent/core";
|
|
2459
2253
|
import { DesignSystemManager as DesignSystemManager2 } from "@getcoherent/core";
|
|
2460
2254
|
|
|
2461
2255
|
// src/utils/claude-code.ts
|
|
2462
|
-
import { writeFileSync as writeFileSync3, existsSync as
|
|
2463
|
-
import { join as
|
|
2256
|
+
import { writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
2257
|
+
import { join as join3 } from "path";
|
|
2464
2258
|
import { loadManifest } from "@getcoherent/core";
|
|
2465
2259
|
import { DesignSystemManager } from "@getcoherent/core";
|
|
2466
2260
|
function buildSharedComponentsListForClaude(manifest) {
|
|
@@ -2905,28 +2699,28 @@ var SETTINGS_JSON = `{
|
|
|
2905
2699
|
`;
|
|
2906
2700
|
function writeClaudeMd(projectRoot, manifest, config2) {
|
|
2907
2701
|
const content = buildClaudeMdContent(manifest, config2);
|
|
2908
|
-
const outPath =
|
|
2702
|
+
const outPath = join3(projectRoot, "CLAUDE.md");
|
|
2909
2703
|
writeFileSync3(outPath, content, "utf-8");
|
|
2910
2704
|
}
|
|
2911
2705
|
function writeClaudeCommands(projectRoot) {
|
|
2912
|
-
const dir =
|
|
2706
|
+
const dir = join3(projectRoot, ".claude", "commands");
|
|
2913
2707
|
ensureDir(dir);
|
|
2914
2708
|
for (const [name, body] of Object.entries(COMMANDS)) {
|
|
2915
|
-
writeFileSync3(
|
|
2709
|
+
writeFileSync3(join3(dir, name), body, "utf-8");
|
|
2916
2710
|
}
|
|
2917
2711
|
}
|
|
2918
2712
|
function writeClaudeSkills(projectRoot) {
|
|
2919
|
-
const dirCoherent =
|
|
2920
|
-
const dirFrontend =
|
|
2713
|
+
const dirCoherent = join3(projectRoot, ".claude", "skills", "coherent-project");
|
|
2714
|
+
const dirFrontend = join3(projectRoot, ".claude", "skills", "frontend-ux");
|
|
2921
2715
|
ensureDir(dirCoherent);
|
|
2922
2716
|
ensureDir(dirFrontend);
|
|
2923
|
-
writeFileSync3(
|
|
2924
|
-
writeFileSync3(
|
|
2717
|
+
writeFileSync3(join3(dirCoherent, "SKILL.md"), SKILL_COHERENT, "utf-8");
|
|
2718
|
+
writeFileSync3(join3(dirFrontend, "SKILL.md"), SKILL_FRONTEND_UX, "utf-8");
|
|
2925
2719
|
}
|
|
2926
2720
|
function writeClaudeSettings(projectRoot) {
|
|
2927
|
-
const dir =
|
|
2721
|
+
const dir = join3(projectRoot, ".claude");
|
|
2928
2722
|
ensureDir(dir);
|
|
2929
|
-
writeFileSync3(
|
|
2723
|
+
writeFileSync3(join3(dir, "settings.json"), SETTINGS_JSON.trim(), "utf-8");
|
|
2930
2724
|
}
|
|
2931
2725
|
async function loadManifestAndConfig(projectRoot) {
|
|
2932
2726
|
let manifest;
|
|
@@ -2936,8 +2730,8 @@ async function loadManifestAndConfig(projectRoot) {
|
|
|
2936
2730
|
manifest = { shared: [], nextId: 1 };
|
|
2937
2731
|
}
|
|
2938
2732
|
let config2 = null;
|
|
2939
|
-
const configPath =
|
|
2940
|
-
if (
|
|
2733
|
+
const configPath = join3(projectRoot, "design-system.config.ts");
|
|
2734
|
+
if (existsSync4(configPath)) {
|
|
2941
2735
|
try {
|
|
2942
2736
|
const dsm = new DesignSystemManager(configPath);
|
|
2943
2737
|
await dsm.load();
|
|
@@ -3316,8 +3110,8 @@ async function writeCursorRules(projectRoot) {
|
|
|
3316
3110
|
manifest = { shared: [], nextId: 1 };
|
|
3317
3111
|
}
|
|
3318
3112
|
let config2 = null;
|
|
3319
|
-
const configPath =
|
|
3320
|
-
if (
|
|
3113
|
+
const configPath = join4(projectRoot, "design-system.config.ts");
|
|
3114
|
+
if (existsSync5(configPath)) {
|
|
3321
3115
|
try {
|
|
3322
3116
|
const dsm = new DesignSystemManager2(configPath);
|
|
3323
3117
|
await dsm.load();
|
|
@@ -3326,7 +3120,7 @@ async function writeCursorRules(projectRoot) {
|
|
|
3326
3120
|
}
|
|
3327
3121
|
}
|
|
3328
3122
|
const content = buildCursorRules(manifest, config2);
|
|
3329
|
-
const outPath =
|
|
3123
|
+
const outPath = join4(projectRoot, ".cursorrules");
|
|
3330
3124
|
writeFileSync4(outPath, content, "utf-8");
|
|
3331
3125
|
writeClaudeMd(projectRoot, manifest, config2);
|
|
3332
3126
|
const tokenKeys = config2?.tokens ? [
|
|
@@ -3350,13 +3144,13 @@ async function regenerateCursorRules() {
|
|
|
3350
3144
|
}
|
|
3351
3145
|
|
|
3352
3146
|
// src/utils/tailwind-version.ts
|
|
3353
|
-
import { existsSync as
|
|
3147
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
3354
3148
|
import { resolve as resolve3 } from "path";
|
|
3355
3149
|
function isTailwindV4(projectRoot) {
|
|
3356
3150
|
const pkgPath = resolve3(projectRoot, "package.json");
|
|
3357
|
-
if (
|
|
3151
|
+
if (existsSync6(pkgPath)) {
|
|
3358
3152
|
try {
|
|
3359
|
-
const pkg = JSON.parse(
|
|
3153
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
3360
3154
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3361
3155
|
if (allDeps["@tailwindcss/postcss"]) return true;
|
|
3362
3156
|
const twVersion = allDeps["tailwindcss"] || "";
|
|
@@ -3365,8 +3159,8 @@ function isTailwindV4(projectRoot) {
|
|
|
3365
3159
|
}
|
|
3366
3160
|
}
|
|
3367
3161
|
const globalsPath = resolve3(projectRoot, "app", "globals.css");
|
|
3368
|
-
if (
|
|
3369
|
-
const content =
|
|
3162
|
+
if (existsSync6(globalsPath)) {
|
|
3163
|
+
const content = readFileSync3(globalsPath, "utf-8");
|
|
3370
3164
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'")) return true;
|
|
3371
3165
|
}
|
|
3372
3166
|
return false;
|
|
@@ -3550,10 +3344,10 @@ function toKebabCase(str) {
|
|
|
3550
3344
|
|
|
3551
3345
|
// src/commands/init.ts
|
|
3552
3346
|
function hasNextInPackageJson(projectPath) {
|
|
3553
|
-
const pkgPath =
|
|
3554
|
-
if (!
|
|
3347
|
+
const pkgPath = join5(projectPath, "package.json");
|
|
3348
|
+
if (!existsSync7(pkgPath)) return false;
|
|
3555
3349
|
try {
|
|
3556
|
-
const json = JSON.parse(
|
|
3350
|
+
const json = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
3557
3351
|
const deps = { ...json.dependencies, ...json.devDependencies };
|
|
3558
3352
|
return typeof deps?.next === "string";
|
|
3559
3353
|
} catch {
|
|
@@ -3563,30 +3357,30 @@ function hasNextInPackageJson(projectPath) {
|
|
|
3563
3357
|
function cleanConflictingFiles(projectPath) {
|
|
3564
3358
|
const conflicts = [".next", ".coherent", ".cursorrules", ".eslintrc.json", "CLAUDE.md", ".claude", ".vscode"];
|
|
3565
3359
|
for (const name of conflicts) {
|
|
3566
|
-
const fullPath =
|
|
3567
|
-
if (
|
|
3360
|
+
const fullPath = join5(projectPath, name);
|
|
3361
|
+
if (existsSync7(fullPath)) {
|
|
3568
3362
|
rmSync(fullPath, { recursive: true, force: true });
|
|
3569
3363
|
}
|
|
3570
3364
|
}
|
|
3571
3365
|
}
|
|
3572
3366
|
function runCreateNextApp(projectPath) {
|
|
3573
3367
|
cleanConflictingFiles(projectPath);
|
|
3574
|
-
const envPath =
|
|
3575
|
-
const envBackup =
|
|
3368
|
+
const envPath = join5(projectPath, ".env");
|
|
3369
|
+
const envBackup = existsSync7(envPath) ? readFileSync4(envPath, "utf-8") : null;
|
|
3576
3370
|
if (envBackup !== null) rmSync(envPath, { force: true });
|
|
3577
3371
|
const cmd = "npx --yes create-next-app@15.2.4 . --typescript --tailwind --eslint --app --no-src-dir --no-turbopack --yes";
|
|
3578
|
-
|
|
3372
|
+
execSync(cmd, { cwd: projectPath, stdio: "inherit" });
|
|
3579
3373
|
if (envBackup !== null) {
|
|
3580
|
-
const existing =
|
|
3374
|
+
const existing = existsSync7(envPath) ? readFileSync4(envPath, "utf-8") : "";
|
|
3581
3375
|
writeFileSync5(envPath, existing ? existing + "\n" + envBackup : envBackup, "utf-8");
|
|
3582
3376
|
}
|
|
3583
3377
|
}
|
|
3584
3378
|
async function ensureCoherentPrerequisites(projectPath) {
|
|
3585
|
-
const libPath =
|
|
3586
|
-
const utilsPath =
|
|
3587
|
-
const componentsUiPath =
|
|
3588
|
-
if (!
|
|
3589
|
-
if (!
|
|
3379
|
+
const libPath = join5(projectPath, "lib");
|
|
3380
|
+
const utilsPath = join5(projectPath, "lib", "utils.ts");
|
|
3381
|
+
const componentsUiPath = join5(projectPath, "components", "ui");
|
|
3382
|
+
if (!existsSync7(utilsPath)) {
|
|
3383
|
+
if (!existsSync7(libPath)) mkdirSync3(libPath, { recursive: true });
|
|
3590
3384
|
const cnContent = `import { type ClassValue, clsx } from 'clsx'
|
|
3591
3385
|
import { twMerge } from 'tailwind-merge'
|
|
3592
3386
|
|
|
@@ -3596,20 +3390,20 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
3596
3390
|
`;
|
|
3597
3391
|
await writeFile(utilsPath, cnContent);
|
|
3598
3392
|
}
|
|
3599
|
-
if (!
|
|
3393
|
+
if (!existsSync7(componentsUiPath)) mkdirSync3(componentsUiPath, { recursive: true });
|
|
3600
3394
|
}
|
|
3601
3395
|
async function ensureRegistryComponents(config2, projectPath) {
|
|
3602
3396
|
const provider = getComponentProvider();
|
|
3603
3397
|
const baseComponents = ["button", "card", "input", "label", "switch"];
|
|
3604
3398
|
await provider.installBatch(baseComponents, projectPath);
|
|
3605
3399
|
const generator = new ComponentGenerator(config2);
|
|
3606
|
-
const uiDir =
|
|
3607
|
-
if (!
|
|
3400
|
+
const uiDir = join5(projectPath, "components", "ui");
|
|
3401
|
+
if (!existsSync7(uiDir)) mkdirSync3(uiDir, { recursive: true });
|
|
3608
3402
|
for (const comp of config2.components) {
|
|
3609
3403
|
if (comp.source === "shadcn") continue;
|
|
3610
3404
|
const fileName = toKebabCase(comp.name) + ".tsx";
|
|
3611
|
-
const filePath =
|
|
3612
|
-
if (
|
|
3405
|
+
const filePath = join5(uiDir, fileName);
|
|
3406
|
+
if (existsSync7(filePath)) continue;
|
|
3613
3407
|
const code = await generator.generate(comp);
|
|
3614
3408
|
await writeFile(filePath, code);
|
|
3615
3409
|
}
|
|
@@ -3637,8 +3431,8 @@ async function initCommand(name) {
|
|
|
3637
3431
|
);
|
|
3638
3432
|
process.exit(1);
|
|
3639
3433
|
}
|
|
3640
|
-
const targetDir =
|
|
3641
|
-
if (!
|
|
3434
|
+
const targetDir = join5(cwd2(), name);
|
|
3435
|
+
if (!existsSync7(targetDir)) {
|
|
3642
3436
|
mkdirSync3(targetDir, { recursive: true });
|
|
3643
3437
|
}
|
|
3644
3438
|
process.chdir(targetDir);
|
|
@@ -3646,7 +3440,7 @@ async function initCommand(name) {
|
|
|
3646
3440
|
let projectPath;
|
|
3647
3441
|
try {
|
|
3648
3442
|
projectPath = cwd2();
|
|
3649
|
-
if (!
|
|
3443
|
+
if (!existsSync7(projectPath)) {
|
|
3650
3444
|
throw new Error("ENOENT");
|
|
3651
3445
|
}
|
|
3652
3446
|
} catch (err) {
|
|
@@ -3717,7 +3511,7 @@ async function initCommand(name) {
|
|
|
3717
3511
|
await ensureCoherentPrerequisites(projectPath);
|
|
3718
3512
|
const depsSpinner = ora("Installing component dependencies...").start();
|
|
3719
3513
|
try {
|
|
3720
|
-
|
|
3514
|
+
execSync(`npm install --legacy-peer-deps ${COHERENT_REQUIRED_PACKAGES.join(" ")}`, {
|
|
3721
3515
|
cwd: projectPath,
|
|
3722
3516
|
stdio: "pipe"
|
|
3723
3517
|
});
|
|
@@ -3730,7 +3524,7 @@ async function initCommand(name) {
|
|
|
3730
3524
|
const usesV4 = isTailwindV4(projectPath);
|
|
3731
3525
|
if (usesV4) {
|
|
3732
3526
|
const v4Css = generateV4GlobalsCss(config2);
|
|
3733
|
-
await writeFile(
|
|
3527
|
+
await writeFile(join5(projectPath, "app", "globals.css"), v4Css);
|
|
3734
3528
|
} else {
|
|
3735
3529
|
await scaffolder.generateGlobalsCss();
|
|
3736
3530
|
await scaffolder.generateTailwindConfigTs();
|
|
@@ -3744,14 +3538,14 @@ const config = {
|
|
|
3744
3538
|
|
|
3745
3539
|
export default config
|
|
3746
3540
|
`;
|
|
3747
|
-
await writeFile(
|
|
3541
|
+
await writeFile(join5(projectPath, "postcss.config.mjs"), postcssContent);
|
|
3748
3542
|
}
|
|
3749
3543
|
await scaffolder.generateRootLayout();
|
|
3750
3544
|
await configureNextImages(projectPath);
|
|
3751
3545
|
await createAppRouteGroupLayout(projectPath);
|
|
3752
3546
|
const welcomeMarkdown = getWelcomeMarkdown();
|
|
3753
3547
|
const homePageContent = generateWelcomeComponent(welcomeMarkdown);
|
|
3754
|
-
await writeFile(
|
|
3548
|
+
await writeFile(join5(projectPath, "app", "page.tsx"), homePageContent);
|
|
3755
3549
|
const designSystemSpinner = ora("Creating design system pages...").start();
|
|
3756
3550
|
await scaffolder.generateDesignSystemPages();
|
|
3757
3551
|
designSystemSpinner.succeed("Design system pages created");
|
|
@@ -3766,7 +3560,7 @@ export default config
|
|
|
3766
3560
|
scaffoldSpinner.succeed("Project structure created");
|
|
3767
3561
|
const depsSpinner = ora("Installing component dependencies...").start();
|
|
3768
3562
|
try {
|
|
3769
|
-
|
|
3563
|
+
execSync(`npm install --legacy-peer-deps ${COHERENT_REQUIRED_PACKAGES.join(" ")}`, {
|
|
3770
3564
|
cwd: projectPath,
|
|
3771
3565
|
stdio: "pipe"
|
|
3772
3566
|
});
|
|
@@ -3804,13 +3598,13 @@ export default config
|
|
|
3804
3598
|
}
|
|
3805
3599
|
}
|
|
3806
3600
|
async function configureNextImages(projectPath) {
|
|
3807
|
-
const tsPath =
|
|
3808
|
-
const jsPath =
|
|
3809
|
-
const mjsPath =
|
|
3601
|
+
const tsPath = join5(projectPath, "next.config.ts");
|
|
3602
|
+
const jsPath = join5(projectPath, "next.config.js");
|
|
3603
|
+
const mjsPath = join5(projectPath, "next.config.mjs");
|
|
3810
3604
|
let configPath = "";
|
|
3811
|
-
if (
|
|
3812
|
-
else if (
|
|
3813
|
-
else if (
|
|
3605
|
+
if (existsSync7(tsPath)) configPath = tsPath;
|
|
3606
|
+
else if (existsSync7(mjsPath)) configPath = mjsPath;
|
|
3607
|
+
else if (existsSync7(jsPath)) configPath = jsPath;
|
|
3814
3608
|
else return;
|
|
3815
3609
|
const content = `import type { NextConfig } from "next";
|
|
3816
3610
|
|
|
@@ -3829,7 +3623,7 @@ export default nextConfig;
|
|
|
3829
3623
|
await writeFile(configPath, content);
|
|
3830
3624
|
}
|
|
3831
3625
|
async function createAppRouteGroupLayout(projectPath) {
|
|
3832
|
-
const dir =
|
|
3626
|
+
const dir = join5(projectPath, "app", "(app)");
|
|
3833
3627
|
mkdirSync3(dir, { recursive: true });
|
|
3834
3628
|
const layoutCode = `export default function AppLayout({
|
|
3835
3629
|
children,
|
|
@@ -3843,14 +3637,14 @@ async function createAppRouteGroupLayout(projectPath) {
|
|
|
3843
3637
|
)
|
|
3844
3638
|
}
|
|
3845
3639
|
`;
|
|
3846
|
-
await writeFile(
|
|
3640
|
+
await writeFile(join5(dir, "layout.tsx"), layoutCode);
|
|
3847
3641
|
}
|
|
3848
3642
|
|
|
3849
3643
|
// src/commands/chat.ts
|
|
3850
3644
|
import chalk13 from "chalk";
|
|
3851
3645
|
import ora2 from "ora";
|
|
3852
|
-
import { resolve as resolve10, relative as relative2, join as
|
|
3853
|
-
import { existsSync as
|
|
3646
|
+
import { resolve as resolve10, relative as relative2, join as join10 } from "path";
|
|
3647
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
|
|
3854
3648
|
import {
|
|
3855
3649
|
DesignSystemManager as DesignSystemManager7,
|
|
3856
3650
|
ComponentManager as ComponentManager5,
|
|
@@ -4612,9 +4406,9 @@ function extractComponentSpec(changes) {
|
|
|
4612
4406
|
}
|
|
4613
4407
|
|
|
4614
4408
|
// src/utils/auth-route-group.ts
|
|
4615
|
-
import { join as
|
|
4616
|
-
import { readFile as
|
|
4617
|
-
import { existsSync as
|
|
4409
|
+
import { join as join6 } from "path";
|
|
4410
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
4411
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4618
4412
|
var AUTH_LAYOUT = `export default function AuthLayout({
|
|
4619
4413
|
children,
|
|
4620
4414
|
}: {
|
|
@@ -4646,20 +4440,20 @@ export default function ShowWhenNotAuthRoute({
|
|
|
4646
4440
|
}
|
|
4647
4441
|
`;
|
|
4648
4442
|
async function ensureAuthRouteGroup(projectRoot) {
|
|
4649
|
-
const authLayoutPath =
|
|
4650
|
-
const guardPath2 =
|
|
4651
|
-
const rootLayoutPath =
|
|
4652
|
-
if (!
|
|
4443
|
+
const authLayoutPath = join6(projectRoot, "app", "(auth)", "layout.tsx");
|
|
4444
|
+
const guardPath2 = join6(projectRoot, "app", "ShowWhenNotAuthRoute.tsx");
|
|
4445
|
+
const rootLayoutPath = join6(projectRoot, "app", "layout.tsx");
|
|
4446
|
+
if (!existsSync8(authLayoutPath)) {
|
|
4653
4447
|
const { mkdir: mkdir8 } = await import("fs/promises");
|
|
4654
|
-
await mkdir8(
|
|
4448
|
+
await mkdir8(join6(projectRoot, "app", "(auth)"), { recursive: true });
|
|
4655
4449
|
await writeFile2(authLayoutPath, AUTH_LAYOUT, "utf-8");
|
|
4656
4450
|
}
|
|
4657
|
-
if (!
|
|
4451
|
+
if (!existsSync8(guardPath2)) {
|
|
4658
4452
|
await writeFile2(guardPath2, SHOW_WHEN_NOT_AUTH, "utf-8");
|
|
4659
4453
|
}
|
|
4660
4454
|
let layoutContent;
|
|
4661
4455
|
try {
|
|
4662
|
-
layoutContent = await
|
|
4456
|
+
layoutContent = await readFile2(rootLayoutPath, "utf-8");
|
|
4663
4457
|
} catch {
|
|
4664
4458
|
return;
|
|
4665
4459
|
}
|
|
@@ -4688,9 +4482,9 @@ async function ensureAuthRouteGroup(projectRoot) {
|
|
|
4688
4482
|
}
|
|
4689
4483
|
|
|
4690
4484
|
// src/utils/dark-mode.ts
|
|
4691
|
-
import { readFile as
|
|
4692
|
-
import { join as
|
|
4693
|
-
import { existsSync as
|
|
4485
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
4486
|
+
import { join as join7 } from "path";
|
|
4487
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4694
4488
|
import { generateSharedComponent, loadManifest as loadManifest3, integrateSharedLayoutIntoRootLayout } from "@getcoherent/core";
|
|
4695
4489
|
var THEME_TOGGLE_CODE = `'use client'
|
|
4696
4490
|
|
|
@@ -4716,9 +4510,9 @@ export function ThemeToggle() {
|
|
|
4716
4510
|
}
|
|
4717
4511
|
`;
|
|
4718
4512
|
async function setDefaultDarkTheme(projectRoot) {
|
|
4719
|
-
const layoutPath =
|
|
4720
|
-
if (!
|
|
4721
|
-
let content = await
|
|
4513
|
+
const layoutPath = join7(projectRoot, "app", "layout.tsx");
|
|
4514
|
+
if (!existsSync9(layoutPath)) return false;
|
|
4515
|
+
let content = await readFile3(layoutPath, "utf-8");
|
|
4722
4516
|
if (content.includes('<html className="dark"') || content.includes("<html className='dark'")) return true;
|
|
4723
4517
|
content = content.replace(/<html(\s|>)/, '<html className="dark"$1');
|
|
4724
4518
|
await writeFile3(layoutPath, content, "utf-8");
|
|
@@ -4745,8 +4539,8 @@ async function ensureThemeToggle(projectRoot) {
|
|
|
4745
4539
|
import { appendFile } from "fs/promises";
|
|
4746
4540
|
|
|
4747
4541
|
// src/utils/backup.ts
|
|
4748
|
-
import { existsSync as
|
|
4749
|
-
import { join as
|
|
4542
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync6, readdirSync, rmSync as rmSync2, statSync } from "fs";
|
|
4543
|
+
import { join as join8, relative, dirname as dirname4 } from "path";
|
|
4750
4544
|
import chalk6 from "chalk";
|
|
4751
4545
|
var DEBUG = process.env.COHERENT_DEBUG === "1";
|
|
4752
4546
|
var BACKUP_DIR = ".coherent/backups";
|
|
@@ -4761,28 +4555,28 @@ var CRITICAL_FILES = [
|
|
|
4761
4555
|
var CRITICAL_DIRS = ["app", "components"];
|
|
4762
4556
|
function createBackup(projectRoot) {
|
|
4763
4557
|
try {
|
|
4764
|
-
const backupBase =
|
|
4558
|
+
const backupBase = join8(projectRoot, BACKUP_DIR);
|
|
4765
4559
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
4766
|
-
const backupPath =
|
|
4560
|
+
const backupPath = join8(backupBase, timestamp);
|
|
4767
4561
|
mkdirSync4(backupPath, { recursive: true });
|
|
4768
4562
|
let fileCount = 0;
|
|
4769
4563
|
for (const file of CRITICAL_FILES) {
|
|
4770
|
-
const src =
|
|
4771
|
-
if (
|
|
4772
|
-
const dest =
|
|
4564
|
+
const src = join8(projectRoot, file);
|
|
4565
|
+
if (existsSync10(src)) {
|
|
4566
|
+
const dest = join8(backupPath, file);
|
|
4773
4567
|
mkdirSync4(dirname4(dest), { recursive: true });
|
|
4774
|
-
writeFileSync6(dest,
|
|
4568
|
+
writeFileSync6(dest, readFileSync5(src));
|
|
4775
4569
|
fileCount++;
|
|
4776
4570
|
}
|
|
4777
4571
|
}
|
|
4778
4572
|
for (const dir of CRITICAL_DIRS) {
|
|
4779
|
-
const srcDir =
|
|
4780
|
-
if (!
|
|
4573
|
+
const srcDir = join8(projectRoot, dir);
|
|
4574
|
+
if (!existsSync10(srcDir)) continue;
|
|
4781
4575
|
backupDirectory(srcDir, projectRoot, backupPath);
|
|
4782
4576
|
fileCount += countFiles(srcDir);
|
|
4783
4577
|
}
|
|
4784
4578
|
writeFileSync6(
|
|
4785
|
-
|
|
4579
|
+
join8(backupPath, ".backup-meta.json"),
|
|
4786
4580
|
JSON.stringify(
|
|
4787
4581
|
{
|
|
4788
4582
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4803,15 +4597,15 @@ function backupDirectory(srcDir, projectRoot, backupPath) {
|
|
|
4803
4597
|
const entries = readdirSync(srcDir, { withFileTypes: true });
|
|
4804
4598
|
for (const entry of entries) {
|
|
4805
4599
|
if (entry.name === "node_modules" || entry.name === ".next" || entry.name === ".git") continue;
|
|
4806
|
-
const fullPath =
|
|
4600
|
+
const fullPath = join8(srcDir, entry.name);
|
|
4807
4601
|
const relPath = relative(projectRoot, fullPath);
|
|
4808
|
-
const destPath =
|
|
4602
|
+
const destPath = join8(backupPath, relPath);
|
|
4809
4603
|
if (entry.isDirectory()) {
|
|
4810
4604
|
mkdirSync4(destPath, { recursive: true });
|
|
4811
4605
|
backupDirectory(fullPath, projectRoot, backupPath);
|
|
4812
4606
|
} else if (entry.isFile()) {
|
|
4813
4607
|
mkdirSync4(dirname4(destPath), { recursive: true });
|
|
4814
|
-
writeFileSync6(destPath,
|
|
4608
|
+
writeFileSync6(destPath, readFileSync5(fullPath));
|
|
4815
4609
|
}
|
|
4816
4610
|
}
|
|
4817
4611
|
}
|
|
@@ -4821,7 +4615,7 @@ function countFiles(dir) {
|
|
|
4821
4615
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
4822
4616
|
for (const entry of entries) {
|
|
4823
4617
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4824
|
-
const fullPath =
|
|
4618
|
+
const fullPath = join8(dir, entry.name);
|
|
4825
4619
|
if (entry.isDirectory()) {
|
|
4826
4620
|
count += countFiles(fullPath);
|
|
4827
4621
|
} else if (entry.isFile()) {
|
|
@@ -4835,24 +4629,24 @@ function countFiles(dir) {
|
|
|
4835
4629
|
}
|
|
4836
4630
|
function pruneOldBackups(backupBase) {
|
|
4837
4631
|
try {
|
|
4838
|
-
const entries = readdirSync(backupBase).filter((e) => e !== ".gitkeep" && !e.startsWith(".")).map((name) => ({ name, time: statSync(
|
|
4632
|
+
const entries = readdirSync(backupBase).filter((e) => e !== ".gitkeep" && !e.startsWith(".")).map((name) => ({ name, time: statSync(join8(backupBase, name)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
4839
4633
|
for (const old of entries.slice(MAX_BACKUPS)) {
|
|
4840
|
-
rmSync2(
|
|
4634
|
+
rmSync2(join8(backupBase, old.name), { recursive: true, force: true });
|
|
4841
4635
|
}
|
|
4842
4636
|
} catch (e) {
|
|
4843
4637
|
if (DEBUG) console.error("Failed to prune old backups:", e);
|
|
4844
4638
|
}
|
|
4845
4639
|
}
|
|
4846
4640
|
function listBackups(projectRoot) {
|
|
4847
|
-
const backupBase =
|
|
4848
|
-
if (!
|
|
4641
|
+
const backupBase = join8(projectRoot, BACKUP_DIR);
|
|
4642
|
+
if (!existsSync10(backupBase)) return [];
|
|
4849
4643
|
try {
|
|
4850
4644
|
return readdirSync(backupBase).filter((e) => !e.startsWith(".")).map((name) => {
|
|
4851
|
-
const metaPath =
|
|
4645
|
+
const metaPath = join8(backupBase, name, ".backup-meta.json");
|
|
4852
4646
|
let meta = { timestamp: name, files: 0 };
|
|
4853
|
-
if (
|
|
4647
|
+
if (existsSync10(metaPath)) {
|
|
4854
4648
|
try {
|
|
4855
|
-
meta = JSON.parse(
|
|
4649
|
+
meta = JSON.parse(readFileSync5(metaPath, "utf-8"));
|
|
4856
4650
|
} catch (e) {
|
|
4857
4651
|
if (DEBUG) console.error("Bad backup meta:", metaPath, e);
|
|
4858
4652
|
}
|
|
@@ -4865,8 +4659,8 @@ function listBackups(projectRoot) {
|
|
|
4865
4659
|
}
|
|
4866
4660
|
}
|
|
4867
4661
|
function restoreBackup(projectRoot, backupName) {
|
|
4868
|
-
const backupPath =
|
|
4869
|
-
if (!
|
|
4662
|
+
const backupPath = join8(projectRoot, BACKUP_DIR, backupName);
|
|
4663
|
+
if (!existsSync10(backupPath)) return false;
|
|
4870
4664
|
try {
|
|
4871
4665
|
restoreDirectory(backupPath, backupPath, projectRoot);
|
|
4872
4666
|
return true;
|
|
@@ -4879,15 +4673,15 @@ function restoreDirectory(currentDir, backupRoot, projectRoot) {
|
|
|
4879
4673
|
const entries = readdirSync(currentDir, { withFileTypes: true });
|
|
4880
4674
|
for (const entry of entries) {
|
|
4881
4675
|
if (entry.name === ".backup-meta.json") continue;
|
|
4882
|
-
const fullPath =
|
|
4676
|
+
const fullPath = join8(currentDir, entry.name);
|
|
4883
4677
|
const relPath = relative(backupRoot, fullPath);
|
|
4884
|
-
const destPath =
|
|
4678
|
+
const destPath = join8(projectRoot, relPath);
|
|
4885
4679
|
if (entry.isDirectory()) {
|
|
4886
4680
|
mkdirSync4(destPath, { recursive: true });
|
|
4887
4681
|
restoreDirectory(fullPath, backupRoot, projectRoot);
|
|
4888
4682
|
} else if (entry.isFile()) {
|
|
4889
4683
|
mkdirSync4(dirname4(destPath), { recursive: true });
|
|
4890
|
-
writeFileSync6(destPath,
|
|
4684
|
+
writeFileSync6(destPath, readFileSync5(fullPath));
|
|
4891
4685
|
}
|
|
4892
4686
|
}
|
|
4893
4687
|
}
|
|
@@ -4899,15 +4693,15 @@ function logBackupCreated(backupPath) {
|
|
|
4899
4693
|
}
|
|
4900
4694
|
|
|
4901
4695
|
// src/utils/fix-globals-css.ts
|
|
4902
|
-
import { existsSync as
|
|
4696
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
4903
4697
|
import { resolve as resolve4 } from "path";
|
|
4904
4698
|
import { buildCssVariables as buildCssVariables2 } from "@getcoherent/core";
|
|
4905
4699
|
function needsGlobalsFix(projectRoot) {
|
|
4906
4700
|
const globalsPath = resolve4(projectRoot, "app", "globals.css");
|
|
4907
|
-
if (!
|
|
4701
|
+
if (!existsSync11(globalsPath)) {
|
|
4908
4702
|
return false;
|
|
4909
4703
|
}
|
|
4910
|
-
const content =
|
|
4704
|
+
const content = readFileSync6(globalsPath, "utf-8");
|
|
4911
4705
|
if (isTailwindV4(projectRoot)) {
|
|
4912
4706
|
if (!content.includes("@theme inline")) return true;
|
|
4913
4707
|
if (content.includes("@tailwind base")) return true;
|
|
@@ -4932,7 +4726,7 @@ function needsGlobalsFix(projectRoot) {
|
|
|
4932
4726
|
function fixGlobalsCss(projectRoot, config2) {
|
|
4933
4727
|
const globalsPath = resolve4(projectRoot, "app", "globals.css");
|
|
4934
4728
|
const layoutPath = resolve4(projectRoot, "app", "layout.tsx");
|
|
4935
|
-
if (!
|
|
4729
|
+
if (!existsSync11(globalsPath)) {
|
|
4936
4730
|
return;
|
|
4937
4731
|
}
|
|
4938
4732
|
if (isTailwindV4(projectRoot)) {
|
|
@@ -4955,10 +4749,10 @@ function fixGlobalsCss(projectRoot, config2) {
|
|
|
4955
4749
|
}
|
|
4956
4750
|
`;
|
|
4957
4751
|
writeFileSync7(globalsPath, minimalCss, "utf-8");
|
|
4958
|
-
if (!
|
|
4752
|
+
if (!existsSync11(layoutPath)) {
|
|
4959
4753
|
return;
|
|
4960
4754
|
}
|
|
4961
|
-
let layoutContent =
|
|
4755
|
+
let layoutContent = readFileSync6(layoutPath, "utf-8");
|
|
4962
4756
|
if (layoutContent.includes("dangerouslySetInnerHTML")) {
|
|
4963
4757
|
return;
|
|
4964
4758
|
}
|
|
@@ -4972,7 +4766,7 @@ function fixGlobalsCss(projectRoot, config2) {
|
|
|
4972
4766
|
|
|
4973
4767
|
// src/commands/chat/utils.ts
|
|
4974
4768
|
import { resolve as resolve5 } from "path";
|
|
4975
|
-
import { existsSync as
|
|
4769
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
|
|
4976
4770
|
import { DesignSystemManager as DesignSystemManager3, loadManifest as loadManifest4 } from "@getcoherent/core";
|
|
4977
4771
|
import chalk7 from "chalk";
|
|
4978
4772
|
var MARKETING_ROUTES = /* @__PURE__ */ new Set(["", "landing", "pricing", "about", "contact", "blog", "features"]);
|
|
@@ -4994,10 +4788,10 @@ function inferRouteUsesAuthSegment(route) {
|
|
|
4994
4788
|
function readAnchorPageCodeFromDisk(projectRoot, route) {
|
|
4995
4789
|
const useAuthSegment = inferRouteUsesAuthSegment(route);
|
|
4996
4790
|
const abs = routeToFsPath(projectRoot, route, useAuthSegment);
|
|
4997
|
-
if (!
|
|
4791
|
+
if (!existsSync12(abs)) return null;
|
|
4998
4792
|
let code;
|
|
4999
4793
|
try {
|
|
5000
|
-
code =
|
|
4794
|
+
code = readFileSync7(abs, "utf-8");
|
|
5001
4795
|
} catch {
|
|
5002
4796
|
return null;
|
|
5003
4797
|
}
|
|
@@ -5013,11 +4807,11 @@ function routeToFsPath(projectRoot, route, isAuthOrPlan) {
|
|
|
5013
4807
|
const isAuth = typeof isAuthOrPlan === "boolean" ? isAuthOrPlan : false;
|
|
5014
4808
|
const slug = route.replace(/^\//, "");
|
|
5015
4809
|
if (!slug) return resolve5(projectRoot, "app", "page.tsx");
|
|
4810
|
+
if (isAuth || isAuthRoute(route)) return resolve5(projectRoot, "app", "(auth)", slug || "login", "page.tsx");
|
|
5016
4811
|
if (plan) {
|
|
5017
4812
|
const group = getPageGroup(route, plan);
|
|
5018
4813
|
if (group) return resolve5(projectRoot, "app", `(${group.id})`, slug, "page.tsx");
|
|
5019
4814
|
}
|
|
5020
|
-
if (isAuth) return resolve5(projectRoot, "app", "(auth)", slug || "login", "page.tsx");
|
|
5021
4815
|
if (isMarketingRoute(route)) return resolve5(projectRoot, "app", slug, "page.tsx");
|
|
5022
4816
|
return resolve5(projectRoot, "app", "(app)", slug, "page.tsx");
|
|
5023
4817
|
}
|
|
@@ -5026,11 +4820,11 @@ function routeToRelPath(route, isAuthOrPlan) {
|
|
|
5026
4820
|
const isAuth = typeof isAuthOrPlan === "boolean" ? isAuthOrPlan : false;
|
|
5027
4821
|
const slug = route.replace(/^\//, "");
|
|
5028
4822
|
if (!slug) return "app/page.tsx";
|
|
4823
|
+
if (isAuth || isAuthRoute(route)) return `app/(auth)/${slug || "login"}/page.tsx`;
|
|
5029
4824
|
if (plan) {
|
|
5030
4825
|
const group = getPageGroup(route, plan);
|
|
5031
4826
|
if (group) return `app/(${group.id})/${slug}/page.tsx`;
|
|
5032
4827
|
}
|
|
5033
|
-
if (isAuth) return `app/(auth)/${slug || "login"}/page.tsx`;
|
|
5034
4828
|
if (isMarketingRoute(route)) return `app/${slug}/page.tsx`;
|
|
5035
4829
|
return `app/(app)/${slug}/page.tsx`;
|
|
5036
4830
|
}
|
|
@@ -5115,7 +4909,7 @@ async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, mani
|
|
|
5115
4909
|
}
|
|
5116
4910
|
}
|
|
5117
4911
|
async function loadConfig(configPath) {
|
|
5118
|
-
if (!
|
|
4912
|
+
if (!existsSync12(configPath)) {
|
|
5119
4913
|
throw new Error(
|
|
5120
4914
|
`Design system config not found at ${configPath}
|
|
5121
4915
|
Run "coherent init" first to create a project.`
|
|
@@ -5143,8 +4937,8 @@ async function resolveTargetFlags(message, options, config2, projectRoot) {
|
|
|
5143
4937
|
if (entry) {
|
|
5144
4938
|
const filePath = resolve5(projectRoot, entry.file);
|
|
5145
4939
|
let currentCode = "";
|
|
5146
|
-
if (
|
|
5147
|
-
currentCode =
|
|
4940
|
+
if (existsSync12(filePath)) {
|
|
4941
|
+
currentCode = readFileSync7(filePath, "utf-8");
|
|
5148
4942
|
}
|
|
5149
4943
|
const codeSnippet = currentCode ? `
|
|
5150
4944
|
|
|
@@ -5168,8 +4962,8 @@ ${currentCode}
|
|
|
5168
4962
|
const relPath = page.route === "/" ? "app/page.tsx" : `app${page.route}/page.tsx`;
|
|
5169
4963
|
const filePath = resolve5(projectRoot, relPath);
|
|
5170
4964
|
let currentCode = "";
|
|
5171
|
-
if (
|
|
5172
|
-
currentCode =
|
|
4965
|
+
if (existsSync12(filePath)) {
|
|
4966
|
+
currentCode = readFileSync7(filePath, "utf-8");
|
|
5173
4967
|
}
|
|
5174
4968
|
const codeSnippet = currentCode ? `
|
|
5175
4969
|
|
|
@@ -5624,7 +5418,7 @@ function applyDefaults(request) {
|
|
|
5624
5418
|
}
|
|
5625
5419
|
|
|
5626
5420
|
// src/commands/chat/split-generator.ts
|
|
5627
|
-
import { existsSync as
|
|
5421
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
5628
5422
|
import { resolve as resolve6 } from "path";
|
|
5629
5423
|
import { z } from "zod";
|
|
5630
5424
|
import {
|
|
@@ -6075,8 +5869,8 @@ function readExistingAppPageForReference(projectRoot, plan) {
|
|
|
6075
5869
|
for (const group of ["(app)", "(admin)", "(dashboard)"]) {
|
|
6076
5870
|
const filePath = resolve6(projectRoot, "app", group, key, "page.tsx");
|
|
6077
5871
|
try {
|
|
6078
|
-
if (
|
|
6079
|
-
const code =
|
|
5872
|
+
if (existsSync13(filePath)) {
|
|
5873
|
+
const code = readFileSync8(filePath, "utf-8");
|
|
6080
5874
|
const lines = code.split("\n");
|
|
6081
5875
|
return lines.slice(0, 60).join("\n");
|
|
6082
5876
|
}
|
|
@@ -6087,7 +5881,7 @@ function readExistingAppPageForReference(projectRoot, plan) {
|
|
|
6087
5881
|
}
|
|
6088
5882
|
}
|
|
6089
5883
|
const appDir = resolve6(projectRoot, "app");
|
|
6090
|
-
if (!
|
|
5884
|
+
if (!existsSync13(appDir)) return null;
|
|
6091
5885
|
try {
|
|
6092
5886
|
const entries = readdirSync2(appDir);
|
|
6093
5887
|
for (const entry of entries) {
|
|
@@ -6097,8 +5891,8 @@ function readExistingAppPageForReference(projectRoot, plan) {
|
|
|
6097
5891
|
const subDirs = readdirSync2(groupDir);
|
|
6098
5892
|
for (const sub of subDirs) {
|
|
6099
5893
|
const pagePath = resolve6(groupDir, sub, "page.tsx");
|
|
6100
|
-
if (
|
|
6101
|
-
const code =
|
|
5894
|
+
if (existsSync13(pagePath)) {
|
|
5895
|
+
const code = readFileSync8(pagePath, "utf-8");
|
|
6102
5896
|
const lines = code.split("\n");
|
|
6103
5897
|
return lines.slice(0, 60).join("\n");
|
|
6104
5898
|
}
|
|
@@ -6315,7 +6109,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
6315
6109
|
if (plan && plan.sharedComponents.length > 0) {
|
|
6316
6110
|
spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
|
|
6317
6111
|
try {
|
|
6318
|
-
const { generateSharedComponentsFromPlan } = await import("./plan-generator-
|
|
6112
|
+
const { generateSharedComponentsFromPlan } = await import("./plan-generator-WPYWUA7U.js");
|
|
6319
6113
|
const generated = await generateSharedComponentsFromPlan(
|
|
6320
6114
|
plan,
|
|
6321
6115
|
styleContext,
|
|
@@ -6372,13 +6166,13 @@ ${existingAppPageCode}
|
|
|
6372
6166
|
const existingPageCode = {};
|
|
6373
6167
|
if (projectRoot) {
|
|
6374
6168
|
const appDir = resolve6(projectRoot, "app");
|
|
6375
|
-
if (
|
|
6169
|
+
if (existsSync13(appDir)) {
|
|
6376
6170
|
const pageFiles = readdirSync2(appDir, { recursive: true }).filter(
|
|
6377
6171
|
(f) => typeof f === "string" && f.endsWith("page.tsx")
|
|
6378
6172
|
);
|
|
6379
6173
|
for (const pf of pageFiles) {
|
|
6380
6174
|
try {
|
|
6381
|
-
const code =
|
|
6175
|
+
const code = readFileSync8(resolve6(appDir, pf), "utf-8");
|
|
6382
6176
|
const route = "/" + pf.replace(/\/page\.tsx$/, "").replace(/\(.*?\)\//g, "");
|
|
6383
6177
|
existingPageCode[route === "/" ? "/" : route] = code;
|
|
6384
6178
|
} catch {
|
|
@@ -6673,7 +6467,7 @@ import {
|
|
|
6673
6467
|
|
|
6674
6468
|
// src/commands/chat/code-generator.ts
|
|
6675
6469
|
import { resolve as resolve7 } from "path";
|
|
6676
|
-
import { existsSync as
|
|
6470
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
6677
6471
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
6678
6472
|
import { dirname as dirname5 } from "path";
|
|
6679
6473
|
import {
|
|
@@ -6686,25 +6480,25 @@ import chalk9 from "chalk";
|
|
|
6686
6480
|
|
|
6687
6481
|
// src/utils/file-hashes.ts
|
|
6688
6482
|
import { createHash } from "crypto";
|
|
6689
|
-
import { readFile as
|
|
6690
|
-
import { join as
|
|
6483
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir2 } from "fs/promises";
|
|
6484
|
+
import { join as join9 } from "path";
|
|
6691
6485
|
var HASHES_FILE = ".coherent/file-hashes.json";
|
|
6692
6486
|
async function computeFileHash(filePath) {
|
|
6693
|
-
const content = await
|
|
6487
|
+
const content = await readFile4(filePath, "utf-8");
|
|
6694
6488
|
return createHash("sha256").update(content).digest("hex");
|
|
6695
6489
|
}
|
|
6696
6490
|
async function loadHashes(projectRoot) {
|
|
6697
6491
|
try {
|
|
6698
|
-
const raw = await
|
|
6492
|
+
const raw = await readFile4(join9(projectRoot, HASHES_FILE), "utf-8");
|
|
6699
6493
|
return JSON.parse(raw);
|
|
6700
6494
|
} catch {
|
|
6701
6495
|
return {};
|
|
6702
6496
|
}
|
|
6703
6497
|
}
|
|
6704
6498
|
async function saveHashes(projectRoot, hashes) {
|
|
6705
|
-
const dir =
|
|
6499
|
+
const dir = join9(projectRoot, ".coherent");
|
|
6706
6500
|
await mkdir2(dir, { recursive: true });
|
|
6707
|
-
await writeFile4(
|
|
6501
|
+
await writeFile4(join9(projectRoot, HASHES_FILE), JSON.stringify(hashes, null, 2) + "\n");
|
|
6708
6502
|
}
|
|
6709
6503
|
async function isManuallyEdited(filePath, storedHash) {
|
|
6710
6504
|
try {
|
|
@@ -6739,7 +6533,7 @@ async function ensureComponentsInstalled(componentIds, cm, dsm, pm, projectRoot)
|
|
|
6739
6533
|
const isRegistered = !!cm.read(componentId);
|
|
6740
6534
|
const fileName = toKebabCase(componentId) + ".tsx";
|
|
6741
6535
|
const filePath = resolve7(projectRoot, "components", "ui", fileName);
|
|
6742
|
-
const fileExists =
|
|
6536
|
+
const fileExists = existsSync14(filePath);
|
|
6743
6537
|
if (isRegistered && fileExists) continue;
|
|
6744
6538
|
const result = await provider.installComponent(componentId, projectRoot);
|
|
6745
6539
|
if (result.success && result.componentDef) {
|
|
@@ -6775,7 +6569,7 @@ async function regeneratePage(pageId, config2, projectRoot) {
|
|
|
6775
6569
|
const code = await generator.generate(page, appType);
|
|
6776
6570
|
const route = page.route || "/";
|
|
6777
6571
|
const isAuth = isAuthRoute(route) || isAuthRoute(page.name || page.id || "");
|
|
6778
|
-
const { loadPlan: loadPlanForPath } = await import("./plan-generator-
|
|
6572
|
+
const { loadPlan: loadPlanForPath } = await import("./plan-generator-WPYWUA7U.js");
|
|
6779
6573
|
const planForPath = loadPlanForPath(projectRoot);
|
|
6780
6574
|
const filePath = routeToFsPath(projectRoot, route, planForPath || isAuth);
|
|
6781
6575
|
await mkdir3(dirname5(filePath), { recursive: true });
|
|
@@ -6783,7 +6577,7 @@ async function regeneratePage(pageId, config2, projectRoot) {
|
|
|
6783
6577
|
}
|
|
6784
6578
|
async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
|
|
6785
6579
|
const filePath = resolve7(projectRoot, componentFile);
|
|
6786
|
-
if (!
|
|
6580
|
+
if (!existsSync14(filePath)) return true;
|
|
6787
6581
|
const storedHash = storedHashes[componentFile];
|
|
6788
6582
|
if (!storedHash) return true;
|
|
6789
6583
|
const edited = await isManuallyEdited(filePath, storedHash);
|
|
@@ -6859,16 +6653,16 @@ async function regenerateLayout(config2, projectRoot, options = {
|
|
|
6859
6653
|
}
|
|
6860
6654
|
async function scanAndInstallSharedDeps(projectRoot) {
|
|
6861
6655
|
const sharedDir = resolve7(projectRoot, "components", "shared");
|
|
6862
|
-
if (!
|
|
6656
|
+
if (!existsSync14(sharedDir)) return [];
|
|
6863
6657
|
const files = readdirSync3(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
6864
6658
|
const installed = [];
|
|
6865
6659
|
const provider = getComponentProvider();
|
|
6866
6660
|
for (const file of files) {
|
|
6867
|
-
const code =
|
|
6661
|
+
const code = readFileSync9(resolve7(sharedDir, file), "utf-8");
|
|
6868
6662
|
const importMatches = [...code.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g)];
|
|
6869
6663
|
for (const [, componentId] of importMatches) {
|
|
6870
6664
|
const uiPath = resolve7(projectRoot, "components", "ui", `${componentId}.tsx`);
|
|
6871
|
-
if (!
|
|
6665
|
+
if (!existsSync14(uiPath) && provider.has(componentId)) {
|
|
6872
6666
|
try {
|
|
6873
6667
|
await provider.installComponent(componentId, projectRoot);
|
|
6874
6668
|
installed.push(componentId);
|
|
@@ -6882,7 +6676,7 @@ async function scanAndInstallSharedDeps(projectRoot) {
|
|
|
6882
6676
|
async function ensureAppRouteGroupLayout(projectRoot, navType, forceUpdate = false, groupLayouts) {
|
|
6883
6677
|
const effectiveNavType = groupLayouts?.["app"] || navType;
|
|
6884
6678
|
const layoutPath = resolve7(projectRoot, "app", "(app)", "layout.tsx");
|
|
6885
|
-
if (
|
|
6679
|
+
if (existsSync14(layoutPath) && !forceUpdate) return;
|
|
6886
6680
|
const { mkdir: mkdirAsync } = await import("fs/promises");
|
|
6887
6681
|
await mkdirAsync(resolve7(projectRoot, "app", "(app)"), { recursive: true });
|
|
6888
6682
|
const code = buildAppLayoutCode(effectiveNavType);
|
|
@@ -6983,8 +6777,8 @@ async function ensurePlanGroupLayouts(projectRoot, plan, storedHashes = {}, conf
|
|
|
6983
6777
|
await mkdirAsync(groupDir, { recursive: true });
|
|
6984
6778
|
const layoutPath = resolve7(groupDir, "layout.tsx");
|
|
6985
6779
|
const relPath = `app/(${group.id})/layout.tsx`;
|
|
6986
|
-
if (
|
|
6987
|
-
const currentContent =
|
|
6780
|
+
if (existsSync14(layoutPath)) {
|
|
6781
|
+
const currentContent = readFileSync9(layoutPath, "utf-8");
|
|
6988
6782
|
const currentHash = createHash2("md5").update(currentContent).digest("hex");
|
|
6989
6783
|
const storedHash = storedHashes[relPath];
|
|
6990
6784
|
if (storedHash && storedHash !== currentHash) {
|
|
@@ -7026,9 +6820,9 @@ async function regenerateFiles(modified, config2, projectRoot, options = { navCh
|
|
|
7026
6820
|
const twGen = new TailwindConfigGenerator(config2);
|
|
7027
6821
|
const twPath = resolve7(projectRoot, "tailwind.config.ts");
|
|
7028
6822
|
const twCjsPath = resolve7(projectRoot, "tailwind.config.cjs");
|
|
7029
|
-
if (
|
|
6823
|
+
if (existsSync14(twPath)) {
|
|
7030
6824
|
await writeFile(twPath, await twGen.generate());
|
|
7031
|
-
} else if (
|
|
6825
|
+
} else if (existsSync14(twCjsPath)) {
|
|
7032
6826
|
await writeFile(twCjsPath, await twGen.generateCjs());
|
|
7033
6827
|
}
|
|
7034
6828
|
}
|
|
@@ -8291,7 +8085,7 @@ function hasNavChanged(before, after) {
|
|
|
8291
8085
|
// src/commands/chat/interactive.ts
|
|
8292
8086
|
import chalk12 from "chalk";
|
|
8293
8087
|
import { resolve as resolve9 } from "path";
|
|
8294
|
-
import { existsSync as
|
|
8088
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
8295
8089
|
import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager4, loadManifest as loadManifest7 } from "@getcoherent/core";
|
|
8296
8090
|
var DEBUG3 = process.env.COHERENT_DEBUG === "1";
|
|
8297
8091
|
async function interactiveChat(options, chatCommandFn) {
|
|
@@ -8316,8 +8110,8 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
8316
8110
|
let history = [];
|
|
8317
8111
|
try {
|
|
8318
8112
|
mkdirSync5(historyDir, { recursive: true });
|
|
8319
|
-
if (
|
|
8320
|
-
history =
|
|
8113
|
+
if (existsSync15(historyFile)) {
|
|
8114
|
+
history = readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
|
|
8321
8115
|
}
|
|
8322
8116
|
} catch (e) {
|
|
8323
8117
|
if (DEBUG3) console.error("Failed to load REPL history:", e);
|
|
@@ -8495,8 +8289,8 @@ async function chatCommand(message, options) {
|
|
|
8495
8289
|
const project = requireProject();
|
|
8496
8290
|
const projectRoot = project.root;
|
|
8497
8291
|
const configPath = project.configPath;
|
|
8498
|
-
const migrationGuard =
|
|
8499
|
-
if (
|
|
8292
|
+
const migrationGuard = join10(projectRoot, ".coherent", "migration-in-progress");
|
|
8293
|
+
if (existsSync16(migrationGuard)) {
|
|
8500
8294
|
spinner.fail("Migration in progress");
|
|
8501
8295
|
console.error(chalk13.red("\n\u274C A migration is in progress. Run `coherent migrate --rollback` to undo first."));
|
|
8502
8296
|
bail("Migration in progress");
|
|
@@ -8546,7 +8340,7 @@ async function chatCommand(message, options) {
|
|
|
8546
8340
|
spinner.start(`Creating shared component: ${componentName}...`);
|
|
8547
8341
|
const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
|
|
8548
8342
|
const { generateSharedComponent: generateSharedComponent7 } = await import("@getcoherent/core");
|
|
8549
|
-
const { autoFixCode: autoFixCode2 } = await import("./quality-validator-
|
|
8343
|
+
const { autoFixCode: autoFixCode2 } = await import("./quality-validator-VDQXXDAV.js");
|
|
8550
8344
|
const { extractPropsInterface, extractDependencies } = await import("./component-extractor-VYJLT5NR.js");
|
|
8551
8345
|
const aiProvider = await createAIProvider2(provider ?? "auto");
|
|
8552
8346
|
const prompt = `Generate a React component called "${componentName}". Description: ${message}.
|
|
@@ -8654,7 +8448,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
8654
8448
|
let manifest = await loadManifest8(project.root);
|
|
8655
8449
|
const validShared = manifest.shared.filter((s) => {
|
|
8656
8450
|
const fp = resolve10(project.root, s.file);
|
|
8657
|
-
return
|
|
8451
|
+
return existsSync16(fp);
|
|
8658
8452
|
});
|
|
8659
8453
|
if (validShared.length !== manifest.shared.length) {
|
|
8660
8454
|
const cleaned = manifest.shared.length - validShared.length;
|
|
@@ -8875,8 +8669,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
8875
8669
|
for (const entry of manifest.shared) {
|
|
8876
8670
|
try {
|
|
8877
8671
|
const sharedPath = resolve10(projectRoot, entry.file);
|
|
8878
|
-
if (
|
|
8879
|
-
const sharedCode =
|
|
8672
|
+
if (existsSync16(sharedPath)) {
|
|
8673
|
+
const sharedCode = readFileSync11(sharedPath, "utf-8");
|
|
8880
8674
|
const sharedImports = sharedCode.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g);
|
|
8881
8675
|
for (const m of sharedImports) {
|
|
8882
8676
|
if (m[1]) allNeededComponentIds.add(m[1]);
|
|
@@ -8896,8 +8690,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
8896
8690
|
const missingComponents = [];
|
|
8897
8691
|
for (const componentId of allNeededComponentIds) {
|
|
8898
8692
|
const isRegistered = !!cm.read(componentId);
|
|
8899
|
-
const filePath =
|
|
8900
|
-
const fileExists =
|
|
8693
|
+
const filePath = join10(projectRoot, "components", "ui", `${componentId}.tsx`);
|
|
8694
|
+
const fileExists = existsSync16(filePath);
|
|
8901
8695
|
if (DEBUG4) console.log(chalk13.gray(` Checking ${componentId}: registered=${isRegistered} file=${fileExists}`));
|
|
8902
8696
|
if (!isRegistered || !fileExists) {
|
|
8903
8697
|
missingComponents.push(componentId);
|
|
@@ -9013,9 +8807,10 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9013
8807
|
}
|
|
9014
8808
|
}
|
|
9015
8809
|
try {
|
|
9016
|
-
const { validateReuse } = await import("./reuse-validator-
|
|
8810
|
+
const { validateReuse } = await import("./reuse-validator-XR2ZEYC4.js");
|
|
9017
8811
|
const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-EIP2XM7T.js");
|
|
9018
8812
|
const manifest2 = await loadManifest8(projectRoot);
|
|
8813
|
+
const reuseplan = projectRoot ? loadPlan(projectRoot) : null;
|
|
9019
8814
|
if (manifest2.shared.length > 0) {
|
|
9020
8815
|
for (const request of normalizedRequests) {
|
|
9021
8816
|
if (request.type !== "add-page") continue;
|
|
@@ -9024,7 +8819,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9024
8819
|
if (!pageCode) continue;
|
|
9025
8820
|
const route = changes.route || "";
|
|
9026
8821
|
const pageType = inferPageTypeFromRoute2(route);
|
|
9027
|
-
const
|
|
8822
|
+
const planned = reuseplan ? new Set(reuseplan.sharedComponents.filter((c) => c.usedBy.includes(route)).map((c) => c.name)) : void 0;
|
|
8823
|
+
const warnings = validateReuse(manifest2, pageCode, pageType, void 0, planned);
|
|
9028
8824
|
for (const w of warnings) {
|
|
9029
8825
|
console.log(chalk13.yellow(` \u26A0 ${w.message}`));
|
|
9030
8826
|
}
|
|
@@ -9043,9 +8839,9 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9043
8839
|
const route = page.route || `/${page.id || "page"}`;
|
|
9044
8840
|
const pageFilePath = routeToFsPath(projectRoot, route, false);
|
|
9045
8841
|
let pageCode = "";
|
|
9046
|
-
if (
|
|
8842
|
+
if (existsSync16(pageFilePath)) {
|
|
9047
8843
|
try {
|
|
9048
|
-
pageCode =
|
|
8844
|
+
pageCode = readFileSync11(pageFilePath, "utf-8");
|
|
9049
8845
|
} catch {
|
|
9050
8846
|
}
|
|
9051
8847
|
}
|
|
@@ -9065,8 +8861,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9065
8861
|
}
|
|
9066
8862
|
const missingRoutes = [...allLinkedRoutes].filter((route) => {
|
|
9067
8863
|
if (expandedExisting.has(route)) return false;
|
|
9068
|
-
if (
|
|
9069
|
-
if (
|
|
8864
|
+
if (existsSync16(routeToFsPath(projectRoot, route, false))) return false;
|
|
8865
|
+
if (existsSync16(routeToFsPath(projectRoot, route, true))) return false;
|
|
9070
8866
|
return true;
|
|
9071
8867
|
});
|
|
9072
8868
|
const SCAFFOLD_AI_LIMIT = 10;
|
|
@@ -9131,7 +8927,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9131
8927
|
const filePath = routeToFsPath(projectRoot, linkedRoute, isAuth);
|
|
9132
8928
|
if (isAuth) await ensureAuthRouteGroup(projectRoot);
|
|
9133
8929
|
const dir = resolve10(filePath, "..");
|
|
9134
|
-
if (!
|
|
8930
|
+
if (!existsSync16(dir)) {
|
|
9135
8931
|
mkdirSync6(dir, { recursive: true });
|
|
9136
8932
|
}
|
|
9137
8933
|
const placeholderCode = `export default function ${pageName.replace(/\s/g, "")}Page() {
|
|
@@ -9164,7 +8960,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9164
8960
|
for (const mod of result.modified) {
|
|
9165
8961
|
if (mod.startsWith("app/") && mod.endsWith("/page.tsx")) {
|
|
9166
8962
|
try {
|
|
9167
|
-
const code =
|
|
8963
|
+
const code = readFileSync11(resolve10(projectRoot, mod), "utf-8");
|
|
9168
8964
|
const issues = validatePageQuality(code, allRoutes).filter(
|
|
9169
8965
|
(i) => i.type === "BROKEN_INTERNAL_LINK"
|
|
9170
8966
|
);
|
|
@@ -9247,13 +9043,13 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9247
9043
|
const sharedDir = resolve10(projectRoot, "components", "shared");
|
|
9248
9044
|
const layoutFile = resolve10(projectRoot, "app", "layout.tsx");
|
|
9249
9045
|
const filesToHash = [layoutFile];
|
|
9250
|
-
if (
|
|
9046
|
+
if (existsSync16(sharedDir)) {
|
|
9251
9047
|
for (const f of readdirSync4(sharedDir)) {
|
|
9252
9048
|
if (f.endsWith(".tsx")) filesToHash.push(resolve10(sharedDir, f));
|
|
9253
9049
|
}
|
|
9254
9050
|
}
|
|
9255
9051
|
for (const filePath of filesToHash) {
|
|
9256
|
-
if (
|
|
9052
|
+
if (existsSync16(filePath)) {
|
|
9257
9053
|
const rel = relative2(projectRoot, filePath);
|
|
9258
9054
|
updatedHashes[rel] = await computeFileHash(filePath);
|
|
9259
9055
|
}
|
|
@@ -9268,8 +9064,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9268
9064
|
let manifestChanged = false;
|
|
9269
9065
|
for (const entry of currentManifest.shared) {
|
|
9270
9066
|
const fullPath = resolve10(projectRoot, entry.file);
|
|
9271
|
-
if (!
|
|
9272
|
-
const code =
|
|
9067
|
+
if (!existsSync16(fullPath)) continue;
|
|
9068
|
+
const code = readFileSync11(fullPath, "utf-8");
|
|
9273
9069
|
const props = extractPropsInterface(code);
|
|
9274
9070
|
const deps = extractDependencies(code);
|
|
9275
9071
|
if (props && props !== entry.propsInterface || deps.length !== (entry.dependencies?.length ?? 0)) {
|
|
@@ -9283,8 +9079,8 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9283
9079
|
const pageFiles = Array.from(allModified).filter((f) => f.startsWith("app/") && f.endsWith("page.tsx"));
|
|
9284
9080
|
for (const pageFile of pageFiles) {
|
|
9285
9081
|
const fullPath = resolve10(projectRoot, pageFile);
|
|
9286
|
-
if (!
|
|
9287
|
-
const pageCode =
|
|
9082
|
+
if (!existsSync16(fullPath)) continue;
|
|
9083
|
+
const pageCode = readFileSync11(fullPath, "utf-8");
|
|
9288
9084
|
for (const entry of currentManifest.shared) {
|
|
9289
9085
|
const isUsed = pageCode.includes(`from '@/components/shared/`) && (pageCode.includes(`{ ${entry.name} }`) || pageCode.includes(`{ ${entry.name},`));
|
|
9290
9086
|
if (isUsed && !entry.usedIn.includes(pageFile)) {
|
|
@@ -9341,7 +9137,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
|
|
|
9341
9137
|
${uxRecommendations}
|
|
9342
9138
|
`;
|
|
9343
9139
|
try {
|
|
9344
|
-
if (!
|
|
9140
|
+
if (!existsSync16(recPath)) {
|
|
9345
9141
|
await writeFile(
|
|
9346
9142
|
recPath,
|
|
9347
9143
|
"# UX/UI Recommendations\n\nRecommendations are added here when you use `coherent chat` and the AI suggests improvements.\n"
|
|
@@ -9415,9 +9211,9 @@ ${uxRecommendations}
|
|
|
9415
9211
|
import chalk14 from "chalk";
|
|
9416
9212
|
import ora3 from "ora";
|
|
9417
9213
|
import { spawn } from "child_process";
|
|
9418
|
-
import { existsSync as
|
|
9419
|
-
import { resolve as resolve11, join as
|
|
9420
|
-
import { readdir
|
|
9214
|
+
import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10, readdirSync as readdirSync6 } from "fs";
|
|
9215
|
+
import { resolve as resolve11, join as join13 } from "path";
|
|
9216
|
+
import { readdir } from "fs/promises";
|
|
9421
9217
|
|
|
9422
9218
|
// src/utils/css-validator.ts
|
|
9423
9219
|
var REQUIRED_THEME_TOKENS = [
|
|
@@ -9471,13 +9267,13 @@ function validateV4GlobalsCss(css) {
|
|
|
9471
9267
|
import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
|
|
9472
9268
|
|
|
9473
9269
|
// src/utils/file-watcher.ts
|
|
9474
|
-
import { readFileSync as
|
|
9475
|
-
import { relative as relative4, join as
|
|
9270
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
|
|
9271
|
+
import { relative as relative4, join as join12 } from "path";
|
|
9476
9272
|
import { loadManifest as loadManifest9, saveManifest as saveManifest4 } from "@getcoherent/core";
|
|
9477
9273
|
|
|
9478
9274
|
// src/utils/component-integrity.ts
|
|
9479
|
-
import { existsSync as
|
|
9480
|
-
import { join as
|
|
9275
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
|
|
9276
|
+
import { join as join11, relative as relative3 } from "path";
|
|
9481
9277
|
function extractExportedComponentNames(code) {
|
|
9482
9278
|
const names = [];
|
|
9483
9279
|
let m;
|
|
@@ -9509,14 +9305,14 @@ function arraysEqual(a, b) {
|
|
|
9509
9305
|
}
|
|
9510
9306
|
function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
9511
9307
|
const results = [];
|
|
9512
|
-
const appDir =
|
|
9513
|
-
if (!
|
|
9308
|
+
const appDir = join11(projectRoot, "app");
|
|
9309
|
+
if (!existsSync17(appDir)) return results;
|
|
9514
9310
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
9515
9311
|
const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
9516
9312
|
for (const absPath of pageFiles) {
|
|
9517
9313
|
if (absPath.includes("design-system")) continue;
|
|
9518
9314
|
try {
|
|
9519
|
-
const code =
|
|
9315
|
+
const code = readFileSync12(absPath, "utf-8");
|
|
9520
9316
|
const hasNamedImport = new RegExp(`import\\s+\\{[^}]*\\b${componentName}\\b[^}]*\\}\\s+from\\s+['"]`).test(code);
|
|
9521
9317
|
const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
|
|
9522
9318
|
const hasPathImport = code.includes(`@/${componentImportPath}`);
|
|
@@ -9529,10 +9325,10 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
9529
9325
|
return results;
|
|
9530
9326
|
}
|
|
9531
9327
|
function isUsedInLayout(projectRoot, componentName) {
|
|
9532
|
-
const layoutPath =
|
|
9533
|
-
if (!
|
|
9328
|
+
const layoutPath = join11(projectRoot, "app", "layout.tsx");
|
|
9329
|
+
if (!existsSync17(layoutPath)) return false;
|
|
9534
9330
|
try {
|
|
9535
|
-
const code =
|
|
9331
|
+
const code = readFileSync12(layoutPath, "utf-8");
|
|
9536
9332
|
return code.includes(componentName);
|
|
9537
9333
|
} catch {
|
|
9538
9334
|
return false;
|
|
@@ -9540,8 +9336,8 @@ function isUsedInLayout(projectRoot, componentName) {
|
|
|
9540
9336
|
}
|
|
9541
9337
|
function findUnregisteredComponents(projectRoot, manifest) {
|
|
9542
9338
|
const results = [];
|
|
9543
|
-
const componentsDir =
|
|
9544
|
-
if (!
|
|
9339
|
+
const componentsDir = join11(projectRoot, "components");
|
|
9340
|
+
if (!existsSync17(componentsDir)) return results;
|
|
9545
9341
|
const registeredFiles = new Set(manifest.shared.map((s) => s.file));
|
|
9546
9342
|
const registeredNames = new Set(manifest.shared.map((s) => s.name));
|
|
9547
9343
|
const files = collectFiles(
|
|
@@ -9553,7 +9349,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
9553
9349
|
const relFile = relative3(projectRoot, absPath);
|
|
9554
9350
|
if (registeredFiles.has(relFile)) continue;
|
|
9555
9351
|
try {
|
|
9556
|
-
const code =
|
|
9352
|
+
const code = readFileSync12(absPath, "utf-8");
|
|
9557
9353
|
const exports = extractExportedComponentNames(code);
|
|
9558
9354
|
for (const name of exports) {
|
|
9559
9355
|
if (registeredNames.has(name)) continue;
|
|
@@ -9568,14 +9364,14 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
9568
9364
|
}
|
|
9569
9365
|
function findInlineDuplicates(projectRoot, manifest) {
|
|
9570
9366
|
const results = [];
|
|
9571
|
-
const appDir =
|
|
9572
|
-
if (!
|
|
9367
|
+
const appDir = join11(projectRoot, "app");
|
|
9368
|
+
if (!existsSync17(appDir)) return results;
|
|
9573
9369
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
9574
9370
|
for (const absPath of pageFiles) {
|
|
9575
9371
|
if (absPath.includes("design-system")) continue;
|
|
9576
9372
|
let code;
|
|
9577
9373
|
try {
|
|
9578
|
-
code =
|
|
9374
|
+
code = readFileSync12(absPath, "utf-8");
|
|
9579
9375
|
} catch {
|
|
9580
9376
|
continue;
|
|
9581
9377
|
}
|
|
@@ -9598,8 +9394,8 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
9598
9394
|
return results;
|
|
9599
9395
|
}
|
|
9600
9396
|
function findComponentFileByExportName(projectRoot, componentName) {
|
|
9601
|
-
const componentsDir =
|
|
9602
|
-
if (!
|
|
9397
|
+
const componentsDir = join11(projectRoot, "components");
|
|
9398
|
+
if (!existsSync17(componentsDir)) return null;
|
|
9603
9399
|
const files = collectFiles(
|
|
9604
9400
|
componentsDir,
|
|
9605
9401
|
(name) => (name.endsWith(".tsx") || name.endsWith(".jsx")) && !name.startsWith("."),
|
|
@@ -9607,7 +9403,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
9607
9403
|
);
|
|
9608
9404
|
for (const absPath of files) {
|
|
9609
9405
|
try {
|
|
9610
|
-
const code =
|
|
9406
|
+
const code = readFileSync12(absPath, "utf-8");
|
|
9611
9407
|
const exports = extractExportedComponentNames(code);
|
|
9612
9408
|
if (exports.includes(componentName)) {
|
|
9613
9409
|
return relative3(projectRoot, absPath);
|
|
@@ -9620,8 +9416,8 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
9620
9416
|
function removeOrphanedEntries(projectRoot, manifest) {
|
|
9621
9417
|
const removed = [];
|
|
9622
9418
|
const valid = manifest.shared.filter((entry) => {
|
|
9623
|
-
const filePath =
|
|
9624
|
-
if (
|
|
9419
|
+
const filePath = join11(projectRoot, entry.file);
|
|
9420
|
+
if (existsSync17(filePath)) return true;
|
|
9625
9421
|
removed.push({ id: entry.id, name: entry.name });
|
|
9626
9422
|
return false;
|
|
9627
9423
|
});
|
|
@@ -9639,8 +9435,8 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
9639
9435
|
};
|
|
9640
9436
|
const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
|
|
9641
9437
|
m.shared = m.shared.filter((entry) => {
|
|
9642
|
-
const filePath =
|
|
9643
|
-
if (!
|
|
9438
|
+
const filePath = join11(projectRoot, entry.file);
|
|
9439
|
+
if (!existsSync17(filePath)) {
|
|
9644
9440
|
const newPath = findComponentFileByExportName(projectRoot, entry.name);
|
|
9645
9441
|
if (newPath) {
|
|
9646
9442
|
result.updated.push({ id: entry.id, field: "file", from: entry.file, to: newPath });
|
|
@@ -9652,7 +9448,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
9652
9448
|
}
|
|
9653
9449
|
let code;
|
|
9654
9450
|
try {
|
|
9655
|
-
code =
|
|
9451
|
+
code = readFileSync12(join11(projectRoot, entry.file), "utf-8");
|
|
9656
9452
|
} catch {
|
|
9657
9453
|
return true;
|
|
9658
9454
|
}
|
|
@@ -9736,7 +9532,7 @@ function collectFiles(dir, filter, skipDirs = []) {
|
|
|
9736
9532
|
return;
|
|
9737
9533
|
}
|
|
9738
9534
|
for (const e of entries) {
|
|
9739
|
-
const full =
|
|
9535
|
+
const full = join11(d, e.name);
|
|
9740
9536
|
if (e.isDirectory()) {
|
|
9741
9537
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
9742
9538
|
walk(full);
|
|
@@ -9775,9 +9571,9 @@ function findInlineDuplicatesOfShared(content, manifest) {
|
|
|
9775
9571
|
}
|
|
9776
9572
|
function getWatcherConfig(projectRoot) {
|
|
9777
9573
|
try {
|
|
9778
|
-
const pkgPath =
|
|
9779
|
-
if (!
|
|
9780
|
-
const pkg = JSON.parse(
|
|
9574
|
+
const pkgPath = join12(projectRoot, "package.json");
|
|
9575
|
+
if (!existsSync18(pkgPath)) return defaultWatcherConfig();
|
|
9576
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
9781
9577
|
const c = pkg?.coherent?.watcher ?? {};
|
|
9782
9578
|
return {
|
|
9783
9579
|
enabled: c.enabled !== false,
|
|
@@ -9805,7 +9601,7 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
9805
9601
|
if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
|
|
9806
9602
|
let content;
|
|
9807
9603
|
try {
|
|
9808
|
-
content =
|
|
9604
|
+
content = readFileSync13(filePath, "utf-8");
|
|
9809
9605
|
} catch {
|
|
9810
9606
|
return;
|
|
9811
9607
|
}
|
|
@@ -9878,7 +9674,7 @@ async function detectNewComponent(projectRoot, filePath) {
|
|
|
9878
9674
|
const manifest = await loadManifest9(projectRoot);
|
|
9879
9675
|
const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
|
|
9880
9676
|
if (alreadyRegistered) return;
|
|
9881
|
-
const code =
|
|
9677
|
+
const code = readFileSync13(filePath, "utf-8");
|
|
9882
9678
|
const exports = extractExportedComponentNames(code);
|
|
9883
9679
|
if (exports.length > 0) {
|
|
9884
9680
|
const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
|
|
@@ -9904,8 +9700,8 @@ function startFileWatcher(projectRoot) {
|
|
|
9904
9700
|
let watcher = null;
|
|
9905
9701
|
let manifestWatcher = null;
|
|
9906
9702
|
import("chokidar").then((chokidar) => {
|
|
9907
|
-
const appGlob =
|
|
9908
|
-
const compGlob =
|
|
9703
|
+
const appGlob = join12(projectRoot, "app", "**", "*.tsx");
|
|
9704
|
+
const compGlob = join12(projectRoot, "components", "**", "*.tsx");
|
|
9909
9705
|
watcher = chokidar.default.watch([appGlob, compGlob], {
|
|
9910
9706
|
ignoreInitial: true,
|
|
9911
9707
|
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
@@ -9917,8 +9713,8 @@ function startFileWatcher(projectRoot) {
|
|
|
9917
9713
|
});
|
|
9918
9714
|
watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
|
|
9919
9715
|
});
|
|
9920
|
-
const manifestPath =
|
|
9921
|
-
if (
|
|
9716
|
+
const manifestPath = join12(projectRoot, "coherent.components.json");
|
|
9717
|
+
if (existsSync18(manifestPath)) {
|
|
9922
9718
|
import("chokidar").then((chokidar) => {
|
|
9923
9719
|
manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
|
|
9924
9720
|
manifestWatcher.on("change", () => handleManifestChange(projectRoot));
|
|
@@ -9932,7 +9728,7 @@ function startFileWatcher(projectRoot) {
|
|
|
9932
9728
|
|
|
9933
9729
|
// src/commands/preview.ts
|
|
9934
9730
|
function getPackageManager(projectRoot) {
|
|
9935
|
-
const hasPnpm =
|
|
9731
|
+
const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
9936
9732
|
return hasPnpm ? "pnpm" : "npm";
|
|
9937
9733
|
}
|
|
9938
9734
|
function runInstall(projectRoot) {
|
|
@@ -9956,21 +9752,21 @@ function runInstall(projectRoot) {
|
|
|
9956
9752
|
function checkProjectInitialized(projectRoot) {
|
|
9957
9753
|
const configPath = resolve11(projectRoot, "design-system.config.ts");
|
|
9958
9754
|
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
9959
|
-
if (!
|
|
9755
|
+
if (!existsSync19(configPath)) {
|
|
9960
9756
|
return false;
|
|
9961
9757
|
}
|
|
9962
|
-
if (!
|
|
9758
|
+
if (!existsSync19(packageJsonPath)) {
|
|
9963
9759
|
return false;
|
|
9964
9760
|
}
|
|
9965
9761
|
return true;
|
|
9966
9762
|
}
|
|
9967
9763
|
function checkDependenciesInstalled(projectRoot) {
|
|
9968
9764
|
const nodeModulesPath = resolve11(projectRoot, "node_modules");
|
|
9969
|
-
return
|
|
9765
|
+
return existsSync19(nodeModulesPath);
|
|
9970
9766
|
}
|
|
9971
9767
|
function clearStaleCache(projectRoot) {
|
|
9972
|
-
const nextDir =
|
|
9973
|
-
if (
|
|
9768
|
+
const nextDir = join13(projectRoot, ".next");
|
|
9769
|
+
if (existsSync19(nextDir)) {
|
|
9974
9770
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
9975
9771
|
console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
|
|
9976
9772
|
}
|
|
@@ -9986,11 +9782,11 @@ async function preflightDependencyCheck(projectRoot) {
|
|
|
9986
9782
|
}
|
|
9987
9783
|
async function listPageFiles(appDir) {
|
|
9988
9784
|
const out = [];
|
|
9989
|
-
if (!
|
|
9785
|
+
if (!existsSync19(appDir)) return out;
|
|
9990
9786
|
async function walk(dir) {
|
|
9991
|
-
const entries = await
|
|
9787
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
9992
9788
|
for (const e of entries) {
|
|
9993
|
-
const full =
|
|
9789
|
+
const full = join13(dir, e.name);
|
|
9994
9790
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
|
|
9995
9791
|
else if (e.isFile() && e.name === "page.tsx") out.push(full);
|
|
9996
9792
|
}
|
|
@@ -9999,10 +9795,10 @@ async function listPageFiles(appDir) {
|
|
|
9999
9795
|
return out;
|
|
10000
9796
|
}
|
|
10001
9797
|
async function validateSyntax(projectRoot) {
|
|
10002
|
-
const appDir =
|
|
9798
|
+
const appDir = join13(projectRoot, "app");
|
|
10003
9799
|
const pages = await listPageFiles(appDir);
|
|
10004
9800
|
for (const file of pages) {
|
|
10005
|
-
const content =
|
|
9801
|
+
const content = readFileSync14(file, "utf-8");
|
|
10006
9802
|
const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
|
|
10007
9803
|
if (fixed !== content) {
|
|
10008
9804
|
writeFileSync10(file, fixed, "utf-8");
|
|
@@ -10011,18 +9807,18 @@ async function validateSyntax(projectRoot) {
|
|
|
10011
9807
|
}
|
|
10012
9808
|
}
|
|
10013
9809
|
async function fixMissingComponentExports(projectRoot) {
|
|
10014
|
-
const appDir =
|
|
10015
|
-
const uiDir =
|
|
10016
|
-
if (!
|
|
9810
|
+
const appDir = join13(projectRoot, "app");
|
|
9811
|
+
const uiDir = join13(projectRoot, "components", "ui");
|
|
9812
|
+
if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
|
|
10017
9813
|
const pages = await listPageFiles(appDir);
|
|
10018
|
-
const sharedDir =
|
|
10019
|
-
if (
|
|
10020
|
-
const sharedFiles = readdirSync6(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) =>
|
|
9814
|
+
const sharedDir = join13(projectRoot, "components", "shared");
|
|
9815
|
+
if (existsSync19(sharedDir)) {
|
|
9816
|
+
const sharedFiles = readdirSync6(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => join13(sharedDir, f));
|
|
10021
9817
|
pages.push(...sharedFiles);
|
|
10022
9818
|
}
|
|
10023
9819
|
const neededExports = /* @__PURE__ */ new Map();
|
|
10024
9820
|
for (const file of pages) {
|
|
10025
|
-
const content =
|
|
9821
|
+
const content = readFileSync14(file, "utf-8");
|
|
10026
9822
|
const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/ui\/([^'"]+)['"]/g;
|
|
10027
9823
|
let m;
|
|
10028
9824
|
while ((m = importRe.exec(content)) !== null) {
|
|
@@ -10032,7 +9828,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10032
9828
|
for (const name of names) neededExports.get(componentId).add(name);
|
|
10033
9829
|
}
|
|
10034
9830
|
}
|
|
10035
|
-
const configPath =
|
|
9831
|
+
const configPath = join13(projectRoot, "design-system.config.ts");
|
|
10036
9832
|
let config2 = null;
|
|
10037
9833
|
try {
|
|
10038
9834
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -10042,8 +9838,8 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10042
9838
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
10043
9839
|
const provider = getComponentProvider();
|
|
10044
9840
|
for (const [componentId, needed] of neededExports) {
|
|
10045
|
-
const componentFile =
|
|
10046
|
-
if (!
|
|
9841
|
+
const componentFile = join13(uiDir, `${componentId}.tsx`);
|
|
9842
|
+
if (!existsSync19(componentFile)) {
|
|
10047
9843
|
if (provider.has(componentId)) {
|
|
10048
9844
|
try {
|
|
10049
9845
|
const result = await provider.installComponent(componentId, projectRoot);
|
|
@@ -10066,7 +9862,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10066
9862
|
}
|
|
10067
9863
|
continue;
|
|
10068
9864
|
}
|
|
10069
|
-
const content =
|
|
9865
|
+
const content = readFileSync14(componentFile, "utf-8");
|
|
10070
9866
|
const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
|
|
10071
9867
|
const existingExports = /* @__PURE__ */ new Set();
|
|
10072
9868
|
let em;
|
|
@@ -10100,7 +9896,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10100
9896
|
}
|
|
10101
9897
|
const neededSharedExports = /* @__PURE__ */ new Map();
|
|
10102
9898
|
for (const file of pages) {
|
|
10103
|
-
const content =
|
|
9899
|
+
const content = readFileSync14(file, "utf-8");
|
|
10104
9900
|
const sharedImportRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/shared\/([^'"]+)['"]/g;
|
|
10105
9901
|
let sm;
|
|
10106
9902
|
while ((sm = sharedImportRe.exec(content)) !== null) {
|
|
@@ -10111,9 +9907,9 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10111
9907
|
}
|
|
10112
9908
|
}
|
|
10113
9909
|
for (const [componentId, needed] of neededSharedExports) {
|
|
10114
|
-
const componentFile =
|
|
10115
|
-
if (!
|
|
10116
|
-
let content =
|
|
9910
|
+
const componentFile = join13(sharedDir, `${componentId}.tsx`);
|
|
9911
|
+
if (!existsSync19(componentFile)) continue;
|
|
9912
|
+
let content = readFileSync14(componentFile, "utf-8");
|
|
10117
9913
|
const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
|
|
10118
9914
|
const existingExports = /* @__PURE__ */ new Set();
|
|
10119
9915
|
let em;
|
|
@@ -10135,8 +9931,8 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10135
9931
|
}
|
|
10136
9932
|
}
|
|
10137
9933
|
async function backfillPageAnalysis(projectRoot) {
|
|
10138
|
-
const configPath =
|
|
10139
|
-
if (!
|
|
9934
|
+
const configPath = join13(projectRoot, "design-system.config.ts");
|
|
9935
|
+
if (!existsSync19(configPath)) return;
|
|
10140
9936
|
try {
|
|
10141
9937
|
const mgr = new DesignSystemManager8(configPath);
|
|
10142
9938
|
const config2 = mgr.getConfig();
|
|
@@ -10147,14 +9943,14 @@ async function backfillPageAnalysis(projectRoot) {
|
|
|
10147
9943
|
const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
|
|
10148
9944
|
let filePath;
|
|
10149
9945
|
if (route === "/") {
|
|
10150
|
-
filePath =
|
|
9946
|
+
filePath = join13(projectRoot, "app", "page.tsx");
|
|
10151
9947
|
} else if (isAuth) {
|
|
10152
|
-
filePath =
|
|
9948
|
+
filePath = join13(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
|
|
10153
9949
|
} else {
|
|
10154
|
-
filePath =
|
|
9950
|
+
filePath = join13(projectRoot, "app", route.slice(1), "page.tsx");
|
|
10155
9951
|
}
|
|
10156
|
-
if (!
|
|
10157
|
-
const code =
|
|
9952
|
+
if (!existsSync19(filePath)) continue;
|
|
9953
|
+
const code = readFileSync14(filePath, "utf-8");
|
|
10158
9954
|
if (code.length < 50) continue;
|
|
10159
9955
|
page.pageAnalysis = analyzePageCode(code);
|
|
10160
9956
|
changed = true;
|
|
@@ -10305,11 +10101,11 @@ async function openBrowser(url) {
|
|
|
10305
10101
|
}
|
|
10306
10102
|
function startDevServer(projectRoot) {
|
|
10307
10103
|
const packageJsonPath = resolve11(projectRoot, "package.json");
|
|
10308
|
-
if (!
|
|
10104
|
+
if (!existsSync19(packageJsonPath)) {
|
|
10309
10105
|
throw new Error('package.json not found. Run "coherent init" first.');
|
|
10310
10106
|
}
|
|
10311
|
-
const hasPnpm =
|
|
10312
|
-
const hasNpm =
|
|
10107
|
+
const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
|
|
10108
|
+
const hasNpm = existsSync19(resolve11(projectRoot, "package-lock.json"));
|
|
10313
10109
|
const command = hasPnpm ? "pnpm" : hasNpm ? "npm" : "npx";
|
|
10314
10110
|
const args = hasPnpm ? ["dev", "--turbo"] : hasNpm ? ["run", "dev", "--", "--turbo"] : ["next", "dev", "--turbo"];
|
|
10315
10111
|
const child = spawn(command, args, {
|
|
@@ -10367,8 +10163,8 @@ async function previewCommand() {
|
|
|
10367
10163
|
}
|
|
10368
10164
|
if (isTailwindV4(projectRoot)) {
|
|
10369
10165
|
const globalsPath = resolve11(projectRoot, "app", "globals.css");
|
|
10370
|
-
if (
|
|
10371
|
-
const globalsContent =
|
|
10166
|
+
if (existsSync19(globalsPath)) {
|
|
10167
|
+
const globalsContent = readFileSync14(globalsPath, "utf-8");
|
|
10372
10168
|
const cssIssues = validateV4GlobalsCss(globalsContent);
|
|
10373
10169
|
if (cssIssues.length > 0) {
|
|
10374
10170
|
console.log(chalk14.yellow("\n\u26A0\uFE0F globals.css validation warnings:"));
|
|
@@ -10409,9 +10205,9 @@ async function previewCommand() {
|
|
|
10409
10205
|
import chalk15 from "chalk";
|
|
10410
10206
|
import ora4 from "ora";
|
|
10411
10207
|
import { spawn as spawn2 } from "child_process";
|
|
10412
|
-
import { existsSync as
|
|
10413
|
-
import { resolve as resolve12, join as
|
|
10414
|
-
import { readdir as
|
|
10208
|
+
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync7 } from "fs";
|
|
10209
|
+
import { resolve as resolve12, join as join14, dirname as dirname7 } from "path";
|
|
10210
|
+
import { readdir as readdir2, readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
|
|
10415
10211
|
var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
10416
10212
|
"node_modules",
|
|
10417
10213
|
".next",
|
|
@@ -10430,10 +10226,10 @@ var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
10430
10226
|
]);
|
|
10431
10227
|
async function copyDir(src, dest) {
|
|
10432
10228
|
await mkdir5(dest, { recursive: true });
|
|
10433
|
-
const entries = await
|
|
10229
|
+
const entries = await readdir2(src, { withFileTypes: true });
|
|
10434
10230
|
for (const e of entries) {
|
|
10435
|
-
const srcPath =
|
|
10436
|
-
const destPath =
|
|
10231
|
+
const srcPath = join14(src, e.name);
|
|
10232
|
+
const destPath = join14(dest, e.name);
|
|
10437
10233
|
if (COPY_EXCLUDE.has(e.name)) continue;
|
|
10438
10234
|
if (e.isDirectory()) {
|
|
10439
10235
|
await copyDir(srcPath, destPath);
|
|
@@ -10444,18 +10240,18 @@ async function copyDir(src, dest) {
|
|
|
10444
10240
|
}
|
|
10445
10241
|
}
|
|
10446
10242
|
function checkProjectInitialized2(projectRoot) {
|
|
10447
|
-
return
|
|
10243
|
+
return existsSync20(resolve12(projectRoot, "design-system.config.ts")) && existsSync20(resolve12(projectRoot, "package.json"));
|
|
10448
10244
|
}
|
|
10449
10245
|
function getPackageManager2(projectRoot) {
|
|
10450
|
-
if (
|
|
10451
|
-
if (
|
|
10246
|
+
if (existsSync20(resolve12(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
10247
|
+
if (existsSync20(resolve12(projectRoot, "package-lock.json"))) return "npm";
|
|
10452
10248
|
return "npx";
|
|
10453
10249
|
}
|
|
10454
10250
|
async function patchNextConfigForExport(outRoot) {
|
|
10455
10251
|
for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
|
|
10456
|
-
const p =
|
|
10457
|
-
if (!
|
|
10458
|
-
let content = await
|
|
10252
|
+
const p = join14(outRoot, name);
|
|
10253
|
+
if (!existsSync20(p)) continue;
|
|
10254
|
+
let content = await readFile5(p, "utf-8");
|
|
10459
10255
|
if (content.includes("ignoreDuringBuilds")) return;
|
|
10460
10256
|
content = content.replace(
|
|
10461
10257
|
/(const\s+nextConfig\s*(?::\s*\w+)?\s*=\s*\{)/,
|
|
@@ -10495,10 +10291,10 @@ EXPOSE 3000
|
|
|
10495
10291
|
\`\`\`
|
|
10496
10292
|
`;
|
|
10497
10293
|
async function ensureReadmeDeploySection(outRoot) {
|
|
10498
|
-
const readmePath =
|
|
10499
|
-
if (!
|
|
10294
|
+
const readmePath = join14(outRoot, "README.md");
|
|
10295
|
+
if (!existsSync20(readmePath)) return;
|
|
10500
10296
|
try {
|
|
10501
|
-
let content = await
|
|
10297
|
+
let content = await readFile5(readmePath, "utf-8");
|
|
10502
10298
|
if (/##\s+Deploy\b/m.test(content)) return;
|
|
10503
10299
|
content = content.trimEnd() + DEPLOY_SECTION + "\n";
|
|
10504
10300
|
await writeFile5(readmePath, content);
|
|
@@ -10510,25 +10306,25 @@ async function countPages(outRoot) {
|
|
|
10510
10306
|
async function walk(dir) {
|
|
10511
10307
|
let entries;
|
|
10512
10308
|
try {
|
|
10513
|
-
entries = await
|
|
10309
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
10514
10310
|
} catch {
|
|
10515
10311
|
return;
|
|
10516
10312
|
}
|
|
10517
10313
|
for (const e of entries) {
|
|
10518
|
-
const full =
|
|
10314
|
+
const full = join14(dir, e.name);
|
|
10519
10315
|
if (e.isFile() && e.name === "page.tsx") n++;
|
|
10520
10316
|
else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
|
|
10521
10317
|
}
|
|
10522
10318
|
}
|
|
10523
|
-
const appDir =
|
|
10524
|
-
if (
|
|
10319
|
+
const appDir = join14(outRoot, "app");
|
|
10320
|
+
if (existsSync20(appDir)) await walk(appDir);
|
|
10525
10321
|
return n;
|
|
10526
10322
|
}
|
|
10527
10323
|
function countComponents(outRoot) {
|
|
10528
10324
|
let n = 0;
|
|
10529
10325
|
for (const sub of ["ui", "shared"]) {
|
|
10530
|
-
const dir =
|
|
10531
|
-
if (!
|
|
10326
|
+
const dir = join14(outRoot, "components", sub);
|
|
10327
|
+
if (!existsSync20(dir)) continue;
|
|
10532
10328
|
try {
|
|
10533
10329
|
n += readdirSync7(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
|
|
10534
10330
|
} catch {
|
|
@@ -10536,14 +10332,14 @@ function countComponents(outRoot) {
|
|
|
10536
10332
|
}
|
|
10537
10333
|
return n;
|
|
10538
10334
|
}
|
|
10539
|
-
var
|
|
10540
|
-
async function
|
|
10335
|
+
var IMPORT_FROM_REGEX = /from\s+['"]([^'"]+)['"]/g;
|
|
10336
|
+
async function collectImportedPackages(dir, extensions) {
|
|
10541
10337
|
const packages = /* @__PURE__ */ new Set();
|
|
10542
|
-
if (!
|
|
10338
|
+
if (!existsSync20(dir)) return packages;
|
|
10543
10339
|
async function walk(d) {
|
|
10544
|
-
const entries = await
|
|
10340
|
+
const entries = await readdir2(d, { withFileTypes: true });
|
|
10545
10341
|
for (const e of entries) {
|
|
10546
|
-
const full =
|
|
10342
|
+
const full = join14(d, e.name);
|
|
10547
10343
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
10548
10344
|
await walk(full);
|
|
10549
10345
|
continue;
|
|
@@ -10551,10 +10347,10 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
10551
10347
|
if (!e.isFile()) continue;
|
|
10552
10348
|
const ext = e.name.replace(/^.*\./, "");
|
|
10553
10349
|
if (!extensions.has(ext)) continue;
|
|
10554
|
-
const content = await
|
|
10350
|
+
const content = await readFile5(full, "utf-8").catch(() => "");
|
|
10555
10351
|
let m;
|
|
10556
|
-
|
|
10557
|
-
while ((m =
|
|
10352
|
+
IMPORT_FROM_REGEX.lastIndex = 0;
|
|
10353
|
+
while ((m = IMPORT_FROM_REGEX.exec(content)) !== null) {
|
|
10558
10354
|
const spec = m[1];
|
|
10559
10355
|
if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("@/")) continue;
|
|
10560
10356
|
const pkg = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
|
|
@@ -10566,64 +10362,64 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
10566
10362
|
return packages;
|
|
10567
10363
|
}
|
|
10568
10364
|
async function findMissingDepsInExport(outRoot) {
|
|
10569
|
-
const pkgPath =
|
|
10570
|
-
if (!
|
|
10365
|
+
const pkgPath = join14(outRoot, "package.json");
|
|
10366
|
+
if (!existsSync20(pkgPath)) return [];
|
|
10571
10367
|
let pkg;
|
|
10572
10368
|
try {
|
|
10573
|
-
pkg = JSON.parse(await
|
|
10369
|
+
pkg = JSON.parse(await readFile5(pkgPath, "utf-8"));
|
|
10574
10370
|
} catch {
|
|
10575
10371
|
return [];
|
|
10576
10372
|
}
|
|
10577
10373
|
const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
10578
|
-
const codeDirs = [
|
|
10374
|
+
const codeDirs = [join14(outRoot, "app"), join14(outRoot, "components")];
|
|
10579
10375
|
const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
|
|
10580
10376
|
const imported = /* @__PURE__ */ new Set();
|
|
10581
10377
|
for (const dir of codeDirs) {
|
|
10582
10378
|
;
|
|
10583
|
-
(await
|
|
10379
|
+
(await collectImportedPackages(dir, extensions)).forEach((p) => imported.add(p));
|
|
10584
10380
|
}
|
|
10585
10381
|
return [...imported].filter((p) => !inDeps.has(p)).sort();
|
|
10586
10382
|
}
|
|
10587
10383
|
async function stripCoherentArtifacts(outputDir) {
|
|
10588
10384
|
const removed = [];
|
|
10589
10385
|
for (const p of ["app/design-system", "app/api/design-system"]) {
|
|
10590
|
-
const full =
|
|
10591
|
-
if (
|
|
10386
|
+
const full = join14(outputDir, p);
|
|
10387
|
+
if (existsSync20(full)) {
|
|
10592
10388
|
rmSync4(full, { recursive: true, force: true });
|
|
10593
10389
|
removed.push(p);
|
|
10594
10390
|
}
|
|
10595
10391
|
}
|
|
10596
|
-
const appNavPath =
|
|
10597
|
-
if (
|
|
10392
|
+
const appNavPath = join14(outputDir, "app", "AppNav.tsx");
|
|
10393
|
+
if (existsSync20(appNavPath)) {
|
|
10598
10394
|
rmSync4(appNavPath, { force: true });
|
|
10599
10395
|
removed.push("app/AppNav.tsx");
|
|
10600
10396
|
}
|
|
10601
|
-
const layoutPath =
|
|
10602
|
-
if (
|
|
10603
|
-
let layout = await
|
|
10397
|
+
const layoutPath = join14(outputDir, "app", "layout.tsx");
|
|
10398
|
+
if (existsSync20(layoutPath)) {
|
|
10399
|
+
let layout = await readFile5(layoutPath, "utf-8");
|
|
10604
10400
|
layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
|
|
10605
10401
|
layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
|
|
10606
10402
|
await writeFile5(layoutPath, layout, "utf-8");
|
|
10607
10403
|
}
|
|
10608
|
-
const sharedHeaderPath =
|
|
10609
|
-
if (
|
|
10610
|
-
let header = await
|
|
10404
|
+
const sharedHeaderPath = join14(outputDir, "components", "shared", "header.tsx");
|
|
10405
|
+
if (existsSync20(sharedHeaderPath)) {
|
|
10406
|
+
let header = await readFile5(sharedHeaderPath, "utf-8");
|
|
10611
10407
|
header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
|
|
10612
10408
|
header = header.replace(/\n\s*<>\s*\n/, "\n");
|
|
10613
10409
|
header = header.replace(/\n\s*<\/>\s*\n/, "\n");
|
|
10614
10410
|
await writeFile5(sharedHeaderPath, header, "utf-8");
|
|
10615
10411
|
}
|
|
10616
|
-
const guardPath2 =
|
|
10617
|
-
if (
|
|
10618
|
-
let guard = await
|
|
10412
|
+
const guardPath2 = join14(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
|
|
10413
|
+
if (existsSync20(guardPath2)) {
|
|
10414
|
+
let guard = await readFile5(guardPath2, "utf-8");
|
|
10619
10415
|
guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
|
|
10620
10416
|
const pathsMatch = guard.match(/HIDDEN_PATHS\s*=\s*\[([^\]]*)\]/);
|
|
10621
10417
|
const remaining = pathsMatch ? pathsMatch[1].replace(/['"]/g, "").split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
10622
10418
|
if (remaining.length === 0) {
|
|
10623
10419
|
rmSync4(guardPath2, { force: true });
|
|
10624
10420
|
removed.push("app/ShowWhenNotAuthRoute.tsx");
|
|
10625
|
-
if (
|
|
10626
|
-
let layout = await
|
|
10421
|
+
if (existsSync20(layoutPath)) {
|
|
10422
|
+
let layout = await readFile5(layoutPath, "utf-8");
|
|
10627
10423
|
layout = layout.replace(/import\s+\w+\s+from\s*['"]\.\/ShowWhenNotAuthRoute['"]\s*\n?/g, "");
|
|
10628
10424
|
layout = layout.replace(/\s*<ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
10629
10425
|
layout = layout.replace(/\s*<\/ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
@@ -10642,15 +10438,15 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
10642
10438
|
".env.local",
|
|
10643
10439
|
"recommendations.md"
|
|
10644
10440
|
]) {
|
|
10645
|
-
const full =
|
|
10646
|
-
if (
|
|
10441
|
+
const full = join14(outputDir, name);
|
|
10442
|
+
if (existsSync20(full)) {
|
|
10647
10443
|
rmSync4(full, { force: true });
|
|
10648
10444
|
removed.push(name);
|
|
10649
10445
|
}
|
|
10650
10446
|
}
|
|
10651
10447
|
for (const dir of [".claude", ".coherent"]) {
|
|
10652
|
-
const full =
|
|
10653
|
-
if (
|
|
10448
|
+
const full = join14(outputDir, dir);
|
|
10449
|
+
if (existsSync20(full)) {
|
|
10654
10450
|
rmSync4(full, { recursive: true, force: true });
|
|
10655
10451
|
removed.push(dir + "/");
|
|
10656
10452
|
}
|
|
@@ -10674,7 +10470,7 @@ async function exportCommand(options = {}) {
|
|
|
10674
10470
|
process.exit(1);
|
|
10675
10471
|
}
|
|
10676
10472
|
spinner.text = "Copying project...";
|
|
10677
|
-
if (
|
|
10473
|
+
if (existsSync20(outputDir)) rmSync4(outputDir, { recursive: true, force: true });
|
|
10678
10474
|
await copyDir(projectRoot, outputDir);
|
|
10679
10475
|
spinner.succeed("Project copied");
|
|
10680
10476
|
if (!keepDs) {
|
|
@@ -10849,8 +10645,8 @@ async function regenerateDocsCommand() {
|
|
|
10849
10645
|
|
|
10850
10646
|
// src/commands/fix.ts
|
|
10851
10647
|
import chalk18 from "chalk";
|
|
10852
|
-
import { readdirSync as readdirSync8, readFileSync as
|
|
10853
|
-
import { resolve as resolve13, join as
|
|
10648
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
10649
|
+
import { resolve as resolve13, join as join15 } from "path";
|
|
10854
10650
|
import {
|
|
10855
10651
|
DesignSystemManager as DesignSystemManager11,
|
|
10856
10652
|
ComponentManager as ComponentManager6,
|
|
@@ -10876,7 +10672,7 @@ function listTsxFiles(dir) {
|
|
|
10876
10672
|
try {
|
|
10877
10673
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
10878
10674
|
for (const e of entries) {
|
|
10879
|
-
const full =
|
|
10675
|
+
const full = join15(dir, e.name);
|
|
10880
10676
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
10881
10677
|
files.push(...listTsxFiles(full));
|
|
10882
10678
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -10905,8 +10701,8 @@ async function fixCommand(opts = {}) {
|
|
|
10905
10701
|
console.log(chalk18.cyan("\ncoherent fix\n"));
|
|
10906
10702
|
}
|
|
10907
10703
|
if (!skipCache) {
|
|
10908
|
-
const nextDir =
|
|
10909
|
-
if (
|
|
10704
|
+
const nextDir = join15(projectRoot, ".next");
|
|
10705
|
+
if (existsSync21(nextDir)) {
|
|
10910
10706
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
10911
10707
|
fixes.push("Cleared build cache");
|
|
10912
10708
|
console.log(chalk18.green(" \u2714 Cleared build cache"));
|
|
@@ -10933,7 +10729,7 @@ async function fixCommand(opts = {}) {
|
|
|
10933
10729
|
const componentsTsxFiles = listTsxFiles(resolve13(projectRoot, "components"));
|
|
10934
10730
|
const allComponentIds = /* @__PURE__ */ new Set();
|
|
10935
10731
|
for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
|
|
10936
|
-
const content =
|
|
10732
|
+
const content = readFileSync15(file, "utf-8");
|
|
10937
10733
|
extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
|
|
10938
10734
|
}
|
|
10939
10735
|
let dsm = null;
|
|
@@ -10953,7 +10749,7 @@ async function fixCommand(opts = {}) {
|
|
|
10953
10749
|
} else {
|
|
10954
10750
|
const fileName = toKebabCase(id) + ".tsx";
|
|
10955
10751
|
const filePath = resolve13(projectRoot, "components", "ui", fileName);
|
|
10956
|
-
if (!
|
|
10752
|
+
if (!existsSync21(filePath)) missingFiles.push(id);
|
|
10957
10753
|
}
|
|
10958
10754
|
}
|
|
10959
10755
|
const provider = getComponentProvider();
|
|
@@ -11005,7 +10801,7 @@ async function fixCommand(opts = {}) {
|
|
|
11005
10801
|
const userTsxFiles = allTsxFiles.filter((f) => !f.includes("/design-system/"));
|
|
11006
10802
|
let syntaxFixed = 0;
|
|
11007
10803
|
for (const file of userTsxFiles) {
|
|
11008
|
-
const content =
|
|
10804
|
+
const content = readFileSync15(file, "utf-8");
|
|
11009
10805
|
const fixed = fixUnescapedLtInJsx(
|
|
11010
10806
|
fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
|
|
11011
10807
|
);
|
|
@@ -11023,7 +10819,7 @@ async function fixCommand(opts = {}) {
|
|
|
11023
10819
|
let qualityFixCount = 0;
|
|
11024
10820
|
const qualityFixDetails = [];
|
|
11025
10821
|
for (const file of userTsxFiles) {
|
|
11026
|
-
const content =
|
|
10822
|
+
const content = readFileSync15(file, "utf-8");
|
|
11027
10823
|
const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
|
|
11028
10824
|
if (autoFixed !== content) {
|
|
11029
10825
|
if (!dryRun) writeFileSync11(file, autoFixed, "utf-8");
|
|
@@ -11042,7 +10838,7 @@ async function fixCommand(opts = {}) {
|
|
|
11042
10838
|
let totalWarnings = 0;
|
|
11043
10839
|
const fileIssues = [];
|
|
11044
10840
|
for (const file of allTsxFiles) {
|
|
11045
|
-
const code = dryRun ?
|
|
10841
|
+
const code = dryRun ? readFileSync15(file, "utf-8") : readFileSync15(file, "utf-8");
|
|
11046
10842
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11047
10843
|
const baseName = file.split("/").pop() || "";
|
|
11048
10844
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -11167,7 +10963,7 @@ async function fixCommand(opts = {}) {
|
|
|
11167
10963
|
// src/commands/check.ts
|
|
11168
10964
|
import chalk19 from "chalk";
|
|
11169
10965
|
import { resolve as resolve14 } from "path";
|
|
11170
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
10966
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync16, statSync as statSync3, existsSync as existsSync22 } from "fs";
|
|
11171
10967
|
import { loadManifest as loadManifest11 } from "@getcoherent/core";
|
|
11172
10968
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
11173
10969
|
function findTsxFiles(dir) {
|
|
@@ -11224,7 +11020,7 @@ async function checkCommand(opts = {}) {
|
|
|
11224
11020
|
"NATIVE_TABLE"
|
|
11225
11021
|
]);
|
|
11226
11022
|
for (const file of files) {
|
|
11227
|
-
const code =
|
|
11023
|
+
const code = readFileSync16(file, "utf-8");
|
|
11228
11024
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11229
11025
|
const baseName = file.split("/").pop() || "";
|
|
11230
11026
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -11266,7 +11062,7 @@ async function checkCommand(opts = {}) {
|
|
|
11266
11062
|
routeSet.add("/");
|
|
11267
11063
|
routeSet.add("#");
|
|
11268
11064
|
for (const file of files) {
|
|
11269
|
-
const code =
|
|
11065
|
+
const code = readFileSync16(file, "utf-8");
|
|
11270
11066
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11271
11067
|
const lines = code.split("\n");
|
|
11272
11068
|
const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
|
|
@@ -11299,7 +11095,7 @@ async function checkCommand(opts = {}) {
|
|
|
11299
11095
|
if (manifest.shared.length > 0) {
|
|
11300
11096
|
for (const entry of manifest.shared) {
|
|
11301
11097
|
const fullPath = resolve14(project.root, entry.file);
|
|
11302
|
-
if (!
|
|
11098
|
+
if (!existsSync22(fullPath)) {
|
|
11303
11099
|
result.pages.withErrors++;
|
|
11304
11100
|
if (!opts.json) console.log(chalk19.red(`
|
|
11305
11101
|
\u2717 Missing shared component file: ${entry.id} (${entry.file})`));
|
|
@@ -11324,7 +11120,7 @@ async function checkCommand(opts = {}) {
|
|
|
11324
11120
|
let _nameMismatch = 0;
|
|
11325
11121
|
for (const entry of manifest.shared) {
|
|
11326
11122
|
const filePath = resolve14(projectRoot, entry.file);
|
|
11327
|
-
const fileExists =
|
|
11123
|
+
const fileExists = existsSync22(filePath);
|
|
11328
11124
|
if (!fileExists) {
|
|
11329
11125
|
_orphaned++;
|
|
11330
11126
|
if (!opts.json) {
|
|
@@ -11334,7 +11130,7 @@ async function checkCommand(opts = {}) {
|
|
|
11334
11130
|
continue;
|
|
11335
11131
|
}
|
|
11336
11132
|
try {
|
|
11337
|
-
const code =
|
|
11133
|
+
const code = readFileSync16(filePath, "utf-8");
|
|
11338
11134
|
const actualExports = extractExportedComponentNames(code);
|
|
11339
11135
|
if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
|
|
11340
11136
|
_nameMismatch++;
|
|
@@ -11402,7 +11198,7 @@ async function checkCommand(opts = {}) {
|
|
|
11402
11198
|
id: e.id,
|
|
11403
11199
|
name: e.name,
|
|
11404
11200
|
type: e.type,
|
|
11405
|
-
status:
|
|
11201
|
+
status: existsSync22(resolve14(projectRoot, e.file)) ? "ok" : "unused",
|
|
11406
11202
|
message: "",
|
|
11407
11203
|
suggestions: void 0
|
|
11408
11204
|
}))
|
|
@@ -11412,15 +11208,15 @@ async function checkCommand(opts = {}) {
|
|
|
11412
11208
|
}
|
|
11413
11209
|
if (!skipShared) {
|
|
11414
11210
|
try {
|
|
11415
|
-
const { validateReuse } = await import("./reuse-validator-
|
|
11211
|
+
const { validateReuse } = await import("./reuse-validator-XR2ZEYC4.js");
|
|
11416
11212
|
const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-EIP2XM7T.js");
|
|
11417
11213
|
const manifest = await loadManifest11(projectRoot);
|
|
11418
11214
|
const appDir = resolve14(projectRoot, "app");
|
|
11419
|
-
const pageFiles =
|
|
11215
|
+
const pageFiles = existsSync22(appDir) ? findTsxFiles(appDir) : [];
|
|
11420
11216
|
if (manifest.shared.length > 0 && pageFiles.length > 0) {
|
|
11421
11217
|
const reuseWarnings = [];
|
|
11422
11218
|
for (const file of pageFiles) {
|
|
11423
|
-
const code =
|
|
11219
|
+
const code = readFileSync16(file, "utf-8");
|
|
11424
11220
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
11425
11221
|
const route = "/" + relativePath.replace(/^app\//, "").replace(/\/page\.tsx$/, "").replace(/^\(.*?\)\//, "");
|
|
11426
11222
|
const pageType = inferPageTypeFromRoute2(route);
|
|
@@ -11531,12 +11327,12 @@ import {
|
|
|
11531
11327
|
generateSharedComponent as generateSharedComponent5,
|
|
11532
11328
|
integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3
|
|
11533
11329
|
} from "@getcoherent/core";
|
|
11534
|
-
import { existsSync as
|
|
11330
|
+
import { existsSync as existsSync23 } from "fs";
|
|
11535
11331
|
import { resolve as resolve15 } from "path";
|
|
11536
11332
|
|
|
11537
11333
|
// src/utils/ds-files.ts
|
|
11538
11334
|
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
11539
|
-
import { join as
|
|
11335
|
+
import { join as join16, dirname as dirname8 } from "path";
|
|
11540
11336
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
11541
11337
|
var SHARED_DS_KEYS = [
|
|
11542
11338
|
"app/design-system/shared/page.tsx",
|
|
@@ -11550,7 +11346,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
11550
11346
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path4]) => SHARED_DS_KEYS.includes(path4))) : files;
|
|
11551
11347
|
const written = [];
|
|
11552
11348
|
for (const [relativePath, content] of toWrite) {
|
|
11553
|
-
const fullPath =
|
|
11349
|
+
const fullPath = join16(projectRoot, relativePath);
|
|
11554
11350
|
await mkdir6(dirname8(fullPath), { recursive: true });
|
|
11555
11351
|
await writeFile6(fullPath, content, "utf-8");
|
|
11556
11352
|
written.push(relativePath);
|
|
@@ -11686,7 +11482,7 @@ function createComponentsCommand() {
|
|
|
11686
11482
|
if (updated) console.log(chalk25.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
11687
11483
|
}
|
|
11688
11484
|
const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
|
|
11689
|
-
if (!
|
|
11485
|
+
if (!existsSync23(sharedPagePath)) {
|
|
11690
11486
|
try {
|
|
11691
11487
|
const dsm = new DesignSystemManager12(project.configPath);
|
|
11692
11488
|
await dsm.load();
|
|
@@ -11712,8 +11508,8 @@ function createComponentsCommand() {
|
|
|
11712
11508
|
import chalk26 from "chalk";
|
|
11713
11509
|
import ora6 from "ora";
|
|
11714
11510
|
import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
11715
|
-
import { resolve as resolve16, join as
|
|
11716
|
-
import { existsSync as
|
|
11511
|
+
import { resolve as resolve16, join as join17, dirname as dirname9 } from "path";
|
|
11512
|
+
import { existsSync as existsSync24 } from "fs";
|
|
11717
11513
|
import {
|
|
11718
11514
|
FigmaClient,
|
|
11719
11515
|
parseFigmaFileResponse,
|
|
@@ -11860,7 +11656,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11860
11656
|
stats.filesWritten.push(filePath);
|
|
11861
11657
|
return;
|
|
11862
11658
|
}
|
|
11863
|
-
const fullPath =
|
|
11659
|
+
const fullPath = join17(projectRoot, filePath);
|
|
11864
11660
|
await mkdir7(dirname9(fullPath), { recursive: true });
|
|
11865
11661
|
await writeFile7(fullPath, content, "utf-8");
|
|
11866
11662
|
stats.filesWritten.push(filePath);
|
|
@@ -11962,7 +11758,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11962
11758
|
spinner.start("Updating design-system.config.ts...");
|
|
11963
11759
|
const configPath = resolve16(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
|
|
11964
11760
|
const dsm = new DesignSystemManager13(configPath);
|
|
11965
|
-
if (
|
|
11761
|
+
if (existsSync24(configPath)) {
|
|
11966
11762
|
await dsm.load();
|
|
11967
11763
|
const existing = dsm.getConfig();
|
|
11968
11764
|
dsm.updateConfig({
|
|
@@ -11991,8 +11787,8 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
11991
11787
|
stats.configUpdated = true;
|
|
11992
11788
|
spinner.succeed("design-system.config.ts updated");
|
|
11993
11789
|
spinner.start("Ensuring root layout...");
|
|
11994
|
-
const layoutPath =
|
|
11995
|
-
if (!
|
|
11790
|
+
const layoutPath = join17(projectRoot, "app/layout.tsx");
|
|
11791
|
+
if (!existsSync24(layoutPath)) {
|
|
11996
11792
|
await mkdir7(dirname9(layoutPath), { recursive: true });
|
|
11997
11793
|
await writeFile7(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
11998
11794
|
stats.filesWritten.push("app/layout.tsx");
|
|
@@ -12082,8 +11878,8 @@ async function dsRegenerateCommand() {
|
|
|
12082
11878
|
// src/commands/update.ts
|
|
12083
11879
|
import chalk28 from "chalk";
|
|
12084
11880
|
import ora8 from "ora";
|
|
12085
|
-
import { readFileSync as
|
|
12086
|
-
import { join as
|
|
11881
|
+
import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
|
|
11882
|
+
import { join as join18 } from "path";
|
|
12087
11883
|
import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
12088
11884
|
|
|
12089
11885
|
// src/utils/migrations.ts
|
|
@@ -12252,20 +12048,20 @@ var EXPECTED_CSS_VARS = [
|
|
|
12252
12048
|
"--sidebar-ring"
|
|
12253
12049
|
];
|
|
12254
12050
|
function checkMissingCssVars(projectRoot) {
|
|
12255
|
-
const globalsPath =
|
|
12256
|
-
if (!
|
|
12051
|
+
const globalsPath = join18(projectRoot, "app", "globals.css");
|
|
12052
|
+
if (!existsSync25(globalsPath)) return [];
|
|
12257
12053
|
try {
|
|
12258
|
-
const content =
|
|
12054
|
+
const content = readFileSync17(globalsPath, "utf-8");
|
|
12259
12055
|
return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
|
|
12260
12056
|
} catch {
|
|
12261
12057
|
return [];
|
|
12262
12058
|
}
|
|
12263
12059
|
}
|
|
12264
12060
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
12265
|
-
const globalsPath =
|
|
12266
|
-
if (!
|
|
12061
|
+
const globalsPath = join18(projectRoot, "app", "globals.css");
|
|
12062
|
+
if (!existsSync25(globalsPath) || missingVars.length === 0) return;
|
|
12267
12063
|
const { writeFileSync: writeFileSync14 } = __require("fs");
|
|
12268
|
-
let content =
|
|
12064
|
+
let content = readFileSync17(globalsPath, "utf-8");
|
|
12269
12065
|
const defaultValues = {
|
|
12270
12066
|
"--chart-1": "220 70% 50%",
|
|
12271
12067
|
"--chart-2": "160 60% 45%",
|
|
@@ -12343,26 +12139,26 @@ async function undoCommand(options) {
|
|
|
12343
12139
|
// src/commands/sync.ts
|
|
12344
12140
|
import chalk30 from "chalk";
|
|
12345
12141
|
import ora9 from "ora";
|
|
12346
|
-
import { existsSync as
|
|
12347
|
-
import { join as
|
|
12348
|
-
import { readdir as
|
|
12142
|
+
import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
|
|
12143
|
+
import { join as join19, relative as relative5, dirname as dirname10 } from "path";
|
|
12144
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
12349
12145
|
import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
|
|
12350
12146
|
import { loadManifest as loadManifest13, saveManifest as saveManifest6, findSharedComponent } from "@getcoherent/core";
|
|
12351
12147
|
function extractTokensFromProject(projectRoot) {
|
|
12352
12148
|
const lightColors = {};
|
|
12353
12149
|
const darkColors = {};
|
|
12354
|
-
const globalsPath =
|
|
12355
|
-
if (
|
|
12356
|
-
const css =
|
|
12150
|
+
const globalsPath = join19(projectRoot, "app", "globals.css");
|
|
12151
|
+
if (existsSync26(globalsPath)) {
|
|
12152
|
+
const css = readFileSync18(globalsPath, "utf-8");
|
|
12357
12153
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
12358
12154
|
if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
|
|
12359
12155
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
12360
12156
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
12361
12157
|
}
|
|
12362
|
-
const layoutPath =
|
|
12158
|
+
const layoutPath = join19(projectRoot, "app", "layout.tsx");
|
|
12363
12159
|
let layoutCode = "";
|
|
12364
|
-
if (
|
|
12365
|
-
layoutCode =
|
|
12160
|
+
if (existsSync26(layoutPath)) {
|
|
12161
|
+
layoutCode = readFileSync18(layoutPath, "utf-8");
|
|
12366
12162
|
const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
|
|
12367
12163
|
if (rootInline && Object.keys(lightColors).length === 0) {
|
|
12368
12164
|
parseVarsInto(rootInline[1], lightColors);
|
|
@@ -12380,7 +12176,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
12380
12176
|
defaultMode = "dark";
|
|
12381
12177
|
}
|
|
12382
12178
|
let radius;
|
|
12383
|
-
const allCss = [
|
|
12179
|
+
const allCss = [existsSync26(globalsPath) ? readFileSync18(globalsPath, "utf-8") : "", layoutCode].join("\n");
|
|
12384
12180
|
const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
|
|
12385
12181
|
if (radiusMatch) radius = radiusMatch[1].trim();
|
|
12386
12182
|
return {
|
|
@@ -12403,13 +12199,13 @@ function parseVarsInto(block, target) {
|
|
|
12403
12199
|
}
|
|
12404
12200
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
12405
12201
|
const results = [];
|
|
12406
|
-
const componentsDir =
|
|
12407
|
-
if (!
|
|
12202
|
+
const componentsDir = join19(projectRoot, "components");
|
|
12203
|
+
if (!existsSync26(componentsDir)) return results;
|
|
12408
12204
|
const files = [];
|
|
12409
12205
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
12410
12206
|
const fileResults = await Promise.all(
|
|
12411
12207
|
files.map(async (filePath) => {
|
|
12412
|
-
const code = await
|
|
12208
|
+
const code = await readFile6(filePath, "utf-8");
|
|
12413
12209
|
const relFile = relative5(projectRoot, filePath);
|
|
12414
12210
|
const exportedNames = extractExportedComponentNames2(code);
|
|
12415
12211
|
return exportedNames.map((name) => ({
|
|
@@ -12426,12 +12222,12 @@ async function detectCustomComponents(projectRoot, allPageCode) {
|
|
|
12426
12222
|
async function walkForTsx(dir, files, skipDirs) {
|
|
12427
12223
|
let entries;
|
|
12428
12224
|
try {
|
|
12429
|
-
entries = await
|
|
12225
|
+
entries = await readdir3(dir, { withFileTypes: true });
|
|
12430
12226
|
} catch {
|
|
12431
12227
|
return;
|
|
12432
12228
|
}
|
|
12433
12229
|
for (const e of entries) {
|
|
12434
|
-
const full =
|
|
12230
|
+
const full = join19(dir, e.name);
|
|
12435
12231
|
if (e.isDirectory()) {
|
|
12436
12232
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
12437
12233
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -12500,18 +12296,18 @@ async function discoverPages(appDir) {
|
|
|
12500
12296
|
async function walk(dir) {
|
|
12501
12297
|
let entries;
|
|
12502
12298
|
try {
|
|
12503
|
-
entries = await
|
|
12299
|
+
entries = await readdir3(dir, { withFileTypes: true });
|
|
12504
12300
|
} catch {
|
|
12505
12301
|
return;
|
|
12506
12302
|
}
|
|
12507
12303
|
for (const entry of entries) {
|
|
12508
|
-
const full =
|
|
12304
|
+
const full = join19(dir, entry.name);
|
|
12509
12305
|
if (entry.isDirectory()) {
|
|
12510
12306
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
12511
12307
|
if (entry.name.startsWith(".")) continue;
|
|
12512
12308
|
await walk(full);
|
|
12513
12309
|
} else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
|
|
12514
|
-
const code = await
|
|
12310
|
+
const code = await readFile6(full, "utf-8");
|
|
12515
12311
|
const routeDir = dirname10(relative5(appDir, full));
|
|
12516
12312
|
let route = routeDir === "." ? "/" : "/" + routeDir;
|
|
12517
12313
|
route = route.replace(/\/\([^)]+\)/g, "");
|
|
@@ -12594,8 +12390,8 @@ async function syncCommand(options = {}) {
|
|
|
12594
12390
|
if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
|
|
12595
12391
|
const spinner = ora9("Scanning project files...").start();
|
|
12596
12392
|
try {
|
|
12597
|
-
const appDir =
|
|
12598
|
-
if (!
|
|
12393
|
+
const appDir = join19(project.root, "app");
|
|
12394
|
+
if (!existsSync26(appDir)) {
|
|
12599
12395
|
spinner.fail("No app/ directory found");
|
|
12600
12396
|
process.exit(1);
|
|
12601
12397
|
}
|
|
@@ -12825,53 +12621,53 @@ async function syncCommand(options = {}) {
|
|
|
12825
12621
|
// src/commands/migrate.ts
|
|
12826
12622
|
import chalk31 from "chalk";
|
|
12827
12623
|
import ora10 from "ora";
|
|
12828
|
-
import { existsSync as
|
|
12829
|
-
import { join as
|
|
12624
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync19, readdirSync as readdirSync10 } from "fs";
|
|
12625
|
+
import { join as join20 } from "path";
|
|
12830
12626
|
function backupDir(projectRoot) {
|
|
12831
12627
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12832
|
-
return
|
|
12628
|
+
return join20(projectRoot, ".coherent", "backups", `pre-migrate-${ts}`);
|
|
12833
12629
|
}
|
|
12834
12630
|
function guardPath(projectRoot) {
|
|
12835
|
-
return
|
|
12631
|
+
return join20(projectRoot, ".coherent", "migration-in-progress");
|
|
12836
12632
|
}
|
|
12837
12633
|
function createBackup2(projectRoot) {
|
|
12838
|
-
const uiDir =
|
|
12634
|
+
const uiDir = join20(projectRoot, "components", "ui");
|
|
12839
12635
|
const dest = backupDir(projectRoot);
|
|
12840
12636
|
mkdirSync8(dest, { recursive: true });
|
|
12841
|
-
if (
|
|
12842
|
-
cpSync(uiDir,
|
|
12637
|
+
if (existsSync27(uiDir)) {
|
|
12638
|
+
cpSync(uiDir, join20(dest, "components-ui"), { recursive: true });
|
|
12843
12639
|
}
|
|
12844
|
-
const configPath =
|
|
12845
|
-
if (
|
|
12846
|
-
cpSync(configPath,
|
|
12640
|
+
const configPath = join20(projectRoot, "design-system.config.ts");
|
|
12641
|
+
if (existsSync27(configPath)) {
|
|
12642
|
+
cpSync(configPath, join20(dest, "design-system.config.ts"));
|
|
12847
12643
|
}
|
|
12848
12644
|
return dest;
|
|
12849
12645
|
}
|
|
12850
12646
|
function setGuard(projectRoot, backupPath) {
|
|
12851
12647
|
const guard = guardPath(projectRoot);
|
|
12852
|
-
mkdirSync8(
|
|
12648
|
+
mkdirSync8(join20(projectRoot, ".coherent"), { recursive: true });
|
|
12853
12649
|
writeFileSync12(guard, JSON.stringify({ backup: backupPath, startedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
12854
12650
|
}
|
|
12855
12651
|
function clearGuard(projectRoot) {
|
|
12856
12652
|
const guard = guardPath(projectRoot);
|
|
12857
|
-
if (
|
|
12653
|
+
if (existsSync27(guard)) rmSync6(guard);
|
|
12858
12654
|
}
|
|
12859
12655
|
function rollback(projectRoot) {
|
|
12860
12656
|
const guard = guardPath(projectRoot);
|
|
12861
|
-
if (!
|
|
12657
|
+
if (!existsSync27(guard)) return false;
|
|
12862
12658
|
try {
|
|
12863
|
-
const data = JSON.parse(
|
|
12659
|
+
const data = JSON.parse(readFileSync19(guard, "utf-8"));
|
|
12864
12660
|
const backup = data.backup;
|
|
12865
|
-
if (!
|
|
12866
|
-
const uiBackup =
|
|
12867
|
-
const uiDir =
|
|
12868
|
-
if (
|
|
12869
|
-
if (
|
|
12661
|
+
if (!existsSync27(backup)) return false;
|
|
12662
|
+
const uiBackup = join20(backup, "components-ui");
|
|
12663
|
+
const uiDir = join20(projectRoot, "components", "ui");
|
|
12664
|
+
if (existsSync27(uiBackup)) {
|
|
12665
|
+
if (existsSync27(uiDir)) rmSync6(uiDir, { recursive: true });
|
|
12870
12666
|
cpSync(uiBackup, uiDir, { recursive: true });
|
|
12871
12667
|
}
|
|
12872
|
-
const configBackup =
|
|
12873
|
-
const configDest =
|
|
12874
|
-
if (
|
|
12668
|
+
const configBackup = join20(backup, "design-system.config.ts");
|
|
12669
|
+
const configDest = join20(projectRoot, "design-system.config.ts");
|
|
12670
|
+
if (existsSync27(configBackup)) {
|
|
12875
12671
|
cpSync(configBackup, configDest);
|
|
12876
12672
|
}
|
|
12877
12673
|
clearGuard(projectRoot);
|
|
@@ -12899,13 +12695,13 @@ async function migrateAction(options) {
|
|
|
12899
12695
|
return;
|
|
12900
12696
|
}
|
|
12901
12697
|
const guard = guardPath(projectRoot);
|
|
12902
|
-
if (
|
|
12698
|
+
if (existsSync27(guard)) {
|
|
12903
12699
|
console.log(chalk31.yellow("A migration is already in progress."));
|
|
12904
12700
|
console.log(chalk31.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
|
|
12905
12701
|
return;
|
|
12906
12702
|
}
|
|
12907
|
-
const uiDir =
|
|
12908
|
-
if (!
|
|
12703
|
+
const uiDir = join20(projectRoot, "components", "ui");
|
|
12704
|
+
if (!existsSync27(uiDir)) {
|
|
12909
12705
|
console.log(chalk31.yellow("No components/ui directory found. Nothing to migrate."));
|
|
12910
12706
|
return;
|
|
12911
12707
|
}
|
|
@@ -12931,8 +12727,8 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
12931
12727
|
setGuard(projectRoot, backup);
|
|
12932
12728
|
try {
|
|
12933
12729
|
for (const id of migratable) {
|
|
12934
|
-
const filePath =
|
|
12935
|
-
if (
|
|
12730
|
+
const filePath = join20(uiDir, `${id}.tsx`);
|
|
12731
|
+
if (existsSync27(filePath)) rmSync6(filePath);
|
|
12936
12732
|
}
|
|
12937
12733
|
const results = await provider.installBatch(migratable, projectRoot, { force: true });
|
|
12938
12734
|
let migrated = 0;
|
|
@@ -12954,20 +12750,20 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
12954
12750
|
}
|
|
12955
12751
|
|
|
12956
12752
|
// src/utils/update-notifier.ts
|
|
12957
|
-
import { existsSync as
|
|
12958
|
-
import { join as
|
|
12753
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
|
|
12754
|
+
import { join as join21 } from "path";
|
|
12959
12755
|
import { homedir } from "os";
|
|
12960
12756
|
import chalk32 from "chalk";
|
|
12961
12757
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
12962
12758
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
12963
12759
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
12964
|
-
var CACHE_DIR =
|
|
12965
|
-
var CACHE_FILE =
|
|
12760
|
+
var CACHE_DIR = join21(homedir(), ".coherent");
|
|
12761
|
+
var CACHE_FILE = join21(CACHE_DIR, "update-check.json");
|
|
12966
12762
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
12967
12763
|
function readCache() {
|
|
12968
12764
|
try {
|
|
12969
|
-
if (!
|
|
12970
|
-
const raw =
|
|
12765
|
+
if (!existsSync28(CACHE_FILE)) return null;
|
|
12766
|
+
const raw = readFileSync20(CACHE_FILE, "utf-8");
|
|
12971
12767
|
return JSON.parse(raw);
|
|
12972
12768
|
} catch (e) {
|
|
12973
12769
|
if (DEBUG5) console.error("Failed to read update cache:", e);
|
|
@@ -12976,7 +12772,7 @@ function readCache() {
|
|
|
12976
12772
|
}
|
|
12977
12773
|
function writeCache(data) {
|
|
12978
12774
|
try {
|
|
12979
|
-
if (!
|
|
12775
|
+
if (!existsSync28(CACHE_DIR)) mkdirSync9(CACHE_DIR, { recursive: true });
|
|
12980
12776
|
writeFileSync13(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
12981
12777
|
} catch (e) {
|
|
12982
12778
|
if (DEBUG5) console.error("Failed to write update cache:", e);
|