@getcoherent/cli 0.6.46 → 0.6.48
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 +230 -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,65 @@ 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
|
+
let bestSnapshot = codeToWrite;
|
|
5032
|
+
const detResult = await applyDeterministicFixes(tscErrors, projectRoot, tscBackups);
|
|
5033
|
+
const bestErrorCount = detResult.remaining.length;
|
|
5034
|
+
if (detResult.fixed.length > 0) {
|
|
5035
|
+
codeToWrite = await readFile(filePath);
|
|
5036
|
+
bestSnapshot = codeToWrite;
|
|
5037
|
+
console.log(
|
|
5038
|
+
chalk8.green(` \u2714 Fixed ${tscErrors.length - detResult.remaining.length} TypeScript error(s)`)
|
|
5039
|
+
);
|
|
5040
|
+
}
|
|
5041
|
+
if (detResult.remaining.length > 0 && aiProvider) {
|
|
5042
|
+
try {
|
|
5043
|
+
const ai = await createAIProvider(aiProvider);
|
|
5044
|
+
if (ai.editPageCode) {
|
|
5045
|
+
const errorList = detResult.remaining.map((e) => `Line ${e.line}: [${e.code}] ${e.message.split("\n")[0]}`).join("\n");
|
|
5046
|
+
const tscFixed = await ai.editPageCode(
|
|
5047
|
+
codeToWrite,
|
|
5048
|
+
`Fix these TypeScript errors:
|
|
5049
|
+
${errorList}
|
|
5050
|
+
|
|
5051
|
+
Keep all existing functionality intact.`,
|
|
5052
|
+
page.name || page.id || "Page"
|
|
5053
|
+
);
|
|
5054
|
+
if (tscFixed && tscFixed.length > 100) {
|
|
5055
|
+
const { code: reFixed } = await autoFixCode(tscFixed, autoFixCtx);
|
|
5056
|
+
await writeFile(filePath, reFixed);
|
|
5057
|
+
const afterErrors = runTscCheck(projectRoot).filter((e) => e.file === relPath);
|
|
5058
|
+
if (afterErrors.length > bestErrorCount) {
|
|
5059
|
+
await writeFile(filePath, bestSnapshot);
|
|
5060
|
+
codeToWrite = bestSnapshot;
|
|
5061
|
+
console.log(chalk8.yellow(` \u26A0 AI fix regressed TypeScript errors. Reverted to best version.`));
|
|
5062
|
+
} else {
|
|
5063
|
+
codeToWrite = reFixed;
|
|
5064
|
+
console.log(
|
|
5065
|
+
chalk8.green(
|
|
5066
|
+
` \u2714 Fixed ${detResult.remaining.length - afterErrors.length} TypeScript error(s) via AI`
|
|
5067
|
+
)
|
|
5068
|
+
);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
} catch (tscAiErr) {
|
|
5073
|
+
console.log(
|
|
5074
|
+
chalk8.dim(` \u26A0 AI tsc fix skipped: ${tscAiErr instanceof Error ? tscAiErr.message : "unknown"}`)
|
|
5075
|
+
);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
} catch (tscErr) {
|
|
5080
|
+
console.log(
|
|
5081
|
+
chalk8.dim(` \u26A0 TypeScript check skipped: ${tscErr instanceof Error ? tscErr.message : "unknown"}`)
|
|
5082
|
+
);
|
|
5083
|
+
}
|
|
5021
5084
|
const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
|
|
5022
5085
|
if (pageIdx !== -1) {
|
|
5023
5086
|
const cfg = dsm.getConfig();
|
|
@@ -8084,8 +8147,8 @@ async function regenerateDocsCommand() {
|
|
|
8084
8147
|
|
|
8085
8148
|
// src/commands/fix.ts
|
|
8086
8149
|
import chalk15 from "chalk";
|
|
8087
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
8088
|
-
import { resolve as resolve8, join as
|
|
8150
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync10, existsSync as existsSync14, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
8151
|
+
import { resolve as resolve8, join as join11, relative as relative5, basename as basename3 } from "path";
|
|
8089
8152
|
import {
|
|
8090
8153
|
DesignSystemManager as DesignSystemManager9,
|
|
8091
8154
|
ComponentManager as ComponentManager5,
|
|
@@ -8094,54 +8157,6 @@ import {
|
|
|
8094
8157
|
loadManifest as loadManifest9,
|
|
8095
8158
|
saveManifest as saveManifest5
|
|
8096
8159
|
} 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
8160
|
function extractComponentIdsFromCode2(code) {
|
|
8146
8161
|
const ids = /* @__PURE__ */ new Set();
|
|
8147
8162
|
const allMatches = code.matchAll(/@\/components\/((?:ui\/)?[a-z0-9-]+)/g);
|
|
@@ -8159,7 +8174,7 @@ function listTsxFiles(dir) {
|
|
|
8159
8174
|
try {
|
|
8160
8175
|
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
8161
8176
|
for (const e of entries) {
|
|
8162
|
-
const full =
|
|
8177
|
+
const full = join11(dir, e.name);
|
|
8163
8178
|
if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
|
|
8164
8179
|
files.push(...listTsxFiles(full));
|
|
8165
8180
|
} else if (e.isFile() && e.name.endsWith(".tsx")) {
|
|
@@ -8190,8 +8205,8 @@ async function fixCommand(opts = {}) {
|
|
|
8190
8205
|
console.log(chalk15.cyan("\ncoherent fix\n"));
|
|
8191
8206
|
}
|
|
8192
8207
|
if (!skipCache) {
|
|
8193
|
-
const nextDir =
|
|
8194
|
-
if (
|
|
8208
|
+
const nextDir = join11(projectRoot, ".next");
|
|
8209
|
+
if (existsSync14(nextDir)) {
|
|
8195
8210
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
8196
8211
|
fixes.push("Cleared build cache");
|
|
8197
8212
|
console.log(chalk15.green(" \u2714 Cleared build cache"));
|
|
@@ -8218,7 +8233,7 @@ async function fixCommand(opts = {}) {
|
|
|
8218
8233
|
const componentsTsxFiles = listTsxFiles(resolve8(projectRoot, "components"));
|
|
8219
8234
|
const allComponentIds = /* @__PURE__ */ new Set();
|
|
8220
8235
|
for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
|
|
8221
|
-
const content =
|
|
8236
|
+
const content = readFileSync10(file, "utf-8");
|
|
8222
8237
|
extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
|
|
8223
8238
|
}
|
|
8224
8239
|
let dsm = null;
|
|
@@ -8238,7 +8253,7 @@ async function fixCommand(opts = {}) {
|
|
|
8238
8253
|
} else {
|
|
8239
8254
|
const fileName = toKebabCase(id) + ".tsx";
|
|
8240
8255
|
const filePath = resolve8(projectRoot, "components", "ui", fileName);
|
|
8241
|
-
if (!
|
|
8256
|
+
if (!existsSync14(filePath)) missingFiles.push(id);
|
|
8242
8257
|
}
|
|
8243
8258
|
}
|
|
8244
8259
|
const provider = getComponentProvider();
|
|
@@ -8287,7 +8302,7 @@ async function fixCommand(opts = {}) {
|
|
|
8287
8302
|
}
|
|
8288
8303
|
}
|
|
8289
8304
|
}
|
|
8290
|
-
if (!dsm &&
|
|
8305
|
+
if (!dsm && existsSync14(project.configPath)) {
|
|
8291
8306
|
dsm = new DesignSystemManager9(project.configPath);
|
|
8292
8307
|
await dsm.load();
|
|
8293
8308
|
}
|
|
@@ -8296,8 +8311,8 @@ async function fixCommand(opts = {}) {
|
|
|
8296
8311
|
let derivedName = null;
|
|
8297
8312
|
try {
|
|
8298
8313
|
const pkgPath = resolve8(projectRoot, "package.json");
|
|
8299
|
-
if (
|
|
8300
|
-
const pkg = JSON.parse(
|
|
8314
|
+
if (existsSync14(pkgPath)) {
|
|
8315
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
8301
8316
|
if (typeof pkg.name === "string" && pkg.name) {
|
|
8302
8317
|
derivedName = toTitleCase2(pkg.name);
|
|
8303
8318
|
}
|
|
@@ -8334,8 +8349,8 @@ async function fixCommand(opts = {}) {
|
|
|
8334
8349
|
const configName = dsm.getConfig().name;
|
|
8335
8350
|
if (configName && configName !== "My App") {
|
|
8336
8351
|
const appLayoutPath = resolve8(projectRoot, "app", "(app)", "layout.tsx");
|
|
8337
|
-
if (
|
|
8338
|
-
let appLayoutCode =
|
|
8352
|
+
if (existsSync14(appLayoutPath)) {
|
|
8353
|
+
let appLayoutCode = readFileSync10(appLayoutPath, "utf-8");
|
|
8339
8354
|
if (appLayoutCode.includes("My App")) {
|
|
8340
8355
|
appLayoutCode = appLayoutCode.replace(/My App/g, configName);
|
|
8341
8356
|
if (!dryRun) {
|
|
@@ -8353,11 +8368,11 @@ async function fixCommand(opts = {}) {
|
|
|
8353
8368
|
}
|
|
8354
8369
|
}
|
|
8355
8370
|
const sharedDir = resolve8(projectRoot, "components", "shared");
|
|
8356
|
-
if (
|
|
8371
|
+
if (existsSync14(sharedDir)) {
|
|
8357
8372
|
try {
|
|
8358
8373
|
for (const f of readdirSync7(sharedDir).filter((n) => n.endsWith(".tsx"))) {
|
|
8359
|
-
const sharedPath =
|
|
8360
|
-
const sharedCode =
|
|
8374
|
+
const sharedPath = join11(sharedDir, f);
|
|
8375
|
+
const sharedCode = readFileSync10(sharedPath, "utf-8");
|
|
8361
8376
|
if (sharedCode.includes("My App")) {
|
|
8362
8377
|
const updated = sharedCode.replace(/My App/g, configName);
|
|
8363
8378
|
if (!dryRun) {
|
|
@@ -8381,7 +8396,7 @@ async function fixCommand(opts = {}) {
|
|
|
8381
8396
|
let syntaxFixed = 0;
|
|
8382
8397
|
for (const file of userTsxFiles) {
|
|
8383
8398
|
try {
|
|
8384
|
-
const content =
|
|
8399
|
+
const content = readFileSync10(file, "utf-8");
|
|
8385
8400
|
const fixed = fixUnescapedLtInJsx(
|
|
8386
8401
|
fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
|
|
8387
8402
|
);
|
|
@@ -8424,7 +8439,7 @@ async function fixCommand(opts = {}) {
|
|
|
8424
8439
|
console.log(chalk15.green(` \u2714 Verified group layouts: ${layoutTypes}`));
|
|
8425
8440
|
const hasSidebar = plan.groups.some((g) => g.layout === "sidebar" || g.layout === "both");
|
|
8426
8441
|
const sidebarPath = resolve8(projectRoot, "components", "shared", "sidebar.tsx");
|
|
8427
|
-
if (hasSidebar && !
|
|
8442
|
+
if (hasSidebar && !existsSync14(sidebarPath) && !dryRun) {
|
|
8428
8443
|
if (!dsm) {
|
|
8429
8444
|
dsm = new DesignSystemManager9(project.configPath);
|
|
8430
8445
|
await dsm.load();
|
|
@@ -8443,8 +8458,8 @@ async function fixCommand(opts = {}) {
|
|
|
8443
8458
|
}
|
|
8444
8459
|
if (hasSidebar && !dryRun) {
|
|
8445
8460
|
const rootLayoutPath = resolve8(projectRoot, "app", "layout.tsx");
|
|
8446
|
-
if (
|
|
8447
|
-
let rootCode =
|
|
8461
|
+
if (existsSync14(rootLayoutPath)) {
|
|
8462
|
+
let rootCode = readFileSync10(rootLayoutPath, "utf-8");
|
|
8448
8463
|
if (rootCode.includes("<Header")) {
|
|
8449
8464
|
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
8465
|
const inner = match.replace(/<\/?ShowWhenNotAuthRoute>/g, "").trim();
|
|
@@ -8462,8 +8477,8 @@ async function fixCommand(opts = {}) {
|
|
|
8462
8477
|
}
|
|
8463
8478
|
}
|
|
8464
8479
|
const publicLayoutPath = resolve8(projectRoot, "app", "(public)", "layout.tsx");
|
|
8465
|
-
const publicExists =
|
|
8466
|
-
const needsPublicLayout = !publicExists || !
|
|
8480
|
+
const publicExists = existsSync14(publicLayoutPath);
|
|
8481
|
+
const needsPublicLayout = !publicExists || !readFileSync10(publicLayoutPath, "utf-8").includes("<Header");
|
|
8467
8482
|
if (needsPublicLayout) {
|
|
8468
8483
|
const { buildPublicLayoutCodeForSidebar } = await import("./code-generator-YSGVHVNN.js");
|
|
8469
8484
|
mkdirSync7(resolve8(projectRoot, "app", "(public)"), { recursive: true });
|
|
@@ -8476,8 +8491,8 @@ async function fixCommand(opts = {}) {
|
|
|
8476
8491
|
}
|
|
8477
8492
|
}
|
|
8478
8493
|
const sidebarComponentPath2 = resolve8(projectRoot, "components", "shared", "sidebar.tsx");
|
|
8479
|
-
if (
|
|
8480
|
-
const existingSidebarCode =
|
|
8494
|
+
if (existsSync14(sidebarComponentPath2)) {
|
|
8495
|
+
const existingSidebarCode = readFileSync10(sidebarComponentPath2, "utf-8");
|
|
8481
8496
|
const sidebarConfigName = dsm?.getConfig().name ?? "";
|
|
8482
8497
|
const hasWrongName = existingSidebarCode.includes("My App") && sidebarConfigName !== "My App";
|
|
8483
8498
|
const hasTrigger = existingSidebarCode.includes("SidebarTrigger");
|
|
@@ -8505,7 +8520,7 @@ async function fixCommand(opts = {}) {
|
|
|
8505
8520
|
}
|
|
8506
8521
|
const rootPagePath = resolve8(projectRoot, "app", "page.tsx");
|
|
8507
8522
|
const publicPagePath = resolve8(projectRoot, "app", "(public)", "page.tsx");
|
|
8508
|
-
if (
|
|
8523
|
+
if (existsSync14(rootPagePath) && !existsSync14(publicPagePath)) {
|
|
8509
8524
|
const { renameSync } = await import("fs");
|
|
8510
8525
|
mkdirSync7(resolve8(projectRoot, "app", "(public)"), { recursive: true });
|
|
8511
8526
|
renameSync(rootPagePath, publicPagePath);
|
|
@@ -8513,7 +8528,7 @@ async function fixCommand(opts = {}) {
|
|
|
8513
8528
|
console.log(chalk15.green(" \u2714 Moved app/page.tsx \u2192 app/(public)/page.tsx (gets Header/Footer)"));
|
|
8514
8529
|
}
|
|
8515
8530
|
const themeTogglePath = resolve8(projectRoot, "components", "shared", "theme-toggle.tsx");
|
|
8516
|
-
if (!
|
|
8531
|
+
if (!existsSync14(themeTogglePath)) {
|
|
8517
8532
|
const { generateThemeToggleCode } = await import("./code-generator-YSGVHVNN.js");
|
|
8518
8533
|
mkdirSync7(resolve8(projectRoot, "components", "shared"), { recursive: true });
|
|
8519
8534
|
const themeResult = safeWrite(themeTogglePath, generateThemeToggleCode(), projectRoot, backups);
|
|
@@ -8530,8 +8545,8 @@ async function fixCommand(opts = {}) {
|
|
|
8530
8545
|
console.log(chalk15.yellow(` \u26A0 Layout repair skipped: ${err instanceof Error ? err.message : "unknown error"}`));
|
|
8531
8546
|
}
|
|
8532
8547
|
const appLayoutRepairPath = resolve8(projectRoot, "app", "(app)", "layout.tsx");
|
|
8533
|
-
if (
|
|
8534
|
-
const appLayoutCode =
|
|
8548
|
+
if (existsSync14(appLayoutRepairPath) && dsm) {
|
|
8549
|
+
const appLayoutCode = readFileSync10(appLayoutRepairPath, "utf-8");
|
|
8535
8550
|
const isMinimal = appLayoutCode.length < 500 && !appLayoutCode.includes("Header") && !appLayoutCode.includes("Footer") && !appLayoutCode.includes("Sidebar") && !appLayoutCode.includes("SidebarProvider") && !appLayoutCode.includes("SidebarTrigger") && !appLayoutCode.includes("Sheet");
|
|
8536
8551
|
const navType = dsm.getConfig().navigation?.type || "header";
|
|
8537
8552
|
if (isMinimal && navType !== "none") {
|
|
@@ -8559,7 +8574,7 @@ async function fixCommand(opts = {}) {
|
|
|
8559
8574
|
const qualityFixDetails = [];
|
|
8560
8575
|
for (const file of allValidationFiles) {
|
|
8561
8576
|
try {
|
|
8562
|
-
const content =
|
|
8577
|
+
const content = readFileSync10(file, "utf-8");
|
|
8563
8578
|
const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
|
|
8564
8579
|
if (autoFixed !== content) {
|
|
8565
8580
|
if (!dryRun) {
|
|
@@ -8594,7 +8609,7 @@ async function fixCommand(opts = {}) {
|
|
|
8594
8609
|
let mockFixed = 0;
|
|
8595
8610
|
for (const file of allValidationFiles) {
|
|
8596
8611
|
try {
|
|
8597
|
-
const content =
|
|
8612
|
+
const content = readFileSync10(file, "utf-8");
|
|
8598
8613
|
const mockIssues = validateMockData(content);
|
|
8599
8614
|
if (mockIssues.length > 0) {
|
|
8600
8615
|
const fixed = applyMockDataFixes(content, mockIssues);
|
|
@@ -8625,7 +8640,7 @@ async function fixCommand(opts = {}) {
|
|
|
8625
8640
|
for (const file of modifiedFiles) {
|
|
8626
8641
|
if (!backups.has(file)) continue;
|
|
8627
8642
|
const before = backups.get(file);
|
|
8628
|
-
const after =
|
|
8643
|
+
const after = readFileSync10(file, "utf-8");
|
|
8629
8644
|
const issues = verifyIncrementalEdit(before, after);
|
|
8630
8645
|
if (issues.length > 0) {
|
|
8631
8646
|
for (const issue of issues) {
|
|
@@ -8638,7 +8653,7 @@ async function fixCommand(opts = {}) {
|
|
|
8638
8653
|
const fileIssues = [];
|
|
8639
8654
|
for (const file of allValidationFiles) {
|
|
8640
8655
|
try {
|
|
8641
|
-
const code =
|
|
8656
|
+
const code = readFileSync10(file, "utf-8");
|
|
8642
8657
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8643
8658
|
const baseName = file.split("/").pop() || "";
|
|
8644
8659
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -8748,26 +8763,64 @@ async function fixCommand(opts = {}) {
|
|
|
8748
8763
|
}
|
|
8749
8764
|
try {
|
|
8750
8765
|
const tsconfigPath = resolve8(projectRoot, "tsconfig.json");
|
|
8751
|
-
if (
|
|
8752
|
-
const {
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8766
|
+
if (existsSync14(tsconfigPath)) {
|
|
8767
|
+
const { runTscCheck, applyDeterministicFixes } = await import("./tsc-autofix-S5PKMFSC.js");
|
|
8768
|
+
const { applyAiFixes } = await import("./tsc-ai-fix-O3EMRWV2.js");
|
|
8769
|
+
const tscErrors = runTscCheck(projectRoot);
|
|
8770
|
+
if (tscErrors.length === 0) {
|
|
8771
|
+
fixes.push("TypeScript compilation clean");
|
|
8772
|
+
console.log(chalk15.green(" \u2714 TypeScript compilation clean"));
|
|
8773
|
+
} else {
|
|
8774
|
+
const detResult = await applyDeterministicFixes(tscErrors, projectRoot, backups);
|
|
8775
|
+
if (detResult.fixed.length > 0) {
|
|
8776
|
+
fixes.push(`TypeScript: fixed ${detResult.fixed.length} file(s) deterministically`);
|
|
8777
|
+
console.log(chalk15.green(` \u2714 TypeScript: fixed ${detResult.fixed.length} file(s) deterministically`));
|
|
8778
|
+
}
|
|
8779
|
+
if (detResult.remaining.length > 0) {
|
|
8780
|
+
let aiProvider;
|
|
8781
|
+
try {
|
|
8782
|
+
const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
|
|
8783
|
+
aiProvider = await createAIProvider2("auto");
|
|
8784
|
+
} catch {
|
|
8785
|
+
}
|
|
8786
|
+
if (aiProvider?.editPageCode) {
|
|
8787
|
+
console.log(chalk15.dim(` \u23F3 Using AI to fix ${detResult.remaining.length} TypeScript error(s)...`));
|
|
8788
|
+
const aiResult = await applyAiFixes(detResult.remaining, projectRoot, backups, aiProvider);
|
|
8789
|
+
if (aiResult.fixed.length > 0) {
|
|
8790
|
+
fixes.push(`TypeScript: fixed ${aiResult.fixed.length} file(s) via AI`);
|
|
8791
|
+
console.log(chalk15.green(` \u2714 TypeScript: fixed ${aiResult.fixed.length} file(s) via AI`));
|
|
8792
|
+
}
|
|
8793
|
+
if (aiResult.failed.length > 0) {
|
|
8794
|
+
for (const e of aiResult.failed.slice(0, 10)) {
|
|
8795
|
+
remaining.push(`${e.file}(${e.line}): [${e.code}] ${e.message.split("\n")[0]}`);
|
|
8796
|
+
}
|
|
8797
|
+
if (aiResult.failed.length > 10) {
|
|
8798
|
+
remaining.push(`... and ${aiResult.failed.length - 10} more TypeScript errors`);
|
|
8799
|
+
}
|
|
8800
|
+
console.log(chalk15.yellow(` \u26A0 TypeScript: ${aiResult.failed.length} error(s) remaining`));
|
|
8801
|
+
}
|
|
8802
|
+
} else {
|
|
8803
|
+
for (const e of detResult.remaining.slice(0, 10)) {
|
|
8804
|
+
remaining.push(`${e.file}(${e.line}): [${e.code}] ${e.message.split("\n")[0]}`);
|
|
8805
|
+
}
|
|
8806
|
+
if (detResult.remaining.length > 10) {
|
|
8807
|
+
remaining.push(`... and ${detResult.remaining.length - 10} more TypeScript errors`);
|
|
8808
|
+
}
|
|
8809
|
+
console.log(
|
|
8810
|
+
chalk15.yellow(
|
|
8811
|
+
` \u26A0 TypeScript: ${detResult.remaining.length} error(s) remaining. Configure API key for auto-fix.`
|
|
8812
|
+
)
|
|
8813
|
+
);
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
const finalErrors = runTscCheck(projectRoot);
|
|
8817
|
+
if (finalErrors.length === 0) {
|
|
8818
|
+
console.log(chalk15.green(" \u2714 TypeScript compilation now clean"));
|
|
8819
|
+
}
|
|
8767
8820
|
}
|
|
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
8821
|
}
|
|
8822
|
+
} catch (err) {
|
|
8823
|
+
console.log(chalk15.yellow(` \u26A0 TypeScript check skipped: ${err instanceof Error ? err.message : "unknown error"}`));
|
|
8771
8824
|
}
|
|
8772
8825
|
if (fixes.length === 0 && totalErrors === 0 && totalWarnings === 0 && remaining.length === 0) {
|
|
8773
8826
|
console.log(chalk15.green("\n \u2705 Everything looks good \u2014 no issues found\n"));
|
|
@@ -8798,7 +8851,7 @@ async function fixCommand(opts = {}) {
|
|
|
8798
8851
|
// src/commands/check.ts
|
|
8799
8852
|
import chalk16 from "chalk";
|
|
8800
8853
|
import { resolve as resolve9 } from "path";
|
|
8801
|
-
import { readdirSync as readdirSync8, readFileSync as
|
|
8854
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync3, existsSync as existsSync15 } from "fs";
|
|
8802
8855
|
import { loadManifest as loadManifest10 } from "@getcoherent/core";
|
|
8803
8856
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
|
|
8804
8857
|
function findTsxFiles(dir) {
|
|
@@ -8855,7 +8908,7 @@ async function checkCommand(opts = {}) {
|
|
|
8855
8908
|
"NATIVE_TABLE"
|
|
8856
8909
|
]);
|
|
8857
8910
|
for (const file of files) {
|
|
8858
|
-
const code =
|
|
8911
|
+
const code = readFileSync11(file, "utf-8");
|
|
8859
8912
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8860
8913
|
const baseName = file.split("/").pop() || "";
|
|
8861
8914
|
const isAuthPage = relativePath.includes("(auth)");
|
|
@@ -8897,7 +8950,7 @@ async function checkCommand(opts = {}) {
|
|
|
8897
8950
|
routeSet.add("/");
|
|
8898
8951
|
routeSet.add("#");
|
|
8899
8952
|
for (const file of files) {
|
|
8900
|
-
const code =
|
|
8953
|
+
const code = readFileSync11(file, "utf-8");
|
|
8901
8954
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
8902
8955
|
const lines = code.split("\n");
|
|
8903
8956
|
const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
|
|
@@ -8930,7 +8983,7 @@ async function checkCommand(opts = {}) {
|
|
|
8930
8983
|
if (manifest.shared.length > 0) {
|
|
8931
8984
|
for (const entry of manifest.shared) {
|
|
8932
8985
|
const fullPath = resolve9(project.root, entry.file);
|
|
8933
|
-
if (!
|
|
8986
|
+
if (!existsSync15(fullPath)) {
|
|
8934
8987
|
result.pages.withErrors++;
|
|
8935
8988
|
if (!opts.json) console.log(chalk16.red(`
|
|
8936
8989
|
\u2717 Missing shared component file: ${entry.id} (${entry.file})`));
|
|
@@ -8955,7 +9008,7 @@ async function checkCommand(opts = {}) {
|
|
|
8955
9008
|
let _nameMismatch = 0;
|
|
8956
9009
|
for (const entry of manifest.shared) {
|
|
8957
9010
|
const filePath = resolve9(projectRoot, entry.file);
|
|
8958
|
-
const fileExists =
|
|
9011
|
+
const fileExists = existsSync15(filePath);
|
|
8959
9012
|
if (!fileExists) {
|
|
8960
9013
|
_orphaned++;
|
|
8961
9014
|
if (!opts.json) {
|
|
@@ -8965,7 +9018,7 @@ async function checkCommand(opts = {}) {
|
|
|
8965
9018
|
continue;
|
|
8966
9019
|
}
|
|
8967
9020
|
try {
|
|
8968
|
-
const code =
|
|
9021
|
+
const code = readFileSync11(filePath, "utf-8");
|
|
8969
9022
|
const actualExports = extractExportedComponentNames(code);
|
|
8970
9023
|
if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
|
|
8971
9024
|
_nameMismatch++;
|
|
@@ -9033,7 +9086,7 @@ async function checkCommand(opts = {}) {
|
|
|
9033
9086
|
id: e.id,
|
|
9034
9087
|
name: e.name,
|
|
9035
9088
|
type: e.type,
|
|
9036
|
-
status:
|
|
9089
|
+
status: existsSync15(resolve9(projectRoot, e.file)) ? "ok" : "unused",
|
|
9037
9090
|
message: "",
|
|
9038
9091
|
suggestions: void 0
|
|
9039
9092
|
}))
|
|
@@ -9047,11 +9100,11 @@ async function checkCommand(opts = {}) {
|
|
|
9047
9100
|
const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-HGNEY3W3.js");
|
|
9048
9101
|
const manifest = await loadManifest10(projectRoot);
|
|
9049
9102
|
const appDir = resolve9(projectRoot, "app");
|
|
9050
|
-
const pageFiles =
|
|
9103
|
+
const pageFiles = existsSync15(appDir) ? findTsxFiles(appDir) : [];
|
|
9051
9104
|
if (manifest.shared.length > 0 && pageFiles.length > 0) {
|
|
9052
9105
|
const reuseWarnings = [];
|
|
9053
9106
|
for (const file of pageFiles) {
|
|
9054
|
-
const code =
|
|
9107
|
+
const code = readFileSync11(file, "utf-8");
|
|
9055
9108
|
const relativePath = file.replace(projectRoot + "/", "");
|
|
9056
9109
|
const route = "/" + relativePath.replace(/^app\//, "").replace(/\/page\.tsx$/, "").replace(/^\(.*?\)\//, "");
|
|
9057
9110
|
const pageType = inferPageTypeFromRoute2(route);
|
|
@@ -9162,12 +9215,12 @@ import {
|
|
|
9162
9215
|
generateSharedComponent as generateSharedComponent4,
|
|
9163
9216
|
integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2
|
|
9164
9217
|
} from "@getcoherent/core";
|
|
9165
|
-
import { existsSync as
|
|
9218
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9166
9219
|
import { resolve as resolve10 } from "path";
|
|
9167
9220
|
|
|
9168
9221
|
// src/utils/ds-files.ts
|
|
9169
9222
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
9170
|
-
import { join as
|
|
9223
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
9171
9224
|
import { DesignSystemGenerator } from "@getcoherent/core";
|
|
9172
9225
|
var SHARED_DS_KEYS = [
|
|
9173
9226
|
"app/design-system/shared/page.tsx",
|
|
@@ -9181,7 +9234,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
|
|
|
9181
9234
|
const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
|
|
9182
9235
|
const written = [];
|
|
9183
9236
|
for (const [relativePath, content] of toWrite) {
|
|
9184
|
-
const fullPath =
|
|
9237
|
+
const fullPath = join12(projectRoot, relativePath);
|
|
9185
9238
|
await mkdir3(dirname5(fullPath), { recursive: true });
|
|
9186
9239
|
await writeFile4(fullPath, content, "utf-8");
|
|
9187
9240
|
written.push(relativePath);
|
|
@@ -9317,7 +9370,7 @@ function createComponentsCommand() {
|
|
|
9317
9370
|
if (updated) console.log(chalk22.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
9318
9371
|
}
|
|
9319
9372
|
const sharedPagePath = resolve10(project.root, "app/design-system/shared/page.tsx");
|
|
9320
|
-
if (!
|
|
9373
|
+
if (!existsSync16(sharedPagePath)) {
|
|
9321
9374
|
try {
|
|
9322
9375
|
const dsm = new DesignSystemManager10(project.configPath);
|
|
9323
9376
|
await dsm.load();
|
|
@@ -9343,8 +9396,8 @@ function createComponentsCommand() {
|
|
|
9343
9396
|
import chalk23 from "chalk";
|
|
9344
9397
|
import ora6 from "ora";
|
|
9345
9398
|
import { writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
9346
|
-
import { resolve as resolve11, join as
|
|
9347
|
-
import { existsSync as
|
|
9399
|
+
import { resolve as resolve11, join as join13, dirname as dirname6 } from "path";
|
|
9400
|
+
import { existsSync as existsSync17 } from "fs";
|
|
9348
9401
|
import {
|
|
9349
9402
|
FigmaClient,
|
|
9350
9403
|
parseFigmaFileResponse,
|
|
@@ -9491,7 +9544,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
9491
9544
|
stats.filesWritten.push(filePath);
|
|
9492
9545
|
return;
|
|
9493
9546
|
}
|
|
9494
|
-
const fullPath =
|
|
9547
|
+
const fullPath = join13(projectRoot, filePath);
|
|
9495
9548
|
await mkdir4(dirname6(fullPath), { recursive: true });
|
|
9496
9549
|
await writeFile5(fullPath, content, "utf-8");
|
|
9497
9550
|
stats.filesWritten.push(filePath);
|
|
@@ -9593,7 +9646,7 @@ async function importFigmaAction(urlOrKey, opts) {
|
|
|
9593
9646
|
spinner.start("Updating design-system.config.ts...");
|
|
9594
9647
|
const configPath = resolve11(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
|
|
9595
9648
|
const dsm = new DesignSystemManager11(configPath);
|
|
9596
|
-
if (
|
|
9649
|
+
if (existsSync17(configPath)) {
|
|
9597
9650
|
await dsm.load();
|
|
9598
9651
|
const existing = dsm.getConfig();
|
|
9599
9652
|
dsm.updateConfig({
|
|
@@ -9622,8 +9675,8 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
9622
9675
|
stats.configUpdated = true;
|
|
9623
9676
|
spinner.succeed("design-system.config.ts updated");
|
|
9624
9677
|
spinner.start("Ensuring root layout...");
|
|
9625
|
-
const layoutPath =
|
|
9626
|
-
if (!
|
|
9678
|
+
const layoutPath = join13(projectRoot, "app/layout.tsx");
|
|
9679
|
+
if (!existsSync17(layoutPath)) {
|
|
9627
9680
|
await mkdir4(dirname6(layoutPath), { recursive: true });
|
|
9628
9681
|
await writeFile5(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
|
|
9629
9682
|
stats.filesWritten.push("app/layout.tsx");
|
|
@@ -9713,8 +9766,8 @@ async function dsRegenerateCommand() {
|
|
|
9713
9766
|
// src/commands/update.ts
|
|
9714
9767
|
import chalk25 from "chalk";
|
|
9715
9768
|
import ora8 from "ora";
|
|
9716
|
-
import { readFileSync as
|
|
9717
|
-
import { join as
|
|
9769
|
+
import { readFileSync as readFileSync12, existsSync as existsSync18 } from "fs";
|
|
9770
|
+
import { join as join14 } from "path";
|
|
9718
9771
|
import { DesignSystemManager as DesignSystemManager13, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
|
|
9719
9772
|
|
|
9720
9773
|
// src/utils/migrations.ts
|
|
@@ -9883,20 +9936,20 @@ var EXPECTED_CSS_VARS = [
|
|
|
9883
9936
|
"--sidebar-ring"
|
|
9884
9937
|
];
|
|
9885
9938
|
function checkMissingCssVars(projectRoot) {
|
|
9886
|
-
const globalsPath =
|
|
9887
|
-
if (!
|
|
9939
|
+
const globalsPath = join14(projectRoot, "app", "globals.css");
|
|
9940
|
+
if (!existsSync18(globalsPath)) return [];
|
|
9888
9941
|
try {
|
|
9889
|
-
const content =
|
|
9942
|
+
const content = readFileSync12(globalsPath, "utf-8");
|
|
9890
9943
|
return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
|
|
9891
9944
|
} catch {
|
|
9892
9945
|
return [];
|
|
9893
9946
|
}
|
|
9894
9947
|
}
|
|
9895
9948
|
function patchGlobalsCss(projectRoot, missingVars) {
|
|
9896
|
-
const globalsPath =
|
|
9897
|
-
if (!
|
|
9898
|
-
const { writeFileSync:
|
|
9899
|
-
let content =
|
|
9949
|
+
const globalsPath = join14(projectRoot, "app", "globals.css");
|
|
9950
|
+
if (!existsSync18(globalsPath) || missingVars.length === 0) return;
|
|
9951
|
+
const { writeFileSync: writeFileSync11 } = __require("fs");
|
|
9952
|
+
let content = readFileSync12(globalsPath, "utf-8");
|
|
9900
9953
|
const defaultValues = {
|
|
9901
9954
|
"--chart-1": "220 70% 50%",
|
|
9902
9955
|
"--chart-2": "160 60% 45%",
|
|
@@ -9924,7 +9977,7 @@ function patchGlobalsCss(projectRoot, missingVars) {
|
|
|
9924
9977
|
const lightSectionEnd = content.indexOf("}");
|
|
9925
9978
|
if (lightSectionEnd > 0) {
|
|
9926
9979
|
content = content.slice(0, lightSectionEnd) + "\n" + injection + "\n" + content.slice(lightSectionEnd);
|
|
9927
|
-
|
|
9980
|
+
writeFileSync11(globalsPath, content, "utf-8");
|
|
9928
9981
|
}
|
|
9929
9982
|
}
|
|
9930
9983
|
|
|
@@ -9974,26 +10027,26 @@ async function undoCommand(options) {
|
|
|
9974
10027
|
// src/commands/sync.ts
|
|
9975
10028
|
import chalk27 from "chalk";
|
|
9976
10029
|
import ora9 from "ora";
|
|
9977
|
-
import { existsSync as
|
|
9978
|
-
import { join as
|
|
10030
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
|
|
10031
|
+
import { join as join15, relative as relative6, dirname as dirname7 } from "path";
|
|
9979
10032
|
import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
|
|
9980
10033
|
import { DesignSystemManager as DesignSystemManager14 } from "@getcoherent/core";
|
|
9981
10034
|
import { loadManifest as loadManifest12, saveManifest as saveManifest6, findSharedComponent } from "@getcoherent/core";
|
|
9982
10035
|
function extractTokensFromProject(projectRoot) {
|
|
9983
10036
|
const lightColors = {};
|
|
9984
10037
|
const darkColors = {};
|
|
9985
|
-
const globalsPath =
|
|
9986
|
-
if (
|
|
9987
|
-
const css =
|
|
10038
|
+
const globalsPath = join15(projectRoot, "app", "globals.css");
|
|
10039
|
+
if (existsSync19(globalsPath)) {
|
|
10040
|
+
const css = readFileSync13(globalsPath, "utf-8");
|
|
9988
10041
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
|
|
9989
10042
|
if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
|
|
9990
10043
|
const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
|
|
9991
10044
|
if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
|
|
9992
10045
|
}
|
|
9993
|
-
const layoutPath =
|
|
10046
|
+
const layoutPath = join15(projectRoot, "app", "layout.tsx");
|
|
9994
10047
|
let layoutCode = "";
|
|
9995
|
-
if (
|
|
9996
|
-
layoutCode =
|
|
10048
|
+
if (existsSync19(layoutPath)) {
|
|
10049
|
+
layoutCode = readFileSync13(layoutPath, "utf-8");
|
|
9997
10050
|
const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
|
|
9998
10051
|
if (rootInline && Object.keys(lightColors).length === 0) {
|
|
9999
10052
|
parseVarsInto(rootInline[1], lightColors);
|
|
@@ -10011,7 +10064,7 @@ function extractTokensFromProject(projectRoot) {
|
|
|
10011
10064
|
defaultMode = "dark";
|
|
10012
10065
|
}
|
|
10013
10066
|
let radius;
|
|
10014
|
-
const allCss = [
|
|
10067
|
+
const allCss = [existsSync19(globalsPath) ? readFileSync13(globalsPath, "utf-8") : "", layoutCode].join("\n");
|
|
10015
10068
|
const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
|
|
10016
10069
|
if (radiusMatch) radius = radiusMatch[1].trim();
|
|
10017
10070
|
return {
|
|
@@ -10034,8 +10087,8 @@ function parseVarsInto(block, target) {
|
|
|
10034
10087
|
}
|
|
10035
10088
|
async function detectCustomComponents(projectRoot, allPageCode) {
|
|
10036
10089
|
const results = [];
|
|
10037
|
-
const componentsDir =
|
|
10038
|
-
if (!
|
|
10090
|
+
const componentsDir = join15(projectRoot, "components");
|
|
10091
|
+
if (!existsSync19(componentsDir)) return results;
|
|
10039
10092
|
const files = [];
|
|
10040
10093
|
await walkForTsx(componentsDir, files, ["ui"]);
|
|
10041
10094
|
const fileResults = await Promise.all(
|
|
@@ -10062,7 +10115,7 @@ async function walkForTsx(dir, files, skipDirs) {
|
|
|
10062
10115
|
return;
|
|
10063
10116
|
}
|
|
10064
10117
|
for (const e of entries) {
|
|
10065
|
-
const full =
|
|
10118
|
+
const full = join15(dir, e.name);
|
|
10066
10119
|
if (e.isDirectory()) {
|
|
10067
10120
|
if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
|
|
10068
10121
|
await walkForTsx(full, files, skipDirs);
|
|
@@ -10136,7 +10189,7 @@ async function discoverPages(appDir) {
|
|
|
10136
10189
|
return;
|
|
10137
10190
|
}
|
|
10138
10191
|
for (const entry of entries) {
|
|
10139
|
-
const full =
|
|
10192
|
+
const full = join15(dir, entry.name);
|
|
10140
10193
|
if (entry.isDirectory()) {
|
|
10141
10194
|
if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
|
|
10142
10195
|
if (entry.name.startsWith(".")) continue;
|
|
@@ -10225,8 +10278,8 @@ async function syncCommand(options = {}) {
|
|
|
10225
10278
|
if (dryRun) console.log(chalk27.yellow(" [dry-run] No files will be written\n"));
|
|
10226
10279
|
const spinner = ora9("Scanning project files...").start();
|
|
10227
10280
|
try {
|
|
10228
|
-
const appDir =
|
|
10229
|
-
if (!
|
|
10281
|
+
const appDir = join15(project.root, "app");
|
|
10282
|
+
if (!existsSync19(appDir)) {
|
|
10230
10283
|
spinner.fail("No app/ directory found");
|
|
10231
10284
|
process.exit(1);
|
|
10232
10285
|
}
|
|
@@ -10456,53 +10509,53 @@ async function syncCommand(options = {}) {
|
|
|
10456
10509
|
// src/commands/migrate.ts
|
|
10457
10510
|
import chalk28 from "chalk";
|
|
10458
10511
|
import ora10 from "ora";
|
|
10459
|
-
import { existsSync as
|
|
10460
|
-
import { join as
|
|
10512
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync9, readFileSync as readFileSync14, readdirSync as readdirSync9 } from "fs";
|
|
10513
|
+
import { join as join16 } from "path";
|
|
10461
10514
|
function backupDir(projectRoot) {
|
|
10462
10515
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10463
|
-
return
|
|
10516
|
+
return join16(projectRoot, ".coherent", "backups", `pre-migrate-${ts}`);
|
|
10464
10517
|
}
|
|
10465
10518
|
function guardPath(projectRoot) {
|
|
10466
|
-
return
|
|
10519
|
+
return join16(projectRoot, ".coherent", "migration-in-progress");
|
|
10467
10520
|
}
|
|
10468
10521
|
function createBackup2(projectRoot) {
|
|
10469
|
-
const uiDir =
|
|
10522
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10470
10523
|
const dest = backupDir(projectRoot);
|
|
10471
10524
|
mkdirSync8(dest, { recursive: true });
|
|
10472
|
-
if (
|
|
10473
|
-
cpSync(uiDir,
|
|
10525
|
+
if (existsSync20(uiDir)) {
|
|
10526
|
+
cpSync(uiDir, join16(dest, "components-ui"), { recursive: true });
|
|
10474
10527
|
}
|
|
10475
|
-
const configPath =
|
|
10476
|
-
if (
|
|
10477
|
-
cpSync(configPath,
|
|
10528
|
+
const configPath = join16(projectRoot, "design-system.config.ts");
|
|
10529
|
+
if (existsSync20(configPath)) {
|
|
10530
|
+
cpSync(configPath, join16(dest, "design-system.config.ts"));
|
|
10478
10531
|
}
|
|
10479
10532
|
return dest;
|
|
10480
10533
|
}
|
|
10481
10534
|
function setGuard(projectRoot, backupPath) {
|
|
10482
10535
|
const guard = guardPath(projectRoot);
|
|
10483
|
-
mkdirSync8(
|
|
10484
|
-
|
|
10536
|
+
mkdirSync8(join16(projectRoot, ".coherent"), { recursive: true });
|
|
10537
|
+
writeFileSync9(guard, JSON.stringify({ backup: backupPath, startedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
10485
10538
|
}
|
|
10486
10539
|
function clearGuard(projectRoot) {
|
|
10487
10540
|
const guard = guardPath(projectRoot);
|
|
10488
|
-
if (
|
|
10541
|
+
if (existsSync20(guard)) rmSync6(guard);
|
|
10489
10542
|
}
|
|
10490
10543
|
function rollback(projectRoot) {
|
|
10491
10544
|
const guard = guardPath(projectRoot);
|
|
10492
|
-
if (!
|
|
10545
|
+
if (!existsSync20(guard)) return false;
|
|
10493
10546
|
try {
|
|
10494
|
-
const data = JSON.parse(
|
|
10547
|
+
const data = JSON.parse(readFileSync14(guard, "utf-8"));
|
|
10495
10548
|
const backup = data.backup;
|
|
10496
|
-
if (!
|
|
10497
|
-
const uiBackup =
|
|
10498
|
-
const uiDir =
|
|
10499
|
-
if (
|
|
10500
|
-
if (
|
|
10549
|
+
if (!existsSync20(backup)) return false;
|
|
10550
|
+
const uiBackup = join16(backup, "components-ui");
|
|
10551
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10552
|
+
if (existsSync20(uiBackup)) {
|
|
10553
|
+
if (existsSync20(uiDir)) rmSync6(uiDir, { recursive: true });
|
|
10501
10554
|
cpSync(uiBackup, uiDir, { recursive: true });
|
|
10502
10555
|
}
|
|
10503
|
-
const configBackup =
|
|
10504
|
-
const configDest =
|
|
10505
|
-
if (
|
|
10556
|
+
const configBackup = join16(backup, "design-system.config.ts");
|
|
10557
|
+
const configDest = join16(projectRoot, "design-system.config.ts");
|
|
10558
|
+
if (existsSync20(configBackup)) {
|
|
10506
10559
|
cpSync(configBackup, configDest);
|
|
10507
10560
|
}
|
|
10508
10561
|
clearGuard(projectRoot);
|
|
@@ -10530,13 +10583,13 @@ async function migrateAction(options) {
|
|
|
10530
10583
|
return;
|
|
10531
10584
|
}
|
|
10532
10585
|
const guard = guardPath(projectRoot);
|
|
10533
|
-
if (
|
|
10586
|
+
if (existsSync20(guard)) {
|
|
10534
10587
|
console.log(chalk28.yellow("A migration is already in progress."));
|
|
10535
10588
|
console.log(chalk28.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
|
|
10536
10589
|
return;
|
|
10537
10590
|
}
|
|
10538
|
-
const uiDir =
|
|
10539
|
-
if (!
|
|
10591
|
+
const uiDir = join16(projectRoot, "components", "ui");
|
|
10592
|
+
if (!existsSync20(uiDir)) {
|
|
10540
10593
|
console.log(chalk28.yellow("No components/ui directory found. Nothing to migrate."));
|
|
10541
10594
|
return;
|
|
10542
10595
|
}
|
|
@@ -10562,8 +10615,8 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
10562
10615
|
setGuard(projectRoot, backup);
|
|
10563
10616
|
try {
|
|
10564
10617
|
for (const id of migratable) {
|
|
10565
|
-
const filePath =
|
|
10566
|
-
if (
|
|
10618
|
+
const filePath = join16(uiDir, `${id}.tsx`);
|
|
10619
|
+
if (existsSync20(filePath)) rmSync6(filePath);
|
|
10567
10620
|
}
|
|
10568
10621
|
const results = await provider.installBatch(migratable, projectRoot, { force: true });
|
|
10569
10622
|
let migrated = 0;
|
|
@@ -10585,20 +10638,20 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
10585
10638
|
}
|
|
10586
10639
|
|
|
10587
10640
|
// src/utils/update-notifier.ts
|
|
10588
|
-
import { existsSync as
|
|
10589
|
-
import { join as
|
|
10641
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
|
|
10642
|
+
import { join as join17 } from "path";
|
|
10590
10643
|
import { homedir } from "os";
|
|
10591
10644
|
import chalk29 from "chalk";
|
|
10592
10645
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
10593
10646
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
10594
10647
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
10595
|
-
var CACHE_DIR =
|
|
10596
|
-
var CACHE_FILE =
|
|
10648
|
+
var CACHE_DIR = join17(homedir(), ".coherent");
|
|
10649
|
+
var CACHE_FILE = join17(CACHE_DIR, "update-check.json");
|
|
10597
10650
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
10598
10651
|
function readCache() {
|
|
10599
10652
|
try {
|
|
10600
|
-
if (!
|
|
10601
|
-
const raw =
|
|
10653
|
+
if (!existsSync21(CACHE_FILE)) return null;
|
|
10654
|
+
const raw = readFileSync15(CACHE_FILE, "utf-8");
|
|
10602
10655
|
return JSON.parse(raw);
|
|
10603
10656
|
} catch (e) {
|
|
10604
10657
|
if (DEBUG5) console.error("Failed to read update cache:", e);
|
|
@@ -10607,8 +10660,8 @@ function readCache() {
|
|
|
10607
10660
|
}
|
|
10608
10661
|
function writeCache(data) {
|
|
10609
10662
|
try {
|
|
10610
|
-
if (!
|
|
10611
|
-
|
|
10663
|
+
if (!existsSync21(CACHE_DIR)) mkdirSync9(CACHE_DIR, { recursive: true });
|
|
10664
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
10612
10665
|
} catch (e) {
|
|
10613
10666
|
if (DEBUG5) console.error("Failed to write update cache:", e);
|
|
10614
10667
|
}
|
|
@@ -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
|
+
};
|