@getcoherent/cli 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +351 -162
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -362,6 +362,7 @@ function createMinimalConfig() {
|
|
|
362
362
|
}
|
|
363
363
|
},
|
|
364
364
|
settings: {
|
|
365
|
+
initialized: false,
|
|
365
366
|
appType: "multi-page",
|
|
366
367
|
framework: "next",
|
|
367
368
|
typescript: true,
|
|
@@ -2427,8 +2428,8 @@ async function createAppRouteGroupLayout(projectPath) {
|
|
|
2427
2428
|
// src/commands/chat.ts
|
|
2428
2429
|
import chalk13 from "chalk";
|
|
2429
2430
|
import ora2 from "ora";
|
|
2430
|
-
import { resolve as resolve9 } from "path";
|
|
2431
|
-
import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6 } from "fs";
|
|
2431
|
+
import { resolve as resolve9, relative as relative2 } from "path";
|
|
2432
|
+
import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync2 } from "fs";
|
|
2432
2433
|
import {
|
|
2433
2434
|
DesignSystemManager as DesignSystemManager7,
|
|
2434
2435
|
ComponentManager as ComponentManager4,
|
|
@@ -4605,6 +4606,26 @@ FEW-SHOT EXAMPLE \u2014 correct stat card in pageCode (follow this pattern exact
|
|
|
4605
4606
|
\`\`\`
|
|
4606
4607
|
Key: CardTitle is text-sm font-medium (NOT text-lg). Metric is text-2xl font-bold. Subtext is text-xs text-muted-foreground. Icon is size-4 text-muted-foreground.
|
|
4607
4608
|
|
|
4609
|
+
SURGICAL MODIFICATION RULES (CRITICAL for incremental edits):
|
|
4610
|
+
- When modifying an existing page, return the COMPLETE page code
|
|
4611
|
+
- Change ONLY the specific section, component, or element the user requested
|
|
4612
|
+
- Do NOT modify imports unless the change requires new imports
|
|
4613
|
+
- Do NOT change state variables, event handlers, or data in unrelated sections
|
|
4614
|
+
- Do NOT restyle sections the user did not mention
|
|
4615
|
+
- Preserve all existing className values on unchanged elements
|
|
4616
|
+
- If the user asks to change a "section" or "block", identify it by heading, content, or position
|
|
4617
|
+
|
|
4618
|
+
Component Promotion Rules:
|
|
4619
|
+
- When the user asks to "make X a shared component" or "reuse X across pages":
|
|
4620
|
+
- Use request type "promote-and-link"
|
|
4621
|
+
- Extract the JSX block into a separate component file
|
|
4622
|
+
- Replace inline code with the component import on all specified pages
|
|
4623
|
+
|
|
4624
|
+
Global Component Change Rules:
|
|
4625
|
+
- When the user asks to change "all cards" or "every button" or similar:
|
|
4626
|
+
- If the pattern is already a shared component, modify the shared component file
|
|
4627
|
+
- If the pattern is inline across pages, first promote it to a shared component, then modify it
|
|
4628
|
+
|
|
4608
4629
|
OPTIONAL UX RECOMMENDATIONS:
|
|
4609
4630
|
If you see opportunities to improve UX (accessibility, layout, consistency, responsiveness, visual hierarchy), add a short markdown block in "uxRecommendations". Otherwise omit it.
|
|
4610
4631
|
|
|
@@ -4712,8 +4733,8 @@ async function ensureAuthRouteGroup(projectRoot) {
|
|
|
4712
4733
|
const guardPath = join6(projectRoot, "app", "ShowWhenNotAuthRoute.tsx");
|
|
4713
4734
|
const rootLayoutPath = join6(projectRoot, "app", "layout.tsx");
|
|
4714
4735
|
if (!existsSync9(authLayoutPath)) {
|
|
4715
|
-
const { mkdir:
|
|
4716
|
-
await
|
|
4736
|
+
const { mkdir: mkdir8 } = await import("fs/promises");
|
|
4737
|
+
await mkdir8(join6(projectRoot, "app", "(auth)"), { recursive: true });
|
|
4717
4738
|
await writeFile2(authLayoutPath, AUTH_LAYOUT, "utf-8");
|
|
4718
4739
|
}
|
|
4719
4740
|
if (!existsSync9(guardPath)) {
|
|
@@ -5885,7 +5906,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
5885
5906
|
|
|
5886
5907
|
// src/commands/chat/modification-handler.ts
|
|
5887
5908
|
import { resolve as resolve7 } from "path";
|
|
5888
|
-
import { mkdir as
|
|
5909
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
5889
5910
|
import { dirname as dirname6 } from "path";
|
|
5890
5911
|
import chalk11 from "chalk";
|
|
5891
5912
|
import {
|
|
@@ -6673,11 +6694,68 @@ function formatIssues(issues) {
|
|
|
6673
6694
|
}
|
|
6674
6695
|
return lines.join("\n");
|
|
6675
6696
|
}
|
|
6697
|
+
function checkDesignConsistency(code) {
|
|
6698
|
+
const warnings = [];
|
|
6699
|
+
const hexPattern = /\[#[0-9a-fA-F]{3,8}\]/g;
|
|
6700
|
+
for (const match of code.matchAll(hexPattern)) {
|
|
6701
|
+
warnings.push({
|
|
6702
|
+
type: "hardcoded-color",
|
|
6703
|
+
message: `Hardcoded color ${match[0]} \u2014 use a design token (e.g., bg-primary) instead`
|
|
6704
|
+
});
|
|
6705
|
+
}
|
|
6706
|
+
const spacingPattern = /[pm][trblxy]?-\[\d+px\]/g;
|
|
6707
|
+
for (const match of code.matchAll(spacingPattern)) {
|
|
6708
|
+
warnings.push({
|
|
6709
|
+
type: "arbitrary-spacing",
|
|
6710
|
+
message: `Arbitrary spacing ${match[0]} \u2014 use Tailwind spacing scale instead`
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6713
|
+
return warnings;
|
|
6714
|
+
}
|
|
6715
|
+
function verifyIncrementalEdit(before, after) {
|
|
6716
|
+
const issues = [];
|
|
6717
|
+
const hookPattern = /\buse[A-Z]\w+\s*\(/;
|
|
6718
|
+
if (hookPattern.test(after) && !after.includes("'use client'") && !after.includes('"use client"')) {
|
|
6719
|
+
issues.push({
|
|
6720
|
+
type: "missing-use-client",
|
|
6721
|
+
message: 'Code uses React hooks but missing "use client" directive'
|
|
6722
|
+
});
|
|
6723
|
+
}
|
|
6724
|
+
if (!after.includes("export default")) {
|
|
6725
|
+
issues.push({
|
|
6726
|
+
type: "missing-default-export",
|
|
6727
|
+
message: "Missing default export \u2014 page component must have a default export"
|
|
6728
|
+
});
|
|
6729
|
+
}
|
|
6730
|
+
const importRegex = /import\s+\{([^}]+)\}\s+from/g;
|
|
6731
|
+
const beforeImports = /* @__PURE__ */ new Set();
|
|
6732
|
+
const afterImports = /* @__PURE__ */ new Set();
|
|
6733
|
+
for (const match of before.matchAll(importRegex)) {
|
|
6734
|
+
match[1].split(",").forEach((s) => beforeImports.add(s.trim()));
|
|
6735
|
+
}
|
|
6736
|
+
for (const match of after.matchAll(importRegex)) {
|
|
6737
|
+
match[1].split(",").forEach((s) => afterImports.add(s.trim()));
|
|
6738
|
+
}
|
|
6739
|
+
for (const symbol of beforeImports) {
|
|
6740
|
+
if (!afterImports.has(symbol) && symbol.length > 0) {
|
|
6741
|
+
const codeWithoutImports = after.replace(/^import\s+.*$/gm, "");
|
|
6742
|
+
const symbolRegex = new RegExp(`\\b${symbol}\\b`);
|
|
6743
|
+
if (symbolRegex.test(codeWithoutImports)) {
|
|
6744
|
+
issues.push({
|
|
6745
|
+
type: "missing-import",
|
|
6746
|
+
symbol,
|
|
6747
|
+
message: `Import for "${symbol}" was removed but symbol is still used in code`
|
|
6748
|
+
});
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
return issues;
|
|
6753
|
+
}
|
|
6676
6754
|
|
|
6677
6755
|
// src/commands/chat/code-generator.ts
|
|
6678
6756
|
import { resolve as resolve6 } from "path";
|
|
6679
6757
|
import { existsSync as existsSync14 } from "fs";
|
|
6680
|
-
import { mkdir as
|
|
6758
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
6681
6759
|
import { dirname as dirname5 } from "path";
|
|
6682
6760
|
import {
|
|
6683
6761
|
ComponentGenerator as ComponentGenerator2,
|
|
@@ -6686,6 +6764,39 @@ import {
|
|
|
6686
6764
|
} from "@getcoherent/core";
|
|
6687
6765
|
import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent2 } from "@getcoherent/core";
|
|
6688
6766
|
import chalk9 from "chalk";
|
|
6767
|
+
|
|
6768
|
+
// src/utils/file-hashes.ts
|
|
6769
|
+
import { createHash } from "crypto";
|
|
6770
|
+
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir2 } from "fs/promises";
|
|
6771
|
+
import { join as join9 } from "path";
|
|
6772
|
+
var HASHES_FILE = ".coherent/file-hashes.json";
|
|
6773
|
+
async function computeFileHash(filePath) {
|
|
6774
|
+
const content = await readFile5(filePath, "utf-8");
|
|
6775
|
+
return createHash("sha256").update(content).digest("hex");
|
|
6776
|
+
}
|
|
6777
|
+
async function loadHashes(projectRoot) {
|
|
6778
|
+
try {
|
|
6779
|
+
const raw = await readFile5(join9(projectRoot, HASHES_FILE), "utf-8");
|
|
6780
|
+
return JSON.parse(raw);
|
|
6781
|
+
} catch {
|
|
6782
|
+
return {};
|
|
6783
|
+
}
|
|
6784
|
+
}
|
|
6785
|
+
async function saveHashes(projectRoot, hashes) {
|
|
6786
|
+
const dir = join9(projectRoot, ".coherent");
|
|
6787
|
+
await mkdir2(dir, { recursive: true });
|
|
6788
|
+
await writeFile4(join9(projectRoot, HASHES_FILE), JSON.stringify(hashes, null, 2) + "\n");
|
|
6789
|
+
}
|
|
6790
|
+
async function isManuallyEdited(filePath, storedHash) {
|
|
6791
|
+
try {
|
|
6792
|
+
const currentHash = await computeFileHash(filePath);
|
|
6793
|
+
return currentHash !== storedHash;
|
|
6794
|
+
} catch {
|
|
6795
|
+
return false;
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
// src/commands/chat/code-generator.ts
|
|
6689
6800
|
async function validateAndFixGeneratedCode(projectRoot, code, options = {}) {
|
|
6690
6801
|
const fixes = [];
|
|
6691
6802
|
let fixed = fixEscapedClosingQuotes(code);
|
|
@@ -6748,58 +6859,71 @@ async function regeneratePage(pageId, config2, projectRoot) {
|
|
|
6748
6859
|
const route = page.route || "/";
|
|
6749
6860
|
const isAuth = isAuthRoute(route) || isAuthRoute(page.name || page.id || "");
|
|
6750
6861
|
const filePath = routeToFsPath(projectRoot, route, isAuth);
|
|
6751
|
-
await
|
|
6862
|
+
await mkdir3(dirname5(filePath), { recursive: true });
|
|
6752
6863
|
await writeFile(filePath, code);
|
|
6753
6864
|
}
|
|
6754
|
-
async function
|
|
6755
|
-
const
|
|
6865
|
+
async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
|
|
6866
|
+
const filePath = resolve6(projectRoot, componentFile);
|
|
6867
|
+
if (!existsSync14(filePath)) return true;
|
|
6868
|
+
const storedHash = storedHashes[componentFile];
|
|
6869
|
+
if (!storedHash) return true;
|
|
6870
|
+
const edited = await isManuallyEdited(filePath, storedHash);
|
|
6871
|
+
if (edited) {
|
|
6872
|
+
console.log(chalk9.yellow(` \u26A0 Skipping ${componentFile} \u2014 manually edited since last generation`));
|
|
6873
|
+
}
|
|
6874
|
+
return !edited;
|
|
6875
|
+
}
|
|
6876
|
+
async function regenerateLayout(config2, projectRoot, options = { navChanged: false }) {
|
|
6756
6877
|
const appType = config2.settings.appType || "multi-page";
|
|
6757
6878
|
const generator = new PageGenerator(config2);
|
|
6758
|
-
const
|
|
6759
|
-
const
|
|
6760
|
-
|
|
6879
|
+
const initialized = config2.settings.initialized !== false;
|
|
6880
|
+
const hashes = options.storedHashes ?? {};
|
|
6881
|
+
if (!initialized) {
|
|
6882
|
+
const layout = config2.pages[0]?.layout || "centered";
|
|
6883
|
+
const code = await generator.generateLayout(layout, appType, { skipNav: true });
|
|
6884
|
+
await writeFile(resolve6(projectRoot, "app", "layout.tsx"), code);
|
|
6885
|
+
}
|
|
6761
6886
|
if (config2.navigation?.enabled && appType === "multi-page") {
|
|
6762
6887
|
const navType = config2.navigation.type || "header";
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
});
|
|
6888
|
+
const shouldRegenShared = !initialized || options.navChanged;
|
|
6889
|
+
if (shouldRegenShared) {
|
|
6890
|
+
if (navType === "header" || navType === "both") {
|
|
6891
|
+
if (await canOverwriteShared(projectRoot, "components/shared/header.tsx", hashes)) {
|
|
6892
|
+
const headerCode = generator.generateSharedHeaderCode();
|
|
6893
|
+
await generateSharedComponent2(projectRoot, {
|
|
6894
|
+
name: "Header",
|
|
6895
|
+
type: "layout",
|
|
6896
|
+
code: headerCode,
|
|
6897
|
+
description: "Main site header with navigation and theme toggle",
|
|
6898
|
+
usedIn: ["app/layout.tsx"],
|
|
6899
|
+
overwrite: true
|
|
6900
|
+
});
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
if (await canOverwriteShared(projectRoot, "components/shared/footer.tsx", hashes)) {
|
|
6904
|
+
const footerCode = generator.generateSharedFooterCode();
|
|
6905
|
+
await generateSharedComponent2(projectRoot, {
|
|
6906
|
+
name: "Footer",
|
|
6907
|
+
type: "layout",
|
|
6908
|
+
code: footerCode,
|
|
6909
|
+
description: "Site footer",
|
|
6910
|
+
usedIn: ["app/layout.tsx"],
|
|
6911
|
+
overwrite: true
|
|
6912
|
+
});
|
|
6913
|
+
}
|
|
6914
|
+
if (navType === "sidebar" || navType === "both") {
|
|
6915
|
+
if (await canOverwriteShared(projectRoot, "components/shared/sidebar.tsx", hashes)) {
|
|
6916
|
+
const sidebarCode = generator.generateSharedSidebarCode();
|
|
6917
|
+
await generateSharedComponent2(projectRoot, {
|
|
6918
|
+
name: "Sidebar",
|
|
6919
|
+
type: "layout",
|
|
6920
|
+
code: sidebarCode,
|
|
6921
|
+
description: "Vertical sidebar navigation with collapsible sections",
|
|
6922
|
+
usedIn: ["app/(app)/layout.tsx"],
|
|
6923
|
+
overwrite: true
|
|
6924
|
+
});
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6803
6927
|
}
|
|
6804
6928
|
}
|
|
6805
6929
|
try {
|
|
@@ -6854,7 +6978,7 @@ export default function AppLayout({
|
|
|
6854
6978
|
}
|
|
6855
6979
|
`;
|
|
6856
6980
|
}
|
|
6857
|
-
async function regenerateFiles(modified, config2, projectRoot) {
|
|
6981
|
+
async function regenerateFiles(modified, config2, projectRoot, options = { navChanged: false }) {
|
|
6858
6982
|
const componentIds = /* @__PURE__ */ new Set();
|
|
6859
6983
|
const pageIds = /* @__PURE__ */ new Set();
|
|
6860
6984
|
for (const item of modified) {
|
|
@@ -6865,7 +6989,10 @@ async function regenerateFiles(modified, config2, projectRoot) {
|
|
|
6865
6989
|
}
|
|
6866
6990
|
}
|
|
6867
6991
|
if (config2.navigation?.enabled && modified.length > 0) {
|
|
6868
|
-
await regenerateLayout(config2, projectRoot
|
|
6992
|
+
await regenerateLayout(config2, projectRoot, {
|
|
6993
|
+
navChanged: options.navChanged,
|
|
6994
|
+
storedHashes: options.storedHashes
|
|
6995
|
+
});
|
|
6869
6996
|
}
|
|
6870
6997
|
if (componentIds.size > 0) {
|
|
6871
6998
|
const twGen = new TailwindConfigGenerator(config2);
|
|
@@ -7592,7 +7719,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
7592
7719
|
await ensureAuthRouteGroup(projectRoot);
|
|
7593
7720
|
}
|
|
7594
7721
|
const filePath = routeToFsPath(projectRoot, route, isAuth);
|
|
7595
|
-
await
|
|
7722
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
7596
7723
|
const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, finalPageCode, {
|
|
7597
7724
|
isPage: true
|
|
7598
7725
|
});
|
|
@@ -7682,6 +7809,12 @@ Rules:
|
|
|
7682
7809
|
\u{1F50D} Quality check for ${page.name || page.id}:`));
|
|
7683
7810
|
console.log(chalk11.dim(report));
|
|
7684
7811
|
}
|
|
7812
|
+
const consistency = checkDesignConsistency(codeToWrite);
|
|
7813
|
+
if (consistency.length > 0) {
|
|
7814
|
+
console.log(chalk11.yellow(`
|
|
7815
|
+
\u{1F3A8} Design consistency for ${page.name || page.id}:`));
|
|
7816
|
+
consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
|
|
7817
|
+
}
|
|
7685
7818
|
}
|
|
7686
7819
|
}
|
|
7687
7820
|
return {
|
|
@@ -7747,6 +7880,12 @@ ${routeRules}
|
|
|
7747
7880
|
${pagesCtx}`
|
|
7748
7881
|
);
|
|
7749
7882
|
if (DEBUG2) console.log(chalk11.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
|
|
7883
|
+
const editIssues = verifyIncrementalEdit(currentCode, resolvedPageCode);
|
|
7884
|
+
if (editIssues.length > 0) {
|
|
7885
|
+
console.log(chalk11.yellow(`
|
|
7886
|
+
\u26A0 Incremental edit issues for ${pageDef.name || pageDef.id}:`));
|
|
7887
|
+
editIssues.forEach((issue) => console.log(chalk11.dim(` [${issue.type}] ${issue.message}`)));
|
|
7888
|
+
}
|
|
7750
7889
|
} else {
|
|
7751
7890
|
console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
|
|
7752
7891
|
}
|
|
@@ -7771,7 +7910,7 @@ ${pagesCtx}`
|
|
|
7771
7910
|
if (installed.length > 0) {
|
|
7772
7911
|
result.modified = [...result.modified, ...installed.map((id) => `component:${id}`)];
|
|
7773
7912
|
}
|
|
7774
|
-
await
|
|
7913
|
+
await mkdir4(dirname6(absPath), { recursive: true });
|
|
7775
7914
|
const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, resolvedPageCode, {
|
|
7776
7915
|
isPage: true
|
|
7777
7916
|
});
|
|
@@ -7833,6 +7972,12 @@ ${pagesCtx}`
|
|
|
7833
7972
|
\u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
|
|
7834
7973
|
console.log(chalk11.dim(report));
|
|
7835
7974
|
}
|
|
7975
|
+
const consistency = checkDesignConsistency(codeToWrite);
|
|
7976
|
+
if (consistency.length > 0) {
|
|
7977
|
+
console.log(chalk11.yellow(`
|
|
7978
|
+
\u{1F3A8} Design consistency for ${pageDef.name || pageDef.id}:`));
|
|
7979
|
+
consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
|
|
7980
|
+
}
|
|
7836
7981
|
} else {
|
|
7837
7982
|
try {
|
|
7838
7983
|
let code = await readFile(absPath);
|
|
@@ -7889,6 +8034,15 @@ ${pagesCtx}`
|
|
|
7889
8034
|
}
|
|
7890
8035
|
}
|
|
7891
8036
|
|
|
8037
|
+
// src/utils/nav-snapshot.ts
|
|
8038
|
+
function takeNavSnapshot(items) {
|
|
8039
|
+
if (!items || items.length === 0) return "[]";
|
|
8040
|
+
return JSON.stringify(items.map((i) => `${i.label}:${i.href}`).sort());
|
|
8041
|
+
}
|
|
8042
|
+
function hasNavChanged(before, after) {
|
|
8043
|
+
return before !== after;
|
|
8044
|
+
}
|
|
8045
|
+
|
|
7892
8046
|
// src/commands/chat/interactive.ts
|
|
7893
8047
|
import chalk12 from "chalk";
|
|
7894
8048
|
import { resolve as resolve8 } from "path";
|
|
@@ -8121,6 +8275,7 @@ async function chatCommand(message, options) {
|
|
|
8121
8275
|
}
|
|
8122
8276
|
spinner.text = "Loading design system configuration...";
|
|
8123
8277
|
}
|
|
8278
|
+
const storedHashes = await loadHashes(projectRoot);
|
|
8124
8279
|
const dsm = new DesignSystemManager7(configPath);
|
|
8125
8280
|
await dsm.load();
|
|
8126
8281
|
const cm = new ComponentManager4(config2);
|
|
@@ -8471,6 +8626,9 @@ async function chatCommand(message, options) {
|
|
|
8471
8626
|
if (DEBUG4) console.log(chalk13.dim("[backup] Created snapshot"));
|
|
8472
8627
|
} catch {
|
|
8473
8628
|
}
|
|
8629
|
+
const navBefore = takeNavSnapshot(
|
|
8630
|
+
config2.navigation?.items?.map((i) => ({ label: i.label, href: i.route || `/${i.label.toLowerCase()}` }))
|
|
8631
|
+
);
|
|
8474
8632
|
spinner.start("Applying modifications...");
|
|
8475
8633
|
const results = [];
|
|
8476
8634
|
for (const request of normalizedRequests) {
|
|
@@ -8618,6 +8776,10 @@ async function chatCommand(message, options) {
|
|
|
8618
8776
|
} catch {
|
|
8619
8777
|
}
|
|
8620
8778
|
}
|
|
8779
|
+
if (updatedConfig.settings.initialized === false) {
|
|
8780
|
+
updatedConfig.settings.initialized = true;
|
|
8781
|
+
dsm.updateConfig(updatedConfig);
|
|
8782
|
+
}
|
|
8621
8783
|
spinner.text = "Saving configuration...";
|
|
8622
8784
|
await dsm.save();
|
|
8623
8785
|
spinner.succeed("Configuration saved");
|
|
@@ -8627,15 +8789,42 @@ async function chatCommand(message, options) {
|
|
|
8627
8789
|
scaffoldedPages.forEach(({ route }) => {
|
|
8628
8790
|
allModified.add(`page:${route.slice(1) || "home"}`);
|
|
8629
8791
|
});
|
|
8792
|
+
const navAfter = takeNavSnapshot(
|
|
8793
|
+
updatedConfig.navigation?.items?.map((i) => ({
|
|
8794
|
+
label: i.label,
|
|
8795
|
+
href: i.route || `/${i.label.toLowerCase()}`
|
|
8796
|
+
}))
|
|
8797
|
+
);
|
|
8798
|
+
const navChanged = hasNavChanged(navBefore, navAfter);
|
|
8630
8799
|
if (allModified.size > 0) {
|
|
8631
8800
|
spinner.start("Regenerating affected files...");
|
|
8632
|
-
await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot);
|
|
8801
|
+
await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot, { navChanged, storedHashes });
|
|
8633
8802
|
spinner.succeed("Files regenerated");
|
|
8634
8803
|
}
|
|
8635
8804
|
try {
|
|
8636
8805
|
fixGlobalsCss(projectRoot, updatedConfig);
|
|
8637
8806
|
} catch {
|
|
8638
8807
|
}
|
|
8808
|
+
try {
|
|
8809
|
+
const updatedHashes = { ...storedHashes };
|
|
8810
|
+
const sharedDir = resolve9(projectRoot, "components", "shared");
|
|
8811
|
+
const layoutFile = resolve9(projectRoot, "app", "layout.tsx");
|
|
8812
|
+
const filesToHash = [layoutFile];
|
|
8813
|
+
if (existsSync16(sharedDir)) {
|
|
8814
|
+
for (const f of readdirSync2(sharedDir)) {
|
|
8815
|
+
if (f.endsWith(".tsx")) filesToHash.push(resolve9(sharedDir, f));
|
|
8816
|
+
}
|
|
8817
|
+
}
|
|
8818
|
+
for (const filePath of filesToHash) {
|
|
8819
|
+
if (existsSync16(filePath)) {
|
|
8820
|
+
const rel = relative2(projectRoot, filePath);
|
|
8821
|
+
updatedHashes[rel] = await computeFileHash(filePath);
|
|
8822
|
+
}
|
|
8823
|
+
}
|
|
8824
|
+
await saveHashes(projectRoot, updatedHashes);
|
|
8825
|
+
} catch {
|
|
8826
|
+
if (DEBUG4) console.log(chalk13.dim("[hashes] Could not save file hashes"));
|
|
8827
|
+
}
|
|
8639
8828
|
const successfulPairs = normalizedRequests.map((request, index) => ({ request, result: results[index] })).filter(({ result }) => result.success);
|
|
8640
8829
|
if (successfulPairs.length > 0) {
|
|
8641
8830
|
const changes = successfulPairs.map(({ request }) => ({
|
|
@@ -8743,18 +8932,18 @@ import chalk14 from "chalk";
|
|
|
8743
8932
|
import ora3 from "ora";
|
|
8744
8933
|
import { spawn } from "child_process";
|
|
8745
8934
|
import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
|
|
8746
|
-
import { resolve as resolve10, join as
|
|
8935
|
+
import { resolve as resolve10, join as join12 } from "path";
|
|
8747
8936
|
import { readdir as readdir2 } from "fs/promises";
|
|
8748
8937
|
import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
|
|
8749
8938
|
|
|
8750
8939
|
// src/utils/file-watcher.ts
|
|
8751
8940
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
|
|
8752
|
-
import { relative as
|
|
8941
|
+
import { relative as relative4, join as join11 } from "path";
|
|
8753
8942
|
import { loadManifest as loadManifest8, saveManifest as saveManifest3 } from "@getcoherent/core";
|
|
8754
8943
|
|
|
8755
8944
|
// src/utils/component-integrity.ts
|
|
8756
|
-
import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as
|
|
8757
|
-
import { join as
|
|
8945
|
+
import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync3 } from "fs";
|
|
8946
|
+
import { join as join10, relative as relative3 } from "path";
|
|
8758
8947
|
function extractExportedComponentNames(code) {
|
|
8759
8948
|
const names = [];
|
|
8760
8949
|
let m;
|
|
@@ -8780,7 +8969,7 @@ function arraysEqual(a, b) {
|
|
|
8780
8969
|
}
|
|
8781
8970
|
function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
8782
8971
|
const results = [];
|
|
8783
|
-
const appDir =
|
|
8972
|
+
const appDir = join10(projectRoot, "app");
|
|
8784
8973
|
if (!existsSync17(appDir)) return results;
|
|
8785
8974
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8786
8975
|
const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
@@ -8792,7 +8981,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
8792
8981
|
const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
|
|
8793
8982
|
const hasPathImport = code.includes(`@/${componentImportPath}`);
|
|
8794
8983
|
if (hasNamedImport || hasDefaultImport || hasPathImport) {
|
|
8795
|
-
results.push(
|
|
8984
|
+
results.push(relative3(projectRoot, absPath));
|
|
8796
8985
|
}
|
|
8797
8986
|
} catch {
|
|
8798
8987
|
}
|
|
@@ -8800,7 +8989,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
8800
8989
|
return results;
|
|
8801
8990
|
}
|
|
8802
8991
|
function isUsedInLayout(projectRoot, componentName) {
|
|
8803
|
-
const layoutPath =
|
|
8992
|
+
const layoutPath = join10(projectRoot, "app", "layout.tsx");
|
|
8804
8993
|
if (!existsSync17(layoutPath)) return false;
|
|
8805
8994
|
try {
|
|
8806
8995
|
const code = readFileSync11(layoutPath, "utf-8");
|
|
@@ -8811,7 +9000,7 @@ function isUsedInLayout(projectRoot, componentName) {
|
|
|
8811
9000
|
}
|
|
8812
9001
|
function findUnregisteredComponents(projectRoot, manifest) {
|
|
8813
9002
|
const results = [];
|
|
8814
|
-
const componentsDir =
|
|
9003
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8815
9004
|
if (!existsSync17(componentsDir)) return results;
|
|
8816
9005
|
const registeredFiles = new Set(manifest.shared.map((s) => s.file));
|
|
8817
9006
|
const registeredNames = new Set(manifest.shared.map((s) => s.name));
|
|
@@ -8821,7 +9010,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
8821
9010
|
["ui", "node_modules"]
|
|
8822
9011
|
);
|
|
8823
9012
|
for (const absPath of files) {
|
|
8824
|
-
const relFile =
|
|
9013
|
+
const relFile = relative3(projectRoot, absPath);
|
|
8825
9014
|
if (registeredFiles.has(relFile)) continue;
|
|
8826
9015
|
try {
|
|
8827
9016
|
const code = readFileSync11(absPath, "utf-8");
|
|
@@ -8839,7 +9028,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
8839
9028
|
}
|
|
8840
9029
|
function findInlineDuplicates(projectRoot, manifest) {
|
|
8841
9030
|
const results = [];
|
|
8842
|
-
const appDir =
|
|
9031
|
+
const appDir = join10(projectRoot, "app");
|
|
8843
9032
|
if (!existsSync17(appDir)) return results;
|
|
8844
9033
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8845
9034
|
for (const absPath of pageFiles) {
|
|
@@ -8850,7 +9039,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
8850
9039
|
} catch {
|
|
8851
9040
|
continue;
|
|
8852
9041
|
}
|
|
8853
|
-
const relPath =
|
|
9042
|
+
const relPath = relative3(projectRoot, absPath);
|
|
8854
9043
|
for (const shared of manifest.shared) {
|
|
8855
9044
|
const importPath = shared.file.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
8856
9045
|
const isImported = code.includes(`@/${importPath}`) || code.includes(`from './${importPath}'`) || code.includes(`from "../${importPath}"`);
|
|
@@ -8869,7 +9058,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
8869
9058
|
return results;
|
|
8870
9059
|
}
|
|
8871
9060
|
function findComponentFileByExportName(projectRoot, componentName) {
|
|
8872
|
-
const componentsDir =
|
|
9061
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8873
9062
|
if (!existsSync17(componentsDir)) return null;
|
|
8874
9063
|
const files = collectFiles(
|
|
8875
9064
|
componentsDir,
|
|
@@ -8881,7 +9070,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
8881
9070
|
const code = readFileSync11(absPath, "utf-8");
|
|
8882
9071
|
const exports = extractExportedComponentNames(code);
|
|
8883
9072
|
if (exports.includes(componentName)) {
|
|
8884
|
-
return
|
|
9073
|
+
return relative3(projectRoot, absPath);
|
|
8885
9074
|
}
|
|
8886
9075
|
} catch {
|
|
8887
9076
|
}
|
|
@@ -8891,7 +9080,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
8891
9080
|
function removeOrphanedEntries(projectRoot, manifest) {
|
|
8892
9081
|
const removed = [];
|
|
8893
9082
|
const valid = manifest.shared.filter((entry) => {
|
|
8894
|
-
const filePath =
|
|
9083
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8895
9084
|
if (existsSync17(filePath)) return true;
|
|
8896
9085
|
removed.push({ id: entry.id, name: entry.name });
|
|
8897
9086
|
return false;
|
|
@@ -8910,7 +9099,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8910
9099
|
};
|
|
8911
9100
|
const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
|
|
8912
9101
|
m.shared = m.shared.filter((entry) => {
|
|
8913
|
-
const filePath =
|
|
9102
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8914
9103
|
if (!existsSync17(filePath)) {
|
|
8915
9104
|
const newPath = findComponentFileByExportName(projectRoot, entry.name);
|
|
8916
9105
|
if (newPath) {
|
|
@@ -8923,7 +9112,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8923
9112
|
}
|
|
8924
9113
|
let code;
|
|
8925
9114
|
try {
|
|
8926
|
-
code = readFileSync11(
|
|
9115
|
+
code = readFileSync11(join10(projectRoot, entry.file), "utf-8");
|
|
8927
9116
|
} catch {
|
|
8928
9117
|
return true;
|
|
8929
9118
|
}
|
|
@@ -9000,12 +9189,12 @@ function collectFiles(dir, filter, skipDirs = []) {
|
|
|
9000
9189
|
function walk(d) {
|
|
9001
9190
|
let entries;
|
|
9002
9191
|
try {
|
|
9003
|
-
entries =
|
|
9192
|
+
entries = readdirSync3(d, { withFileTypes: true });
|
|
9004
9193
|
} catch {
|
|
9005
9194
|
return;
|
|
9006
9195
|
}
|
|
9007
9196
|
for (const e of entries) {
|
|
9008
|
-
const full =
|
|
9197
|
+
const full = join10(d, e.name);
|
|
9009
9198
|
if (e.isDirectory()) {
|
|
9010
9199
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
9011
9200
|
walk(full);
|
|
@@ -9044,7 +9233,7 @@ function findInlineDuplicatesOfShared(content, manifest) {
|
|
|
9044
9233
|
}
|
|
9045
9234
|
function getWatcherConfig(projectRoot) {
|
|
9046
9235
|
try {
|
|
9047
|
-
const pkgPath =
|
|
9236
|
+
const pkgPath = join11(projectRoot, "package.json");
|
|
9048
9237
|
if (!existsSync18(pkgPath)) return defaultWatcherConfig();
|
|
9049
9238
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
9050
9239
|
const c = pkg?.coherent?.watcher ?? {};
|
|
@@ -9069,7 +9258,7 @@ function defaultWatcherConfig() {
|
|
|
9069
9258
|
};
|
|
9070
9259
|
}
|
|
9071
9260
|
async function handleFileChange(projectRoot, filePath) {
|
|
9072
|
-
const relativePath =
|
|
9261
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9073
9262
|
if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".ts")) return;
|
|
9074
9263
|
if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
|
|
9075
9264
|
let content;
|
|
@@ -9119,7 +9308,7 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
9119
9308
|
}
|
|
9120
9309
|
}
|
|
9121
9310
|
async function handleFileDelete(projectRoot, filePath) {
|
|
9122
|
-
const relativePath =
|
|
9311
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9123
9312
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
9124
9313
|
try {
|
|
9125
9314
|
const chalk32 = (await import("chalk")).default;
|
|
@@ -9139,7 +9328,7 @@ async function handleFileDelete(projectRoot, filePath) {
|
|
|
9139
9328
|
}
|
|
9140
9329
|
}
|
|
9141
9330
|
async function detectNewComponent(projectRoot, filePath) {
|
|
9142
|
-
const relativePath =
|
|
9331
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9143
9332
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
9144
9333
|
if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
|
|
9145
9334
|
try {
|
|
@@ -9173,8 +9362,8 @@ function startFileWatcher(projectRoot) {
|
|
|
9173
9362
|
let watcher = null;
|
|
9174
9363
|
let manifestWatcher = null;
|
|
9175
9364
|
import("chokidar").then((chokidar) => {
|
|
9176
|
-
const appGlob =
|
|
9177
|
-
const compGlob =
|
|
9365
|
+
const appGlob = join11(projectRoot, "app", "**", "*.tsx");
|
|
9366
|
+
const compGlob = join11(projectRoot, "components", "**", "*.tsx");
|
|
9178
9367
|
watcher = chokidar.default.watch([appGlob, compGlob], {
|
|
9179
9368
|
ignoreInitial: true,
|
|
9180
9369
|
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
@@ -9186,7 +9375,7 @@ function startFileWatcher(projectRoot) {
|
|
|
9186
9375
|
});
|
|
9187
9376
|
watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
|
|
9188
9377
|
});
|
|
9189
|
-
const manifestPath =
|
|
9378
|
+
const manifestPath = join11(projectRoot, "coherent.components.json");
|
|
9190
9379
|
if (existsSync18(manifestPath)) {
|
|
9191
9380
|
import("chokidar").then((chokidar) => {
|
|
9192
9381
|
manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
|
|
@@ -9238,7 +9427,7 @@ function checkDependenciesInstalled(projectRoot) {
|
|
|
9238
9427
|
return existsSync19(nodeModulesPath);
|
|
9239
9428
|
}
|
|
9240
9429
|
function clearStaleCache(projectRoot) {
|
|
9241
|
-
const nextDir =
|
|
9430
|
+
const nextDir = join12(projectRoot, ".next");
|
|
9242
9431
|
if (existsSync19(nextDir)) {
|
|
9243
9432
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
9244
9433
|
console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
|
|
@@ -9259,7 +9448,7 @@ async function listPageFiles(appDir) {
|
|
|
9259
9448
|
async function walk(dir) {
|
|
9260
9449
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
9261
9450
|
for (const e of entries) {
|
|
9262
|
-
const full =
|
|
9451
|
+
const full = join12(dir, e.name);
|
|
9263
9452
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
|
|
9264
9453
|
else if (e.isFile() && e.name === "page.tsx") out.push(full);
|
|
9265
9454
|
}
|
|
@@ -9268,7 +9457,7 @@ async function listPageFiles(appDir) {
|
|
|
9268
9457
|
return out;
|
|
9269
9458
|
}
|
|
9270
9459
|
async function validateSyntax(projectRoot) {
|
|
9271
|
-
const appDir =
|
|
9460
|
+
const appDir = join12(projectRoot, "app");
|
|
9272
9461
|
const pages = await listPageFiles(appDir);
|
|
9273
9462
|
for (const file of pages) {
|
|
9274
9463
|
const content = readFileSync13(file, "utf-8");
|
|
@@ -9280,8 +9469,8 @@ async function validateSyntax(projectRoot) {
|
|
|
9280
9469
|
}
|
|
9281
9470
|
}
|
|
9282
9471
|
async function fixMissingComponentExports(projectRoot) {
|
|
9283
|
-
const appDir =
|
|
9284
|
-
const uiDir =
|
|
9472
|
+
const appDir = join12(projectRoot, "app");
|
|
9473
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9285
9474
|
if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
|
|
9286
9475
|
const pages = await listPageFiles(appDir);
|
|
9287
9476
|
const neededExports = /* @__PURE__ */ new Map();
|
|
@@ -9296,7 +9485,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9296
9485
|
for (const name of names) neededExports.get(componentId).add(name);
|
|
9297
9486
|
}
|
|
9298
9487
|
}
|
|
9299
|
-
const configPath =
|
|
9488
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9300
9489
|
let config2 = null;
|
|
9301
9490
|
try {
|
|
9302
9491
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9305,7 +9494,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9305
9494
|
}
|
|
9306
9495
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9307
9496
|
for (const [componentId, needed] of neededExports) {
|
|
9308
|
-
const componentFile =
|
|
9497
|
+
const componentFile = join12(uiDir, `${componentId}.tsx`);
|
|
9309
9498
|
const def = getShadcnComponent(componentId);
|
|
9310
9499
|
if (!existsSync19(componentFile)) {
|
|
9311
9500
|
if (!def) continue;
|
|
@@ -9342,7 +9531,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9342
9531
|
}
|
|
9343
9532
|
}
|
|
9344
9533
|
async function backfillPageAnalysis(projectRoot) {
|
|
9345
|
-
const configPath =
|
|
9534
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9346
9535
|
if (!existsSync19(configPath)) return;
|
|
9347
9536
|
try {
|
|
9348
9537
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9354,11 +9543,11 @@ async function backfillPageAnalysis(projectRoot) {
|
|
|
9354
9543
|
const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
|
|
9355
9544
|
let filePath;
|
|
9356
9545
|
if (route === "/") {
|
|
9357
|
-
filePath =
|
|
9546
|
+
filePath = join12(projectRoot, "app", "page.tsx");
|
|
9358
9547
|
} else if (isAuth) {
|
|
9359
|
-
filePath =
|
|
9548
|
+
filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
|
|
9360
9549
|
} else {
|
|
9361
|
-
filePath =
|
|
9550
|
+
filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
|
|
9362
9551
|
}
|
|
9363
9552
|
if (!existsSync19(filePath)) continue;
|
|
9364
9553
|
const code = readFileSync13(filePath, "utf-8");
|
|
@@ -9388,7 +9577,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9388
9577
|
const def = getShadcnComponent(componentId);
|
|
9389
9578
|
if (!def) return false;
|
|
9390
9579
|
try {
|
|
9391
|
-
const configPath =
|
|
9580
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9392
9581
|
let config2 = null;
|
|
9393
9582
|
try {
|
|
9394
9583
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9397,10 +9586,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9397
9586
|
}
|
|
9398
9587
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9399
9588
|
const code = await generator.generate(def);
|
|
9400
|
-
const uiDir =
|
|
9589
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9401
9590
|
const { mkdirSync: mkdirSync9 } = await import("fs");
|
|
9402
9591
|
mkdirSync9(uiDir, { recursive: true });
|
|
9403
|
-
writeFileSync10(
|
|
9592
|
+
writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
|
|
9404
9593
|
return true;
|
|
9405
9594
|
} catch {
|
|
9406
9595
|
return false;
|
|
@@ -9614,9 +9803,9 @@ async function previewCommand() {
|
|
|
9614
9803
|
import chalk15 from "chalk";
|
|
9615
9804
|
import ora4 from "ora";
|
|
9616
9805
|
import { spawn as spawn2 } from "child_process";
|
|
9617
|
-
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as
|
|
9618
|
-
import { resolve as resolve11, join as
|
|
9619
|
-
import { readdir as readdir3, readFile as readFile6, writeFile as
|
|
9806
|
+
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
|
|
9807
|
+
import { resolve as resolve11, join as join13, dirname as dirname7 } from "path";
|
|
9808
|
+
import { readdir as readdir3, readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
|
|
9620
9809
|
var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
9621
9810
|
"node_modules",
|
|
9622
9811
|
".next",
|
|
@@ -9634,16 +9823,16 @@ var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
9634
9823
|
".env.local"
|
|
9635
9824
|
]);
|
|
9636
9825
|
async function copyDir(src, dest) {
|
|
9637
|
-
await
|
|
9826
|
+
await mkdir5(dest, { recursive: true });
|
|
9638
9827
|
const entries = await readdir3(src, { withFileTypes: true });
|
|
9639
9828
|
for (const e of entries) {
|
|
9640
|
-
const srcPath =
|
|
9641
|
-
const destPath =
|
|
9829
|
+
const srcPath = join13(src, e.name);
|
|
9830
|
+
const destPath = join13(dest, e.name);
|
|
9642
9831
|
if (COPY_EXCLUDE.has(e.name)) continue;
|
|
9643
9832
|
if (e.isDirectory()) {
|
|
9644
9833
|
await copyDir(srcPath, destPath);
|
|
9645
9834
|
} else {
|
|
9646
|
-
await
|
|
9835
|
+
await mkdir5(dirname7(destPath), { recursive: true });
|
|
9647
9836
|
await copyFile2(srcPath, destPath);
|
|
9648
9837
|
}
|
|
9649
9838
|
}
|
|
@@ -9658,7 +9847,7 @@ function getPackageManager2(projectRoot) {
|
|
|
9658
9847
|
}
|
|
9659
9848
|
async function patchNextConfigForExport(outRoot) {
|
|
9660
9849
|
for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
|
|
9661
|
-
const p =
|
|
9850
|
+
const p = join13(outRoot, name);
|
|
9662
9851
|
if (!existsSync20(p)) continue;
|
|
9663
9852
|
let content = await readFile6(p, "utf-8");
|
|
9664
9853
|
if (content.includes("ignoreDuringBuilds")) return;
|
|
@@ -9666,7 +9855,7 @@ async function patchNextConfigForExport(outRoot) {
|
|
|
9666
9855
|
/(const\s+nextConfig\s*(?::\s*\w+)?\s*=\s*\{)/,
|
|
9667
9856
|
"$1\n eslint: { ignoreDuringBuilds: true },\n typescript: { ignoreBuildErrors: true },"
|
|
9668
9857
|
);
|
|
9669
|
-
await
|
|
9858
|
+
await writeFile5(p, content, "utf-8");
|
|
9670
9859
|
return;
|
|
9671
9860
|
}
|
|
9672
9861
|
}
|
|
@@ -9700,13 +9889,13 @@ EXPOSE 3000
|
|
|
9700
9889
|
\`\`\`
|
|
9701
9890
|
`;
|
|
9702
9891
|
async function ensureReadmeDeploySection(outRoot) {
|
|
9703
|
-
const readmePath =
|
|
9892
|
+
const readmePath = join13(outRoot, "README.md");
|
|
9704
9893
|
if (!existsSync20(readmePath)) return;
|
|
9705
9894
|
try {
|
|
9706
9895
|
let content = await readFile6(readmePath, "utf-8");
|
|
9707
9896
|
if (/##\s+Deploy\b/m.test(content)) return;
|
|
9708
9897
|
content = content.trimEnd() + DEPLOY_SECTION + "\n";
|
|
9709
|
-
await
|
|
9898
|
+
await writeFile5(readmePath, content);
|
|
9710
9899
|
} catch {
|
|
9711
9900
|
}
|
|
9712
9901
|
}
|
|
@@ -9720,22 +9909,22 @@ async function countPages(outRoot) {
|
|
|
9720
9909
|
return;
|
|
9721
9910
|
}
|
|
9722
9911
|
for (const e of entries) {
|
|
9723
|
-
const full =
|
|
9912
|
+
const full = join13(dir, e.name);
|
|
9724
9913
|
if (e.isFile() && e.name === "page.tsx") n++;
|
|
9725
9914
|
else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
|
|
9726
9915
|
}
|
|
9727
9916
|
}
|
|
9728
|
-
const appDir =
|
|
9917
|
+
const appDir = join13(outRoot, "app");
|
|
9729
9918
|
if (existsSync20(appDir)) await walk(appDir);
|
|
9730
9919
|
return n;
|
|
9731
9920
|
}
|
|
9732
9921
|
function countComponents(outRoot) {
|
|
9733
9922
|
let n = 0;
|
|
9734
9923
|
for (const sub of ["ui", "shared"]) {
|
|
9735
|
-
const dir =
|
|
9924
|
+
const dir = join13(outRoot, "components", sub);
|
|
9736
9925
|
if (!existsSync20(dir)) continue;
|
|
9737
9926
|
try {
|
|
9738
|
-
n +=
|
|
9927
|
+
n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
|
|
9739
9928
|
} catch {
|
|
9740
9929
|
}
|
|
9741
9930
|
}
|
|
@@ -9748,7 +9937,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9748
9937
|
async function walk(d) {
|
|
9749
9938
|
const entries = await readdir3(d, { withFileTypes: true });
|
|
9750
9939
|
for (const e of entries) {
|
|
9751
|
-
const full =
|
|
9940
|
+
const full = join13(d, e.name);
|
|
9752
9941
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
9753
9942
|
await walk(full);
|
|
9754
9943
|
continue;
|
|
@@ -9771,7 +9960,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9771
9960
|
return packages;
|
|
9772
9961
|
}
|
|
9773
9962
|
async function findMissingDepsInExport(outRoot) {
|
|
9774
|
-
const pkgPath =
|
|
9963
|
+
const pkgPath = join13(outRoot, "package.json");
|
|
9775
9964
|
if (!existsSync20(pkgPath)) return [];
|
|
9776
9965
|
let pkg;
|
|
9777
9966
|
try {
|
|
@@ -9780,7 +9969,7 @@ async function findMissingDepsInExport(outRoot) {
|
|
|
9780
9969
|
return [];
|
|
9781
9970
|
}
|
|
9782
9971
|
const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
9783
|
-
const codeDirs = [
|
|
9972
|
+
const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
|
|
9784
9973
|
const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
|
|
9785
9974
|
const imported = /* @__PURE__ */ new Set();
|
|
9786
9975
|
for (const dir of codeDirs) {
|
|
@@ -9792,33 +9981,33 @@ async function findMissingDepsInExport(outRoot) {
|
|
|
9792
9981
|
async function stripCoherentArtifacts(outputDir) {
|
|
9793
9982
|
const removed = [];
|
|
9794
9983
|
for (const p of ["app/design-system", "app/api/design-system"]) {
|
|
9795
|
-
const full =
|
|
9984
|
+
const full = join13(outputDir, p);
|
|
9796
9985
|
if (existsSync20(full)) {
|
|
9797
9986
|
rmSync4(full, { recursive: true, force: true });
|
|
9798
9987
|
removed.push(p);
|
|
9799
9988
|
}
|
|
9800
9989
|
}
|
|
9801
|
-
const appNavPath =
|
|
9990
|
+
const appNavPath = join13(outputDir, "app", "AppNav.tsx");
|
|
9802
9991
|
if (existsSync20(appNavPath)) {
|
|
9803
9992
|
rmSync4(appNavPath, { force: true });
|
|
9804
9993
|
removed.push("app/AppNav.tsx");
|
|
9805
9994
|
}
|
|
9806
|
-
const layoutPath =
|
|
9995
|
+
const layoutPath = join13(outputDir, "app", "layout.tsx");
|
|
9807
9996
|
if (existsSync20(layoutPath)) {
|
|
9808
9997
|
let layout = await readFile6(layoutPath, "utf-8");
|
|
9809
9998
|
layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
|
|
9810
9999
|
layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
|
|
9811
|
-
await
|
|
10000
|
+
await writeFile5(layoutPath, layout, "utf-8");
|
|
9812
10001
|
}
|
|
9813
|
-
const sharedHeaderPath =
|
|
10002
|
+
const sharedHeaderPath = join13(outputDir, "components", "shared", "header.tsx");
|
|
9814
10003
|
if (existsSync20(sharedHeaderPath)) {
|
|
9815
10004
|
let header = await readFile6(sharedHeaderPath, "utf-8");
|
|
9816
10005
|
header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
|
|
9817
10006
|
header = header.replace(/\n\s*<>\s*\n/, "\n");
|
|
9818
10007
|
header = header.replace(/\n\s*<\/>\s*\n/, "\n");
|
|
9819
|
-
await
|
|
10008
|
+
await writeFile5(sharedHeaderPath, header, "utf-8");
|
|
9820
10009
|
}
|
|
9821
|
-
const guardPath =
|
|
10010
|
+
const guardPath = join13(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
|
|
9822
10011
|
if (existsSync20(guardPath)) {
|
|
9823
10012
|
let guard = await readFile6(guardPath, "utf-8");
|
|
9824
10013
|
guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
|
|
@@ -9832,10 +10021,10 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9832
10021
|
layout = layout.replace(/import\s+\w+\s+from\s*['"]\.\/ShowWhenNotAuthRoute['"]\s*\n?/g, "");
|
|
9833
10022
|
layout = layout.replace(/\s*<ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
9834
10023
|
layout = layout.replace(/\s*<\/ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
9835
|
-
await
|
|
10024
|
+
await writeFile5(layoutPath, layout, "utf-8");
|
|
9836
10025
|
}
|
|
9837
10026
|
} else {
|
|
9838
|
-
await
|
|
10027
|
+
await writeFile5(guardPath, guard, "utf-8");
|
|
9839
10028
|
}
|
|
9840
10029
|
}
|
|
9841
10030
|
for (const name of [
|
|
@@ -9847,14 +10036,14 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9847
10036
|
".env.local",
|
|
9848
10037
|
"recommendations.md"
|
|
9849
10038
|
]) {
|
|
9850
|
-
const full =
|
|
10039
|
+
const full = join13(outputDir, name);
|
|
9851
10040
|
if (existsSync20(full)) {
|
|
9852
10041
|
rmSync4(full, { force: true });
|
|
9853
10042
|
removed.push(name);
|
|
9854
10043
|
}
|
|
9855
10044
|
}
|
|
9856
10045
|
for (const dir of [".claude", ".coherent"]) {
|
|
9857
|
-
const full =
|
|
10046
|
+
const full = join13(outputDir, dir);
|
|
9858
10047
|
if (existsSync20(full)) {
|
|
9859
10048
|
rmSync4(full, { recursive: true, force: true });
|
|
9860
10049
|
removed.push(dir + "/");
|
|
@@ -10054,8 +10243,8 @@ async function regenerateDocsCommand() {
|
|
|
10054
10243
|
|
|
10055
10244
|
// src/commands/fix.ts
|
|
10056
10245
|
import chalk18 from "chalk";
|
|
10057
|
-
import { readdirSync as
|
|
10058
|
-
import { resolve as resolve12, join as
|
|
10246
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync14, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
10247
|
+
import { resolve as resolve12, join as join14 } from "path";
|
|
10059
10248
|
import {
|
|
10060
10249
|
DesignSystemManager as DesignSystemManager11,
|
|
10061
10250
|
ComponentManager as ComponentManager5,
|
|
@@ -10079,9 +10268,9 @@ function extractComponentIdsFromCode2(code) {
|
|
|
10079
10268
|
function listTsxFiles(dir) {
|
|
10080
10269
|
const files = [];
|
|
10081
10270
|
try {
|
|
10082
|
-
const entries =
|
|
10271
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
10083
10272
|
for (const e of entries) {
|
|
10084
|
-
const full =
|
|
10273
|
+
const full = join14(dir, e.name);
|
|
10085
10274
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
10086
10275
|
files.push(...listTsxFiles(full));
|
|
10087
10276
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -10110,7 +10299,7 @@ async function fixCommand(opts = {}) {
|
|
|
10110
10299
|
console.log(chalk18.cyan("\ncoherent fix\n"));
|
|
10111
10300
|
}
|
|
10112
10301
|
if (!skipCache) {
|
|
10113
|
-
const nextDir =
|
|
10302
|
+
const nextDir = join14(projectRoot, ".next");
|
|
10114
10303
|
if (existsSync21(nextDir)) {
|
|
10115
10304
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
10116
10305
|
fixes.push("Cleared build cache");
|
|
@@ -10367,13 +10556,13 @@ async function fixCommand(opts = {}) {
|
|
|
10367
10556
|
// src/commands/check.ts
|
|
10368
10557
|
import chalk19 from "chalk";
|
|
10369
10558
|
import { resolve as resolve13 } from "path";
|
|
10370
|
-
import { readdirSync as
|
|
10559
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
|
|
10371
10560
|
import { loadManifest as loadManifest10 } from "@getcoherent/core";
|
|
10372
10561
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
10373
10562
|
function findTsxFiles(dir) {
|
|
10374
10563
|
const results = [];
|
|
10375
10564
|
try {
|
|
10376
|
-
const entries =
|
|
10565
|
+
const entries = readdirSync6(dir);
|
|
10377
10566
|
for (const entry of entries) {
|
|
10378
10567
|
const full = resolve13(dir, entry);
|
|
10379
10568
|
const stat = statSync2(full);
|
|
@@ -10701,8 +10890,8 @@ import { existsSync as existsSync23 } from "fs";
|
|
|
10701
10890
|
import { resolve as resolve14 } from "path";
|
|
10702
10891
|
|
|
10703
10892
|
// src/utils/ds-files.ts
|
|
10704
|
-
import { mkdir as
|
|
10705
|
-
import { join as
|
|
10893
|
+
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
10894
|
+
import { join as join15, dirname as dirname8 } from "path";
|
|
10706
10895
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
10707
10896
|
var SHARED_DS_KEYS = [
|
|
10708
10897
|
"app/design-system/shared/page.tsx",
|
|
@@ -10716,9 +10905,9 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
10716
10905
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
|
|
10717
10906
|
const written = [];
|
|
10718
10907
|
for (const [relativePath, content] of toWrite) {
|
|
10719
|
-
const fullPath =
|
|
10720
|
-
await
|
|
10721
|
-
await
|
|
10908
|
+
const fullPath = join15(projectRoot, relativePath);
|
|
10909
|
+
await mkdir6(dirname8(fullPath), { recursive: true });
|
|
10910
|
+
await writeFile6(fullPath, content, "utf-8");
|
|
10722
10911
|
written.push(relativePath);
|
|
10723
10912
|
}
|
|
10724
10913
|
return written;
|
|
@@ -10854,8 +11043,8 @@ function createComponentsCommand() {
|
|
|
10854
11043
|
// src/commands/import-cmd.ts
|
|
10855
11044
|
import chalk26 from "chalk";
|
|
10856
11045
|
import ora6 from "ora";
|
|
10857
|
-
import { writeFile as
|
|
10858
|
-
import { resolve as resolve15, join as
|
|
11046
|
+
import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
11047
|
+
import { resolve as resolve15, join as join16, dirname as dirname9 } from "path";
|
|
10859
11048
|
import { existsSync as existsSync24 } from "fs";
|
|
10860
11049
|
import {
|
|
10861
11050
|
FigmaClient,
|
|
@@ -11003,9 +11192,9 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11003
11192
|
stats.filesWritten.push(filePath);
|
|
11004
11193
|
return;
|
|
11005
11194
|
}
|
|
11006
|
-
const fullPath =
|
|
11007
|
-
await
|
|
11008
|
-
await
|
|
11195
|
+
const fullPath = join16(projectRoot, filePath);
|
|
11196
|
+
await mkdir7(dirname9(fullPath), { recursive: true });
|
|
11197
|
+
await writeFile7(fullPath, content, "utf-8");
|
|
11009
11198
|
stats.filesWritten.push(filePath);
|
|
11010
11199
|
};
|
|
11011
11200
|
try {
|
|
@@ -11079,7 +11268,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11079
11268
|
});
|
|
11080
11269
|
if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
|
|
11081
11270
|
else
|
|
11082
|
-
await
|
|
11271
|
+
await writeFile7(
|
|
11083
11272
|
resolve15(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
|
|
11084
11273
|
JSON.stringify(componentMapObj, null, 2),
|
|
11085
11274
|
"utf-8"
|
|
@@ -11119,7 +11308,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11119
11308
|
});
|
|
11120
11309
|
await dsm.save();
|
|
11121
11310
|
} else {
|
|
11122
|
-
await
|
|
11311
|
+
await writeFile7(
|
|
11123
11312
|
configPath,
|
|
11124
11313
|
`/**
|
|
11125
11314
|
* Design System Configuration
|
|
@@ -11134,10 +11323,10 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
11134
11323
|
stats.configUpdated = true;
|
|
11135
11324
|
spinner.succeed("design-system.config.ts updated");
|
|
11136
11325
|
spinner.start("Ensuring root layout...");
|
|
11137
|
-
const layoutPath =
|
|
11326
|
+
const layoutPath = join16(projectRoot, "app/layout.tsx");
|
|
11138
11327
|
if (!existsSync24(layoutPath)) {
|
|
11139
|
-
await
|
|
11140
|
-
await
|
|
11328
|
+
await mkdir7(dirname9(layoutPath), { recursive: true });
|
|
11329
|
+
await writeFile7(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
11141
11330
|
stats.filesWritten.push("app/layout.tsx");
|
|
11142
11331
|
}
|
|
11143
11332
|
spinner.succeed("Root layout OK");
|
|
@@ -11226,7 +11415,7 @@ async function dsRegenerateCommand() {
|
|
|
11226
11415
|
import chalk28 from "chalk";
|
|
11227
11416
|
import ora8 from "ora";
|
|
11228
11417
|
import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
|
|
11229
|
-
import { join as
|
|
11418
|
+
import { join as join17 } from "path";
|
|
11230
11419
|
import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
11231
11420
|
|
|
11232
11421
|
// src/utils/migrations.ts
|
|
@@ -11395,7 +11584,7 @@ var EXPECTED_CSS_VARS = [
|
|
|
11395
11584
|
"--sidebar-ring"
|
|
11396
11585
|
];
|
|
11397
11586
|
function checkMissingCssVars(projectRoot) {
|
|
11398
|
-
const globalsPath =
|
|
11587
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11399
11588
|
if (!existsSync25(globalsPath)) return [];
|
|
11400
11589
|
try {
|
|
11401
11590
|
const content = readFileSync16(globalsPath, "utf-8");
|
|
@@ -11405,7 +11594,7 @@ function checkMissingCssVars(projectRoot) {
|
|
|
11405
11594
|
}
|
|
11406
11595
|
}
|
|
11407
11596
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
11408
|
-
const globalsPath =
|
|
11597
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11409
11598
|
if (!existsSync25(globalsPath) || missingVars.length === 0) return;
|
|
11410
11599
|
const { writeFileSync: writeFileSync13 } = __require("fs");
|
|
11411
11600
|
let content = readFileSync16(globalsPath, "utf-8");
|
|
@@ -11487,14 +11676,14 @@ async function undoCommand(options) {
|
|
|
11487
11676
|
import chalk30 from "chalk";
|
|
11488
11677
|
import ora9 from "ora";
|
|
11489
11678
|
import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
|
|
11490
|
-
import { join as
|
|
11679
|
+
import { join as join18, relative as relative5, dirname as dirname10 } from "path";
|
|
11491
11680
|
import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
11492
11681
|
import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
|
|
11493
11682
|
import { loadManifest as loadManifest12, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
|
|
11494
11683
|
function extractTokensFromProject(projectRoot) {
|
|
11495
11684
|
const lightColors = {};
|
|
11496
11685
|
const darkColors = {};
|
|
11497
|
-
const globalsPath =
|
|
11686
|
+
const globalsPath = join18(projectRoot, "app", "globals.css");
|
|
11498
11687
|
if (existsSync26(globalsPath)) {
|
|
11499
11688
|
const css = readFileSync17(globalsPath, "utf-8");
|
|
11500
11689
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
@@ -11502,7 +11691,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
11502
11691
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
11503
11692
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
11504
11693
|
}
|
|
11505
|
-
const layoutPath =
|
|
11694
|
+
const layoutPath = join18(projectRoot, "app", "layout.tsx");
|
|
11506
11695
|
let layoutCode = "";
|
|
11507
11696
|
if (existsSync26(layoutPath)) {
|
|
11508
11697
|
layoutCode = readFileSync17(layoutPath, "utf-8");
|
|
@@ -11546,14 +11735,14 @@ function parseVarsInto(block, target) {
|
|
|
11546
11735
|
}
|
|
11547
11736
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
11548
11737
|
const results = [];
|
|
11549
|
-
const componentsDir =
|
|
11738
|
+
const componentsDir = join18(projectRoot, "components");
|
|
11550
11739
|
if (!existsSync26(componentsDir)) return results;
|
|
11551
11740
|
const files = [];
|
|
11552
11741
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
11553
11742
|
const fileResults = await Promise.all(
|
|
11554
11743
|
files.map(async (filePath) => {
|
|
11555
11744
|
const code = await readFile7(filePath, "utf-8");
|
|
11556
|
-
const relFile =
|
|
11745
|
+
const relFile = relative5(projectRoot, filePath);
|
|
11557
11746
|
const exportedNames = extractExportedComponentNames2(code);
|
|
11558
11747
|
return exportedNames.map((name) => ({
|
|
11559
11748
|
name,
|
|
@@ -11574,7 +11763,7 @@ async function walkForTsx(dir, files, skipDirs) {
|
|
|
11574
11763
|
return;
|
|
11575
11764
|
}
|
|
11576
11765
|
for (const e of entries) {
|
|
11577
|
-
const full =
|
|
11766
|
+
const full = join18(dir, e.name);
|
|
11578
11767
|
if (e.isDirectory()) {
|
|
11579
11768
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
11580
11769
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -11648,14 +11837,14 @@ async function discoverPages(appDir) {
|
|
|
11648
11837
|
return;
|
|
11649
11838
|
}
|
|
11650
11839
|
for (const entry of entries) {
|
|
11651
|
-
const full =
|
|
11840
|
+
const full = join18(dir, entry.name);
|
|
11652
11841
|
if (entry.isDirectory()) {
|
|
11653
11842
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
11654
11843
|
if (entry.name.startsWith(".")) continue;
|
|
11655
11844
|
await walk(full);
|
|
11656
11845
|
} else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
|
|
11657
11846
|
const code = await readFile7(full, "utf-8");
|
|
11658
|
-
const routeDir = dirname10(
|
|
11847
|
+
const routeDir = dirname10(relative5(appDir, full));
|
|
11659
11848
|
let route = routeDir === "." ? "/" : "/" + routeDir;
|
|
11660
11849
|
route = route.replace(/\/\([^)]+\)/g, "");
|
|
11661
11850
|
if (!route.startsWith("/")) route = "/" + route;
|
|
@@ -11725,7 +11914,7 @@ async function syncCommand(options = {}) {
|
|
|
11725
11914
|
if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
|
|
11726
11915
|
const spinner = ora9("Scanning project files...").start();
|
|
11727
11916
|
try {
|
|
11728
|
-
const appDir =
|
|
11917
|
+
const appDir = join18(project.root, "app");
|
|
11729
11918
|
if (!existsSync26(appDir)) {
|
|
11730
11919
|
spinner.fail("No app/ directory found");
|
|
11731
11920
|
process.exit(1);
|
|
@@ -11952,14 +12141,14 @@ async function syncCommand(options = {}) {
|
|
|
11952
12141
|
|
|
11953
12142
|
// src/utils/update-notifier.ts
|
|
11954
12143
|
import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
|
|
11955
|
-
import { join as
|
|
12144
|
+
import { join as join19 } from "path";
|
|
11956
12145
|
import { homedir } from "os";
|
|
11957
12146
|
import chalk31 from "chalk";
|
|
11958
12147
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
11959
12148
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
11960
12149
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
11961
|
-
var CACHE_DIR =
|
|
11962
|
-
var CACHE_FILE =
|
|
12150
|
+
var CACHE_DIR = join19(homedir(), ".coherent");
|
|
12151
|
+
var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
|
|
11963
12152
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
11964
12153
|
function readCache() {
|
|
11965
12154
|
try {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.3",
|
|
7
7
|
"description": "CLI interface for Coherent Design Method",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"ora": "^7.0.1",
|
|
44
44
|
"prompts": "^2.4.2",
|
|
45
45
|
"zod": "^3.22.4",
|
|
46
|
-
"@getcoherent/core": "0.5.
|
|
46
|
+
"@getcoherent/core": "0.5.3"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^20.11.0",
|