@getcoherent/cli 0.5.1 → 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 +362 -163
- 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,48 +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
|
-
|
|
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
|
+
}
|
|
6793
6927
|
}
|
|
6794
6928
|
}
|
|
6795
6929
|
try {
|
|
@@ -6844,7 +6978,7 @@ export default function AppLayout({
|
|
|
6844
6978
|
}
|
|
6845
6979
|
`;
|
|
6846
6980
|
}
|
|
6847
|
-
async function regenerateFiles(modified, config2, projectRoot) {
|
|
6981
|
+
async function regenerateFiles(modified, config2, projectRoot, options = { navChanged: false }) {
|
|
6848
6982
|
const componentIds = /* @__PURE__ */ new Set();
|
|
6849
6983
|
const pageIds = /* @__PURE__ */ new Set();
|
|
6850
6984
|
for (const item of modified) {
|
|
@@ -6855,7 +6989,10 @@ async function regenerateFiles(modified, config2, projectRoot) {
|
|
|
6855
6989
|
}
|
|
6856
6990
|
}
|
|
6857
6991
|
if (config2.navigation?.enabled && modified.length > 0) {
|
|
6858
|
-
await regenerateLayout(config2, projectRoot
|
|
6992
|
+
await regenerateLayout(config2, projectRoot, {
|
|
6993
|
+
navChanged: options.navChanged,
|
|
6994
|
+
storedHashes: options.storedHashes
|
|
6995
|
+
});
|
|
6859
6996
|
}
|
|
6860
6997
|
if (componentIds.size > 0) {
|
|
6861
6998
|
const twGen = new TailwindConfigGenerator(config2);
|
|
@@ -7582,7 +7719,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
7582
7719
|
await ensureAuthRouteGroup(projectRoot);
|
|
7583
7720
|
}
|
|
7584
7721
|
const filePath = routeToFsPath(projectRoot, route, isAuth);
|
|
7585
|
-
await
|
|
7722
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
7586
7723
|
const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, finalPageCode, {
|
|
7587
7724
|
isPage: true
|
|
7588
7725
|
});
|
|
@@ -7672,6 +7809,12 @@ Rules:
|
|
|
7672
7809
|
\u{1F50D} Quality check for ${page.name || page.id}:`));
|
|
7673
7810
|
console.log(chalk11.dim(report));
|
|
7674
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
|
+
}
|
|
7675
7818
|
}
|
|
7676
7819
|
}
|
|
7677
7820
|
return {
|
|
@@ -7737,6 +7880,12 @@ ${routeRules}
|
|
|
7737
7880
|
${pagesCtx}`
|
|
7738
7881
|
);
|
|
7739
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
|
+
}
|
|
7740
7889
|
} else {
|
|
7741
7890
|
console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
|
|
7742
7891
|
}
|
|
@@ -7761,7 +7910,7 @@ ${pagesCtx}`
|
|
|
7761
7910
|
if (installed.length > 0) {
|
|
7762
7911
|
result.modified = [...result.modified, ...installed.map((id) => `component:${id}`)];
|
|
7763
7912
|
}
|
|
7764
|
-
await
|
|
7913
|
+
await mkdir4(dirname6(absPath), { recursive: true });
|
|
7765
7914
|
const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, resolvedPageCode, {
|
|
7766
7915
|
isPage: true
|
|
7767
7916
|
});
|
|
@@ -7823,6 +7972,12 @@ ${pagesCtx}`
|
|
|
7823
7972
|
\u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
|
|
7824
7973
|
console.log(chalk11.dim(report));
|
|
7825
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
|
+
}
|
|
7826
7981
|
} else {
|
|
7827
7982
|
try {
|
|
7828
7983
|
let code = await readFile(absPath);
|
|
@@ -7879,6 +8034,15 @@ ${pagesCtx}`
|
|
|
7879
8034
|
}
|
|
7880
8035
|
}
|
|
7881
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
|
+
|
|
7882
8046
|
// src/commands/chat/interactive.ts
|
|
7883
8047
|
import chalk12 from "chalk";
|
|
7884
8048
|
import { resolve as resolve8 } from "path";
|
|
@@ -8111,6 +8275,7 @@ async function chatCommand(message, options) {
|
|
|
8111
8275
|
}
|
|
8112
8276
|
spinner.text = "Loading design system configuration...";
|
|
8113
8277
|
}
|
|
8278
|
+
const storedHashes = await loadHashes(projectRoot);
|
|
8114
8279
|
const dsm = new DesignSystemManager7(configPath);
|
|
8115
8280
|
await dsm.load();
|
|
8116
8281
|
const cm = new ComponentManager4(config2);
|
|
@@ -8461,6 +8626,9 @@ async function chatCommand(message, options) {
|
|
|
8461
8626
|
if (DEBUG4) console.log(chalk13.dim("[backup] Created snapshot"));
|
|
8462
8627
|
} catch {
|
|
8463
8628
|
}
|
|
8629
|
+
const navBefore = takeNavSnapshot(
|
|
8630
|
+
config2.navigation?.items?.map((i) => ({ label: i.label, href: i.route || `/${i.label.toLowerCase()}` }))
|
|
8631
|
+
);
|
|
8464
8632
|
spinner.start("Applying modifications...");
|
|
8465
8633
|
const results = [];
|
|
8466
8634
|
for (const request of normalizedRequests) {
|
|
@@ -8608,6 +8776,10 @@ async function chatCommand(message, options) {
|
|
|
8608
8776
|
} catch {
|
|
8609
8777
|
}
|
|
8610
8778
|
}
|
|
8779
|
+
if (updatedConfig.settings.initialized === false) {
|
|
8780
|
+
updatedConfig.settings.initialized = true;
|
|
8781
|
+
dsm.updateConfig(updatedConfig);
|
|
8782
|
+
}
|
|
8611
8783
|
spinner.text = "Saving configuration...";
|
|
8612
8784
|
await dsm.save();
|
|
8613
8785
|
spinner.succeed("Configuration saved");
|
|
@@ -8617,15 +8789,42 @@ async function chatCommand(message, options) {
|
|
|
8617
8789
|
scaffoldedPages.forEach(({ route }) => {
|
|
8618
8790
|
allModified.add(`page:${route.slice(1) || "home"}`);
|
|
8619
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);
|
|
8620
8799
|
if (allModified.size > 0) {
|
|
8621
8800
|
spinner.start("Regenerating affected files...");
|
|
8622
|
-
await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot);
|
|
8801
|
+
await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot, { navChanged, storedHashes });
|
|
8623
8802
|
spinner.succeed("Files regenerated");
|
|
8624
8803
|
}
|
|
8625
8804
|
try {
|
|
8626
8805
|
fixGlobalsCss(projectRoot, updatedConfig);
|
|
8627
8806
|
} catch {
|
|
8628
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
|
+
}
|
|
8629
8828
|
const successfulPairs = normalizedRequests.map((request, index) => ({ request, result: results[index] })).filter(({ result }) => result.success);
|
|
8630
8829
|
if (successfulPairs.length > 0) {
|
|
8631
8830
|
const changes = successfulPairs.map(({ request }) => ({
|
|
@@ -8733,18 +8932,18 @@ import chalk14 from "chalk";
|
|
|
8733
8932
|
import ora3 from "ora";
|
|
8734
8933
|
import { spawn } from "child_process";
|
|
8735
8934
|
import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
|
|
8736
|
-
import { resolve as resolve10, join as
|
|
8935
|
+
import { resolve as resolve10, join as join12 } from "path";
|
|
8737
8936
|
import { readdir as readdir2 } from "fs/promises";
|
|
8738
8937
|
import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
|
|
8739
8938
|
|
|
8740
8939
|
// src/utils/file-watcher.ts
|
|
8741
8940
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
|
|
8742
|
-
import { relative as
|
|
8941
|
+
import { relative as relative4, join as join11 } from "path";
|
|
8743
8942
|
import { loadManifest as loadManifest8, saveManifest as saveManifest3 } from "@getcoherent/core";
|
|
8744
8943
|
|
|
8745
8944
|
// src/utils/component-integrity.ts
|
|
8746
|
-
import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as
|
|
8747
|
-
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";
|
|
8748
8947
|
function extractExportedComponentNames(code) {
|
|
8749
8948
|
const names = [];
|
|
8750
8949
|
let m;
|
|
@@ -8770,7 +8969,7 @@ function arraysEqual(a, b) {
|
|
|
8770
8969
|
}
|
|
8771
8970
|
function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
8772
8971
|
const results = [];
|
|
8773
|
-
const appDir =
|
|
8972
|
+
const appDir = join10(projectRoot, "app");
|
|
8774
8973
|
if (!existsSync17(appDir)) return results;
|
|
8775
8974
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8776
8975
|
const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
@@ -8782,7 +8981,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
8782
8981
|
const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
|
|
8783
8982
|
const hasPathImport = code.includes(`@/${componentImportPath}`);
|
|
8784
8983
|
if (hasNamedImport || hasDefaultImport || hasPathImport) {
|
|
8785
|
-
results.push(
|
|
8984
|
+
results.push(relative3(projectRoot, absPath));
|
|
8786
8985
|
}
|
|
8787
8986
|
} catch {
|
|
8788
8987
|
}
|
|
@@ -8790,7 +8989,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
|
|
|
8790
8989
|
return results;
|
|
8791
8990
|
}
|
|
8792
8991
|
function isUsedInLayout(projectRoot, componentName) {
|
|
8793
|
-
const layoutPath =
|
|
8992
|
+
const layoutPath = join10(projectRoot, "app", "layout.tsx");
|
|
8794
8993
|
if (!existsSync17(layoutPath)) return false;
|
|
8795
8994
|
try {
|
|
8796
8995
|
const code = readFileSync11(layoutPath, "utf-8");
|
|
@@ -8801,7 +9000,7 @@ function isUsedInLayout(projectRoot, componentName) {
|
|
|
8801
9000
|
}
|
|
8802
9001
|
function findUnregisteredComponents(projectRoot, manifest) {
|
|
8803
9002
|
const results = [];
|
|
8804
|
-
const componentsDir =
|
|
9003
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8805
9004
|
if (!existsSync17(componentsDir)) return results;
|
|
8806
9005
|
const registeredFiles = new Set(manifest.shared.map((s) => s.file));
|
|
8807
9006
|
const registeredNames = new Set(manifest.shared.map((s) => s.name));
|
|
@@ -8811,7 +9010,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
8811
9010
|
["ui", "node_modules"]
|
|
8812
9011
|
);
|
|
8813
9012
|
for (const absPath of files) {
|
|
8814
|
-
const relFile =
|
|
9013
|
+
const relFile = relative3(projectRoot, absPath);
|
|
8815
9014
|
if (registeredFiles.has(relFile)) continue;
|
|
8816
9015
|
try {
|
|
8817
9016
|
const code = readFileSync11(absPath, "utf-8");
|
|
@@ -8829,7 +9028,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
|
|
|
8829
9028
|
}
|
|
8830
9029
|
function findInlineDuplicates(projectRoot, manifest) {
|
|
8831
9030
|
const results = [];
|
|
8832
|
-
const appDir =
|
|
9031
|
+
const appDir = join10(projectRoot, "app");
|
|
8833
9032
|
if (!existsSync17(appDir)) return results;
|
|
8834
9033
|
const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
|
|
8835
9034
|
for (const absPath of pageFiles) {
|
|
@@ -8840,7 +9039,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
8840
9039
|
} catch {
|
|
8841
9040
|
continue;
|
|
8842
9041
|
}
|
|
8843
|
-
const relPath =
|
|
9042
|
+
const relPath = relative3(projectRoot, absPath);
|
|
8844
9043
|
for (const shared of manifest.shared) {
|
|
8845
9044
|
const importPath = shared.file.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
|
|
8846
9045
|
const isImported = code.includes(`@/${importPath}`) || code.includes(`from './${importPath}'`) || code.includes(`from "../${importPath}"`);
|
|
@@ -8859,7 +9058,7 @@ function findInlineDuplicates(projectRoot, manifest) {
|
|
|
8859
9058
|
return results;
|
|
8860
9059
|
}
|
|
8861
9060
|
function findComponentFileByExportName(projectRoot, componentName) {
|
|
8862
|
-
const componentsDir =
|
|
9061
|
+
const componentsDir = join10(projectRoot, "components");
|
|
8863
9062
|
if (!existsSync17(componentsDir)) return null;
|
|
8864
9063
|
const files = collectFiles(
|
|
8865
9064
|
componentsDir,
|
|
@@ -8871,7 +9070,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
8871
9070
|
const code = readFileSync11(absPath, "utf-8");
|
|
8872
9071
|
const exports = extractExportedComponentNames(code);
|
|
8873
9072
|
if (exports.includes(componentName)) {
|
|
8874
|
-
return
|
|
9073
|
+
return relative3(projectRoot, absPath);
|
|
8875
9074
|
}
|
|
8876
9075
|
} catch {
|
|
8877
9076
|
}
|
|
@@ -8881,7 +9080,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
|
|
|
8881
9080
|
function removeOrphanedEntries(projectRoot, manifest) {
|
|
8882
9081
|
const removed = [];
|
|
8883
9082
|
const valid = manifest.shared.filter((entry) => {
|
|
8884
|
-
const filePath =
|
|
9083
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8885
9084
|
if (existsSync17(filePath)) return true;
|
|
8886
9085
|
removed.push({ id: entry.id, name: entry.name });
|
|
8887
9086
|
return false;
|
|
@@ -8900,7 +9099,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8900
9099
|
};
|
|
8901
9100
|
const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
|
|
8902
9101
|
m.shared = m.shared.filter((entry) => {
|
|
8903
|
-
const filePath =
|
|
9102
|
+
const filePath = join10(projectRoot, entry.file);
|
|
8904
9103
|
if (!existsSync17(filePath)) {
|
|
8905
9104
|
const newPath = findComponentFileByExportName(projectRoot, entry.name);
|
|
8906
9105
|
if (newPath) {
|
|
@@ -8913,7 +9112,7 @@ function reconcileComponents(projectRoot, manifest) {
|
|
|
8913
9112
|
}
|
|
8914
9113
|
let code;
|
|
8915
9114
|
try {
|
|
8916
|
-
code = readFileSync11(
|
|
9115
|
+
code = readFileSync11(join10(projectRoot, entry.file), "utf-8");
|
|
8917
9116
|
} catch {
|
|
8918
9117
|
return true;
|
|
8919
9118
|
}
|
|
@@ -8990,12 +9189,12 @@ function collectFiles(dir, filter, skipDirs = []) {
|
|
|
8990
9189
|
function walk(d) {
|
|
8991
9190
|
let entries;
|
|
8992
9191
|
try {
|
|
8993
|
-
entries =
|
|
9192
|
+
entries = readdirSync3(d, { withFileTypes: true });
|
|
8994
9193
|
} catch {
|
|
8995
9194
|
return;
|
|
8996
9195
|
}
|
|
8997
9196
|
for (const e of entries) {
|
|
8998
|
-
const full =
|
|
9197
|
+
const full = join10(d, e.name);
|
|
8999
9198
|
if (e.isDirectory()) {
|
|
9000
9199
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
9001
9200
|
walk(full);
|
|
@@ -9034,7 +9233,7 @@ function findInlineDuplicatesOfShared(content, manifest) {
|
|
|
9034
9233
|
}
|
|
9035
9234
|
function getWatcherConfig(projectRoot) {
|
|
9036
9235
|
try {
|
|
9037
|
-
const pkgPath =
|
|
9236
|
+
const pkgPath = join11(projectRoot, "package.json");
|
|
9038
9237
|
if (!existsSync18(pkgPath)) return defaultWatcherConfig();
|
|
9039
9238
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
9040
9239
|
const c = pkg?.coherent?.watcher ?? {};
|
|
@@ -9059,7 +9258,7 @@ function defaultWatcherConfig() {
|
|
|
9059
9258
|
};
|
|
9060
9259
|
}
|
|
9061
9260
|
async function handleFileChange(projectRoot, filePath) {
|
|
9062
|
-
const relativePath =
|
|
9261
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9063
9262
|
if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".ts")) return;
|
|
9064
9263
|
if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
|
|
9065
9264
|
let content;
|
|
@@ -9109,7 +9308,7 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
9109
9308
|
}
|
|
9110
9309
|
}
|
|
9111
9310
|
async function handleFileDelete(projectRoot, filePath) {
|
|
9112
|
-
const relativePath =
|
|
9311
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9113
9312
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
9114
9313
|
try {
|
|
9115
9314
|
const chalk32 = (await import("chalk")).default;
|
|
@@ -9129,7 +9328,7 @@ async function handleFileDelete(projectRoot, filePath) {
|
|
|
9129
9328
|
}
|
|
9130
9329
|
}
|
|
9131
9330
|
async function detectNewComponent(projectRoot, filePath) {
|
|
9132
|
-
const relativePath =
|
|
9331
|
+
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
9133
9332
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
9134
9333
|
if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
|
|
9135
9334
|
try {
|
|
@@ -9163,8 +9362,8 @@ function startFileWatcher(projectRoot) {
|
|
|
9163
9362
|
let watcher = null;
|
|
9164
9363
|
let manifestWatcher = null;
|
|
9165
9364
|
import("chokidar").then((chokidar) => {
|
|
9166
|
-
const appGlob =
|
|
9167
|
-
const compGlob =
|
|
9365
|
+
const appGlob = join11(projectRoot, "app", "**", "*.tsx");
|
|
9366
|
+
const compGlob = join11(projectRoot, "components", "**", "*.tsx");
|
|
9168
9367
|
watcher = chokidar.default.watch([appGlob, compGlob], {
|
|
9169
9368
|
ignoreInitial: true,
|
|
9170
9369
|
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
@@ -9176,7 +9375,7 @@ function startFileWatcher(projectRoot) {
|
|
|
9176
9375
|
});
|
|
9177
9376
|
watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
|
|
9178
9377
|
});
|
|
9179
|
-
const manifestPath =
|
|
9378
|
+
const manifestPath = join11(projectRoot, "coherent.components.json");
|
|
9180
9379
|
if (existsSync18(manifestPath)) {
|
|
9181
9380
|
import("chokidar").then((chokidar) => {
|
|
9182
9381
|
manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
|
|
@@ -9228,7 +9427,7 @@ function checkDependenciesInstalled(projectRoot) {
|
|
|
9228
9427
|
return existsSync19(nodeModulesPath);
|
|
9229
9428
|
}
|
|
9230
9429
|
function clearStaleCache(projectRoot) {
|
|
9231
|
-
const nextDir =
|
|
9430
|
+
const nextDir = join12(projectRoot, ".next");
|
|
9232
9431
|
if (existsSync19(nextDir)) {
|
|
9233
9432
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
9234
9433
|
console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
|
|
@@ -9249,7 +9448,7 @@ async function listPageFiles(appDir) {
|
|
|
9249
9448
|
async function walk(dir) {
|
|
9250
9449
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
9251
9450
|
for (const e of entries) {
|
|
9252
|
-
const full =
|
|
9451
|
+
const full = join12(dir, e.name);
|
|
9253
9452
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
|
|
9254
9453
|
else if (e.isFile() && e.name === "page.tsx") out.push(full);
|
|
9255
9454
|
}
|
|
@@ -9258,7 +9457,7 @@ async function listPageFiles(appDir) {
|
|
|
9258
9457
|
return out;
|
|
9259
9458
|
}
|
|
9260
9459
|
async function validateSyntax(projectRoot) {
|
|
9261
|
-
const appDir =
|
|
9460
|
+
const appDir = join12(projectRoot, "app");
|
|
9262
9461
|
const pages = await listPageFiles(appDir);
|
|
9263
9462
|
for (const file of pages) {
|
|
9264
9463
|
const content = readFileSync13(file, "utf-8");
|
|
@@ -9270,8 +9469,8 @@ async function validateSyntax(projectRoot) {
|
|
|
9270
9469
|
}
|
|
9271
9470
|
}
|
|
9272
9471
|
async function fixMissingComponentExports(projectRoot) {
|
|
9273
|
-
const appDir =
|
|
9274
|
-
const uiDir =
|
|
9472
|
+
const appDir = join12(projectRoot, "app");
|
|
9473
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9275
9474
|
if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
|
|
9276
9475
|
const pages = await listPageFiles(appDir);
|
|
9277
9476
|
const neededExports = /* @__PURE__ */ new Map();
|
|
@@ -9286,7 +9485,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9286
9485
|
for (const name of names) neededExports.get(componentId).add(name);
|
|
9287
9486
|
}
|
|
9288
9487
|
}
|
|
9289
|
-
const configPath =
|
|
9488
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9290
9489
|
let config2 = null;
|
|
9291
9490
|
try {
|
|
9292
9491
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9295,7 +9494,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9295
9494
|
}
|
|
9296
9495
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9297
9496
|
for (const [componentId, needed] of neededExports) {
|
|
9298
|
-
const componentFile =
|
|
9497
|
+
const componentFile = join12(uiDir, `${componentId}.tsx`);
|
|
9299
9498
|
const def = getShadcnComponent(componentId);
|
|
9300
9499
|
if (!existsSync19(componentFile)) {
|
|
9301
9500
|
if (!def) continue;
|
|
@@ -9332,7 +9531,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
9332
9531
|
}
|
|
9333
9532
|
}
|
|
9334
9533
|
async function backfillPageAnalysis(projectRoot) {
|
|
9335
|
-
const configPath =
|
|
9534
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9336
9535
|
if (!existsSync19(configPath)) return;
|
|
9337
9536
|
try {
|
|
9338
9537
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9344,11 +9543,11 @@ async function backfillPageAnalysis(projectRoot) {
|
|
|
9344
9543
|
const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
|
|
9345
9544
|
let filePath;
|
|
9346
9545
|
if (route === "/") {
|
|
9347
|
-
filePath =
|
|
9546
|
+
filePath = join12(projectRoot, "app", "page.tsx");
|
|
9348
9547
|
} else if (isAuth) {
|
|
9349
|
-
filePath =
|
|
9548
|
+
filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
|
|
9350
9549
|
} else {
|
|
9351
|
-
filePath =
|
|
9550
|
+
filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
|
|
9352
9551
|
}
|
|
9353
9552
|
if (!existsSync19(filePath)) continue;
|
|
9354
9553
|
const code = readFileSync13(filePath, "utf-8");
|
|
@@ -9378,7 +9577,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9378
9577
|
const def = getShadcnComponent(componentId);
|
|
9379
9578
|
if (!def) return false;
|
|
9380
9579
|
try {
|
|
9381
|
-
const configPath =
|
|
9580
|
+
const configPath = join12(projectRoot, "design-system.config.ts");
|
|
9382
9581
|
let config2 = null;
|
|
9383
9582
|
try {
|
|
9384
9583
|
const mgr = new DesignSystemManager8(configPath);
|
|
@@ -9387,10 +9586,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
|
|
|
9387
9586
|
}
|
|
9388
9587
|
const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
|
|
9389
9588
|
const code = await generator.generate(def);
|
|
9390
|
-
const uiDir =
|
|
9589
|
+
const uiDir = join12(projectRoot, "components", "ui");
|
|
9391
9590
|
const { mkdirSync: mkdirSync9 } = await import("fs");
|
|
9392
9591
|
mkdirSync9(uiDir, { recursive: true });
|
|
9393
|
-
writeFileSync10(
|
|
9592
|
+
writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
|
|
9394
9593
|
return true;
|
|
9395
9594
|
} catch {
|
|
9396
9595
|
return false;
|
|
@@ -9604,9 +9803,9 @@ async function previewCommand() {
|
|
|
9604
9803
|
import chalk15 from "chalk";
|
|
9605
9804
|
import ora4 from "ora";
|
|
9606
9805
|
import { spawn as spawn2 } from "child_process";
|
|
9607
|
-
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as
|
|
9608
|
-
import { resolve as resolve11, join as
|
|
9609
|
-
import { readdir as readdir3, readFile 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";
|
|
9610
9809
|
var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
9611
9810
|
"node_modules",
|
|
9612
9811
|
".next",
|
|
@@ -9624,16 +9823,16 @@ var COPY_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
9624
9823
|
".env.local"
|
|
9625
9824
|
]);
|
|
9626
9825
|
async function copyDir(src, dest) {
|
|
9627
|
-
await
|
|
9826
|
+
await mkdir5(dest, { recursive: true });
|
|
9628
9827
|
const entries = await readdir3(src, { withFileTypes: true });
|
|
9629
9828
|
for (const e of entries) {
|
|
9630
|
-
const srcPath =
|
|
9631
|
-
const destPath =
|
|
9829
|
+
const srcPath = join13(src, e.name);
|
|
9830
|
+
const destPath = join13(dest, e.name);
|
|
9632
9831
|
if (COPY_EXCLUDE.has(e.name)) continue;
|
|
9633
9832
|
if (e.isDirectory()) {
|
|
9634
9833
|
await copyDir(srcPath, destPath);
|
|
9635
9834
|
} else {
|
|
9636
|
-
await
|
|
9835
|
+
await mkdir5(dirname7(destPath), { recursive: true });
|
|
9637
9836
|
await copyFile2(srcPath, destPath);
|
|
9638
9837
|
}
|
|
9639
9838
|
}
|
|
@@ -9648,15 +9847,15 @@ function getPackageManager2(projectRoot) {
|
|
|
9648
9847
|
}
|
|
9649
9848
|
async function patchNextConfigForExport(outRoot) {
|
|
9650
9849
|
for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
|
|
9651
|
-
const p =
|
|
9850
|
+
const p = join13(outRoot, name);
|
|
9652
9851
|
if (!existsSync20(p)) continue;
|
|
9653
|
-
let content = await
|
|
9852
|
+
let content = await readFile6(p, "utf-8");
|
|
9654
9853
|
if (content.includes("ignoreDuringBuilds")) return;
|
|
9655
9854
|
content = content.replace(
|
|
9656
9855
|
/(const\s+nextConfig\s*(?::\s*\w+)?\s*=\s*\{)/,
|
|
9657
9856
|
"$1\n eslint: { ignoreDuringBuilds: true },\n typescript: { ignoreBuildErrors: true },"
|
|
9658
9857
|
);
|
|
9659
|
-
await
|
|
9858
|
+
await writeFile5(p, content, "utf-8");
|
|
9660
9859
|
return;
|
|
9661
9860
|
}
|
|
9662
9861
|
}
|
|
@@ -9690,13 +9889,13 @@ EXPOSE 3000
|
|
|
9690
9889
|
\`\`\`
|
|
9691
9890
|
`;
|
|
9692
9891
|
async function ensureReadmeDeploySection(outRoot) {
|
|
9693
|
-
const readmePath =
|
|
9892
|
+
const readmePath = join13(outRoot, "README.md");
|
|
9694
9893
|
if (!existsSync20(readmePath)) return;
|
|
9695
9894
|
try {
|
|
9696
|
-
let content = await
|
|
9895
|
+
let content = await readFile6(readmePath, "utf-8");
|
|
9697
9896
|
if (/##\s+Deploy\b/m.test(content)) return;
|
|
9698
9897
|
content = content.trimEnd() + DEPLOY_SECTION + "\n";
|
|
9699
|
-
await
|
|
9898
|
+
await writeFile5(readmePath, content);
|
|
9700
9899
|
} catch {
|
|
9701
9900
|
}
|
|
9702
9901
|
}
|
|
@@ -9710,22 +9909,22 @@ async function countPages(outRoot) {
|
|
|
9710
9909
|
return;
|
|
9711
9910
|
}
|
|
9712
9911
|
for (const e of entries) {
|
|
9713
|
-
const full =
|
|
9912
|
+
const full = join13(dir, e.name);
|
|
9714
9913
|
if (e.isFile() && e.name === "page.tsx") n++;
|
|
9715
9914
|
else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
|
|
9716
9915
|
}
|
|
9717
9916
|
}
|
|
9718
|
-
const appDir =
|
|
9917
|
+
const appDir = join13(outRoot, "app");
|
|
9719
9918
|
if (existsSync20(appDir)) await walk(appDir);
|
|
9720
9919
|
return n;
|
|
9721
9920
|
}
|
|
9722
9921
|
function countComponents(outRoot) {
|
|
9723
9922
|
let n = 0;
|
|
9724
9923
|
for (const sub of ["ui", "shared"]) {
|
|
9725
|
-
const dir =
|
|
9924
|
+
const dir = join13(outRoot, "components", sub);
|
|
9726
9925
|
if (!existsSync20(dir)) continue;
|
|
9727
9926
|
try {
|
|
9728
|
-
n +=
|
|
9927
|
+
n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
|
|
9729
9928
|
} catch {
|
|
9730
9929
|
}
|
|
9731
9930
|
}
|
|
@@ -9738,7 +9937,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9738
9937
|
async function walk(d) {
|
|
9739
9938
|
const entries = await readdir3(d, { withFileTypes: true });
|
|
9740
9939
|
for (const e of entries) {
|
|
9741
|
-
const full =
|
|
9940
|
+
const full = join13(d, e.name);
|
|
9742
9941
|
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
9743
9942
|
await walk(full);
|
|
9744
9943
|
continue;
|
|
@@ -9746,7 +9945,7 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9746
9945
|
if (!e.isFile()) continue;
|
|
9747
9946
|
const ext = e.name.replace(/^.*\./, "");
|
|
9748
9947
|
if (!extensions.has(ext)) continue;
|
|
9749
|
-
const content = await
|
|
9948
|
+
const content = await readFile6(full, "utf-8").catch(() => "");
|
|
9750
9949
|
let m;
|
|
9751
9950
|
IMPORT_FROM_REGEX2.lastIndex = 0;
|
|
9752
9951
|
while ((m = IMPORT_FROM_REGEX2.exec(content)) !== null) {
|
|
@@ -9761,16 +9960,16 @@ async function collectImportedPackages2(dir, extensions) {
|
|
|
9761
9960
|
return packages;
|
|
9762
9961
|
}
|
|
9763
9962
|
async function findMissingDepsInExport(outRoot) {
|
|
9764
|
-
const pkgPath =
|
|
9963
|
+
const pkgPath = join13(outRoot, "package.json");
|
|
9765
9964
|
if (!existsSync20(pkgPath)) return [];
|
|
9766
9965
|
let pkg;
|
|
9767
9966
|
try {
|
|
9768
|
-
pkg = JSON.parse(await
|
|
9967
|
+
pkg = JSON.parse(await readFile6(pkgPath, "utf-8"));
|
|
9769
9968
|
} catch {
|
|
9770
9969
|
return [];
|
|
9771
9970
|
}
|
|
9772
9971
|
const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
9773
|
-
const codeDirs = [
|
|
9972
|
+
const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
|
|
9774
9973
|
const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
|
|
9775
9974
|
const imported = /* @__PURE__ */ new Set();
|
|
9776
9975
|
for (const dir of codeDirs) {
|
|
@@ -9782,35 +9981,35 @@ async function findMissingDepsInExport(outRoot) {
|
|
|
9782
9981
|
async function stripCoherentArtifacts(outputDir) {
|
|
9783
9982
|
const removed = [];
|
|
9784
9983
|
for (const p of ["app/design-system", "app/api/design-system"]) {
|
|
9785
|
-
const full =
|
|
9984
|
+
const full = join13(outputDir, p);
|
|
9786
9985
|
if (existsSync20(full)) {
|
|
9787
9986
|
rmSync4(full, { recursive: true, force: true });
|
|
9788
9987
|
removed.push(p);
|
|
9789
9988
|
}
|
|
9790
9989
|
}
|
|
9791
|
-
const appNavPath =
|
|
9990
|
+
const appNavPath = join13(outputDir, "app", "AppNav.tsx");
|
|
9792
9991
|
if (existsSync20(appNavPath)) {
|
|
9793
9992
|
rmSync4(appNavPath, { force: true });
|
|
9794
9993
|
removed.push("app/AppNav.tsx");
|
|
9795
9994
|
}
|
|
9796
|
-
const layoutPath =
|
|
9995
|
+
const layoutPath = join13(outputDir, "app", "layout.tsx");
|
|
9797
9996
|
if (existsSync20(layoutPath)) {
|
|
9798
|
-
let layout = await
|
|
9997
|
+
let layout = await readFile6(layoutPath, "utf-8");
|
|
9799
9998
|
layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
|
|
9800
9999
|
layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
|
|
9801
|
-
await
|
|
10000
|
+
await writeFile5(layoutPath, layout, "utf-8");
|
|
9802
10001
|
}
|
|
9803
|
-
const sharedHeaderPath =
|
|
10002
|
+
const sharedHeaderPath = join13(outputDir, "components", "shared", "header.tsx");
|
|
9804
10003
|
if (existsSync20(sharedHeaderPath)) {
|
|
9805
|
-
let header = await
|
|
10004
|
+
let header = await readFile6(sharedHeaderPath, "utf-8");
|
|
9806
10005
|
header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
|
|
9807
10006
|
header = header.replace(/\n\s*<>\s*\n/, "\n");
|
|
9808
10007
|
header = header.replace(/\n\s*<\/>\s*\n/, "\n");
|
|
9809
|
-
await
|
|
10008
|
+
await writeFile5(sharedHeaderPath, header, "utf-8");
|
|
9810
10009
|
}
|
|
9811
|
-
const guardPath =
|
|
10010
|
+
const guardPath = join13(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
|
|
9812
10011
|
if (existsSync20(guardPath)) {
|
|
9813
|
-
let guard = await
|
|
10012
|
+
let guard = await readFile6(guardPath, "utf-8");
|
|
9814
10013
|
guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
|
|
9815
10014
|
const pathsMatch = guard.match(/HIDDEN_PATHS\s*=\s*\[([^\]]*)\]/);
|
|
9816
10015
|
const remaining = pathsMatch ? pathsMatch[1].replace(/['"]/g, "").split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
@@ -9818,14 +10017,14 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9818
10017
|
rmSync4(guardPath, { force: true });
|
|
9819
10018
|
removed.push("app/ShowWhenNotAuthRoute.tsx");
|
|
9820
10019
|
if (existsSync20(layoutPath)) {
|
|
9821
|
-
let layout = await
|
|
10020
|
+
let layout = await readFile6(layoutPath, "utf-8");
|
|
9822
10021
|
layout = layout.replace(/import\s+\w+\s+from\s*['"]\.\/ShowWhenNotAuthRoute['"]\s*\n?/g, "");
|
|
9823
10022
|
layout = layout.replace(/\s*<ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
9824
10023
|
layout = layout.replace(/\s*<\/ShowWhenNotAuthRoute>\s*\n?/g, "\n");
|
|
9825
|
-
await
|
|
10024
|
+
await writeFile5(layoutPath, layout, "utf-8");
|
|
9826
10025
|
}
|
|
9827
10026
|
} else {
|
|
9828
|
-
await
|
|
10027
|
+
await writeFile5(guardPath, guard, "utf-8");
|
|
9829
10028
|
}
|
|
9830
10029
|
}
|
|
9831
10030
|
for (const name of [
|
|
@@ -9837,14 +10036,14 @@ async function stripCoherentArtifacts(outputDir) {
|
|
|
9837
10036
|
".env.local",
|
|
9838
10037
|
"recommendations.md"
|
|
9839
10038
|
]) {
|
|
9840
|
-
const full =
|
|
10039
|
+
const full = join13(outputDir, name);
|
|
9841
10040
|
if (existsSync20(full)) {
|
|
9842
10041
|
rmSync4(full, { force: true });
|
|
9843
10042
|
removed.push(name);
|
|
9844
10043
|
}
|
|
9845
10044
|
}
|
|
9846
10045
|
for (const dir of [".claude", ".coherent"]) {
|
|
9847
|
-
const full =
|
|
10046
|
+
const full = join13(outputDir, dir);
|
|
9848
10047
|
if (existsSync20(full)) {
|
|
9849
10048
|
rmSync4(full, { recursive: true, force: true });
|
|
9850
10049
|
removed.push(dir + "/");
|
|
@@ -10044,8 +10243,8 @@ async function regenerateDocsCommand() {
|
|
|
10044
10243
|
|
|
10045
10244
|
// src/commands/fix.ts
|
|
10046
10245
|
import chalk18 from "chalk";
|
|
10047
|
-
import { readdirSync as
|
|
10048
|
-
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";
|
|
10049
10248
|
import {
|
|
10050
10249
|
DesignSystemManager as DesignSystemManager11,
|
|
10051
10250
|
ComponentManager as ComponentManager5,
|
|
@@ -10069,9 +10268,9 @@ function extractComponentIdsFromCode2(code) {
|
|
|
10069
10268
|
function listTsxFiles(dir) {
|
|
10070
10269
|
const files = [];
|
|
10071
10270
|
try {
|
|
10072
|
-
const entries =
|
|
10271
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
10073
10272
|
for (const e of entries) {
|
|
10074
|
-
const full =
|
|
10273
|
+
const full = join14(dir, e.name);
|
|
10075
10274
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
10076
10275
|
files.push(...listTsxFiles(full));
|
|
10077
10276
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -10100,7 +10299,7 @@ async function fixCommand(opts = {}) {
|
|
|
10100
10299
|
console.log(chalk18.cyan("\ncoherent fix\n"));
|
|
10101
10300
|
}
|
|
10102
10301
|
if (!skipCache) {
|
|
10103
|
-
const nextDir =
|
|
10302
|
+
const nextDir = join14(projectRoot, ".next");
|
|
10104
10303
|
if (existsSync21(nextDir)) {
|
|
10105
10304
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
10106
10305
|
fixes.push("Cleared build cache");
|
|
@@ -10357,13 +10556,13 @@ async function fixCommand(opts = {}) {
|
|
|
10357
10556
|
// src/commands/check.ts
|
|
10358
10557
|
import chalk19 from "chalk";
|
|
10359
10558
|
import { resolve as resolve13 } from "path";
|
|
10360
|
-
import { readdirSync as
|
|
10559
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
|
|
10361
10560
|
import { loadManifest as loadManifest10 } from "@getcoherent/core";
|
|
10362
10561
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
10363
10562
|
function findTsxFiles(dir) {
|
|
10364
10563
|
const results = [];
|
|
10365
10564
|
try {
|
|
10366
|
-
const entries =
|
|
10565
|
+
const entries = readdirSync6(dir);
|
|
10367
10566
|
for (const entry of entries) {
|
|
10368
10567
|
const full = resolve13(dir, entry);
|
|
10369
10568
|
const stat = statSync2(full);
|
|
@@ -10691,8 +10890,8 @@ import { existsSync as existsSync23 } from "fs";
|
|
|
10691
10890
|
import { resolve as resolve14 } from "path";
|
|
10692
10891
|
|
|
10693
10892
|
// src/utils/ds-files.ts
|
|
10694
|
-
import { mkdir as
|
|
10695
|
-
import { join as
|
|
10893
|
+
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
10894
|
+
import { join as join15, dirname as dirname8 } from "path";
|
|
10696
10895
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
10697
10896
|
var SHARED_DS_KEYS = [
|
|
10698
10897
|
"app/design-system/shared/page.tsx",
|
|
@@ -10706,9 +10905,9 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
10706
10905
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
|
|
10707
10906
|
const written = [];
|
|
10708
10907
|
for (const [relativePath, content] of toWrite) {
|
|
10709
|
-
const fullPath =
|
|
10710
|
-
await
|
|
10711
|
-
await
|
|
10908
|
+
const fullPath = join15(projectRoot, relativePath);
|
|
10909
|
+
await mkdir6(dirname8(fullPath), { recursive: true });
|
|
10910
|
+
await writeFile6(fullPath, content, "utf-8");
|
|
10712
10911
|
written.push(relativePath);
|
|
10713
10912
|
}
|
|
10714
10913
|
return written;
|
|
@@ -10844,8 +11043,8 @@ function createComponentsCommand() {
|
|
|
10844
11043
|
// src/commands/import-cmd.ts
|
|
10845
11044
|
import chalk26 from "chalk";
|
|
10846
11045
|
import ora6 from "ora";
|
|
10847
|
-
import { writeFile as
|
|
10848
|
-
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";
|
|
10849
11048
|
import { existsSync as existsSync24 } from "fs";
|
|
10850
11049
|
import {
|
|
10851
11050
|
FigmaClient,
|
|
@@ -10993,9 +11192,9 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
10993
11192
|
stats.filesWritten.push(filePath);
|
|
10994
11193
|
return;
|
|
10995
11194
|
}
|
|
10996
|
-
const fullPath =
|
|
10997
|
-
await
|
|
10998
|
-
await
|
|
11195
|
+
const fullPath = join16(projectRoot, filePath);
|
|
11196
|
+
await mkdir7(dirname9(fullPath), { recursive: true });
|
|
11197
|
+
await writeFile7(fullPath, content, "utf-8");
|
|
10999
11198
|
stats.filesWritten.push(filePath);
|
|
11000
11199
|
};
|
|
11001
11200
|
try {
|
|
@@ -11069,7 +11268,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11069
11268
|
});
|
|
11070
11269
|
if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
|
|
11071
11270
|
else
|
|
11072
|
-
await
|
|
11271
|
+
await writeFile7(
|
|
11073
11272
|
resolve15(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
|
|
11074
11273
|
JSON.stringify(componentMapObj, null, 2),
|
|
11075
11274
|
"utf-8"
|
|
@@ -11109,7 +11308,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
11109
11308
|
});
|
|
11110
11309
|
await dsm.save();
|
|
11111
11310
|
} else {
|
|
11112
|
-
await
|
|
11311
|
+
await writeFile7(
|
|
11113
11312
|
configPath,
|
|
11114
11313
|
`/**
|
|
11115
11314
|
* Design System Configuration
|
|
@@ -11124,10 +11323,10 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
11124
11323
|
stats.configUpdated = true;
|
|
11125
11324
|
spinner.succeed("design-system.config.ts updated");
|
|
11126
11325
|
spinner.start("Ensuring root layout...");
|
|
11127
|
-
const layoutPath =
|
|
11326
|
+
const layoutPath = join16(projectRoot, "app/layout.tsx");
|
|
11128
11327
|
if (!existsSync24(layoutPath)) {
|
|
11129
|
-
await
|
|
11130
|
-
await
|
|
11328
|
+
await mkdir7(dirname9(layoutPath), { recursive: true });
|
|
11329
|
+
await writeFile7(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
11131
11330
|
stats.filesWritten.push("app/layout.tsx");
|
|
11132
11331
|
}
|
|
11133
11332
|
spinner.succeed("Root layout OK");
|
|
@@ -11216,7 +11415,7 @@ async function dsRegenerateCommand() {
|
|
|
11216
11415
|
import chalk28 from "chalk";
|
|
11217
11416
|
import ora8 from "ora";
|
|
11218
11417
|
import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
|
|
11219
|
-
import { join as
|
|
11418
|
+
import { join as join17 } from "path";
|
|
11220
11419
|
import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
11221
11420
|
|
|
11222
11421
|
// src/utils/migrations.ts
|
|
@@ -11385,7 +11584,7 @@ var EXPECTED_CSS_VARS = [
|
|
|
11385
11584
|
"--sidebar-ring"
|
|
11386
11585
|
];
|
|
11387
11586
|
function checkMissingCssVars(projectRoot) {
|
|
11388
|
-
const globalsPath =
|
|
11587
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11389
11588
|
if (!existsSync25(globalsPath)) return [];
|
|
11390
11589
|
try {
|
|
11391
11590
|
const content = readFileSync16(globalsPath, "utf-8");
|
|
@@ -11395,7 +11594,7 @@ function checkMissingCssVars(projectRoot) {
|
|
|
11395
11594
|
}
|
|
11396
11595
|
}
|
|
11397
11596
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
11398
|
-
const globalsPath =
|
|
11597
|
+
const globalsPath = join17(projectRoot, "app", "globals.css");
|
|
11399
11598
|
if (!existsSync25(globalsPath) || missingVars.length === 0) return;
|
|
11400
11599
|
const { writeFileSync: writeFileSync13 } = __require("fs");
|
|
11401
11600
|
let content = readFileSync16(globalsPath, "utf-8");
|
|
@@ -11477,14 +11676,14 @@ async function undoCommand(options) {
|
|
|
11477
11676
|
import chalk30 from "chalk";
|
|
11478
11677
|
import ora9 from "ora";
|
|
11479
11678
|
import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
|
|
11480
|
-
import { join as
|
|
11481
|
-
import { readdir as readdir4, readFile as
|
|
11679
|
+
import { join as join18, relative as relative5, dirname as dirname10 } from "path";
|
|
11680
|
+
import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
11482
11681
|
import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
|
|
11483
11682
|
import { loadManifest as loadManifest12, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
|
|
11484
11683
|
function extractTokensFromProject(projectRoot) {
|
|
11485
11684
|
const lightColors = {};
|
|
11486
11685
|
const darkColors = {};
|
|
11487
|
-
const globalsPath =
|
|
11686
|
+
const globalsPath = join18(projectRoot, "app", "globals.css");
|
|
11488
11687
|
if (existsSync26(globalsPath)) {
|
|
11489
11688
|
const css = readFileSync17(globalsPath, "utf-8");
|
|
11490
11689
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
@@ -11492,7 +11691,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
11492
11691
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
11493
11692
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
11494
11693
|
}
|
|
11495
|
-
const layoutPath =
|
|
11694
|
+
const layoutPath = join18(projectRoot, "app", "layout.tsx");
|
|
11496
11695
|
let layoutCode = "";
|
|
11497
11696
|
if (existsSync26(layoutPath)) {
|
|
11498
11697
|
layoutCode = readFileSync17(layoutPath, "utf-8");
|
|
@@ -11536,14 +11735,14 @@ function parseVarsInto(block, target) {
|
|
|
11536
11735
|
}
|
|
11537
11736
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
11538
11737
|
const results = [];
|
|
11539
|
-
const componentsDir =
|
|
11738
|
+
const componentsDir = join18(projectRoot, "components");
|
|
11540
11739
|
if (!existsSync26(componentsDir)) return results;
|
|
11541
11740
|
const files = [];
|
|
11542
11741
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
11543
11742
|
const fileResults = await Promise.all(
|
|
11544
11743
|
files.map(async (filePath) => {
|
|
11545
|
-
const code = await
|
|
11546
|
-
const relFile =
|
|
11744
|
+
const code = await readFile7(filePath, "utf-8");
|
|
11745
|
+
const relFile = relative5(projectRoot, filePath);
|
|
11547
11746
|
const exportedNames = extractExportedComponentNames2(code);
|
|
11548
11747
|
return exportedNames.map((name) => ({
|
|
11549
11748
|
name,
|
|
@@ -11564,7 +11763,7 @@ async function walkForTsx(dir, files, skipDirs) {
|
|
|
11564
11763
|
return;
|
|
11565
11764
|
}
|
|
11566
11765
|
for (const e of entries) {
|
|
11567
|
-
const full =
|
|
11766
|
+
const full = join18(dir, e.name);
|
|
11568
11767
|
if (e.isDirectory()) {
|
|
11569
11768
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
11570
11769
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -11638,14 +11837,14 @@ async function discoverPages(appDir) {
|
|
|
11638
11837
|
return;
|
|
11639
11838
|
}
|
|
11640
11839
|
for (const entry of entries) {
|
|
11641
|
-
const full =
|
|
11840
|
+
const full = join18(dir, entry.name);
|
|
11642
11841
|
if (entry.isDirectory()) {
|
|
11643
11842
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
11644
11843
|
if (entry.name.startsWith(".")) continue;
|
|
11645
11844
|
await walk(full);
|
|
11646
11845
|
} else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
|
|
11647
|
-
const code = await
|
|
11648
|
-
const routeDir = dirname10(
|
|
11846
|
+
const code = await readFile7(full, "utf-8");
|
|
11847
|
+
const routeDir = dirname10(relative5(appDir, full));
|
|
11649
11848
|
let route = routeDir === "." ? "/" : "/" + routeDir;
|
|
11650
11849
|
route = route.replace(/\/\([^)]+\)/g, "");
|
|
11651
11850
|
if (!route.startsWith("/")) route = "/" + route;
|
|
@@ -11715,7 +11914,7 @@ async function syncCommand(options = {}) {
|
|
|
11715
11914
|
if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
|
|
11716
11915
|
const spinner = ora9("Scanning project files...").start();
|
|
11717
11916
|
try {
|
|
11718
|
-
const appDir =
|
|
11917
|
+
const appDir = join18(project.root, "app");
|
|
11719
11918
|
if (!existsSync26(appDir)) {
|
|
11720
11919
|
spinner.fail("No app/ directory found");
|
|
11721
11920
|
process.exit(1);
|
|
@@ -11942,14 +12141,14 @@ async function syncCommand(options = {}) {
|
|
|
11942
12141
|
|
|
11943
12142
|
// src/utils/update-notifier.ts
|
|
11944
12143
|
import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
|
|
11945
|
-
import { join as
|
|
12144
|
+
import { join as join19 } from "path";
|
|
11946
12145
|
import { homedir } from "os";
|
|
11947
12146
|
import chalk31 from "chalk";
|
|
11948
12147
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
11949
12148
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
11950
12149
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
11951
|
-
var CACHE_DIR =
|
|
11952
|
-
var CACHE_FILE =
|
|
12150
|
+
var CACHE_DIR = join19(homedir(), ".coherent");
|
|
12151
|
+
var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
|
|
11953
12152
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
11954
12153
|
function readCache() {
|
|
11955
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",
|