@getcoherent/cli 0.6.46 → 0.6.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-U6M76BKY.js +50 -0
- package/dist/chunk-XPBD3L7V.js +260 -0
- package/dist/index.js +237 -177
- package/dist/tsc-ai-fix-O3EMRWV2.js +98 -0
- package/dist/tsc-autofix-S5PKMFSC.js +16 -0
- package/package.json +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/commands/fix-validation.ts
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
var cachedTs = null;
|
|
6
|
+
var cachedProjectRoot = null;
|
|
7
|
+
function isValidTsx(code, projectRoot, ext = ".tsx") {
|
|
8
|
+
if (ext !== ".tsx" && ext !== ".ts") return true;
|
|
9
|
+
const pkgJson = join(projectRoot, "package.json");
|
|
10
|
+
if (!existsSync(pkgJson)) return true;
|
|
11
|
+
try {
|
|
12
|
+
if (!cachedTs || cachedProjectRoot !== projectRoot) {
|
|
13
|
+
const req = createRequire(pkgJson);
|
|
14
|
+
cachedTs = req("typescript");
|
|
15
|
+
cachedProjectRoot = projectRoot;
|
|
16
|
+
}
|
|
17
|
+
const sf = cachedTs.createSourceFile(
|
|
18
|
+
"check.tsx",
|
|
19
|
+
code,
|
|
20
|
+
cachedTs.ScriptTarget.Latest,
|
|
21
|
+
false,
|
|
22
|
+
cachedTs.ScriptKind.TSX
|
|
23
|
+
);
|
|
24
|
+
const diagnostics = sf.parseDiagnostics;
|
|
25
|
+
return !diagnostics || diagnostics.length === 0;
|
|
26
|
+
} catch {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function safeWrite(filePath, newContent, projectRoot, backups) {
|
|
31
|
+
if (!backups.has(filePath)) {
|
|
32
|
+
try {
|
|
33
|
+
backups.set(filePath, readFileSync(filePath, "utf-8"));
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
38
|
+
writeFileSync(filePath, newContent, "utf-8");
|
|
39
|
+
if (!isValidTsx(newContent, projectRoot, ext)) {
|
|
40
|
+
const original = backups.get(filePath);
|
|
41
|
+
if (original) writeFileSync(filePath, original, "utf-8");
|
|
42
|
+
return { ok: false };
|
|
43
|
+
}
|
|
44
|
+
return { ok: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
isValidTsx,
|
|
49
|
+
safeWrite
|
|
50
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import {
|
|
2
|
+
safeWrite
|
|
3
|
+
} from "./chunk-U6M76BKY.js";
|
|
4
|
+
|
|
5
|
+
// src/utils/tsc-autofix.ts
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
|
|
10
|
+
// src/utils/tsc-error-parser.ts
|
|
11
|
+
var ERROR_RE = /^(.+?)\((\d+),(\d+)\): error (TS\d+): (.+)$/;
|
|
12
|
+
var RELATED_LOCATION_RE = /^(.+?)\((\d+),(\d+)\):\s/;
|
|
13
|
+
function parseTscOutput(output) {
|
|
14
|
+
const lines = output.split("\n");
|
|
15
|
+
const errors = [];
|
|
16
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17
|
+
let current = null;
|
|
18
|
+
for (const raw of lines) {
|
|
19
|
+
const trimmed = raw.trimStart();
|
|
20
|
+
const match = trimmed.match(ERROR_RE);
|
|
21
|
+
if (match) {
|
|
22
|
+
const [, file, lineStr, colStr, code, msg] = match;
|
|
23
|
+
const isRelated = raw.startsWith(" ");
|
|
24
|
+
if (isRelated && current) {
|
|
25
|
+
const cleanFile = file.trim();
|
|
26
|
+
if (!current.relatedFiles.includes(cleanFile)) {
|
|
27
|
+
current.relatedFiles.push(cleanFile);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
flushCurrent();
|
|
31
|
+
current = {
|
|
32
|
+
file: file.trim(),
|
|
33
|
+
line: parseInt(lineStr, 10),
|
|
34
|
+
col: parseInt(colStr, 10),
|
|
35
|
+
code,
|
|
36
|
+
message: msg,
|
|
37
|
+
relatedFiles: []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
} else if (current && raw.startsWith(" ") && raw.trim().length > 0) {
|
|
41
|
+
const locMatch = trimmed.match(RELATED_LOCATION_RE);
|
|
42
|
+
if (locMatch) {
|
|
43
|
+
const cleanFile = locMatch[1].trim();
|
|
44
|
+
if (!current.relatedFiles.includes(cleanFile)) {
|
|
45
|
+
current.relatedFiles.push(cleanFile);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
current.message += "\n" + raw.trim();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
flushCurrent();
|
|
53
|
+
return errors;
|
|
54
|
+
function flushCurrent() {
|
|
55
|
+
if (!current) return;
|
|
56
|
+
const key = `${current.file}:${current.line}:${current.code}`;
|
|
57
|
+
if (!seen.has(key)) {
|
|
58
|
+
seen.add(key);
|
|
59
|
+
errors.push(current);
|
|
60
|
+
}
|
|
61
|
+
current = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/utils/tsc-autofix.ts
|
|
66
|
+
function runTscCheck(projectRoot, timeout = 3e4) {
|
|
67
|
+
const tsconfigPath = resolve(projectRoot, "tsconfig.json");
|
|
68
|
+
if (!existsSync(tsconfigPath)) return [];
|
|
69
|
+
try {
|
|
70
|
+
execSync("npx tsc --noEmit 2>&1", {
|
|
71
|
+
cwd: projectRoot,
|
|
72
|
+
timeout,
|
|
73
|
+
encoding: "utf-8"
|
|
74
|
+
});
|
|
75
|
+
return [];
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err && typeof err === "object" && "killed" in err && err.killed) {
|
|
78
|
+
console.log(" \u26A0 TypeScript check timed out \u2014 skipping");
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const e = err;
|
|
82
|
+
const output = (e.stdout || "") + (e.stderr || "");
|
|
83
|
+
return parseTscOutput(output);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function levenshtein(a, b) {
|
|
87
|
+
const m = a.length, n = b.length;
|
|
88
|
+
const dp = Array.from(
|
|
89
|
+
{ length: m + 1 },
|
|
90
|
+
(_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
|
|
91
|
+
);
|
|
92
|
+
for (let i = 1; i <= m; i++)
|
|
93
|
+
for (let j = 1; j <= n; j++)
|
|
94
|
+
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
95
|
+
return dp[m][n];
|
|
96
|
+
}
|
|
97
|
+
function maxLevenshtein(fieldName) {
|
|
98
|
+
return Math.max(1, Math.floor(fieldName.length * 0.4));
|
|
99
|
+
}
|
|
100
|
+
function escapeRegExp(s) {
|
|
101
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
102
|
+
}
|
|
103
|
+
var MISSING_PROP_RE = /Property '(\w+)' is missing in type '\{([^}]*)\}'/;
|
|
104
|
+
var UNION_RE = /Type 'string' is not assignable to type '((?:"[^"]+"\s*\|\s*)*"[^"]+")'/;
|
|
105
|
+
var MISSING_REQUIRED_RE = /Property '(\w+)' is missing in type .* but required/;
|
|
106
|
+
function extractFieldsFromCode(code, line) {
|
|
107
|
+
const lines = code.split("\n");
|
|
108
|
+
const searchRange = lines.slice(Math.max(0, line - 3), line + 3).join(" ");
|
|
109
|
+
const fieldMatches = searchRange.match(/(\w+)\s*:/g);
|
|
110
|
+
if (!fieldMatches) return [];
|
|
111
|
+
return fieldMatches.map((m) => m.replace(/\s*:$/, ""));
|
|
112
|
+
}
|
|
113
|
+
function fixFieldRename(code, error, errorLine) {
|
|
114
|
+
const match = error.message.match(MISSING_PROP_RE);
|
|
115
|
+
const expectedField = match?.[1] ?? error.message.match(/Property '(\w+)' is missing/)?.[1];
|
|
116
|
+
if (!expectedField) return null;
|
|
117
|
+
let typeFields;
|
|
118
|
+
if (match?.[2]) {
|
|
119
|
+
typeFields = match[2].split(";").map((f) => f.trim().split(":")[0]?.trim()).filter(Boolean);
|
|
120
|
+
} else {
|
|
121
|
+
typeFields = extractFieldsFromCode(code, errorLine ?? error.line);
|
|
122
|
+
}
|
|
123
|
+
let bestMatch = null;
|
|
124
|
+
let bestDist = Infinity;
|
|
125
|
+
for (const field of typeFields) {
|
|
126
|
+
if (field === expectedField) continue;
|
|
127
|
+
if (field.includes(expectedField) || expectedField.includes(field)) {
|
|
128
|
+
bestMatch = field;
|
|
129
|
+
bestDist = 0;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
const dist = levenshtein(field.toLowerCase(), expectedField.toLowerCase());
|
|
133
|
+
if (dist <= maxLevenshtein(expectedField) && dist < bestDist) {
|
|
134
|
+
bestDist = dist;
|
|
135
|
+
bestMatch = field;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!bestMatch) return null;
|
|
139
|
+
const targetLine = errorLine ?? error.line;
|
|
140
|
+
const lines = code.split("\n");
|
|
141
|
+
const targetIdx = targetLine - 1;
|
|
142
|
+
const windowStart = Math.max(0, targetIdx - 5);
|
|
143
|
+
const windowEnd = Math.min(lines.length, targetIdx + 6);
|
|
144
|
+
const fieldRe = new RegExp(`(\\b)${escapeRegExp(bestMatch)}(\\s*:)`, "g");
|
|
145
|
+
for (let i = windowStart; i < windowEnd; i++) {
|
|
146
|
+
if (fieldRe.test(lines[i])) {
|
|
147
|
+
lines[i] = lines[i].replace(fieldRe, `$1${expectedField}$2`);
|
|
148
|
+
}
|
|
149
|
+
fieldRe.lastIndex = 0;
|
|
150
|
+
}
|
|
151
|
+
const newCode = lines.join("\n");
|
|
152
|
+
if (newCode === code) return null;
|
|
153
|
+
return { code: newCode, field: `${bestMatch} \u2192 ${expectedField}` };
|
|
154
|
+
}
|
|
155
|
+
function fixUnionType(code, error) {
|
|
156
|
+
const match = error.message.match(UNION_RE);
|
|
157
|
+
if (!match) return null;
|
|
158
|
+
const variants = match[1].match(/"([^"]+)"/g)?.map((v) => v.replace(/"/g, ""));
|
|
159
|
+
if (!variants || variants.length === 0) return null;
|
|
160
|
+
const lines = code.split("\n");
|
|
161
|
+
const errorLine = lines[error.line - 1];
|
|
162
|
+
if (!errorLine) return null;
|
|
163
|
+
for (const variant of variants) {
|
|
164
|
+
const caseInsensitiveRe = new RegExp(`['"]${escapeRegExp(variant)}['"]`, "i");
|
|
165
|
+
const exactRe = new RegExp(`['"]${escapeRegExp(variant)}['"]`);
|
|
166
|
+
if (caseInsensitiveRe.test(errorLine) && !exactRe.test(errorLine)) {
|
|
167
|
+
lines[error.line - 1] = errorLine.replace(caseInsensitiveRe, `'${variant}'`);
|
|
168
|
+
return { code: lines.join("\n"), fix: `union case: '${variant}'` };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
function fixMissingEventHandler(code, error) {
|
|
174
|
+
const match = error.message.match(MISSING_REQUIRED_RE);
|
|
175
|
+
if (!match) return null;
|
|
176
|
+
const propName = match[1];
|
|
177
|
+
if (!propName.startsWith("on") || propName.length < 3) return null;
|
|
178
|
+
if (propName[2] !== propName[2].toUpperCase()) return null;
|
|
179
|
+
const lines = code.split("\n");
|
|
180
|
+
const errorLine = lines[error.line - 1];
|
|
181
|
+
if (!errorLine) return null;
|
|
182
|
+
const closingMatch = errorLine.match(/(\s*\/?>)/);
|
|
183
|
+
if (!closingMatch) return null;
|
|
184
|
+
const insertPos = errorLine.lastIndexOf(closingMatch[1]);
|
|
185
|
+
lines[error.line - 1] = errorLine.slice(0, insertPos) + ` ${propName}={() => {}}` + errorLine.slice(insertPos);
|
|
186
|
+
return { code: lines.join("\n"), prop: propName };
|
|
187
|
+
}
|
|
188
|
+
function deduplicateErrors(errors) {
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
return errors.filter((e) => {
|
|
191
|
+
const key = `${e.file}:${e.line}:${e.code}`;
|
|
192
|
+
if (seen.has(key)) return false;
|
|
193
|
+
seen.add(key);
|
|
194
|
+
return true;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function applyDeterministicFixes(errors, projectRoot, backups) {
|
|
198
|
+
const deduped = deduplicateErrors(errors);
|
|
199
|
+
const fixed = [];
|
|
200
|
+
const remaining = [];
|
|
201
|
+
const fileErrors = /* @__PURE__ */ new Map();
|
|
202
|
+
for (const err of deduped) {
|
|
203
|
+
const list = fileErrors.get(err.file) || [];
|
|
204
|
+
list.push(err);
|
|
205
|
+
fileErrors.set(err.file, list);
|
|
206
|
+
}
|
|
207
|
+
for (const [file, errs] of fileErrors) {
|
|
208
|
+
const absPath = resolve(projectRoot, file);
|
|
209
|
+
let code;
|
|
210
|
+
try {
|
|
211
|
+
code = readFileSync(absPath, "utf-8");
|
|
212
|
+
} catch {
|
|
213
|
+
remaining.push(...errs);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
let changed = false;
|
|
217
|
+
const fileRemaining = [];
|
|
218
|
+
for (const e of errs) {
|
|
219
|
+
const renameResult = fixFieldRename(code, e, e.line);
|
|
220
|
+
if (renameResult) {
|
|
221
|
+
code = renameResult.code;
|
|
222
|
+
changed = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const unionResult = fixUnionType(code, e);
|
|
226
|
+
if (unionResult) {
|
|
227
|
+
code = unionResult.code;
|
|
228
|
+
changed = true;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const handlerResult = fixMissingEventHandler(code, e);
|
|
232
|
+
if (handlerResult) {
|
|
233
|
+
code = handlerResult.code;
|
|
234
|
+
changed = true;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
fileRemaining.push(e);
|
|
238
|
+
}
|
|
239
|
+
if (changed) {
|
|
240
|
+
const { ok } = safeWrite(absPath, code, projectRoot, backups);
|
|
241
|
+
if (ok) {
|
|
242
|
+
fixed.push(file);
|
|
243
|
+
remaining.push(...fileRemaining);
|
|
244
|
+
} else {
|
|
245
|
+
remaining.push(...errs);
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
remaining.push(...fileRemaining);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return { fixed, remaining };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export {
|
|
255
|
+
runTscCheck,
|
|
256
|
+
fixFieldRename,
|
|
257
|
+
fixUnionType,
|
|
258
|
+
fixMissingEventHandler,
|
|
259
|
+
applyDeterministicFixes
|
|
260
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,10 @@ import {
|
|
|
53
53
|
savePlan,
|
|
54
54
|
updateArchitecturePlan
|
|
55
55
|
} from "./chunk-VLBVBF6V.js";
|
|
56
|
+
import {
|
|
57
|
+
isValidTsx,
|
|
58
|
+
safeWrite
|
|
59
|
+
} from "./chunk-U6M76BKY.js";
|
|
56
60
|
import {
|
|
57
61
|
toKebabCase,
|
|
58
62
|
toTitleCase
|
|
@@ -5018,6 +5022,70 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
5018
5022
|
allFixes.forEach((f) => console.log(chalk8.dim(` ${f}`)));
|
|
5019
5023
|
}
|
|
5020
5024
|
await writeFile(filePath, codeToWrite);
|
|
5025
|
+
try {
|
|
5026
|
+
const { runTscCheck, applyDeterministicFixes } = await import("./tsc-autofix-S5PKMFSC.js");
|
|
5027
|
+
const tscBackups = /* @__PURE__ */ new Map();
|
|
5028
|
+
const relPath = filePath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
|
|
5029
|
+
const tscErrors = runTscCheck(projectRoot).filter((e) => e.file === relPath);
|
|
5030
|
+
if (tscErrors.length > 0) {
|
|
5031
|
+
const bestSnapshot = codeToWrite;
|
|
5032
|
+
const detResult = await applyDeterministicFixes(tscErrors, projectRoot, tscBackups);
|
|
5033
|
+
let bestErrorCount = Math.min(tscErrors.length, tscErrors.length - detResult.fixed.length);
|
|
5034
|
+
if (detResult.fixed.length > 0) {
|
|
5035
|
+
codeToWrite = await readFile(filePath);
|
|
5036
|
+
console.log(
|
|
5037
|
+
chalk8.green(` \u2714 Fixed ${tscErrors.length - detResult.remaining.length} TypeScript error(s)`)
|
|
5038
|
+
);
|
|
5039
|
+
}
|
|
5040
|
+
if (detResult.remaining.length > 0 && aiProvider) {
|
|
5041
|
+
try {
|
|
5042
|
+
const ai = await createAIProvider(aiProvider);
|
|
5043
|
+
if (ai.editPageCode) {
|
|
5044
|
+
const errorList = detResult.remaining.map((e) => `Line ${e.line}: [${e.code}] ${e.message.split("\n")[0]}`).join("\n");
|
|
5045
|
+
const tscFixed = await ai.editPageCode(
|
|
5046
|
+
codeToWrite,
|
|
5047
|
+
`Fix these TypeScript errors:
|
|
5048
|
+
${errorList}
|
|
5049
|
+
|
|
5050
|
+
Keep all existing functionality intact.`,
|
|
5051
|
+
page.name || page.id || "Page"
|
|
5052
|
+
);
|
|
5053
|
+
if (tscFixed && tscFixed.length > 100) {
|
|
5054
|
+
const { code: reFixed } = await autoFixCode(tscFixed, autoFixCtx);
|
|
5055
|
+
await writeFile(filePath, reFixed);
|
|
5056
|
+
const afterErrors = runTscCheck(projectRoot).filter((e) => e.file === relPath);
|
|
5057
|
+
if (afterErrors.length > bestErrorCount) {
|
|
5058
|
+
await writeFile(filePath, bestSnapshot);
|
|
5059
|
+
codeToWrite = bestSnapshot;
|
|
5060
|
+
console.log(
|
|
5061
|
+
chalk8.yellow(` \u26A0 AI fix regressed TypeScript errors. Reverted to best version.`)
|
|
5062
|
+
);
|
|
5063
|
+
} else {
|
|
5064
|
+
codeToWrite = reFixed;
|
|
5065
|
+
console.log(
|
|
5066
|
+
chalk8.green(
|
|
5067
|
+
` \u2714 Fixed ${detResult.remaining.length - afterErrors.length} TypeScript error(s) via AI`
|
|
5068
|
+
)
|
|
5069
|
+
);
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
} catch (tscAiErr) {
|
|
5074
|
+
console.log(
|
|
5075
|
+
chalk8.dim(
|
|
5076
|
+
` \u26A0 AI tsc fix skipped: ${tscAiErr instanceof Error ? tscAiErr.message : "unknown"}`
|
|
5077
|
+
)
|
|
5078
|
+
);
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
} catch (tscErr) {
|
|
5083
|
+
console.log(
|
|
5084
|
+
chalk8.dim(
|
|
5085
|
+
` \u26A0 TypeScript check skipped: ${tscErr instanceof Error ? tscErr.message : "unknown"}`
|
|
5086
|
+
)
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5021
5089
|
const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
|
|
5022
5090
|
if (pageIdx !== -1) {
|
|
5023
5091
|
const cfg = dsm.getConfig();
|
|
@@ -8084,8 +8152,8 @@ async function regenerateDocsCommand() {
|
|
|
8084
8152
|
|
|
8085
8153
|
// src/commands/fix.ts
|
|
8086
8154
|
import chalk15 from "chalk";
|
|
8087
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
8088
|
-
import { resolve as resolve8, join as
|
|
8155
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync10, existsSync as existsSync14, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
8156
|
+
import { resolve as resolve8, join as join11, relative as relative5, basename as basename3 } from "path";
|
|
8089
8157
|
import {
|
|
8090
8158
|
DesignSystemManager as DesignSystemManager9,
|
|
8091
8159
|
ComponentManager as ComponentManager5,
|
|
@@ -8094,54 +8162,6 @@ import {
|
|
|
8094
8162
|
loadManifest as loadManifest9,
|
|
8095
8163
|
saveManifest as saveManifest5
|
|
8096
8164
|
} from "@getcoherent/core";
|
|
8097
|
-
|
|
8098
|
-
// src/commands/fix-validation.ts
|
|
8099
|
-
import { createRequire } from "module";
|
|
8100
|
-
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
8101
|
-
import { join as join11 } from "path";
|
|
8102
|
-
var cachedTs = null;
|
|
8103
|
-
var cachedProjectRoot = null;
|
|
8104
|
-
function isValidTsx(code, projectRoot, ext = ".tsx") {
|
|
8105
|
-
if (ext !== ".tsx" && ext !== ".ts") return true;
|
|
8106
|
-
const pkgJson = join11(projectRoot, "package.json");
|
|
8107
|
-
if (!existsSync14(pkgJson)) return true;
|
|
8108
|
-
try {
|
|
8109
|
-
if (!cachedTs || cachedProjectRoot !== projectRoot) {
|
|
8110
|
-
const req = createRequire(pkgJson);
|
|
8111
|
-
cachedTs = req("typescript");
|
|
8112
|
-
cachedProjectRoot = projectRoot;
|
|
8113
|
-
}
|
|
8114
|
-
const sf = cachedTs.createSourceFile(
|
|
8115
|
-
"check.tsx",
|
|
8116
|
-
code,
|
|
8117
|
-
cachedTs.ScriptTarget.Latest,
|
|
8118
|
-
false,
|
|
8119
|
-
cachedTs.ScriptKind.TSX
|
|
8120
|
-
);
|
|
8121
|
-
const diagnostics = sf.parseDiagnostics;
|
|
8122
|
-
return !diagnostics || diagnostics.length === 0;
|
|
8123
|
-
} catch {
|
|
8124
|
-
return true;
|
|
8125
|
-
}
|
|
8126
|
-
}
|
|
8127
|
-
function safeWrite(filePath, newContent, projectRoot, backups) {
|
|
8128
|
-
if (!backups.has(filePath)) {
|
|
8129
|
-
try {
|
|
8130
|
-
backups.set(filePath, readFileSync10(filePath, "utf-8"));
|
|
8131
|
-
} catch {
|
|
8132
|
-
}
|
|
8133
|
-
}
|
|
8134
|
-
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
8135
|
-
writeFileSync9(filePath, newContent, "utf-8");
|
|
8136
|
-
if (!isValidTsx(newContent, projectRoot, ext)) {
|
|
8137
|
-
const original = backups.get(filePath);
|
|
8138
|
-
if (original) writeFileSync9(filePath, original, "utf-8");
|
|
8139
|
-
return { ok: false };
|
|
8140
|
-
}
|
|
8141
|
-
return { ok: true };
|
|
8142
|
-
}
|
|
8143
|
-
|
|
8144
|
-
// src/commands/fix.ts
|
|
8145
8165
|
function extractComponentIdsFromCode2(code) {
|
|
8146
8166
|
const ids = /* @__PURE__ */ new Set();
|
|
8147
8167
|
const allMatches = code.matchAll(/@\/components\/((?:ui\/)?[a-z0-9-]+)/g);
|
|
@@ -8159,7 +8179,7 @@ function listTsxFiles(dir) {
|
|
|
8159
8179
|
try {
|
|
8160
8180
|
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
8161
8181
|
for (const e of entries) {
|
|
8162
|
-
const full =
|
|
8182
|
+
const full = join11(dir, e.name);
|
|
8163
8183
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
8164
8184
|
files.push(...listTsxFiles(full));
|
|
8165
8185
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -8190,8 +8210,8 @@ async function fixCommand(opts = {}) {
|
|
|
8190
8210
|
console.log(chalk15.cyan("\ncoherent fix\n"));
|
|
8191
8211
|
}
|
|
8192
8212
|
if (!skipCache) {
|
|
8193
|
-
const nextDir =
|
|
8194
|
-
if (
|
|
8213
|
+
const nextDir = join11(projectRoot, ".next");
|
|
8214
|
+
if (existsSync14(nextDir)) {
|
|
8195
8215
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
8196
8216
|
fixes.push("Cleared build cache");
|
|
8197
8217
|
console.log(chalk15.green(" \u2714 Cleared build cache"));
|
|
@@ -8218,7 +8238,7 @@ async function fixCommand(opts = {}) {
|
|
|
8218
8238
|
const componentsTsxFiles = listTsxFiles(resolve8(projectRoot, "components"));
|
|
8219
8239
|
const allComponentIds = /* @__PURE__ */ new Set();
|
|
8220
8240
|
for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
|
|
8221
|
-
const content =
|
|
8241
|
+
const content = readFileSync10(file, "utf-8");
|
|
8222
8242
|
extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
|
|
8223
8243
|
}
|
|
8224
8244
|
let dsm = null;
|
|
@@ -8238,7 +8258,7 @@ async function fixCommand(opts = {}) {
|
|
|
8238
8258
|
} else {
|
|
8239
8259
|
const fileName = toKebabCase(id) + ".tsx";
|
|
8240
8260
|
const filePath = resolve8(projectRoot, "components", "ui", fileName);
|
|
8241
|
-
if (!
|
|
8261
|
+
if (!existsSync14(filePath)) missingFiles.push(id);
|
|
8242
8262
|
}
|
|
8243
8263
|
}
|
|
8244
8264
|
const provider = getComponentProvider();
|
|
@@ -8287,7 +8307,7 @@ async function fixCommand(opts = {}) {
|
|
|
8287
8307
|
}
|
|
8288
8308
|
}
|
|
8289
8309
|
}
|
|
8290
|
-
if (!dsm &&
|
|
8310
|
+
if (!dsm && existsSync14(project.configPath)) {
|
|
8291
8311
|
dsm = new DesignSystemManager9(project.configPath);
|
|
8292
8312
|
await dsm.load();
|
|
8293
8313
|
}
|
|
@@ -8296,8 +8316,8 @@ async function fixCommand(opts = {}) {
|
|
|
8296
8316
|
let derivedName = null;
|
|
8297
8317
|
try {
|
|
8298
8318
|
const pkgPath = resolve8(projectRoot, "package.json");
|
|
8299
|
-
if (
|
|
8300
|
-
const pkg = JSON.parse(
|
|
8319
|
+
if (existsSync14(pkgPath)) {
|
|
8320
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
8301
8321
|
if (typeof pkg.name === "string" && pkg.name) {
|
|
8302
8322
|
derivedName = toTitleCase2(pkg.name);
|
|
8303
8323
|
}
|
|
@@ -8334,8 +8354,8 @@ async function fixCommand(opts = {}) {
|
|
|
8334
8354
|
const configName = dsm.getConfig().name;
|
|
8335
8355
|
if (configName && configName !== "My App") {
|
|
8336
8356
|
const appLayoutPath = resolve8(projectRoot, "app", "(app)", "layout.tsx");
|
|
8337
|
-
if (
|
|
8338
|
-
let appLayoutCode =
|
|
8357
|
+
if (existsSync14(appLayoutPath)) {
|
|
8358
|
+
let appLayoutCode = readFileSync10(appLayoutPath, "utf-8");
|
|
8339
8359
|
if (appLayoutCode.includes("My App")) {
|
|
8340
8360
|
appLayoutCode = appLayoutCode.replace(/My App/g, configName);
|
|
8341
8361
|
if (!dryRun) {
|
|
@@ -8353,11 +8373,11 @@ async function fixCommand(opts = {}) {
|
|
|
8353
8373
|
}
|
|
8354
8374
|
}
|
|
8355
8375
|
const sharedDir = resolve8(projectRoot, "components", "shared");
|
|
8356
|
-
if (
|
|
8376
|
+
if (existsSync14(sharedDir)) {
|
|
8357
8377
|
try {
|
|
8358
8378
|
for (const f of readdirSync7(sharedDir).filter((n) => n.endsWith(".tsx"))) {
|
|
8359
|
-
const sharedPath =
|
|
8360
|
-
const sharedCode =
|
|
8379
|
+
const sharedPath = join11(sharedDir, f);
|
|
8380
|
+
const sharedCode = readFileSync10(sharedPath, "utf-8");
|
|
8361
8381
|
if (sharedCode.includes("My App")) {
|
|
8362
8382
|
const updated = sharedCode.replace(/My App/g, configName);
|
|
8363
8383
|
if (!dryRun) {
|
|
@@ -8381,7 +8401,7 @@ async function fixCommand(opts = {}) {
|
|
|
8381
8401
|
let syntaxFixed = 0;
|
|
8382
8402
|
for (const file of userTsxFiles) {
|
|
8383
8403
|
try {
|
|
8384
|
-
const content =
|
|
8404
|
+
const content = readFileSync10(file, "utf-8");
|
|
8385
8405
|
const fixed = fixUnescapedLtInJsx(
|
|
8386
8406
|
fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
|
|
8387
8407
|
);
|
|
@@ -8424,7 +8444,7 @@ async function fixCommand(opts = {}) {
|
|
|
8424
8444
|
console.log(chalk15.green(` \u2714 Verified group layouts: ${layoutTypes}`));
|
|
8425
8445
|
const hasSidebar = plan.groups.some((g) => g.layout === "sidebar" || g.layout === "both");
|
|
8426
8446
|
const sidebarPath = resolve8(projectRoot, "components", "shared", "sidebar.tsx");
|
|
8427
|
-
if (hasSidebar && !
|
|
8447
|
+
if (hasSidebar && !existsSync14(sidebarPath) && !dryRun) {
|
|
8428
8448
|
if (!dsm) {
|
|
8429
8449
|
dsm = new DesignSystemManager9(project.configPath);
|
|
8430
8450
|
await dsm.load();
|
|
@@ -8443,8 +8463,8 @@ async function fixCommand(opts = {}) {
|
|
|
8443
8463
|
}
|
|
8444
8464
|
if (hasSidebar && !dryRun) {
|
|
8445
8465
|
const rootLayoutPath = resolve8(projectRoot, "app", "layout.tsx");
|
|
8446
|
-
if (
|
|
8447
|
-
let rootCode =
|
|
8466
|
+
if (existsSync14(rootLayoutPath)) {
|
|
8467
|
+
let rootCode = readFileSync10(rootLayoutPath, "utf-8");
|
|
8448
8468
|
if (rootCode.includes("<Header")) {
|
|
8449
8469
|
rootCode = rootCode.replace(/import\s*\{[^}]*Header[^}]*\}[^;\n]*[;\n]?\s*/g, "").replace(/import\s*\{[^}]*Footer[^}]*\}[^;\n]*[;\n]?\s*/g, "").replace(/import\s+ShowWhenNotAuthRoute[^;\n]*[;\n]?\s*/g, "").replace(/<ShowWhenNotAuthRoute>[\s\S]*?<\/ShowWhenNotAuthRoute>/g, (match) => {
|
|
8450
8470
|
const inner = match.replace(/<\/?ShowWhenNotAuthRoute>/g, "").trim();
|
|
@@ -8462,8 +8482,8 @@ async function fixCommand(opts = {}) {
|
|
|
8462
8482
|
}
|
|
8463
8483
|
}
|
|
8464
8484
|
const publicLayoutPath = resolve8(projectRoot, "app", "(public)", "layout.tsx");
|
|
8465
|
-
const publicExists =
|
|
8466
|
-
const needsPublicLayout = !publicExists || !
|
|
8485
|
+
const publicExists = existsSync14(publicLayoutPath);
|
|
8486
|
+
const needsPublicLayout = !publicExists || !readFileSync10(publicLayoutPath, "utf-8").includes("<Header");
|
|
8467
8487
|
if (needsPublicLayout) {
|
|
8468
8488
|
const { buildPublicLayoutCodeForSidebar } = await import("./code-generator-YSGVHVNN.js");
|
|
8469
8489
|
mkdirSync7(resolve8(projectRoot, "app", "(public)"), { recursive: true });
|
|
@@ -8476,8 +8496,8 @@ async function fixCommand(opts = {}) {
|
|
|
8476
8496
|
}
|
|
8477
8497
|
}
|
|
8478
8498
|
const sidebarComponentPath2 = resolve8(projectRoot, "components", "shared", "sidebar.tsx");
|
|
8479
|
-
if (
|
|
8480
|
-
const existingSidebarCode =
|
|
8499
|
+
if (existsSync14(sidebarComponentPath2)) {
|
|
8500
|
+
const existingSidebarCode = readFileSync10(sidebarComponentPath2, "utf-8");
|
|
8481
8501
|
const sidebarConfigName = dsm?.getConfig().name ?? "";
|
|
8482
8502
|
const hasWrongName = existingSidebarCode.includes("My App") && sidebarConfigName !== "My App";
|
|
8483
8503
|
const hasTrigger = existingSidebarCode.includes("SidebarTrigger");
|
|
@@ -8505,7 +8525,7 @@ async function fixCommand(opts = {}) {
|
|
|
8505
8525
|
}
|
|
8506
8526
|
const rootPagePath = resolve8(projectRoot, "app", "page.tsx");
|
|
8507
8527
|
const publicPagePath = resolve8(projectRoot, "app", "(public)", "page.tsx");
|
|
8508
|
-
if (
|
|
8528
|
+
if (existsSync14(rootPagePath) && !existsSync14(publicPagePath)) {
|
|
8509
8529
|
const { renameSync } = await import("fs");
|
|
8510
8530
|
mkdirSync7(resolve8(projectRoot, "app", "(public)"), { recursive: true });
|
|
8511
8531
|
renameSync(rootPagePath, publicPagePath);
|
|
@@ -8513,7 +8533,7 @@ async function fixCommand(opts = {}) {
|
|
|
8513
8533
|
console.log(chalk15.green(" \u2714 Moved app/page.tsx \u2192 app/(public)/page.tsx (gets Header/Footer)"));
|
|
8514
8534
|
}
|
|
8515
8535
|
const themeTogglePath = resolve8(projectRoot, "components", "shared", "theme-toggle.tsx");
|
|
8516
|
-
if (!
|
|
8536
|
+
if (!existsSync14(themeTogglePath)) {
|
|
8517
8537
|
const { generateThemeToggleCode } = await import("./code-generator-YSGVHVNN.js");
|
|
8518
8538
|
mkdirSync7(resolve8(projectRoot, "components", "shared"), { recursive: true });
|
|
8519
8539
|
const themeResult = safeWrite(themeTogglePath, generateThemeToggleCode(), projectRoot, backups);
|
|
@@ -8530,8 +8550,8 @@ async function fixCommand(opts = {}) {
|
|
|
8530
8550
|
console.log(chalk15.yellow(` \u26A0 Layout repair skipped: ${err instanceof Error ? err.message : "unknown error"}`));
|
|
8531
8551
|
}
|
|
8532
8552
|
const appLayoutRepairPath = resolve8(projectRoot, "app", "(app)", "layout.tsx");
|
|
8533
|
-
if (
|
|
8534
|
-
const appLayoutCode =
|
|
8553
|
+
if (existsSync14(appLayoutRepairPath) && dsm) {
|
|
8554
|
+
const appLayoutCode = readFileSync10(appLayoutRepairPath, "utf-8");
|
|
8535
8555
|
const isMinimal = appLayoutCode.length < 500 && !appLayoutCode.includes("Header") && !appLayoutCode.includes("Footer") && !appLayoutCode.includes("Sidebar") && !appLayoutCode.includes("SidebarProvider") && !appLayoutCode.includes("SidebarTrigger") && !appLayoutCode.includes("Sheet");
|
|
8536
8556
|
const navType = dsm.getConfig().navigation?.type || "header";
|
|
8537
8557
|
if (isMinimal && navType !== "none") {
|
|
@@ -8559,7 +8579,7 @@ async function fixCommand(opts = {}) {
|
|
|
8559
8579
|
const qualityFixDetails = [];
|
|
8560
8580
|
for (const file of allValidationFiles) {
|
|
8561
8581
|
try {
|
|
8562
|
-
const content =
|
|
8582
|
+
const content = readFileSync10(file, "utf-8");
|
|
8563
8583
|
const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
|
|
8564
8584
|
if (autoFixed !== content) {
|
|
8565
8585
|
if (!dryRun) {
|
|
@@ -8594,7 +8614,7 @@ async function fixCommand(opts = {}) {
|
|
|
8594
8614
|
let mockFixed = 0;
|
|
8595
8615
|
for (const file of allValidationFiles) {
|
|
8596
8616
|
try {
|
|
8597
|
-
const content =
|
|
8617
|
+
const content = readFileSync10(file, "utf-8");
|
|
8598
8618
|
const mockIssues = validateMockData(content);
|
|
8599
8619
|
if (mockIssues.length > 0) {
|
|
8600
8620
|
const fixed = applyMockDataFixes(content, mockIssues);
|
|
@@ -8625,7 +8645,7 @@ async function fixCommand(opts = {}) {
|
|
|
8625
8645
|
for (const file of modifiedFiles) {
|
|
8626
8646
|
if (!backups.has(file)) continue;
|
|
8627
8647
|
const before = backups.get(file);
|
|
8628
|
-
const after =
|
|
8648
|
+
const after = readFileSync10(file, "utf-8");
|
|
8629
8649
|
const issues = verifyIncrementalEdit(before, after);
|
|
8630
8650
|
if (issues.length > 0) {
|
|
8631
8651
|
for (const issue of issues) {
|
|
@@ -8638,7 +8658,7 @@ async function fixCommand(opts = {}) {
|
|
|
8638
8658
|
const fileIssues = [];
|
|
8639
8659
|
for (const file of allValidationFiles) {
|
|
8640
8660
|
try {
|
|
8641
|
-
const code =
|
|
8661
|
+
const code = readFileSync10(file, "utf-8");
|
|
8642
8662
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8643
8663
|
const baseName = file.split("/").pop() || "";
|
|
8644
8664
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -8748,26 +8768,66 @@ async function fixCommand(opts = {}) {
|
|
|
8748
8768
|
}
|
|
8749
8769
|
try {
|
|
8750
8770
|
const tsconfigPath = resolve8(projectRoot, "tsconfig.json");
|
|
8751
|
-
if (
|
|
8752
|
-
const {
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8771
|
+
if (existsSync14(tsconfigPath)) {
|
|
8772
|
+
const { runTscCheck, applyDeterministicFixes } = await import("./tsc-autofix-S5PKMFSC.js");
|
|
8773
|
+
const { applyAiFixes } = await import("./tsc-ai-fix-O3EMRWV2.js");
|
|
8774
|
+
const tscErrors = runTscCheck(projectRoot);
|
|
8775
|
+
if (tscErrors.length === 0) {
|
|
8776
|
+
fixes.push("TypeScript compilation clean");
|
|
8777
|
+
console.log(chalk15.green(" \u2714 TypeScript compilation clean"));
|
|
8778
|
+
} else {
|
|
8779
|
+
const detResult = await applyDeterministicFixes(tscErrors, projectRoot, backups);
|
|
8780
|
+
if (detResult.fixed.length > 0) {
|
|
8781
|
+
fixes.push(`TypeScript: fixed ${detResult.fixed.length} file(s) deterministically`);
|
|
8782
|
+
console.log(chalk15.green(` \u2714 TypeScript: fixed ${detResult.fixed.length} file(s) deterministically`));
|
|
8783
|
+
}
|
|
8784
|
+
if (detResult.remaining.length > 0) {
|
|
8785
|
+
let aiProvider;
|
|
8786
|
+
try {
|
|
8787
|
+
const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
|
|
8788
|
+
aiProvider = await createAIProvider2("auto");
|
|
8789
|
+
} catch {
|
|
8790
|
+
}
|
|
8791
|
+
if (aiProvider?.editPageCode) {
|
|
8792
|
+
console.log(chalk15.dim(` \u23F3 Using AI to fix ${detResult.remaining.length} TypeScript error(s)...`));
|
|
8793
|
+
const aiResult = await applyAiFixes(detResult.remaining, projectRoot, backups, aiProvider);
|
|
8794
|
+
if (aiResult.fixed.length > 0) {
|
|
8795
|
+
fixes.push(`TypeScript: fixed ${aiResult.fixed.length} file(s) via AI`);
|
|
8796
|
+
console.log(chalk15.green(` \u2714 TypeScript: fixed ${aiResult.fixed.length} file(s) via AI`));
|
|
8797
|
+
}
|
|
8798
|
+
if (aiResult.failed.length > 0) {
|
|
8799
|
+
for (const e of aiResult.failed.slice(0, 10)) {
|
|
8800
|
+
remaining.push(`${e.file}(${e.line}): [${e.code}] ${e.message.split("\n")[0]}`);
|
|
8801
|
+
}
|
|
8802
|
+
if (aiResult.failed.length > 10) {
|
|
8803
|
+
remaining.push(`... and ${aiResult.failed.length - 10} more TypeScript errors`);
|
|
8804
|
+
}
|
|
8805
|
+
console.log(chalk15.yellow(` \u26A0 TypeScript: ${aiResult.failed.length} error(s) remaining`));
|
|
8806
|
+
}
|
|
8807
|
+
} else {
|
|
8808
|
+
for (const e of detResult.remaining.slice(0, 10)) {
|
|
8809
|
+
remaining.push(`${e.file}(${e.line}): [${e.code}] ${e.message.split("\n")[0]}`);
|
|
8810
|
+
}
|
|
8811
|
+
if (detResult.remaining.length > 10) {
|
|
8812
|
+
remaining.push(`... and ${detResult.remaining.length - 10} more TypeScript errors`);
|
|
8813
|
+
}
|
|
8814
|
+
console.log(
|
|
8815
|
+
chalk15.yellow(
|
|
8816
|
+
` \u26A0 TypeScript: ${detResult.remaining.length} error(s) remaining. Configure API key for auto-fix.`
|
|
8817
|
+
)
|
|
8818
|
+
);
|
|
8819
|
+
}
|
|
8820
|
+
}
|
|
8821
|
+
const finalErrors = runTscCheck(projectRoot);
|
|
8822
|
+
if (finalErrors.length === 0) {
|
|
8823
|
+
console.log(chalk15.green(" \u2714 TypeScript compilation now clean"));
|
|
8824
|
+
}
|
|
8767
8825
|
}
|
|
8768
|
-
if (errorLines.length > 10) remaining.push(`... and ${errorLines.length - 10} more TypeScript errors`);
|
|
8769
|
-
console.log(chalk15.yellow(` \u26A0 TypeScript: ${errorLines.length} error(s)`));
|
|
8770
8826
|
}
|
|
8827
|
+
} catch (err) {
|
|
8828
|
+
console.log(
|
|
8829
|
+
chalk15.yellow(` \u26A0 TypeScript check skipped: ${err instanceof Error ? err.message : "unknown error"}`)
|
|
8830
|
+
);
|
|
8771
8831
|
}
|
|
8772
8832
|
if (fixes.length === 0 && totalErrors === 0 && totalWarnings === 0 && remaining.length === 0) {
|
|
8773
8833
|
console.log(chalk15.green("\n \u2705 Everything looks good \u2014 no issues found\n"));
|
|
@@ -8798,7 +8858,7 @@ async function fixCommand(opts = {}) {
|
|
|
8798
8858
|
// src/commands/check.ts
|
|
8799
8859
|
import chalk16 from "chalk";
|
|
8800
8860
|
import { resolve as resolve9 } from "path";
|
|
8801
|
-
import { readdirSync as readdirSync8, readFileSync as
|
|
8861
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync3, existsSync as existsSync15 } from "fs";
|
|
8802
8862
|
import { loadManifest as loadManifest10 } from "@getcoherent/core";
|
|
8803
8863
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
8804
8864
|
function findTsxFiles(dir) {
|
|
@@ -8855,7 +8915,7 @@ async function checkCommand(opts = {}) {
|
|
|
8855
8915
|
"NATIVE_TABLE"
|
|
8856
8916
|
]);
|
|
8857
8917
|
for (const file of files) {
|
|
8858
|
-
const code =
|
|
8918
|
+
const code = readFileSync11(file, "utf-8");
|
|
8859
8919
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8860
8920
|
const baseName = file.split("/").pop() || "";
|
|
8861
8921
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -8897,7 +8957,7 @@ async function checkCommand(opts = {}) {
|
|
|
8897
8957
|
routeSet.add("/");
|
|
8898
8958
|
routeSet.add("#");
|
|
8899
8959
|
for (const file of files) {
|
|
8900
|
-
const code =
|
|
8960
|
+
const code = readFileSync11(file, "utf-8");
|
|
8901
8961
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8902
8962
|
const lines = code.split("\n");
|
|
8903
8963
|
const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
|
|
@@ -8930,7 +8990,7 @@ async function checkCommand(opts = {}) {
|
|
|
8930
8990
|
if (manifest.shared.length > 0) {
|
|
8931
8991
|
for (const entry of manifest.shared) {
|
|
8932
8992
|
const fullPath = resolve9(project.root, entry.file);
|
|
8933
|
-
if (!
|
|
8993
|
+
if (!existsSync15(fullPath)) {
|
|
8934
8994
|
result.pages.withErrors++;
|
|
8935
8995
|
if (!opts.json) console.log(chalk16.red(`
|
|
8936
8996
|
\u2717 Missing shared component file: ${entry.id} (${entry.file})`));
|
|
@@ -8955,7 +9015,7 @@ async function checkCommand(opts = {}) {
|
|
|
8955
9015
|
let _nameMismatch = 0;
|
|
8956
9016
|
for (const entry of manifest.shared) {
|
|
8957
9017
|
const filePath = resolve9(projectRoot, entry.file);
|
|
8958
|
-
const fileExists =
|
|
9018
|
+
const fileExists = existsSync15(filePath);
|
|
8959
9019
|
if (!fileExists) {
|
|
8960
9020
|
_orphaned++;
|
|
8961
9021
|
if (!opts.json) {
|
|
@@ -8965,7 +9025,7 @@ async function checkCommand(opts = {}) {
|
|
|
8965
9025
|
continue;
|
|
8966
9026
|
}
|
|
8967
9027
|
try {
|
|
8968
|
-
const code =
|
|
9028
|
+
const code = readFileSync11(filePath, "utf-8");
|
|
8969
9029
|
const actualExports = extractExportedComponentNames(code);
|
|
8970
9030
|
if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
|
|
8971
9031
|
_nameMismatch++;
|
|
@@ -9033,7 +9093,7 @@ async function checkCommand(opts = {}) {
|
|
|
9033
9093
|
id: e.id,
|
|
9034
9094
|
name: e.name,
|
|
9035
9095
|
type: e.type,
|
|
9036
|
-
status:
|
|
9096
|
+
status: existsSync15(resolve9(projectRoot, e.file)) ? "ok" : "unused",
|
|
9037
9097
|
message: "",
|
|
9038
9098
|
suggestions: void 0
|
|
9039
9099
|
}))
|
|
@@ -9047,11 +9107,11 @@ async function checkCommand(opts = {}) {
|
|
|
9047
9107
|
const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-HGNEY3W3.js");
|
|
9048
9108
|
const manifest = await loadManifest10(projectRoot);
|
|
9049
9109
|
const appDir = resolve9(projectRoot, "app");
|
|
9050
|
-
const pageFiles =
|
|
9110
|
+
const pageFiles = existsSync15(appDir) ? findTsxFiles(appDir) : [];
|
|
9051
9111
|
if (manifest.shared.length > 0 && pageFiles.length > 0) {
|
|
9052
9112
|
const reuseWarnings = [];
|
|
9053
9113
|
for (const file of pageFiles) {
|
|
9054
|
-
const code =
|
|
9114
|
+
const code = readFileSync11(file, "utf-8");
|
|
9055
9115
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
9056
9116
|
const route = "/" + relativePath.replace(/^app\//, "").replace(/\/page\.tsx$/, "").replace(/^\(.*?\)\//, "");
|
|
9057
9117
|
const pageType = inferPageTypeFromRoute2(route);
|
|
@@ -9162,12 +9222,12 @@ import {
|
|
|
9162
9222
|
generateSharedComponent as generateSharedComponent4,
|
|
9163
9223
|
integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2
|
|
9164
9224
|
} from "@getcoherent/core";
|
|
9165
|
-
import { existsSync as
|
|
9225
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9166
9226
|
import { resolve as resolve10 } from "path";
|
|
9167
9227
|
|
|
9168
9228
|
// src/utils/ds-files.ts
|
|
9169
9229
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
9170
|
-
import { join as
|
|
9230
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
9171
9231
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
9172
9232
|
var SHARED_DS_KEYS = [
|
|
9173
9233
|
"app/design-system/shared/page.tsx",
|
|
@@ -9181,7 +9241,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
9181
9241
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
|
|
9182
9242
|
const written = [];
|
|
9183
9243
|
for (const [relativePath, content] of toWrite) {
|
|
9184
|
-
const fullPath =
|
|
9244
|
+
const fullPath = join12(projectRoot, relativePath);
|
|
9185
9245
|
await mkdir3(dirname5(fullPath), { recursive: true });
|
|
9186
9246
|
await writeFile4(fullPath, content, "utf-8");
|
|
9187
9247
|
written.push(relativePath);
|
|
@@ -9317,7 +9377,7 @@ function createComponentsCommand() {
|
|
|
9317
9377
|
if (updated) console.log(chalk22.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
9318
9378
|
}
|
|
9319
9379
|
const sharedPagePath = resolve10(project.root, "app/design-system/shared/page.tsx");
|
|
9320
|
-
if (!
|
|
9380
|
+
if (!existsSync16(sharedPagePath)) {
|
|
9321
9381
|
try {
|
|
9322
9382
|
const dsm = new DesignSystemManager10(project.configPath);
|
|
9323
9383
|
await dsm.load();
|
|
@@ -9343,8 +9403,8 @@ function createComponentsCommand() {
|
|
|
9343
9403
|
import chalk23 from "chalk";
|
|
9344
9404
|
import ora6 from "ora";
|
|
9345
9405
|
import { writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
9346
|
-
import { resolve as resolve11, join as
|
|
9347
|
-
import { existsSync as
|
|
9406
|
+
import { resolve as resolve11, join as join13, dirname as dirname6 } from "path";
|
|
9407
|
+
import { existsSync as existsSync17 } from "fs";
|
|
9348
9408
|
import {
|
|
9349
9409
|
FigmaClient,
|
|
9350
9410
|
parseFigmaFileResponse,
|
|
@@ -9491,7 +9551,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
9491
9551
|
stats.filesWritten.push(filePath);
|
|
9492
9552
|
return;
|
|
9493
9553
|
}
|
|
9494
|
-
const fullPath =
|
|
9554
|
+
const fullPath = join13(projectRoot, filePath);
|
|
9495
9555
|
await mkdir4(dirname6(fullPath), { recursive: true });
|
|
9496
9556
|
await writeFile5(fullPath, content, "utf-8");
|
|
9497
9557
|
stats.filesWritten.push(filePath);
|
|
@@ -9593,7 +9653,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
9593
9653
|
spinner.start("Updating design-system.config.ts...");
|
|
9594
9654
|
const configPath = resolve11(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
|
|
9595
9655
|
const dsm = new DesignSystemManager11(configPath);
|
|
9596
|
-
if (
|
|
9656
|
+
if (existsSync17(configPath)) {
|
|
9597
9657
|
await dsm.load();
|
|
9598
9658
|
const existing = dsm.getConfig();
|
|
9599
9659
|
dsm.updateConfig({
|
|
@@ -9622,8 +9682,8 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
9622
9682
|
stats.configUpdated = true;
|
|
9623
9683
|
spinner.succeed("design-system.config.ts updated");
|
|
9624
9684
|
spinner.start("Ensuring root layout...");
|
|
9625
|
-
const layoutPath =
|
|
9626
|
-
if (!
|
|
9685
|
+
const layoutPath = join13(projectRoot, "app/layout.tsx");
|
|
9686
|
+
if (!existsSync17(layoutPath)) {
|
|
9627
9687
|
await mkdir4(dirname6(layoutPath), { recursive: true });
|
|
9628
9688
|
await writeFile5(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
9629
9689
|
stats.filesWritten.push("app/layout.tsx");
|
|
@@ -9713,8 +9773,8 @@ async function dsRegenerateCommand() {
|
|
|
9713
9773
|
// src/commands/update.ts
|
|
9714
9774
|
import chalk25 from "chalk";
|
|
9715
9775
|
import ora8 from "ora";
|
|
9716
|
-
import { readFileSync as
|
|
9717
|
-
import { join as
|
|
9776
|
+
import { readFileSync as readFileSync12, existsSync as existsSync18 } from "fs";
|
|
9777
|
+
import { join as join14 } from "path";
|
|
9718
9778
|
import { DesignSystemManager as DesignSystemManager13, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
9719
9779
|
|
|
9720
9780
|
// src/utils/migrations.ts
|
|
@@ -9883,20 +9943,20 @@ var EXPECTED_CSS_VARS = [
|
|
|
9883
9943
|
"--sidebar-ring"
|
|
9884
9944
|
];
|
|
9885
9945
|
function checkMissingCssVars(projectRoot) {
|
|
9886
|
-
const globalsPath =
|
|
9887
|
-
if (!
|
|
9946
|
+
const globalsPath = join14(projectRoot, "app", "globals.css");
|
|
9947
|
+
if (!existsSync18(globalsPath)) return [];
|
|
9888
9948
|
try {
|
|
9889
|
-
const content =
|
|
9949
|
+
const content = readFileSync12(globalsPath, "utf-8");
|
|
9890
9950
|
return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
|
|
9891
9951
|
} catch {
|
|
9892
9952
|
return [];
|
|
9893
9953
|
}
|
|
9894
9954
|
}
|
|
9895
9955
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
9896
|
-
const globalsPath =
|
|
9897
|
-
if (!
|
|
9898
|
-
const { writeFileSync:
|
|
9899
|
-
let content =
|
|
9956
|
+
const globalsPath = join14(projectRoot, "app", "globals.css");
|
|
9957
|
+
if (!existsSync18(globalsPath) || missingVars.length === 0) return;
|
|
9958
|
+
const { writeFileSync: writeFileSync11 } = __require("fs");
|
|
9959
|
+
let content = readFileSync12(globalsPath, "utf-8");
|
|
9900
9960
|
const defaultValues = {
|
|
9901
9961
|
"--chart-1": "220 70% 50%",
|
|
9902
9962
|
"--chart-2": "160 60% 45%",
|
|
@@ -9924,7 +9984,7 @@ function patchGlobalsCss(projectRoot, missingVars) {
|
|
|
9924
9984
|
const lightSectionEnd = content.indexOf("}");
|
|
9925
9985
|
if (lightSectionEnd > 0) {
|
|
9926
9986
|
content = content.slice(0, lightSectionEnd) + "\n" + injection + "\n" + content.slice(lightSectionEnd);
|
|
9927
|
-
|
|
9987
|
+
writeFileSync11(globalsPath, content, "utf-8");
|
|
9928
9988
|
}
|
|
9929
9989
|
}
|
|
9930
9990
|
|
|
@@ -9974,26 +10034,26 @@ async function undoCommand(options) {
|
|
|
9974
10034
|
// src/commands/sync.ts
|
|
9975
10035
|
import chalk27 from "chalk";
|
|
9976
10036
|
import ora9 from "ora";
|
|
9977
|
-
import { existsSync as
|
|
9978
|
-
import { join as
|
|
10037
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
|
|
10038
|
+
import { join as join15, relative as relative6, dirname as dirname7 } from "path";
|
|
9979
10039
|
import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
|
|
9980
10040
|
import { DesignSystemManager as DesignSystemManager14 } from "@getcoherent/core";
|
|
9981
10041
|
import { loadManifest as loadManifest12, saveManifest as saveManifest6, findSharedComponent } from "@getcoherent/core";
|
|
9982
10042
|
function extractTokensFromProject(projectRoot) {
|
|
9983
10043
|
const lightColors = {};
|
|
9984
10044
|
const darkColors = {};
|
|
9985
|
-
const globalsPath =
|
|
9986
|
-
if (
|
|
9987
|
-
const css =
|
|
10045
|
+
const globalsPath = join15(projectRoot, "app", "globals.css");
|
|
10046
|
+
if (existsSync19(globalsPath)) {
|
|
10047
|
+
const css = readFileSync13(globalsPath, "utf-8");
|
|
9988
10048
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
9989
10049
|
if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
|
|
9990
10050
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
9991
10051
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
9992
10052
|
}
|
|
9993
|
-
const layoutPath =
|
|
10053
|
+
const layoutPath = join15(projectRoot, "app", "layout.tsx");
|
|
9994
10054
|
let layoutCode = "";
|
|
9995
|
-
if (
|
|
9996
|
-
layoutCode =
|
|
10055
|
+
if (existsSync19(layoutPath)) {
|
|
10056
|
+
layoutCode = readFileSync13(layoutPath, "utf-8");
|
|
9997
10057
|
const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
|
|
9998
10058
|
if (rootInline && Object.keys(lightColors).length === 0) {
|
|
9999
10059
|
parseVarsInto(rootInline[1], lightColors);
|
|
@@ -10011,7 +10071,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
10011
10071
|
defaultMode = "dark";
|
|
10012
10072
|
}
|
|
10013
10073
|
let radius;
|
|
10014
|
-
const allCss = [
|
|
10074
|
+
const allCss = [existsSync19(globalsPath) ? readFileSync13(globalsPath, "utf-8") : "", layoutCode].join("\n");
|
|
10015
10075
|
const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
|
|
10016
10076
|
if (radiusMatch) radius = radiusMatch[1].trim();
|
|
10017
10077
|
return {
|
|
@@ -10034,8 +10094,8 @@ function parseVarsInto(block, target) {
|
|
|
10034
10094
|
}
|
|
10035
10095
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
10036
10096
|
const results = [];
|
|
10037
|
-
const componentsDir =
|
|
10038
|
-
if (!
|
|
10097
|
+
const componentsDir = join15(projectRoot, "components");
|
|
10098
|
+
if (!existsSync19(componentsDir)) return results;
|
|
10039
10099
|
const files = [];
|
|
10040
10100
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
10041
10101
|
const fileResults = await Promise.all(
|
|
@@ -10062,7 +10122,7 @@ async function walkForTsx(dir, files, skipDirs) {
|
|
|
10062
10122
|
return;
|
|
10063
10123
|
}
|
|
10064
10124
|
for (const e of entries) {
|
|
10065
|
-
const full =
|
|
10125
|
+
const full = join15(dir, e.name);
|
|
10066
10126
|
if (e.isDirectory()) {
|
|
10067
10127
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
10068
10128
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -10136,7 +10196,7 @@ async function discoverPages(appDir) {
|
|
|
10136
10196
|
return;
|
|
10137
10197
|
}
|
|
10138
10198
|
for (const entry of entries) {
|
|
10139
|
-
const full =
|
|
10199
|
+
const full = join15(dir, entry.name);
|
|
10140
10200
|
if (entry.isDirectory()) {
|
|
10141
10201
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
10142
10202
|
if (entry.name.startsWith(".")) continue;
|
|
@@ -10225,8 +10285,8 @@ async function syncCommand(options = {}) {
|
|
|
10225
10285
|
if (dryRun) console.log(chalk27.yellow(" [dry-run] No files will be written\n"));
|
|
10226
10286
|
const spinner = ora9("Scanning project files...").start();
|
|
10227
10287
|
try {
|
|
10228
|
-
const appDir =
|
|
10229
|
-
if (!
|
|
10288
|
+
const appDir = join15(project.root, "app");
|
|
10289
|
+
if (!existsSync19(appDir)) {
|
|
10230
10290
|
spinner.fail("No app/ directory found");
|
|
10231
10291
|
process.exit(1);
|
|
10232
10292
|
}
|
|
@@ -10456,53 +10516,53 @@ async function syncCommand(options = {}) {
|
|
|
10456
10516
|
// src/commands/migrate.ts
|
|
10457
10517
|
import chalk28 from "chalk";
|
|
10458
10518
|
import ora10 from "ora";
|
|
10459
|
-
import { existsSync as
|
|
10460
|
-
import { join as
|
|
10519
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync9, readFileSync as readFileSync14, readdirSync as readdirSync9 } from "fs";
|
|
10520
|
+
import { join as join16 } from "path";
|
|
10461
10521
|
function backupDir(projectRoot) {
|
|
10462
10522
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10463
|
-
return
|
|
10523
|
+
return join16(projectRoot, ".coherent", "backups", `pre-migrate-${ts}`);
|
|
10464
10524
|
}
|
|
10465
10525
|
function guardPath(projectRoot) {
|
|
10466
|
-
return
|
|
10526
|
+
return join16(projectRoot, ".coherent", "migration-in-progress");
|
|
10467
10527
|
}
|
|
10468
10528
|
function createBackup2(projectRoot) {
|
|
10469
|
-
const uiDir =
|
|
10529
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10470
10530
|
const dest = backupDir(projectRoot);
|
|
10471
10531
|
mkdirSync8(dest, { recursive: true });
|
|
10472
|
-
if (
|
|
10473
|
-
cpSync(uiDir,
|
|
10532
|
+
if (existsSync20(uiDir)) {
|
|
10533
|
+
cpSync(uiDir, join16(dest, "components-ui"), { recursive: true });
|
|
10474
10534
|
}
|
|
10475
|
-
const configPath =
|
|
10476
|
-
if (
|
|
10477
|
-
cpSync(configPath,
|
|
10535
|
+
const configPath = join16(projectRoot, "design-system.config.ts");
|
|
10536
|
+
if (existsSync20(configPath)) {
|
|
10537
|
+
cpSync(configPath, join16(dest, "design-system.config.ts"));
|
|
10478
10538
|
}
|
|
10479
10539
|
return dest;
|
|
10480
10540
|
}
|
|
10481
10541
|
function setGuard(projectRoot, backupPath) {
|
|
10482
10542
|
const guard = guardPath(projectRoot);
|
|
10483
|
-
mkdirSync8(
|
|
10484
|
-
|
|
10543
|
+
mkdirSync8(join16(projectRoot, ".coherent"), { recursive: true });
|
|
10544
|
+
writeFileSync9(guard, JSON.stringify({ backup: backupPath, startedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
10485
10545
|
}
|
|
10486
10546
|
function clearGuard(projectRoot) {
|
|
10487
10547
|
const guard = guardPath(projectRoot);
|
|
10488
|
-
if (
|
|
10548
|
+
if (existsSync20(guard)) rmSync6(guard);
|
|
10489
10549
|
}
|
|
10490
10550
|
function rollback(projectRoot) {
|
|
10491
10551
|
const guard = guardPath(projectRoot);
|
|
10492
|
-
if (!
|
|
10552
|
+
if (!existsSync20(guard)) return false;
|
|
10493
10553
|
try {
|
|
10494
|
-
const data = JSON.parse(
|
|
10554
|
+
const data = JSON.parse(readFileSync14(guard, "utf-8"));
|
|
10495
10555
|
const backup = data.backup;
|
|
10496
|
-
if (!
|
|
10497
|
-
const uiBackup =
|
|
10498
|
-
const uiDir =
|
|
10499
|
-
if (
|
|
10500
|
-
if (
|
|
10556
|
+
if (!existsSync20(backup)) return false;
|
|
10557
|
+
const uiBackup = join16(backup, "components-ui");
|
|
10558
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10559
|
+
if (existsSync20(uiBackup)) {
|
|
10560
|
+
if (existsSync20(uiDir)) rmSync6(uiDir, { recursive: true });
|
|
10501
10561
|
cpSync(uiBackup, uiDir, { recursive: true });
|
|
10502
10562
|
}
|
|
10503
|
-
const configBackup =
|
|
10504
|
-
const configDest =
|
|
10505
|
-
if (
|
|
10563
|
+
const configBackup = join16(backup, "design-system.config.ts");
|
|
10564
|
+
const configDest = join16(projectRoot, "design-system.config.ts");
|
|
10565
|
+
if (existsSync20(configBackup)) {
|
|
10506
10566
|
cpSync(configBackup, configDest);
|
|
10507
10567
|
}
|
|
10508
10568
|
clearGuard(projectRoot);
|
|
@@ -10530,13 +10590,13 @@ async function migrateAction(options) {
|
|
|
10530
10590
|
return;
|
|
10531
10591
|
}
|
|
10532
10592
|
const guard = guardPath(projectRoot);
|
|
10533
|
-
if (
|
|
10593
|
+
if (existsSync20(guard)) {
|
|
10534
10594
|
console.log(chalk28.yellow("A migration is already in progress."));
|
|
10535
10595
|
console.log(chalk28.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
|
|
10536
10596
|
return;
|
|
10537
10597
|
}
|
|
10538
|
-
const uiDir =
|
|
10539
|
-
if (!
|
|
10598
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10599
|
+
if (!existsSync20(uiDir)) {
|
|
10540
10600
|
console.log(chalk28.yellow("No components/ui directory found. Nothing to migrate."));
|
|
10541
10601
|
return;
|
|
10542
10602
|
}
|
|
@@ -10562,8 +10622,8 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
10562
10622
|
setGuard(projectRoot, backup);
|
|
10563
10623
|
try {
|
|
10564
10624
|
for (const id of migratable) {
|
|
10565
|
-
const filePath =
|
|
10566
|
-
if (
|
|
10625
|
+
const filePath = join16(uiDir, `${id}.tsx`);
|
|
10626
|
+
if (existsSync20(filePath)) rmSync6(filePath);
|
|
10567
10627
|
}
|
|
10568
10628
|
const results = await provider.installBatch(migratable, projectRoot, { force: true });
|
|
10569
10629
|
let migrated = 0;
|
|
@@ -10585,20 +10645,20 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
10585
10645
|
}
|
|
10586
10646
|
|
|
10587
10647
|
// src/utils/update-notifier.ts
|
|
10588
|
-
import { existsSync as
|
|
10589
|
-
import { join as
|
|
10648
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
|
|
10649
|
+
import { join as join17 } from "path";
|
|
10590
10650
|
import { homedir } from "os";
|
|
10591
10651
|
import chalk29 from "chalk";
|
|
10592
10652
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
10593
10653
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
10594
10654
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
10595
|
-
var CACHE_DIR =
|
|
10596
|
-
var CACHE_FILE =
|
|
10655
|
+
var CACHE_DIR = join17(homedir(), ".coherent");
|
|
10656
|
+
var CACHE_FILE = join17(CACHE_DIR, "update-check.json");
|
|
10597
10657
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
10598
10658
|
function readCache() {
|
|
10599
10659
|
try {
|
|
10600
|
-
if (!
|
|
10601
|
-
const raw =
|
|
10660
|
+
if (!existsSync21(CACHE_FILE)) return null;
|
|
10661
|
+
const raw = readFileSync15(CACHE_FILE, "utf-8");
|
|
10602
10662
|
return JSON.parse(raw);
|
|
10603
10663
|
} catch (e) {
|
|
10604
10664
|
if (DEBUG5) console.error("Failed to read update cache:", e);
|
|
@@ -10607,8 +10667,8 @@ function readCache() {
|
|
|
10607
10667
|
}
|
|
10608
10668
|
function writeCache(data) {
|
|
10609
10669
|
try {
|
|
10610
|
-
if (!
|
|
10611
|
-
|
|
10670
|
+
if (!existsSync21(CACHE_DIR)) mkdirSync9(CACHE_DIR, { recursive: true });
|
|
10671
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
10612
10672
|
} catch (e) {
|
|
10613
10673
|
if (DEBUG5) console.error("Failed to write update cache:", e);
|
|
10614
10674
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runTscCheck
|
|
3
|
+
} from "./chunk-XPBD3L7V.js";
|
|
4
|
+
import {
|
|
5
|
+
safeWrite
|
|
6
|
+
} from "./chunk-U6M76BKY.js";
|
|
7
|
+
import "./chunk-3RG5ZIWI.js";
|
|
8
|
+
|
|
9
|
+
// src/utils/tsc-ai-fix.ts
|
|
10
|
+
import { readFileSync } from "fs";
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
var MAX_AI_FILES = 5;
|
|
13
|
+
async function applyAiFixes(errors, projectRoot, backups, aiProvider) {
|
|
14
|
+
if (!aiProvider?.editPageCode) {
|
|
15
|
+
return { fixed: [], failed: errors };
|
|
16
|
+
}
|
|
17
|
+
const fileErrors = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const err of errors) {
|
|
19
|
+
const list = fileErrors.get(err.file) || [];
|
|
20
|
+
list.push(err);
|
|
21
|
+
fileErrors.set(err.file, list);
|
|
22
|
+
}
|
|
23
|
+
const fixed = [];
|
|
24
|
+
const failed = [];
|
|
25
|
+
let filesProcessed = 0;
|
|
26
|
+
for (const [file, errs] of fileErrors) {
|
|
27
|
+
filesProcessed++;
|
|
28
|
+
if (filesProcessed > MAX_AI_FILES) {
|
|
29
|
+
failed.push(...errs);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const absPath = resolve(projectRoot, file);
|
|
33
|
+
let code;
|
|
34
|
+
try {
|
|
35
|
+
code = readFileSync(absPath, "utf-8");
|
|
36
|
+
} catch {
|
|
37
|
+
failed.push(...errs);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const relatedContext = gatherRelatedContext(errs, projectRoot);
|
|
41
|
+
const errorList = errs.map((e) => `Line ${e.line}: [${e.code}] ${e.message}`).join("\n");
|
|
42
|
+
const instruction = [
|
|
43
|
+
"Fix these TypeScript compilation errors:",
|
|
44
|
+
errorList,
|
|
45
|
+
"",
|
|
46
|
+
relatedContext ? `Reference interfaces (DO NOT modify these):
|
|
47
|
+
${relatedContext}` : "",
|
|
48
|
+
"",
|
|
49
|
+
"Rules:",
|
|
50
|
+
"- Fix the data/props to match the expected types",
|
|
51
|
+
"- Do NOT change component interfaces or imports from shared components",
|
|
52
|
+
"- Keep all existing functionality intact"
|
|
53
|
+
].filter(Boolean).join("\n");
|
|
54
|
+
try {
|
|
55
|
+
const fixedCode = await aiProvider.editPageCode(code, instruction, file);
|
|
56
|
+
if (!fixedCode || fixedCode.length < 50) {
|
|
57
|
+
failed.push(...errs);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const { ok } = safeWrite(absPath, fixedCode, projectRoot, backups);
|
|
61
|
+
if (!ok) {
|
|
62
|
+
failed.push(...errs);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const afterErrors = runTscCheck(projectRoot).filter((e) => e.file === file);
|
|
66
|
+
if (afterErrors.length >= errs.length) {
|
|
67
|
+
const original = backups.get(absPath);
|
|
68
|
+
if (original) safeWrite(absPath, original, projectRoot, backups);
|
|
69
|
+
failed.push(...errs);
|
|
70
|
+
} else {
|
|
71
|
+
fixed.push(file);
|
|
72
|
+
if (afterErrors.length > 0) failed.push(...afterErrors);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
failed.push(...errs);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { fixed, failed };
|
|
79
|
+
}
|
|
80
|
+
function gatherRelatedContext(errors, projectRoot) {
|
|
81
|
+
const relatedFiles = /* @__PURE__ */ new Set();
|
|
82
|
+
for (const err of errors) {
|
|
83
|
+
for (const f of err.relatedFiles) relatedFiles.add(f);
|
|
84
|
+
}
|
|
85
|
+
const parts = [];
|
|
86
|
+
for (const file of relatedFiles) {
|
|
87
|
+
try {
|
|
88
|
+
const content = readFileSync(resolve(projectRoot, file), "utf-8");
|
|
89
|
+
parts.push(`// --- ${file} ---
|
|
90
|
+
${content}`);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return parts.join("\n\n");
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
applyAiFixes
|
|
98
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyDeterministicFixes,
|
|
3
|
+
fixFieldRename,
|
|
4
|
+
fixMissingEventHandler,
|
|
5
|
+
fixUnionType,
|
|
6
|
+
runTscCheck
|
|
7
|
+
} from "./chunk-XPBD3L7V.js";
|
|
8
|
+
import "./chunk-U6M76BKY.js";
|
|
9
|
+
import "./chunk-3RG5ZIWI.js";
|
|
10
|
+
export {
|
|
11
|
+
applyDeterministicFixes,
|
|
12
|
+
fixFieldRename,
|
|
13
|
+
fixMissingEventHandler,
|
|
14
|
+
fixUnionType,
|
|
15
|
+
runTscCheck
|
|
16
|
+
};
|