uilint 0.2.21 → 0.2.23
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-PB5DLLVC.js +1000 -0
- package/dist/chunk-PB5DLLVC.js.map +1 -0
- package/dist/index.js +383 -2
- package/dist/index.js.map +1 -1
- package/dist/{install-ui-2HMR6TZT.js → install-ui-TXV7A34M.js} +630 -739
- package/dist/install-ui-TXV7A34M.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-FRNXXIEM.js +0 -197
- package/dist/chunk-FRNXXIEM.js.map +0 -1
- package/dist/install-ui-2HMR6TZT.js.map +0 -1
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
import {
|
|
3
3
|
confirm,
|
|
4
4
|
detectNextAppRouter,
|
|
5
|
+
findEslintConfigFile,
|
|
5
6
|
findNextAppRouterProjects,
|
|
7
|
+
getEslintConfigFilename,
|
|
8
|
+
getUilintEslintConfigInfoFromSource,
|
|
9
|
+
installEslintPlugin,
|
|
6
10
|
log,
|
|
7
11
|
multiselect,
|
|
8
12
|
note,
|
|
9
13
|
pc,
|
|
10
|
-
select
|
|
11
|
-
|
|
14
|
+
select,
|
|
15
|
+
uninstallEslintPlugin
|
|
16
|
+
} from "./chunk-PB5DLLVC.js";
|
|
12
17
|
import {
|
|
13
18
|
GENSTYLEGUIDE_COMMAND_MD,
|
|
14
19
|
detectPackageManager,
|
|
@@ -137,8 +142,11 @@ function ProjectSelector({
|
|
|
137
142
|
// src/commands/install/components/MultiSelect.tsx
|
|
138
143
|
import { useState as useState3, useCallback } from "react";
|
|
139
144
|
import { Box as Box2, Text as Text3, useInput as useInput2, useApp as useApp2 } from "ink";
|
|
140
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
141
|
-
function StatusIndicator({ status, isSelected }) {
|
|
145
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
146
|
+
function StatusIndicator({ status, isSelected, isMarkedForUninstall }) {
|
|
147
|
+
if (isMarkedForUninstall) {
|
|
148
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "red", children: "\u2717" });
|
|
149
|
+
}
|
|
142
150
|
if (status === "installed") {
|
|
143
151
|
return /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713" });
|
|
144
152
|
}
|
|
@@ -153,7 +161,10 @@ function StatusIndicator({ status, isSelected }) {
|
|
|
153
161
|
}
|
|
154
162
|
return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u25CB" });
|
|
155
163
|
}
|
|
156
|
-
function StatusLabel({ status }) {
|
|
164
|
+
function StatusLabel({ status, isMarkedForUninstall }) {
|
|
165
|
+
if (isMarkedForUninstall) {
|
|
166
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "red", dimColor: true, children: "uninstall" });
|
|
167
|
+
}
|
|
157
168
|
if (status === "installed") {
|
|
158
169
|
return /* @__PURE__ */ jsx3(Text3, { color: "green", dimColor: true, children: "installed" });
|
|
159
170
|
}
|
|
@@ -177,6 +188,7 @@ function ConfigSelector({
|
|
|
177
188
|
items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
|
|
178
189
|
);
|
|
179
190
|
});
|
|
191
|
+
const [markedForUninstall, setMarkedForUninstall] = useState3(/* @__PURE__ */ new Set());
|
|
180
192
|
const categories = Array.from(new Set(items.map((item) => item.category)));
|
|
181
193
|
const itemsByCategory = /* @__PURE__ */ new Map();
|
|
182
194
|
for (const cat of categories) {
|
|
@@ -185,8 +197,19 @@ function ConfigSelector({
|
|
|
185
197
|
const flatItems = items;
|
|
186
198
|
const handleToggle = useCallback(() => {
|
|
187
199
|
const item = flatItems[cursor];
|
|
188
|
-
|
|
189
|
-
if (
|
|
200
|
+
if (!item || item.disabled) return;
|
|
201
|
+
if (item.status === "installed") {
|
|
202
|
+
setMarkedForUninstall((prev) => {
|
|
203
|
+
const next = new Set(prev);
|
|
204
|
+
if (next.has(item.id)) {
|
|
205
|
+
next.delete(item.id);
|
|
206
|
+
} else {
|
|
207
|
+
next.add(item.id);
|
|
208
|
+
}
|
|
209
|
+
return next;
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
190
213
|
setSelected((prev) => {
|
|
191
214
|
const next = new Set(prev);
|
|
192
215
|
if (next.has(item.id)) {
|
|
@@ -205,7 +228,7 @@ function ConfigSelector({
|
|
|
205
228
|
} else if (input === " ") {
|
|
206
229
|
handleToggle();
|
|
207
230
|
} else if (key.return) {
|
|
208
|
-
onSubmit(Array.from(selected));
|
|
231
|
+
onSubmit(Array.from(selected), Array.from(markedForUninstall));
|
|
209
232
|
} else if (input === "q" || key.escape) {
|
|
210
233
|
onCancel?.();
|
|
211
234
|
exit();
|
|
@@ -215,8 +238,10 @@ function ConfigSelector({
|
|
|
215
238
|
items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
|
|
216
239
|
)
|
|
217
240
|
);
|
|
241
|
+
setMarkedForUninstall(/* @__PURE__ */ new Set());
|
|
218
242
|
} else if (input === "n") {
|
|
219
243
|
setSelected(/* @__PURE__ */ new Set());
|
|
244
|
+
setMarkedForUninstall(/* @__PURE__ */ new Set());
|
|
220
245
|
}
|
|
221
246
|
});
|
|
222
247
|
let globalIndex = 0;
|
|
@@ -234,20 +259,21 @@ function ConfigSelector({
|
|
|
234
259
|
const itemIndex = globalIndex++;
|
|
235
260
|
const isCursor = itemIndex === cursor;
|
|
236
261
|
const isItemSelected = selected.has(item.id);
|
|
237
|
-
const
|
|
262
|
+
const isItemMarkedForUninstall = markedForUninstall.has(item.id);
|
|
263
|
+
const isDisabled = item.disabled === true;
|
|
238
264
|
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 2, children: [
|
|
239
265
|
/* @__PURE__ */ jsx3(Text3, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
240
|
-
/* @__PURE__ */ jsx3(Box2, { width: 2, children: /* @__PURE__ */ jsx3(StatusIndicator, { status: item.status, isSelected: isItemSelected }) }),
|
|
266
|
+
/* @__PURE__ */ jsx3(Box2, { width: 2, children: /* @__PURE__ */ jsx3(StatusIndicator, { status: item.status, isSelected: isItemSelected, isMarkedForUninstall: isItemMarkedForUninstall }) }),
|
|
241
267
|
/* @__PURE__ */ jsx3(Box2, { width: 28, children: /* @__PURE__ */ jsx3(
|
|
242
268
|
Text3,
|
|
243
269
|
{
|
|
244
|
-
color: isDisabled ? void 0 : isCursor ? "cyan" : void 0,
|
|
245
|
-
dimColor: isDisabled,
|
|
270
|
+
color: isItemMarkedForUninstall ? "red" : isDisabled ? void 0 : isCursor ? "cyan" : void 0,
|
|
271
|
+
dimColor: isDisabled && !isItemMarkedForUninstall,
|
|
246
272
|
children: item.label
|
|
247
273
|
}
|
|
248
274
|
) }),
|
|
249
275
|
/* @__PURE__ */ jsx3(Box2, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint || "" }) }),
|
|
250
|
-
/* @__PURE__ */ jsx3(StatusLabel, { status: item.status })
|
|
276
|
+
/* @__PURE__ */ jsx3(StatusLabel, { status: item.status, isMarkedForUninstall: isItemMarkedForUninstall })
|
|
251
277
|
] }, item.id);
|
|
252
278
|
})
|
|
253
279
|
] }, category);
|
|
@@ -273,10 +299,11 @@ function ConfigSelector({
|
|
|
273
299
|
] }) }),
|
|
274
300
|
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text3, { children: [
|
|
275
301
|
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: selected.size }),
|
|
276
|
-
/* @__PURE__ */
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
"
|
|
302
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " to install" }),
|
|
303
|
+
markedForUninstall.size > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
304
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ", " }),
|
|
305
|
+
/* @__PURE__ */ jsx3(Text3, { color: "red", children: markedForUninstall.size }),
|
|
306
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " to uninstall" })
|
|
280
307
|
] })
|
|
281
308
|
] }) })
|
|
282
309
|
] });
|
|
@@ -819,7 +846,7 @@ var nextOverlayInstaller = {
|
|
|
819
846
|
label: app.projectPath.split("/").pop() || app.projectPath,
|
|
820
847
|
path: app.projectPath,
|
|
821
848
|
hint: "App Router",
|
|
822
|
-
isInstalled:
|
|
849
|
+
isInstalled: app.hasUilintOverlay
|
|
823
850
|
}));
|
|
824
851
|
},
|
|
825
852
|
plan(targets, config, project) {
|
|
@@ -892,6 +919,32 @@ var nextOverlayInstaller = {
|
|
|
892
919
|
type: "complete",
|
|
893
920
|
message: "Next.js overlay installed"
|
|
894
921
|
};
|
|
922
|
+
},
|
|
923
|
+
planUninstall(targets, project) {
|
|
924
|
+
const actions = [];
|
|
925
|
+
if (targets.length === 0) return { actions };
|
|
926
|
+
const target = targets[0];
|
|
927
|
+
const appInfo = project.nextApps.find(
|
|
928
|
+
(app) => app.projectPath === target.path
|
|
929
|
+
);
|
|
930
|
+
if (!appInfo) return { actions };
|
|
931
|
+
const { projectPath, detection } = appInfo;
|
|
932
|
+
actions.push({
|
|
933
|
+
type: "remove_react",
|
|
934
|
+
projectPath,
|
|
935
|
+
appRoot: detection.appRoot,
|
|
936
|
+
mode: "next"
|
|
937
|
+
});
|
|
938
|
+
actions.push({
|
|
939
|
+
type: "remove_next_config",
|
|
940
|
+
projectPath
|
|
941
|
+
});
|
|
942
|
+
actions.push({
|
|
943
|
+
type: "remove_next_routes",
|
|
944
|
+
projectPath,
|
|
945
|
+
appRoot: detection.appRoot
|
|
946
|
+
});
|
|
947
|
+
return { actions };
|
|
895
948
|
}
|
|
896
949
|
};
|
|
897
950
|
|
|
@@ -1029,6 +1082,7 @@ function InstallApp({
|
|
|
1029
1082
|
const [selections, setSelections] = useState6([]);
|
|
1030
1083
|
const [configItems, setConfigItems] = useState6([]);
|
|
1031
1084
|
const [selectedFeatureIds, setSelectedFeatureIds] = useState6([]);
|
|
1085
|
+
const [uninstallFeatureIds, setUninstallFeatureIds] = useState6([]);
|
|
1032
1086
|
const [error, setError] = useState6(null);
|
|
1033
1087
|
const [injectionPoints, setInjectionPoints] = useState6([]);
|
|
1034
1088
|
const [selectedInjectionPoint, setSelectedInjectionPoint] = useState6(void 0);
|
|
@@ -1086,8 +1140,9 @@ function InstallApp({
|
|
|
1086
1140
|
setPhase("select-project");
|
|
1087
1141
|
}
|
|
1088
1142
|
};
|
|
1089
|
-
const handleFeatureSubmit = (selectedIds) => {
|
|
1143
|
+
const handleFeatureSubmit = (selectedIds, uninstallIds) => {
|
|
1090
1144
|
setSelectedFeatureIds(selectedIds);
|
|
1145
|
+
setUninstallFeatureIds(uninstallIds);
|
|
1091
1146
|
const nextSelected = selectedIds.some((id) => id.startsWith("next:"));
|
|
1092
1147
|
if (nextSelected && project && selectedProject) {
|
|
1093
1148
|
const appInfo = project.nextApps.find(
|
|
@@ -1143,6 +1198,7 @@ function InstallApp({
|
|
|
1143
1198
|
};
|
|
1144
1199
|
const finishInstallation = (selectedIds, eslintRules, injectionConfig) => {
|
|
1145
1200
|
const selectedSet = new Set(selectedIds);
|
|
1201
|
+
const uninstallSet = new Set(uninstallFeatureIds);
|
|
1146
1202
|
const updatedSelections = selections.map((sel) => {
|
|
1147
1203
|
const selectedTargets = sel.targets.filter(
|
|
1148
1204
|
(t) => selectedSet.has(`${sel.installer.id}:${t.id}`)
|
|
@@ -1153,8 +1209,18 @@ function InstallApp({
|
|
|
1153
1209
|
selected: selectedTargets.length > 0
|
|
1154
1210
|
};
|
|
1155
1211
|
});
|
|
1212
|
+
const uninstallSelections = selections.map((sel) => {
|
|
1213
|
+
const uninstallTargets = sel.targets.filter(
|
|
1214
|
+
(t) => uninstallSet.has(`${sel.installer.id}:${t.id}`)
|
|
1215
|
+
);
|
|
1216
|
+
return {
|
|
1217
|
+
...sel,
|
|
1218
|
+
targets: uninstallTargets,
|
|
1219
|
+
selected: uninstallTargets.length > 0
|
|
1220
|
+
};
|
|
1221
|
+
}).filter((sel) => sel.selected);
|
|
1156
1222
|
setSelections(updatedSelections);
|
|
1157
|
-
onComplete(updatedSelections, eslintRules, injectionConfig);
|
|
1223
|
+
onComplete(updatedSelections, eslintRules, injectionConfig, uninstallSelections.length > 0 ? uninstallSelections : void 0);
|
|
1158
1224
|
};
|
|
1159
1225
|
const handleCancel = () => {
|
|
1160
1226
|
exit();
|
|
@@ -1248,9 +1314,9 @@ function InstallApp({
|
|
|
1248
1314
|
}
|
|
1249
1315
|
|
|
1250
1316
|
// src/commands/install/analyze.ts
|
|
1251
|
-
import { existsSync as
|
|
1252
|
-
import { join as
|
|
1253
|
-
import { findWorkspaceRoot
|
|
1317
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
1318
|
+
import { join as join4 } from "path";
|
|
1319
|
+
import { findWorkspaceRoot } from "uilint-core/node";
|
|
1254
1320
|
|
|
1255
1321
|
// src/utils/vite-detect.ts
|
|
1256
1322
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
@@ -1493,585 +1559,63 @@ function findPackages(rootDir, options) {
|
|
|
1493
1559
|
});
|
|
1494
1560
|
}
|
|
1495
1561
|
|
|
1496
|
-
// src/
|
|
1497
|
-
|
|
1498
|
-
import { join as join4, relative as relative3, dirname as dirname2 } from "path";
|
|
1499
|
-
import { parseExpression, parseModule as parseModule2, generateCode } from "magicast";
|
|
1500
|
-
import { findWorkspaceRoot } from "uilint-core/node";
|
|
1501
|
-
var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
|
|
1502
|
-
function findEslintConfigFile(projectPath) {
|
|
1503
|
-
for (const ext of CONFIG_EXTENSIONS) {
|
|
1504
|
-
const configPath = join4(projectPath, `eslint.config${ext}`);
|
|
1505
|
-
if (existsSync4(configPath)) {
|
|
1506
|
-
return configPath;
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
return null;
|
|
1510
|
-
}
|
|
1511
|
-
function getEslintConfigFilename(configPath) {
|
|
1512
|
-
const parts = configPath.split("/");
|
|
1513
|
-
return parts[parts.length - 1] || "eslint.config.mjs";
|
|
1514
|
-
}
|
|
1515
|
-
function isIdentifier(node, name) {
|
|
1516
|
-
return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
|
|
1517
|
-
}
|
|
1518
|
-
function isStringLiteral(node) {
|
|
1519
|
-
return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
|
|
1520
|
-
}
|
|
1521
|
-
function getObjectPropertyValue(obj, keyName) {
|
|
1522
|
-
if (!obj || obj.type !== "ObjectExpression") return null;
|
|
1523
|
-
for (const prop of obj.properties ?? []) {
|
|
1524
|
-
if (!prop) continue;
|
|
1525
|
-
if (prop.type === "ObjectProperty" || prop.type === "Property") {
|
|
1526
|
-
const key = prop.key;
|
|
1527
|
-
const keyMatch = key?.type === "Identifier" && key.name === keyName || isStringLiteral(key) && key.value === keyName;
|
|
1528
|
-
if (keyMatch) return prop.value;
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
return null;
|
|
1532
|
-
}
|
|
1533
|
-
function hasSpreadProperties(obj) {
|
|
1534
|
-
if (!obj || obj.type !== "ObjectExpression") return false;
|
|
1535
|
-
return (obj.properties ?? []).some(
|
|
1536
|
-
(p) => p && (p.type === "SpreadElement" || p.type === "SpreadProperty")
|
|
1537
|
-
);
|
|
1538
|
-
}
|
|
1539
|
-
var IGNORED_AST_KEYS = /* @__PURE__ */ new Set([
|
|
1540
|
-
"loc",
|
|
1541
|
-
"start",
|
|
1542
|
-
"end",
|
|
1543
|
-
"extra",
|
|
1544
|
-
"leadingComments",
|
|
1545
|
-
"trailingComments",
|
|
1546
|
-
"innerComments"
|
|
1547
|
-
]);
|
|
1548
|
-
function normalizeAstForCompare(node) {
|
|
1549
|
-
if (node === null) return null;
|
|
1550
|
-
if (node === void 0) return void 0;
|
|
1551
|
-
if (typeof node !== "object") return node;
|
|
1552
|
-
if (Array.isArray(node)) return node.map(normalizeAstForCompare);
|
|
1553
|
-
const out = {};
|
|
1554
|
-
const keys = Object.keys(node).filter((k) => !IGNORED_AST_KEYS.has(k)).sort();
|
|
1555
|
-
for (const k of keys) {
|
|
1556
|
-
if (k.startsWith("$")) continue;
|
|
1557
|
-
out[k] = normalizeAstForCompare(node[k]);
|
|
1558
|
-
}
|
|
1559
|
-
return out;
|
|
1560
|
-
}
|
|
1561
|
-
function astEquivalent(a, b) {
|
|
1562
|
-
try {
|
|
1563
|
-
return JSON.stringify(normalizeAstForCompare(a)) === JSON.stringify(normalizeAstForCompare(b));
|
|
1564
|
-
} catch {
|
|
1565
|
-
return false;
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
function collectUilintRuleIdsFromRulesObject(rulesObj) {
|
|
1569
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1570
|
-
if (!rulesObj || rulesObj.type !== "ObjectExpression") return ids;
|
|
1571
|
-
for (const prop of rulesObj.properties ?? []) {
|
|
1572
|
-
if (!prop) continue;
|
|
1573
|
-
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
1574
|
-
const key = prop.key;
|
|
1575
|
-
if (!isStringLiteral(key)) continue;
|
|
1576
|
-
const val = key.value;
|
|
1577
|
-
if (typeof val !== "string") continue;
|
|
1578
|
-
if (val.startsWith("uilint/")) {
|
|
1579
|
-
ids.add(val.slice("uilint/".length));
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
return ids;
|
|
1583
|
-
}
|
|
1584
|
-
function findExportedConfigArrayExpression(mod) {
|
|
1585
|
-
function unwrapExpression2(expr) {
|
|
1586
|
-
let e = expr;
|
|
1587
|
-
while (e) {
|
|
1588
|
-
if (e.type === "TSAsExpression" || e.type === "TSNonNullExpression") {
|
|
1589
|
-
e = e.expression;
|
|
1590
|
-
continue;
|
|
1591
|
-
}
|
|
1592
|
-
if (e.type === "TSSatisfiesExpression") {
|
|
1593
|
-
e = e.expression;
|
|
1594
|
-
continue;
|
|
1595
|
-
}
|
|
1596
|
-
if (e.type === "ParenthesizedExpression") {
|
|
1597
|
-
e = e.expression;
|
|
1598
|
-
continue;
|
|
1599
|
-
}
|
|
1600
|
-
break;
|
|
1601
|
-
}
|
|
1602
|
-
return e;
|
|
1603
|
-
}
|
|
1604
|
-
function resolveTopLevelIdentifierToArrayExpr(program2, name) {
|
|
1605
|
-
if (!program2 || program2.type !== "Program") return null;
|
|
1606
|
-
for (const stmt of program2.body ?? []) {
|
|
1607
|
-
if (stmt?.type !== "VariableDeclaration") continue;
|
|
1608
|
-
for (const decl of stmt.declarations ?? []) {
|
|
1609
|
-
const id = decl?.id;
|
|
1610
|
-
if (!isIdentifier(id, name)) continue;
|
|
1611
|
-
const init = unwrapExpression2(decl?.init);
|
|
1612
|
-
if (!init) return null;
|
|
1613
|
-
if (init.type === "ArrayExpression") return init;
|
|
1614
|
-
if (init.type === "CallExpression" && isIdentifier(init.callee, "defineConfig") && unwrapExpression2(init.arguments?.[0])?.type === "ArrayExpression") {
|
|
1615
|
-
return unwrapExpression2(init.arguments?.[0]);
|
|
1616
|
-
}
|
|
1617
|
-
return null;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
return null;
|
|
1621
|
-
}
|
|
1622
|
-
const program = mod?.$ast;
|
|
1623
|
-
if (program && program.type === "Program") {
|
|
1624
|
-
for (const stmt of program.body ?? []) {
|
|
1625
|
-
if (!stmt || stmt.type !== "ExportDefaultDeclaration") continue;
|
|
1626
|
-
const decl = unwrapExpression2(stmt.declaration);
|
|
1627
|
-
if (!decl) break;
|
|
1628
|
-
if (decl.type === "ArrayExpression") {
|
|
1629
|
-
return { kind: "esm", arrayExpr: decl, program };
|
|
1630
|
-
}
|
|
1631
|
-
if (decl.type === "CallExpression" && isIdentifier(decl.callee, "defineConfig") && unwrapExpression2(decl.arguments?.[0])?.type === "ArrayExpression") {
|
|
1632
|
-
return {
|
|
1633
|
-
kind: "esm",
|
|
1634
|
-
arrayExpr: unwrapExpression2(decl.arguments?.[0]),
|
|
1635
|
-
program
|
|
1636
|
-
};
|
|
1637
|
-
}
|
|
1638
|
-
if (decl.type === "Identifier" && typeof decl.name === "string") {
|
|
1639
|
-
const resolved = resolveTopLevelIdentifierToArrayExpr(
|
|
1640
|
-
program,
|
|
1641
|
-
decl.name
|
|
1642
|
-
);
|
|
1643
|
-
if (resolved) return { kind: "esm", arrayExpr: resolved, program };
|
|
1644
|
-
}
|
|
1645
|
-
break;
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
if (!program || program.type !== "Program") return null;
|
|
1649
|
-
for (const stmt of program.body ?? []) {
|
|
1650
|
-
if (!stmt || stmt.type !== "ExpressionStatement") continue;
|
|
1651
|
-
const expr = stmt.expression;
|
|
1652
|
-
if (!expr || expr.type !== "AssignmentExpression") continue;
|
|
1653
|
-
const left = expr.left;
|
|
1654
|
-
const right = expr.right;
|
|
1655
|
-
const isModuleExports = left?.type === "MemberExpression" && isIdentifier(left.object, "module") && isIdentifier(left.property, "exports");
|
|
1656
|
-
if (!isModuleExports) continue;
|
|
1657
|
-
if (right?.type === "ArrayExpression") {
|
|
1658
|
-
return { kind: "cjs", arrayExpr: right, program };
|
|
1659
|
-
}
|
|
1660
|
-
if (right?.type === "CallExpression" && isIdentifier(right.callee, "defineConfig") && right.arguments?.[0]?.type === "ArrayExpression") {
|
|
1661
|
-
return { kind: "cjs", arrayExpr: right.arguments[0], program };
|
|
1662
|
-
}
|
|
1663
|
-
if (right?.type === "Identifier" && typeof right.name === "string") {
|
|
1664
|
-
const resolved = resolveTopLevelIdentifierToArrayExpr(
|
|
1665
|
-
program,
|
|
1666
|
-
right.name
|
|
1667
|
-
);
|
|
1668
|
-
if (resolved) return { kind: "cjs", arrayExpr: resolved, program };
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
return null;
|
|
1672
|
-
}
|
|
1673
|
-
function collectConfiguredUilintRuleIdsFromConfigArray(arrayExpr) {
|
|
1674
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1675
|
-
if (!arrayExpr || arrayExpr.type !== "ArrayExpression") return ids;
|
|
1676
|
-
for (const el of arrayExpr.elements ?? []) {
|
|
1677
|
-
if (!el || el.type !== "ObjectExpression") continue;
|
|
1678
|
-
const rules = getObjectPropertyValue(el, "rules");
|
|
1679
|
-
for (const id of collectUilintRuleIdsFromRulesObject(rules)) ids.add(id);
|
|
1680
|
-
}
|
|
1681
|
-
return ids;
|
|
1682
|
-
}
|
|
1683
|
-
function findExistingUilintRulesObject(arrayExpr) {
|
|
1684
|
-
if (!arrayExpr || arrayExpr.type !== "ArrayExpression") {
|
|
1685
|
-
return { configObj: null, rulesObj: null, safeToMutate: false };
|
|
1686
|
-
}
|
|
1687
|
-
for (const el of arrayExpr.elements ?? []) {
|
|
1688
|
-
if (!el || el.type !== "ObjectExpression") continue;
|
|
1689
|
-
const plugins = getObjectPropertyValue(el, "plugins");
|
|
1690
|
-
const rules = getObjectPropertyValue(el, "rules");
|
|
1691
|
-
const hasUilintPlugin = plugins?.type === "ObjectExpression" && getObjectPropertyValue(plugins, "uilint") !== null;
|
|
1692
|
-
const uilintIds = collectUilintRuleIdsFromRulesObject(rules);
|
|
1693
|
-
const hasUilintRules = uilintIds.size > 0;
|
|
1694
|
-
if (!hasUilintPlugin && !hasUilintRules) continue;
|
|
1695
|
-
const safe = rules?.type === "ObjectExpression" && !hasSpreadProperties(rules);
|
|
1696
|
-
return { configObj: el, rulesObj: rules, safeToMutate: safe };
|
|
1697
|
-
}
|
|
1698
|
-
return { configObj: null, rulesObj: null, safeToMutate: false };
|
|
1699
|
-
}
|
|
1700
|
-
function collectTopLevelBindings(program) {
|
|
1701
|
-
const names = /* @__PURE__ */ new Set();
|
|
1702
|
-
if (!program || program.type !== "Program") return names;
|
|
1703
|
-
for (const stmt of program.body ?? []) {
|
|
1704
|
-
if (stmt?.type === "VariableDeclaration") {
|
|
1705
|
-
for (const decl of stmt.declarations ?? []) {
|
|
1706
|
-
const id = decl?.id;
|
|
1707
|
-
if (id?.type === "Identifier" && typeof id.name === "string") {
|
|
1708
|
-
names.add(id.name);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
} else if (stmt?.type === "FunctionDeclaration") {
|
|
1712
|
-
if (stmt.id?.type === "Identifier" && typeof stmt.id.name === "string") {
|
|
1713
|
-
names.add(stmt.id.name);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
return names;
|
|
1718
|
-
}
|
|
1719
|
-
function chooseUniqueIdentifier(base, used) {
|
|
1720
|
-
if (!used.has(base)) return base;
|
|
1721
|
-
let i = 2;
|
|
1722
|
-
while (used.has(`${base}${i}`)) i++;
|
|
1723
|
-
return `${base}${i}`;
|
|
1724
|
-
}
|
|
1725
|
-
function addLocalRuleImportsAst(mod, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
|
|
1726
|
-
const importNames = /* @__PURE__ */ new Map();
|
|
1727
|
-
let changed = false;
|
|
1728
|
-
const configDir = dirname2(configPath);
|
|
1729
|
-
const rulesDir = join4(rulesRoot, ".uilint", "rules");
|
|
1730
|
-
const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
|
|
1731
|
-
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1732
|
-
const used = collectTopLevelBindings(mod.$ast);
|
|
1733
|
-
for (const rule of selectedRules) {
|
|
1734
|
-
const importName = chooseUniqueIdentifier(
|
|
1735
|
-
`${rule.id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase())}Rule`,
|
|
1736
|
-
used
|
|
1737
|
-
);
|
|
1738
|
-
importNames.set(rule.id, importName);
|
|
1739
|
-
used.add(importName);
|
|
1740
|
-
const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;
|
|
1741
|
-
mod.imports.$add({
|
|
1742
|
-
imported: "default",
|
|
1743
|
-
local: importName,
|
|
1744
|
-
from: rulePath
|
|
1745
|
-
});
|
|
1746
|
-
changed = true;
|
|
1747
|
-
}
|
|
1748
|
-
return { importNames, changed };
|
|
1749
|
-
}
|
|
1750
|
-
function addLocalRuleRequiresAst(program, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
|
|
1751
|
-
const importNames = /* @__PURE__ */ new Map();
|
|
1752
|
-
let changed = false;
|
|
1753
|
-
if (!program || program.type !== "Program") {
|
|
1754
|
-
return { importNames, changed };
|
|
1755
|
-
}
|
|
1756
|
-
const configDir = dirname2(configPath);
|
|
1757
|
-
const rulesDir = join4(rulesRoot, ".uilint", "rules");
|
|
1758
|
-
const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
|
|
1759
|
-
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1760
|
-
const used = collectTopLevelBindings(program);
|
|
1761
|
-
for (const rule of selectedRules) {
|
|
1762
|
-
const importName = chooseUniqueIdentifier(
|
|
1763
|
-
`${rule.id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase())}Rule`,
|
|
1764
|
-
used
|
|
1765
|
-
);
|
|
1766
|
-
importNames.set(rule.id, importName);
|
|
1767
|
-
used.add(importName);
|
|
1768
|
-
const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;
|
|
1769
|
-
const stmtMod = parseModule2(
|
|
1770
|
-
`const ${importName} = require("${rulePath}");`
|
|
1771
|
-
);
|
|
1772
|
-
const stmt = stmtMod.$ast.body?.[0];
|
|
1773
|
-
if (stmt) {
|
|
1774
|
-
let insertAt = 0;
|
|
1775
|
-
const first = program.body?.[0];
|
|
1776
|
-
if (first?.type === "ExpressionStatement" && first.expression?.type === "StringLiteral" && first.expression.value === "use strict") {
|
|
1777
|
-
insertAt = 1;
|
|
1778
|
-
}
|
|
1779
|
-
program.body.splice(insertAt, 0, stmt);
|
|
1780
|
-
changed = true;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
return { importNames, changed };
|
|
1784
|
-
}
|
|
1785
|
-
function appendUilintConfigBlockToArray(arrayExpr, selectedRules, ruleImportNames) {
|
|
1786
|
-
const pluginRulesCode = Array.from(ruleImportNames.entries()).map(([ruleId, importName]) => ` "${ruleId}": ${importName},`).join("\n");
|
|
1787
|
-
const rulesPropsCode = selectedRules.map((r) => {
|
|
1788
|
-
const ruleKey = `uilint/${r.id}`;
|
|
1789
|
-
const valueCode = r.defaultOptions && r.defaultOptions.length > 0 ? `["${r.defaultSeverity}", ...${JSON.stringify(
|
|
1790
|
-
r.defaultOptions,
|
|
1791
|
-
null,
|
|
1792
|
-
2
|
|
1793
|
-
)}]` : `"${r.defaultSeverity}"`;
|
|
1794
|
-
return ` "${ruleKey}": ${valueCode},`;
|
|
1795
|
-
}).join("\n");
|
|
1796
|
-
const blockCode = `{
|
|
1797
|
-
files: [
|
|
1798
|
-
"src/**/*.{js,jsx,ts,tsx}",
|
|
1799
|
-
"app/**/*.{js,jsx,ts,tsx}",
|
|
1800
|
-
"pages/**/*.{js,jsx,ts,tsx}",
|
|
1801
|
-
],
|
|
1802
|
-
plugins: {
|
|
1803
|
-
uilint: {
|
|
1804
|
-
rules: {
|
|
1805
|
-
${pluginRulesCode}
|
|
1806
|
-
},
|
|
1807
|
-
},
|
|
1808
|
-
},
|
|
1809
|
-
rules: {
|
|
1810
|
-
${rulesPropsCode}
|
|
1811
|
-
},
|
|
1812
|
-
}`;
|
|
1813
|
-
const objExpr = parseExpression(blockCode).$ast;
|
|
1814
|
-
arrayExpr.elements.push(objExpr);
|
|
1815
|
-
}
|
|
1816
|
-
function getUilintEslintConfigInfoFromSourceAst(source) {
|
|
1562
|
+
// src/commands/install/analyze.ts
|
|
1563
|
+
function safeParseJson(filePath) {
|
|
1817
1564
|
try {
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
if (!found) {
|
|
1821
|
-
return {
|
|
1822
|
-
error: "Could not locate an exported ESLint flat config array (expected `export default [...]`, `export default defineConfig([...])`, `module.exports = [...]`, or `module.exports = defineConfig([...])`)."
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1825
|
-
const configuredRuleIds = collectConfiguredUilintRuleIdsFromConfigArray(
|
|
1826
|
-
found.arrayExpr
|
|
1827
|
-
);
|
|
1828
|
-
const existingUilint = findExistingUilintRulesObject(found.arrayExpr);
|
|
1829
|
-
const configured = configuredRuleIds.size > 0 || existingUilint.configObj !== null;
|
|
1830
|
-
return {
|
|
1831
|
-
info: { configuredRuleIds, configured },
|
|
1832
|
-
mod,
|
|
1833
|
-
arrayExpr: found.arrayExpr,
|
|
1834
|
-
kind: found.kind
|
|
1835
|
-
};
|
|
1565
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
1566
|
+
return JSON.parse(content);
|
|
1836
1567
|
} catch {
|
|
1837
|
-
return
|
|
1838
|
-
error: "Unable to parse ESLint config as JavaScript. Please update it manually or simplify the config so it can be safely auto-modified."
|
|
1839
|
-
};
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
function getUilintEslintConfigInfoFromSource(source) {
|
|
1843
|
-
const ast = getUilintEslintConfigInfoFromSourceAst(source);
|
|
1844
|
-
if ("error" in ast) {
|
|
1845
|
-
const configuredRuleIds = extractConfiguredUilintRuleIds(source);
|
|
1846
|
-
return {
|
|
1847
|
-
configuredRuleIds,
|
|
1848
|
-
configured: configuredRuleIds.size > 0
|
|
1849
|
-
};
|
|
1850
|
-
}
|
|
1851
|
-
return ast.info;
|
|
1852
|
-
}
|
|
1853
|
-
function extractConfiguredUilintRuleIds(source) {
|
|
1854
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1855
|
-
const re = /["']uilint\/([^"']+)["']\s*:/g;
|
|
1856
|
-
for (const m of source.matchAll(re)) {
|
|
1857
|
-
if (m[1]) ids.add(m[1]);
|
|
1858
|
-
}
|
|
1859
|
-
return ids;
|
|
1860
|
-
}
|
|
1861
|
-
function getMissingSelectedRules(selectedRules, configuredIds) {
|
|
1862
|
-
return selectedRules.filter((r) => !configuredIds.has(r.id));
|
|
1863
|
-
}
|
|
1864
|
-
function buildDesiredRuleValueExpression(rule) {
|
|
1865
|
-
if (rule.defaultOptions && rule.defaultOptions.length > 0) {
|
|
1866
|
-
return `["${rule.defaultSeverity}", ...${JSON.stringify(
|
|
1867
|
-
rule.defaultOptions,
|
|
1868
|
-
null,
|
|
1869
|
-
2
|
|
1870
|
-
)}]`;
|
|
1871
|
-
}
|
|
1872
|
-
return `"${rule.defaultSeverity}"`;
|
|
1873
|
-
}
|
|
1874
|
-
function collectUilintRuleValueNodesFromConfigArray(arrayExpr) {
|
|
1875
|
-
const out = /* @__PURE__ */ new Map();
|
|
1876
|
-
if (!arrayExpr || arrayExpr.type !== "ArrayExpression") return out;
|
|
1877
|
-
for (const el of arrayExpr.elements ?? []) {
|
|
1878
|
-
if (!el || el.type !== "ObjectExpression") continue;
|
|
1879
|
-
const rules = getObjectPropertyValue(el, "rules");
|
|
1880
|
-
if (!rules || rules.type !== "ObjectExpression") continue;
|
|
1881
|
-
for (const prop of rules.properties ?? []) {
|
|
1882
|
-
if (!prop) continue;
|
|
1883
|
-
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
1884
|
-
const key = prop.key;
|
|
1885
|
-
if (!isStringLiteral(key)) continue;
|
|
1886
|
-
const k = key.value;
|
|
1887
|
-
if (typeof k !== "string" || !k.startsWith("uilint/")) continue;
|
|
1888
|
-
const id = k.slice("uilint/".length);
|
|
1889
|
-
if (!out.has(id)) out.set(id, prop.value);
|
|
1890
|
-
}
|
|
1568
|
+
return void 0;
|
|
1891
1569
|
}
|
|
1892
|
-
return out;
|
|
1893
1570
|
}
|
|
1894
|
-
function
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
if (!existing) return true;
|
|
1900
|
-
const desiredExpr = buildDesiredRuleValueExpression(r);
|
|
1901
|
-
const desiredAst = parseExpression(desiredExpr).$ast;
|
|
1902
|
-
return !astEquivalent(existing, desiredAst);
|
|
1903
|
-
});
|
|
1571
|
+
function hasUilintOverlayInstalled(projectPath) {
|
|
1572
|
+
const pkgPath = join4(projectPath, "package.json");
|
|
1573
|
+
const pkg = safeParseJson(pkgPath);
|
|
1574
|
+
if (!pkg) return false;
|
|
1575
|
+
return !!(pkg.dependencies?.["uilint-react"] || pkg.devDependencies?.["uilint-react"]);
|
|
1904
1576
|
}
|
|
1905
|
-
async function installEslintPlugin(opts) {
|
|
1906
|
-
const configPath = findEslintConfigFile(opts.projectPath);
|
|
1907
|
-
if (!configPath) {
|
|
1908
|
-
return {
|
|
1909
|
-
configFile: null,
|
|
1910
|
-
modified: false,
|
|
1911
|
-
missingRuleIds: [],
|
|
1912
|
-
configured: false
|
|
1913
|
-
};
|
|
1914
|
-
}
|
|
1915
|
-
const configFilename = getEslintConfigFilename(configPath);
|
|
1916
|
-
const original = readFileSync4(configPath, "utf-8");
|
|
1917
|
-
const isCommonJS = configPath.endsWith(".cjs");
|
|
1918
|
-
const ast = getUilintEslintConfigInfoFromSourceAst(original);
|
|
1919
|
-
if ("error" in ast) {
|
|
1920
|
-
return {
|
|
1921
|
-
configFile: configFilename,
|
|
1922
|
-
modified: false,
|
|
1923
|
-
missingRuleIds: [],
|
|
1924
|
-
configured: false,
|
|
1925
|
-
error: ast.error
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
const { info, mod, arrayExpr, kind } = ast;
|
|
1929
|
-
const configuredIds = info.configuredRuleIds;
|
|
1930
|
-
const missingRules = getMissingSelectedRules(
|
|
1931
|
-
opts.selectedRules,
|
|
1932
|
-
configuredIds
|
|
1933
|
-
);
|
|
1934
|
-
const rulesToUpdate = getRulesNeedingUpdate(
|
|
1935
|
-
opts.selectedRules,
|
|
1936
|
-
configuredIds,
|
|
1937
|
-
arrayExpr
|
|
1938
|
-
);
|
|
1939
|
-
let rulesToApply = [];
|
|
1940
|
-
if (!info.configured) {
|
|
1941
|
-
rulesToApply = opts.selectedRules;
|
|
1942
|
-
} else {
|
|
1943
|
-
rulesToApply = [...missingRules, ...rulesToUpdate];
|
|
1944
|
-
if (missingRules.length > 0 && !opts.force) {
|
|
1945
|
-
const ok = await opts.confirmAddMissingRules?.(
|
|
1946
|
-
configFilename,
|
|
1947
|
-
missingRules
|
|
1948
|
-
);
|
|
1949
|
-
if (!ok) {
|
|
1950
|
-
return {
|
|
1951
|
-
configFile: configFilename,
|
|
1952
|
-
modified: false,
|
|
1953
|
-
missingRuleIds: missingRules.map((r) => r.id),
|
|
1954
|
-
configured: true
|
|
1955
|
-
};
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
if (rulesToApply.length === 0) {
|
|
1960
|
-
return {
|
|
1961
|
-
configFile: configFilename,
|
|
1962
|
-
modified: false,
|
|
1963
|
-
missingRuleIds: missingRules.map((r) => r.id),
|
|
1964
|
-
configured: info.configured
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
let modifiedAst = false;
|
|
1968
|
-
const localRulesDir = join4(opts.projectPath, ".uilint", "rules");
|
|
1969
|
-
const workspaceRoot = findWorkspaceRoot(opts.projectPath);
|
|
1970
|
-
const workspaceRulesDir = join4(workspaceRoot, ".uilint", "rules");
|
|
1971
|
-
const rulesRoot = existsSync4(localRulesDir) ? opts.projectPath : workspaceRoot;
|
|
1972
|
-
const isTypeScriptConfig = configPath.endsWith(".ts");
|
|
1973
|
-
let fileExtension = isTypeScriptConfig ? "" : ".js";
|
|
1974
|
-
let ruleImportNames;
|
|
1975
|
-
if (kind === "esm") {
|
|
1976
|
-
const result = addLocalRuleImportsAst(
|
|
1977
|
-
mod,
|
|
1978
|
-
rulesToApply,
|
|
1979
|
-
configPath,
|
|
1980
|
-
rulesRoot,
|
|
1981
|
-
fileExtension
|
|
1982
|
-
);
|
|
1983
|
-
ruleImportNames = result.importNames;
|
|
1984
|
-
if (result.changed) modifiedAst = true;
|
|
1985
|
-
} else {
|
|
1986
|
-
const result = addLocalRuleRequiresAst(
|
|
1987
|
-
mod.$ast,
|
|
1988
|
-
rulesToApply,
|
|
1989
|
-
configPath,
|
|
1990
|
-
rulesRoot,
|
|
1991
|
-
fileExtension
|
|
1992
|
-
);
|
|
1993
|
-
ruleImportNames = result.importNames;
|
|
1994
|
-
if (result.changed) modifiedAst = true;
|
|
1995
|
-
}
|
|
1996
|
-
if (ruleImportNames && ruleImportNames.size > 0) {
|
|
1997
|
-
appendUilintConfigBlockToArray(arrayExpr, rulesToApply, ruleImportNames);
|
|
1998
|
-
modifiedAst = true;
|
|
1999
|
-
}
|
|
2000
|
-
if (!info.configured) {
|
|
2001
|
-
if (kind === "esm") {
|
|
2002
|
-
mod.imports.$add({
|
|
2003
|
-
imported: "createRule",
|
|
2004
|
-
local: "createRule",
|
|
2005
|
-
from: "uilint-eslint"
|
|
2006
|
-
});
|
|
2007
|
-
modifiedAst = true;
|
|
2008
|
-
} else {
|
|
2009
|
-
const stmtMod = parseModule2(
|
|
2010
|
-
`const { createRule } = require("uilint-eslint");`
|
|
2011
|
-
);
|
|
2012
|
-
const stmt = stmtMod.$ast.body?.[0];
|
|
2013
|
-
if (stmt) {
|
|
2014
|
-
let insertAt = 0;
|
|
2015
|
-
const first = mod.$ast.body?.[0];
|
|
2016
|
-
if (first?.type === "ExpressionStatement" && first.expression?.type === "StringLiteral" && first.expression.value === "use strict") {
|
|
2017
|
-
insertAt = 1;
|
|
2018
|
-
}
|
|
2019
|
-
mod.$ast.body.splice(insertAt, 0, stmt);
|
|
2020
|
-
modifiedAst = true;
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
const updated = modifiedAst ? generateCode(mod).code : original;
|
|
2025
|
-
if (updated !== original) {
|
|
2026
|
-
writeFileSync(configPath, updated, "utf-8");
|
|
2027
|
-
return {
|
|
2028
|
-
configFile: configFilename,
|
|
2029
|
-
modified: true,
|
|
2030
|
-
missingRuleIds: missingRules.map((r) => r.id),
|
|
2031
|
-
configured: getUilintEslintConfigInfoFromSource(updated).configured
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
return {
|
|
2035
|
-
configFile: configFilename,
|
|
2036
|
-
modified: false,
|
|
2037
|
-
missingRuleIds: missingRules.map((r) => r.id),
|
|
2038
|
-
configured: getUilintEslintConfigInfoFromSource(updated).configured
|
|
2039
|
-
};
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
// src/commands/install/analyze.ts
|
|
2043
1577
|
async function analyze(projectPath = process.cwd()) {
|
|
2044
|
-
const workspaceRoot =
|
|
1578
|
+
const workspaceRoot = findWorkspaceRoot(projectPath);
|
|
2045
1579
|
const packageManager = detectPackageManager(projectPath);
|
|
2046
|
-
const cursorDir =
|
|
2047
|
-
const cursorDirExists =
|
|
2048
|
-
const styleguidePath =
|
|
2049
|
-
const styleguideExists =
|
|
2050
|
-
const commandsDir =
|
|
2051
|
-
const genstyleguideExists =
|
|
1580
|
+
const cursorDir = join4(projectPath, ".cursor");
|
|
1581
|
+
const cursorDirExists = existsSync4(cursorDir);
|
|
1582
|
+
const styleguidePath = join4(projectPath, ".uilint", "styleguide.md");
|
|
1583
|
+
const styleguideExists = existsSync4(styleguidePath);
|
|
1584
|
+
const commandsDir = join4(cursorDir, "commands");
|
|
1585
|
+
const genstyleguideExists = existsSync4(join4(commandsDir, "genstyleguide.md"));
|
|
2052
1586
|
const nextApps = [];
|
|
2053
1587
|
const directDetection = detectNextAppRouter(projectPath);
|
|
2054
1588
|
if (directDetection) {
|
|
2055
|
-
nextApps.push({
|
|
1589
|
+
nextApps.push({
|
|
1590
|
+
projectPath,
|
|
1591
|
+
detection: directDetection,
|
|
1592
|
+
hasUilintOverlay: hasUilintOverlayInstalled(projectPath)
|
|
1593
|
+
});
|
|
2056
1594
|
} else {
|
|
2057
1595
|
const matches = findNextAppRouterProjects(workspaceRoot, { maxDepth: 5 });
|
|
2058
1596
|
for (const match of matches) {
|
|
2059
1597
|
nextApps.push({
|
|
2060
1598
|
projectPath: match.projectPath,
|
|
2061
|
-
detection: match.detection
|
|
1599
|
+
detection: match.detection,
|
|
1600
|
+
hasUilintOverlay: hasUilintOverlayInstalled(match.projectPath)
|
|
2062
1601
|
});
|
|
2063
1602
|
}
|
|
2064
1603
|
}
|
|
2065
1604
|
const viteApps = [];
|
|
2066
1605
|
const directVite = detectViteReact(projectPath);
|
|
2067
1606
|
if (directVite) {
|
|
2068
|
-
viteApps.push({
|
|
1607
|
+
viteApps.push({
|
|
1608
|
+
projectPath,
|
|
1609
|
+
detection: directVite,
|
|
1610
|
+
hasUilintOverlay: hasUilintOverlayInstalled(projectPath)
|
|
1611
|
+
});
|
|
2069
1612
|
} else {
|
|
2070
1613
|
const matches = findViteReactProjects(workspaceRoot, { maxDepth: 5 });
|
|
2071
1614
|
for (const match of matches) {
|
|
2072
1615
|
viteApps.push({
|
|
2073
1616
|
projectPath: match.projectPath,
|
|
2074
|
-
detection: match.detection
|
|
1617
|
+
detection: match.detection,
|
|
1618
|
+
hasUilintOverlay: hasUilintOverlayInstalled(match.projectPath)
|
|
2075
1619
|
});
|
|
2076
1620
|
}
|
|
2077
1621
|
}
|
|
@@ -2084,7 +1628,7 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
2084
1628
|
if (eslintConfigPath) {
|
|
2085
1629
|
eslintConfigFilename = getEslintConfigFilename(eslintConfigPath);
|
|
2086
1630
|
try {
|
|
2087
|
-
const source =
|
|
1631
|
+
const source = readFileSync4(eslintConfigPath, "utf-8");
|
|
2088
1632
|
const info = getUilintEslintConfigInfoFromSource(source);
|
|
2089
1633
|
hasRules = info.configuredRuleIds.size > 0;
|
|
2090
1634
|
configuredRuleIds = Array.from(info.configuredRuleIds);
|
|
@@ -2122,49 +1666,50 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
2122
1666
|
|
|
2123
1667
|
// src/commands/install/execute.ts
|
|
2124
1668
|
import {
|
|
2125
|
-
existsSync as
|
|
1669
|
+
existsSync as existsSync10,
|
|
2126
1670
|
mkdirSync,
|
|
2127
|
-
writeFileSync as
|
|
2128
|
-
readFileSync as
|
|
1671
|
+
writeFileSync as writeFileSync4,
|
|
1672
|
+
readFileSync as readFileSync8,
|
|
2129
1673
|
unlinkSync,
|
|
2130
|
-
chmodSync
|
|
1674
|
+
chmodSync,
|
|
1675
|
+
rmSync
|
|
2131
1676
|
} from "fs";
|
|
2132
|
-
import { dirname as
|
|
1677
|
+
import { dirname as dirname4 } from "path";
|
|
2133
1678
|
|
|
2134
1679
|
// src/utils/react-inject.ts
|
|
2135
|
-
import { existsSync as
|
|
2136
|
-
import { join as
|
|
2137
|
-
import { parseModule as
|
|
1680
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1681
|
+
import { join as join5, relative as relative3 } from "path";
|
|
1682
|
+
import { parseModule as parseModule2, generateCode } from "magicast";
|
|
2138
1683
|
function getDefaultCandidates(projectPath, appRoot) {
|
|
2139
1684
|
const viteMainCandidates = [
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
1685
|
+
join5(appRoot, "main.tsx"),
|
|
1686
|
+
join5(appRoot, "main.jsx"),
|
|
1687
|
+
join5(appRoot, "main.ts"),
|
|
1688
|
+
join5(appRoot, "main.js")
|
|
2144
1689
|
];
|
|
2145
1690
|
const existingViteMain = viteMainCandidates.filter(
|
|
2146
|
-
(rel) =>
|
|
1691
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
2147
1692
|
);
|
|
2148
1693
|
if (existingViteMain.length > 0) return existingViteMain;
|
|
2149
|
-
const viteAppCandidates = [
|
|
1694
|
+
const viteAppCandidates = [join5(appRoot, "App.tsx"), join5(appRoot, "App.jsx")];
|
|
2150
1695
|
const existingViteApp = viteAppCandidates.filter(
|
|
2151
|
-
(rel) =>
|
|
1696
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
2152
1697
|
);
|
|
2153
1698
|
if (existingViteApp.length > 0) return existingViteApp;
|
|
2154
1699
|
const layoutCandidates = [
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
1700
|
+
join5(appRoot, "layout.tsx"),
|
|
1701
|
+
join5(appRoot, "layout.jsx"),
|
|
1702
|
+
join5(appRoot, "layout.ts"),
|
|
1703
|
+
join5(appRoot, "layout.js")
|
|
2159
1704
|
];
|
|
2160
1705
|
const existingLayouts = layoutCandidates.filter(
|
|
2161
|
-
(rel) =>
|
|
1706
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
2162
1707
|
);
|
|
2163
1708
|
if (existingLayouts.length > 0) {
|
|
2164
1709
|
return existingLayouts;
|
|
2165
1710
|
}
|
|
2166
|
-
const pageCandidates = [
|
|
2167
|
-
return pageCandidates.filter((rel) =>
|
|
1711
|
+
const pageCandidates = [join5(appRoot, "page.tsx"), join5(appRoot, "page.jsx")];
|
|
1712
|
+
return pageCandidates.filter((rel) => existsSync5(join5(projectPath, rel)));
|
|
2168
1713
|
}
|
|
2169
1714
|
function isUseClientDirective(stmt) {
|
|
2170
1715
|
return stmt?.type === "ExpressionStatement" && stmt.expression?.type === "StringLiteral" && stmt.expression.value === "use client";
|
|
@@ -2198,12 +1743,12 @@ function ensureNamedImport(program, from, name) {
|
|
|
2198
1743
|
(s) => s?.type === "ImportSpecifier" && (s.imported?.name === name || s.imported?.value === name)
|
|
2199
1744
|
);
|
|
2200
1745
|
if (has) return { changed: false };
|
|
2201
|
-
const spec =
|
|
1746
|
+
const spec = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0]?.specifiers?.[0];
|
|
2202
1747
|
if (!spec) return { changed: false };
|
|
2203
1748
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
2204
1749
|
return { changed: true };
|
|
2205
1750
|
}
|
|
2206
|
-
const importDecl =
|
|
1751
|
+
const importDecl = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0];
|
|
2207
1752
|
if (!importDecl) return { changed: false };
|
|
2208
1753
|
const body = program.body ?? [];
|
|
2209
1754
|
let insertAt = 0;
|
|
@@ -2233,7 +1778,7 @@ function hasUILintDevtoolsJsx(program) {
|
|
|
2233
1778
|
function addDevtoolsElementNextJs(program) {
|
|
2234
1779
|
if (!program || program.type !== "Program") return { changed: false };
|
|
2235
1780
|
if (hasUILintDevtoolsJsx(program)) return { changed: false };
|
|
2236
|
-
const devtoolsMod =
|
|
1781
|
+
const devtoolsMod = parseModule2(
|
|
2237
1782
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
2238
1783
|
);
|
|
2239
1784
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -2271,12 +1816,12 @@ function addDevtoolsElementVite(program) {
|
|
|
2271
1816
|
const arg0 = node.arguments?.[0];
|
|
2272
1817
|
if (!arg0) return;
|
|
2273
1818
|
if (arg0.type !== "JSXElement" && arg0.type !== "JSXFragment") return;
|
|
2274
|
-
const devtoolsMod =
|
|
1819
|
+
const devtoolsMod = parseModule2(
|
|
2275
1820
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
2276
1821
|
);
|
|
2277
1822
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
2278
1823
|
if (!devtoolsJsx) return;
|
|
2279
|
-
const fragmentMod =
|
|
1824
|
+
const fragmentMod = parseModule2(
|
|
2280
1825
|
"const __fragment = <></>;"
|
|
2281
1826
|
);
|
|
2282
1827
|
const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -2296,7 +1841,7 @@ function ensureSideEffectImport(program, from) {
|
|
|
2296
1841
|
if (!program || program.type !== "Program") return { changed: false };
|
|
2297
1842
|
const existing = findImportDeclaration(program, from);
|
|
2298
1843
|
if (existing) return { changed: false };
|
|
2299
|
-
const importDecl =
|
|
1844
|
+
const importDecl = parseModule2(`import "${from}";`).$ast.body?.[0];
|
|
2300
1845
|
if (!importDecl) return { changed: false };
|
|
2301
1846
|
const body = program.body ?? [];
|
|
2302
1847
|
let insertAt = 0;
|
|
@@ -2312,7 +1857,7 @@ function ensureSideEffectImport(program, from) {
|
|
|
2312
1857
|
function addDevtoolsToClientComponent(program) {
|
|
2313
1858
|
if (!program || program.type !== "Program") return { changed: false };
|
|
2314
1859
|
if (hasUILintDevtoolsJsx(program)) return { changed: false };
|
|
2315
|
-
const devtoolsMod =
|
|
1860
|
+
const devtoolsMod = parseModule2(
|
|
2316
1861
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
2317
1862
|
);
|
|
2318
1863
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -2338,7 +1883,7 @@ function addDevtoolsToClientComponent(program) {
|
|
|
2338
1883
|
const arg = node.argument;
|
|
2339
1884
|
if (!arg) return;
|
|
2340
1885
|
if (arg.type !== "JSXElement" && arg.type !== "JSXFragment") return;
|
|
2341
|
-
const fragmentMod =
|
|
1886
|
+
const fragmentMod = parseModule2("const __fragment = <></>;");
|
|
2342
1887
|
const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
2343
1888
|
if (!fragmentJsx) return;
|
|
2344
1889
|
fragmentJsx.children = [arg, devtoolsJsx];
|
|
@@ -2393,7 +1938,7 @@ function wrapChildrenWithProviders(program, providersImportPath) {
|
|
|
2393
1938
|
(child) => child?.type === "JSXExpressionContainer" && child.expression?.type === "Identifier" && child.expression.name === "children"
|
|
2394
1939
|
);
|
|
2395
1940
|
if (childrenIndex === -1) return;
|
|
2396
|
-
const providersMod =
|
|
1941
|
+
const providersMod = parseModule2(
|
|
2397
1942
|
"const __providers = <Providers>{children}</Providers>;"
|
|
2398
1943
|
);
|
|
2399
1944
|
const providersJsx = providersMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -2411,8 +1956,8 @@ function wrapChildrenWithProviders(program, providersImportPath) {
|
|
|
2411
1956
|
function findLayoutFile2(projectPath, appRoot) {
|
|
2412
1957
|
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
2413
1958
|
for (const ext of extensions) {
|
|
2414
|
-
const layoutPath =
|
|
2415
|
-
if (
|
|
1959
|
+
const layoutPath = join5(projectPath, appRoot, `layout${ext}`);
|
|
1960
|
+
if (existsSync5(layoutPath)) return layoutPath;
|
|
2416
1961
|
}
|
|
2417
1962
|
return null;
|
|
2418
1963
|
}
|
|
@@ -2423,32 +1968,32 @@ async function createProvidersAndModifyLayout(projectPath, appRoot) {
|
|
|
2423
1968
|
}
|
|
2424
1969
|
const isTypeScript = layoutPath.endsWith(".tsx") || layoutPath.endsWith(".ts");
|
|
2425
1970
|
const providersExt = isTypeScript ? ".tsx" : ".jsx";
|
|
2426
|
-
const providersPath =
|
|
2427
|
-
if (
|
|
1971
|
+
const providersPath = join5(projectPath, appRoot, `providers${providersExt}`);
|
|
1972
|
+
if (existsSync5(providersPath)) {
|
|
2428
1973
|
throw new Error(
|
|
2429
1974
|
`providers${providersExt} already exists. Please select it from the list instead.`
|
|
2430
1975
|
);
|
|
2431
1976
|
}
|
|
2432
1977
|
const providersContent = generateProvidersContent(isTypeScript);
|
|
2433
|
-
|
|
2434
|
-
const layoutContent =
|
|
1978
|
+
writeFileSync(providersPath, providersContent, "utf-8");
|
|
1979
|
+
const layoutContent = readFileSync5(layoutPath, "utf-8");
|
|
2435
1980
|
let layoutMod;
|
|
2436
1981
|
try {
|
|
2437
|
-
layoutMod =
|
|
1982
|
+
layoutMod = parseModule2(layoutContent);
|
|
2438
1983
|
} catch {
|
|
2439
1984
|
throw new Error(
|
|
2440
|
-
`Unable to parse ${
|
|
1985
|
+
`Unable to parse ${relative3(projectPath, layoutPath)} as JavaScript/TypeScript.`
|
|
2441
1986
|
);
|
|
2442
1987
|
}
|
|
2443
1988
|
const layoutProgram = layoutMod.$ast;
|
|
2444
1989
|
const wrapRes = wrapChildrenWithProviders(layoutProgram, "./providers");
|
|
2445
1990
|
if (wrapRes.changed) {
|
|
2446
|
-
const updatedLayout =
|
|
2447
|
-
|
|
1991
|
+
const updatedLayout = generateCode(layoutMod).code;
|
|
1992
|
+
writeFileSync(layoutPath, updatedLayout, "utf-8");
|
|
2448
1993
|
}
|
|
2449
1994
|
return {
|
|
2450
|
-
providersFile:
|
|
2451
|
-
layoutFile:
|
|
1995
|
+
providersFile: relative3(projectPath, providersPath),
|
|
1996
|
+
layoutFile: relative3(projectPath, layoutPath),
|
|
2452
1997
|
modified: true
|
|
2453
1998
|
};
|
|
2454
1999
|
}
|
|
@@ -2460,8 +2005,8 @@ async function installReactUILintOverlay(opts) {
|
|
|
2460
2005
|
);
|
|
2461
2006
|
const modifiedFiles = [];
|
|
2462
2007
|
if (result.modified) {
|
|
2463
|
-
modifiedFiles.push(
|
|
2464
|
-
modifiedFiles.push(
|
|
2008
|
+
modifiedFiles.push(join5(opts.projectPath, result.providersFile));
|
|
2009
|
+
modifiedFiles.push(join5(opts.projectPath, result.layoutFile));
|
|
2465
2010
|
}
|
|
2466
2011
|
return {
|
|
2467
2012
|
targetFile: result.providersFile,
|
|
@@ -2473,14 +2018,14 @@ async function installReactUILintOverlay(opts) {
|
|
|
2473
2018
|
}
|
|
2474
2019
|
if (opts.targetFile) {
|
|
2475
2020
|
const absTarget2 = opts.targetFile;
|
|
2476
|
-
const relTarget =
|
|
2477
|
-
if (!
|
|
2021
|
+
const relTarget = relative3(opts.projectPath, absTarget2);
|
|
2022
|
+
if (!existsSync5(absTarget2)) {
|
|
2478
2023
|
throw new Error(`Target file not found: ${relTarget}`);
|
|
2479
2024
|
}
|
|
2480
|
-
const original2 =
|
|
2025
|
+
const original2 = readFileSync5(absTarget2, "utf-8");
|
|
2481
2026
|
let mod2;
|
|
2482
2027
|
try {
|
|
2483
|
-
mod2 =
|
|
2028
|
+
mod2 = parseModule2(original2);
|
|
2484
2029
|
} catch {
|
|
2485
2030
|
throw new Error(
|
|
2486
2031
|
`Unable to parse ${relTarget} as JavaScript/TypeScript. Please update it manually.`
|
|
@@ -2503,10 +2048,10 @@ async function installReactUILintOverlay(opts) {
|
|
|
2503
2048
|
if (importRes2.changed) changed2 = true;
|
|
2504
2049
|
const addRes2 = addDevtoolsToClientComponent(program2);
|
|
2505
2050
|
if (addRes2.changed) changed2 = true;
|
|
2506
|
-
const updated2 = changed2 ?
|
|
2051
|
+
const updated2 = changed2 ? generateCode(mod2).code : original2;
|
|
2507
2052
|
const modified2 = updated2 !== original2;
|
|
2508
2053
|
if (modified2) {
|
|
2509
|
-
|
|
2054
|
+
writeFileSync(absTarget2, updated2, "utf-8");
|
|
2510
2055
|
}
|
|
2511
2056
|
return {
|
|
2512
2057
|
targetFile: relTarget,
|
|
@@ -2527,11 +2072,11 @@ async function installReactUILintOverlay(opts) {
|
|
|
2527
2072
|
} else {
|
|
2528
2073
|
chosen = candidates[0];
|
|
2529
2074
|
}
|
|
2530
|
-
const absTarget =
|
|
2531
|
-
const original =
|
|
2075
|
+
const absTarget = join5(opts.projectPath, chosen);
|
|
2076
|
+
const original = readFileSync5(absTarget, "utf-8");
|
|
2532
2077
|
let mod;
|
|
2533
2078
|
try {
|
|
2534
|
-
mod =
|
|
2079
|
+
mod = parseModule2(original);
|
|
2535
2080
|
} catch {
|
|
2536
2081
|
throw new Error(
|
|
2537
2082
|
`Unable to parse ${chosen} as JavaScript/TypeScript. Please update it manually.`
|
|
@@ -2547,10 +2092,10 @@ async function installReactUILintOverlay(opts) {
|
|
|
2547
2092
|
const mode = opts.mode ?? "next";
|
|
2548
2093
|
const addRes = mode === "vite" ? addDevtoolsElementVite(program) : addDevtoolsElementNextJs(program);
|
|
2549
2094
|
if (addRes.changed) changed = true;
|
|
2550
|
-
const updated = changed ?
|
|
2095
|
+
const updated = changed ? generateCode(mod).code : original;
|
|
2551
2096
|
const modified = updated !== original;
|
|
2552
2097
|
if (modified) {
|
|
2553
|
-
|
|
2098
|
+
writeFileSync(absTarget, updated, "utf-8");
|
|
2554
2099
|
}
|
|
2555
2100
|
return {
|
|
2556
2101
|
targetFile: chosen,
|
|
@@ -2559,16 +2104,53 @@ async function installReactUILintOverlay(opts) {
|
|
|
2559
2104
|
modifiedFiles: modified ? [absTarget] : []
|
|
2560
2105
|
};
|
|
2561
2106
|
}
|
|
2107
|
+
async function uninstallReactUILintOverlay(options) {
|
|
2108
|
+
const { projectPath, appRoot, mode = "next" } = options;
|
|
2109
|
+
const candidates = getDefaultCandidates(projectPath, appRoot);
|
|
2110
|
+
const modifiedFiles = [];
|
|
2111
|
+
for (const candidate of candidates) {
|
|
2112
|
+
const absPath = join5(projectPath, candidate);
|
|
2113
|
+
if (!existsSync5(absPath)) continue;
|
|
2114
|
+
try {
|
|
2115
|
+
const original = readFileSync5(absPath, "utf-8");
|
|
2116
|
+
let updated = original.replace(
|
|
2117
|
+
/^import\s+["']uilint-react\/devtools["'];?\s*$/gm,
|
|
2118
|
+
""
|
|
2119
|
+
);
|
|
2120
|
+
updated = updated.replace(
|
|
2121
|
+
/^import\s+\{[^}]*UILintProvider[^}]*\}\s+from\s+["']uilint-react["'];?\s*$/gm,
|
|
2122
|
+
""
|
|
2123
|
+
);
|
|
2124
|
+
updated = updated.replace(/<uilint-devtools\s*\/>/g, "");
|
|
2125
|
+
updated = updated.replace(/<uilint-devtools><\/uilint-devtools>/g, "");
|
|
2126
|
+
updated = updated.replace(/<uilint-devtools\s*>\s*<\/uilint-devtools>/g, "");
|
|
2127
|
+
updated = updated.replace(
|
|
2128
|
+
/<UILintProvider[^>]*>([\s\S]*?)<\/UILintProvider>/g,
|
|
2129
|
+
"$1"
|
|
2130
|
+
);
|
|
2131
|
+
updated = updated.replace(/\n{3,}/g, "\n\n");
|
|
2132
|
+
if (updated !== original) {
|
|
2133
|
+
writeFileSync(absPath, updated, "utf-8");
|
|
2134
|
+
modifiedFiles.push(absPath);
|
|
2135
|
+
}
|
|
2136
|
+
} catch {
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
success: true,
|
|
2141
|
+
modifiedFiles
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2562
2144
|
|
|
2563
2145
|
// src/utils/next-config-inject.ts
|
|
2564
|
-
import { existsSync as
|
|
2565
|
-
import { join as
|
|
2566
|
-
import { parseModule as
|
|
2567
|
-
var
|
|
2146
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
2147
|
+
import { join as join6 } from "path";
|
|
2148
|
+
import { parseModule as parseModule3, generateCode as generateCode2 } from "magicast";
|
|
2149
|
+
var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
|
|
2568
2150
|
function findNextConfigFile(projectPath) {
|
|
2569
|
-
for (const ext of
|
|
2570
|
-
const configPath =
|
|
2571
|
-
if (
|
|
2151
|
+
for (const ext of CONFIG_EXTENSIONS) {
|
|
2152
|
+
const configPath = join6(projectPath, `next.config${ext}`);
|
|
2153
|
+
if (existsSync6(configPath)) {
|
|
2572
2154
|
return configPath;
|
|
2573
2155
|
}
|
|
2574
2156
|
}
|
|
@@ -2578,10 +2160,10 @@ function getNextConfigFilename(configPath) {
|
|
|
2578
2160
|
const parts = configPath.split("/");
|
|
2579
2161
|
return parts[parts.length - 1] || "next.config.ts";
|
|
2580
2162
|
}
|
|
2581
|
-
function
|
|
2163
|
+
function isIdentifier(node, name) {
|
|
2582
2164
|
return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
|
|
2583
2165
|
}
|
|
2584
|
-
function
|
|
2166
|
+
function isStringLiteral(node) {
|
|
2585
2167
|
return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
|
|
2586
2168
|
}
|
|
2587
2169
|
function ensureEsmWithJsxLocImport(program) {
|
|
@@ -2594,12 +2176,12 @@ function ensureEsmWithJsxLocImport(program) {
|
|
|
2594
2176
|
(sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "withJsxLoc" || sp.imported?.value === "withJsxLoc")
|
|
2595
2177
|
);
|
|
2596
2178
|
if (has) return { changed: false };
|
|
2597
|
-
const spec =
|
|
2179
|
+
const spec = parseModule3('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0]?.specifiers?.[0];
|
|
2598
2180
|
if (!spec) return { changed: false };
|
|
2599
2181
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
2600
2182
|
return { changed: true };
|
|
2601
2183
|
}
|
|
2602
|
-
const importDecl =
|
|
2184
|
+
const importDecl = parseModule3('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0];
|
|
2603
2185
|
if (!importDecl) return { changed: false };
|
|
2604
2186
|
const body = program.body ?? [];
|
|
2605
2187
|
let insertAt = 0;
|
|
@@ -2615,14 +2197,14 @@ function ensureCjsWithJsxLocRequire(program) {
|
|
|
2615
2197
|
if (stmt?.type !== "VariableDeclaration") continue;
|
|
2616
2198
|
for (const decl of stmt.declarations ?? []) {
|
|
2617
2199
|
const init = decl?.init;
|
|
2618
|
-
if (init?.type === "CallExpression" &&
|
|
2200
|
+
if (init?.type === "CallExpression" && isIdentifier(init.callee, "require") && isStringLiteral(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin") {
|
|
2619
2201
|
if (decl.id?.type === "ObjectPattern") {
|
|
2620
2202
|
const has = (decl.id.properties ?? []).some((p) => {
|
|
2621
2203
|
if (p?.type !== "ObjectProperty" && p?.type !== "Property") return false;
|
|
2622
|
-
return
|
|
2204
|
+
return isIdentifier(p.key, "withJsxLoc");
|
|
2623
2205
|
});
|
|
2624
2206
|
if (has) return { changed: false };
|
|
2625
|
-
const prop =
|
|
2207
|
+
const prop = parseModule3('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
|
|
2626
2208
|
if (!prop) return { changed: false };
|
|
2627
2209
|
decl.id.properties = [...decl.id.properties ?? [], prop];
|
|
2628
2210
|
return { changed: true };
|
|
@@ -2631,7 +2213,7 @@ function ensureCjsWithJsxLocRequire(program) {
|
|
|
2631
2213
|
}
|
|
2632
2214
|
}
|
|
2633
2215
|
}
|
|
2634
|
-
const reqDecl =
|
|
2216
|
+
const reqDecl = parseModule3('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0];
|
|
2635
2217
|
if (!reqDecl) return { changed: false };
|
|
2636
2218
|
program.body.unshift(reqDecl);
|
|
2637
2219
|
return { changed: true };
|
|
@@ -2643,7 +2225,7 @@ function wrapEsmExportDefault(program) {
|
|
|
2643
2225
|
);
|
|
2644
2226
|
if (!exportDecl) return { changed: false };
|
|
2645
2227
|
const decl = exportDecl.declaration;
|
|
2646
|
-
if (decl?.type === "CallExpression" &&
|
|
2228
|
+
if (decl?.type === "CallExpression" && isIdentifier(decl.callee, "withJsxLoc")) {
|
|
2647
2229
|
return { changed: false };
|
|
2648
2230
|
}
|
|
2649
2231
|
exportDecl.declaration = {
|
|
@@ -2661,9 +2243,9 @@ function wrapCjsModuleExports(program) {
|
|
|
2661
2243
|
if (!expr || expr.type !== "AssignmentExpression") continue;
|
|
2662
2244
|
const left = expr.left;
|
|
2663
2245
|
const right = expr.right;
|
|
2664
|
-
const isModuleExports = left?.type === "MemberExpression" &&
|
|
2246
|
+
const isModuleExports = left?.type === "MemberExpression" && isIdentifier(left.object, "module") && isIdentifier(left.property, "exports");
|
|
2665
2247
|
if (!isModuleExports) continue;
|
|
2666
|
-
if (right?.type === "CallExpression" &&
|
|
2248
|
+
if (right?.type === "CallExpression" && isIdentifier(right.callee, "withJsxLoc")) {
|
|
2667
2249
|
return { changed: false };
|
|
2668
2250
|
}
|
|
2669
2251
|
expr.right = {
|
|
@@ -2681,10 +2263,10 @@ async function installJsxLocPlugin(opts) {
|
|
|
2681
2263
|
return { configFile: null, modified: false, modifiedFiles: [] };
|
|
2682
2264
|
}
|
|
2683
2265
|
const configFilename = getNextConfigFilename(configPath);
|
|
2684
|
-
const original =
|
|
2266
|
+
const original = readFileSync6(configPath, "utf-8");
|
|
2685
2267
|
let mod;
|
|
2686
2268
|
try {
|
|
2687
|
-
mod =
|
|
2269
|
+
mod = parseModule3(original);
|
|
2688
2270
|
} catch {
|
|
2689
2271
|
return { configFile: configFilename, modified: false, modifiedFiles: [] };
|
|
2690
2272
|
}
|
|
@@ -2702,23 +2284,69 @@ async function installJsxLocPlugin(opts) {
|
|
|
2702
2284
|
const wrapRes = wrapEsmExportDefault(program);
|
|
2703
2285
|
if (wrapRes.changed) changed = true;
|
|
2704
2286
|
}
|
|
2705
|
-
const updated = changed ?
|
|
2287
|
+
const updated = changed ? generateCode2(mod).code : original;
|
|
2706
2288
|
if (updated !== original) {
|
|
2707
|
-
|
|
2289
|
+
writeFileSync2(configPath, updated, "utf-8");
|
|
2708
2290
|
return { configFile: configFilename, modified: true, modifiedFiles: [configPath] };
|
|
2709
2291
|
}
|
|
2710
2292
|
return { configFile: configFilename, modified: false, modifiedFiles: [] };
|
|
2711
2293
|
}
|
|
2294
|
+
async function uninstallJsxLocPlugin(options) {
|
|
2295
|
+
const { projectPath } = options;
|
|
2296
|
+
const configPath = findNextConfigFile(projectPath);
|
|
2297
|
+
if (!configPath) {
|
|
2298
|
+
return {
|
|
2299
|
+
success: true,
|
|
2300
|
+
modifiedFiles: []
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
try {
|
|
2304
|
+
const original = readFileSync6(configPath, "utf-8");
|
|
2305
|
+
let updated = original.replace(
|
|
2306
|
+
/^import\s+\{[^}]*withJsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/next["'];?\s*$/gm,
|
|
2307
|
+
""
|
|
2308
|
+
);
|
|
2309
|
+
updated = updated.replace(
|
|
2310
|
+
/^const\s+\{[^}]*withJsxLoc[^}]*\}\s*=\s*require\s*\(\s*["']jsx-loc-plugin\/next["']\s*\)\s*;?\s*$/gm,
|
|
2311
|
+
""
|
|
2312
|
+
);
|
|
2313
|
+
updated = updated.replace(
|
|
2314
|
+
/export\s+default\s+withJsxLoc\s*\(\s*([\s\S]*?)\s*\)\s*;?/g,
|
|
2315
|
+
"export default $1;"
|
|
2316
|
+
);
|
|
2317
|
+
updated = updated.replace(
|
|
2318
|
+
/module\.exports\s*=\s*withJsxLoc\s*\(\s*([\s\S]*?)\s*\)\s*;?/g,
|
|
2319
|
+
"module.exports = $1;"
|
|
2320
|
+
);
|
|
2321
|
+
updated = updated.replace(/\n{3,}/g, "\n\n");
|
|
2322
|
+
if (updated !== original) {
|
|
2323
|
+
writeFileSync2(configPath, updated, "utf-8");
|
|
2324
|
+
return {
|
|
2325
|
+
success: true,
|
|
2326
|
+
modifiedFiles: [configPath]
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
return {
|
|
2330
|
+
success: true,
|
|
2331
|
+
modifiedFiles: []
|
|
2332
|
+
};
|
|
2333
|
+
} catch (error) {
|
|
2334
|
+
return {
|
|
2335
|
+
success: false,
|
|
2336
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2712
2340
|
|
|
2713
2341
|
// src/utils/vite-config-inject.ts
|
|
2714
|
-
import { existsSync as
|
|
2715
|
-
import { join as
|
|
2716
|
-
import { parseModule as
|
|
2717
|
-
var
|
|
2342
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
2343
|
+
import { join as join7 } from "path";
|
|
2344
|
+
import { parseModule as parseModule4, generateCode as generateCode3 } from "magicast";
|
|
2345
|
+
var CONFIG_EXTENSIONS2 = [".ts", ".mjs", ".js", ".cjs"];
|
|
2718
2346
|
function findViteConfigFile2(projectPath) {
|
|
2719
|
-
for (const ext of
|
|
2720
|
-
const configPath =
|
|
2721
|
-
if (
|
|
2347
|
+
for (const ext of CONFIG_EXTENSIONS2) {
|
|
2348
|
+
const configPath = join7(projectPath, `vite.config${ext}`);
|
|
2349
|
+
if (existsSync7(configPath)) return configPath;
|
|
2722
2350
|
}
|
|
2723
2351
|
return null;
|
|
2724
2352
|
}
|
|
@@ -2726,10 +2354,10 @@ function getViteConfigFilename(configPath) {
|
|
|
2726
2354
|
const parts = configPath.split("/");
|
|
2727
2355
|
return parts[parts.length - 1] || "vite.config.ts";
|
|
2728
2356
|
}
|
|
2729
|
-
function
|
|
2357
|
+
function isIdentifier2(node, name) {
|
|
2730
2358
|
return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
|
|
2731
2359
|
}
|
|
2732
|
-
function
|
|
2360
|
+
function isStringLiteral2(node) {
|
|
2733
2361
|
return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
|
|
2734
2362
|
}
|
|
2735
2363
|
function unwrapExpression(expr) {
|
|
@@ -2761,7 +2389,7 @@ function findExportedConfigObjectExpression(mod) {
|
|
|
2761
2389
|
if (decl.type === "ObjectExpression") {
|
|
2762
2390
|
return { kind: "esm", objExpr: decl, program };
|
|
2763
2391
|
}
|
|
2764
|
-
if (decl.type === "CallExpression" &&
|
|
2392
|
+
if (decl.type === "CallExpression" && isIdentifier2(decl.callee, "defineConfig") && unwrapExpression(decl.arguments?.[0])?.type === "ObjectExpression") {
|
|
2765
2393
|
return {
|
|
2766
2394
|
kind: "esm",
|
|
2767
2395
|
objExpr: unwrapExpression(decl.arguments?.[0]),
|
|
@@ -2776,12 +2404,12 @@ function findExportedConfigObjectExpression(mod) {
|
|
|
2776
2404
|
if (!expr || expr.type !== "AssignmentExpression") continue;
|
|
2777
2405
|
const left = expr.left;
|
|
2778
2406
|
const right = unwrapExpression(expr.right);
|
|
2779
|
-
const isModuleExports = left?.type === "MemberExpression" &&
|
|
2407
|
+
const isModuleExports = left?.type === "MemberExpression" && isIdentifier2(left.object, "module") && isIdentifier2(left.property, "exports");
|
|
2780
2408
|
if (!isModuleExports) continue;
|
|
2781
2409
|
if (right?.type === "ObjectExpression") {
|
|
2782
2410
|
return { kind: "cjs", objExpr: right, program };
|
|
2783
2411
|
}
|
|
2784
|
-
if (right?.type === "CallExpression" &&
|
|
2412
|
+
if (right?.type === "CallExpression" && isIdentifier2(right.callee, "defineConfig") && unwrapExpression(right.arguments?.[0])?.type === "ObjectExpression") {
|
|
2785
2413
|
return {
|
|
2786
2414
|
kind: "cjs",
|
|
2787
2415
|
objExpr: unwrapExpression(right.arguments?.[0]),
|
|
@@ -2797,7 +2425,7 @@ function getObjectProperty(obj, keyName) {
|
|
|
2797
2425
|
if (!prop) continue;
|
|
2798
2426
|
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
2799
2427
|
const key = prop.key;
|
|
2800
|
-
const keyMatch = key?.type === "Identifier" && key.name === keyName ||
|
|
2428
|
+
const keyMatch = key?.type === "Identifier" && key.name === keyName || isStringLiteral2(key) && key.value === keyName;
|
|
2801
2429
|
if (keyMatch) return prop;
|
|
2802
2430
|
}
|
|
2803
2431
|
return null;
|
|
@@ -2812,12 +2440,12 @@ function ensureEsmJsxLocImport(program) {
|
|
|
2812
2440
|
(sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "jsxLoc" || sp.imported?.value === "jsxLoc")
|
|
2813
2441
|
);
|
|
2814
2442
|
if (has) return { changed: false };
|
|
2815
|
-
const spec =
|
|
2443
|
+
const spec = parseModule4('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0]?.specifiers?.[0];
|
|
2816
2444
|
if (!spec) return { changed: false };
|
|
2817
2445
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
2818
2446
|
return { changed: true };
|
|
2819
2447
|
}
|
|
2820
|
-
const importDecl =
|
|
2448
|
+
const importDecl = parseModule4('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0];
|
|
2821
2449
|
if (!importDecl) return { changed: false };
|
|
2822
2450
|
const body = program.body ?? [];
|
|
2823
2451
|
let insertAt = 0;
|
|
@@ -2833,14 +2461,14 @@ function ensureCjsJsxLocRequire(program) {
|
|
|
2833
2461
|
if (stmt?.type !== "VariableDeclaration") continue;
|
|
2834
2462
|
for (const decl of stmt.declarations ?? []) {
|
|
2835
2463
|
const init = decl?.init;
|
|
2836
|
-
if (init?.type === "CallExpression" &&
|
|
2464
|
+
if (init?.type === "CallExpression" && isIdentifier2(init.callee, "require") && isStringLiteral2(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin/vite") {
|
|
2837
2465
|
if (decl.id?.type === "ObjectPattern") {
|
|
2838
2466
|
const has = (decl.id.properties ?? []).some((p) => {
|
|
2839
2467
|
if (p?.type !== "ObjectProperty" && p?.type !== "Property") return false;
|
|
2840
|
-
return
|
|
2468
|
+
return isIdentifier2(p.key, "jsxLoc");
|
|
2841
2469
|
});
|
|
2842
2470
|
if (has) return { changed: false };
|
|
2843
|
-
const prop =
|
|
2471
|
+
const prop = parseModule4('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
|
|
2844
2472
|
if (!prop) return { changed: false };
|
|
2845
2473
|
decl.id.properties = [...decl.id.properties ?? [], prop];
|
|
2846
2474
|
return { changed: true };
|
|
@@ -2849,7 +2477,7 @@ function ensureCjsJsxLocRequire(program) {
|
|
|
2849
2477
|
}
|
|
2850
2478
|
}
|
|
2851
2479
|
}
|
|
2852
|
-
const reqDecl =
|
|
2480
|
+
const reqDecl = parseModule4('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0];
|
|
2853
2481
|
if (!reqDecl) return { changed: false };
|
|
2854
2482
|
program.body.unshift(reqDecl);
|
|
2855
2483
|
return { changed: true };
|
|
@@ -2859,16 +2487,16 @@ function pluginsHasJsxLoc(arr) {
|
|
|
2859
2487
|
for (const el of arr.elements ?? []) {
|
|
2860
2488
|
const e = unwrapExpression(el);
|
|
2861
2489
|
if (!e) continue;
|
|
2862
|
-
if (e.type === "CallExpression" &&
|
|
2490
|
+
if (e.type === "CallExpression" && isIdentifier2(e.callee, "jsxLoc")) return true;
|
|
2863
2491
|
}
|
|
2864
2492
|
return false;
|
|
2865
2493
|
}
|
|
2866
2494
|
function ensurePluginsContainsJsxLoc(configObj) {
|
|
2867
2495
|
const pluginsProp = getObjectProperty(configObj, "plugins");
|
|
2868
2496
|
if (!pluginsProp) {
|
|
2869
|
-
const prop =
|
|
2497
|
+
const prop = parseModule4("export default { plugins: [jsxLoc()] };").$ast.body?.find((s) => s.type === "ExportDefaultDeclaration")?.declaration?.properties?.find((p) => {
|
|
2870
2498
|
const k = p?.key;
|
|
2871
|
-
return k?.type === "Identifier" && k.name === "plugins" ||
|
|
2499
|
+
return k?.type === "Identifier" && k.name === "plugins" || isStringLiteral2(k) && k.value === "plugins";
|
|
2872
2500
|
});
|
|
2873
2501
|
if (!prop) return { changed: false };
|
|
2874
2502
|
configObj.properties = [...configObj.properties ?? [], prop];
|
|
@@ -2878,12 +2506,12 @@ function ensurePluginsContainsJsxLoc(configObj) {
|
|
|
2878
2506
|
if (!value) return { changed: false };
|
|
2879
2507
|
if (value.type === "ArrayExpression") {
|
|
2880
2508
|
if (pluginsHasJsxLoc(value)) return { changed: false };
|
|
2881
|
-
const jsxLocCall2 =
|
|
2509
|
+
const jsxLocCall2 = parseModule4("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
|
|
2882
2510
|
if (!jsxLocCall2) return { changed: false };
|
|
2883
2511
|
value.elements.push(jsxLocCall2);
|
|
2884
2512
|
return { changed: true };
|
|
2885
2513
|
}
|
|
2886
|
-
const jsxLocCall =
|
|
2514
|
+
const jsxLocCall = parseModule4("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
|
|
2887
2515
|
if (!jsxLocCall) return { changed: false };
|
|
2888
2516
|
const spread = { type: "SpreadElement", argument: value };
|
|
2889
2517
|
pluginsProp.value = { type: "ArrayExpression", elements: [spread, jsxLocCall] };
|
|
@@ -2893,11 +2521,11 @@ async function installViteJsxLocPlugin(opts) {
|
|
|
2893
2521
|
const configPath = findViteConfigFile2(opts.projectPath);
|
|
2894
2522
|
if (!configPath) return { configFile: null, modified: false, modifiedFiles: [] };
|
|
2895
2523
|
const configFilename = getViteConfigFilename(configPath);
|
|
2896
|
-
const original =
|
|
2524
|
+
const original = readFileSync7(configPath, "utf-8");
|
|
2897
2525
|
const isCjs = configPath.endsWith(".cjs");
|
|
2898
2526
|
let mod;
|
|
2899
2527
|
try {
|
|
2900
|
-
mod =
|
|
2528
|
+
mod = parseModule4(original);
|
|
2901
2529
|
} catch {
|
|
2902
2530
|
return { configFile: configFilename, modified: false, modifiedFiles: [] };
|
|
2903
2531
|
}
|
|
@@ -2913,18 +2541,61 @@ async function installViteJsxLocPlugin(opts) {
|
|
|
2913
2541
|
}
|
|
2914
2542
|
const pluginsRes = ensurePluginsContainsJsxLoc(found.objExpr);
|
|
2915
2543
|
if (pluginsRes.changed) changed = true;
|
|
2916
|
-
const updated = changed ?
|
|
2544
|
+
const updated = changed ? generateCode3(mod).code : original;
|
|
2917
2545
|
if (updated !== original) {
|
|
2918
|
-
|
|
2546
|
+
writeFileSync3(configPath, updated, "utf-8");
|
|
2919
2547
|
return { configFile: configFilename, modified: true, modifiedFiles: [configPath] };
|
|
2920
2548
|
}
|
|
2921
2549
|
return { configFile: configFilename, modified: false, modifiedFiles: [] };
|
|
2922
2550
|
}
|
|
2551
|
+
async function uninstallViteJsxLocPlugin(options) {
|
|
2552
|
+
const { projectPath } = options;
|
|
2553
|
+
const configPath = findViteConfigFile2(projectPath);
|
|
2554
|
+
if (!configPath) {
|
|
2555
|
+
return {
|
|
2556
|
+
success: true,
|
|
2557
|
+
modifiedFiles: []
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
try {
|
|
2561
|
+
const original = readFileSync7(configPath, "utf-8");
|
|
2562
|
+
let updated = original.replace(
|
|
2563
|
+
/^import\s+\{[^}]*jsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/vite["'];?\s*$/gm,
|
|
2564
|
+
""
|
|
2565
|
+
);
|
|
2566
|
+
updated = updated.replace(
|
|
2567
|
+
/^import\s+jsxLoc\s+from\s+["']jsx-loc-plugin\/vite["'];?\s*$/gm,
|
|
2568
|
+
""
|
|
2569
|
+
);
|
|
2570
|
+
updated = updated.replace(
|
|
2571
|
+
/^const\s+\{[^}]*jsxLoc[^}]*\}\s*=\s*require\s*\(\s*["']jsx-loc-plugin\/vite["']\s*\)\s*;?\s*$/gm,
|
|
2572
|
+
""
|
|
2573
|
+
);
|
|
2574
|
+
updated = updated.replace(/jsxLoc\s*\(\s*\)\s*,?\s*/g, "");
|
|
2575
|
+
updated = updated.replace(/\n{3,}/g, "\n\n");
|
|
2576
|
+
if (updated !== original) {
|
|
2577
|
+
writeFileSync3(configPath, updated, "utf-8");
|
|
2578
|
+
return {
|
|
2579
|
+
success: true,
|
|
2580
|
+
modifiedFiles: [configPath]
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
return {
|
|
2584
|
+
success: true,
|
|
2585
|
+
modifiedFiles: []
|
|
2586
|
+
};
|
|
2587
|
+
} catch (error) {
|
|
2588
|
+
return {
|
|
2589
|
+
success: false,
|
|
2590
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2923
2594
|
|
|
2924
2595
|
// src/utils/next-routes.ts
|
|
2925
|
-
import { existsSync as
|
|
2596
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2926
2597
|
import { mkdir, writeFile } from "fs/promises";
|
|
2927
|
-
import { join as
|
|
2598
|
+
import { join as join8 } from "path";
|
|
2928
2599
|
var DEV_SOURCE_ROUTE_TS = `/**
|
|
2929
2600
|
* Dev-only API route for fetching source files
|
|
2930
2601
|
*
|
|
@@ -3380,40 +3051,56 @@ export async function GET(request: NextRequest) {
|
|
|
3380
3051
|
}
|
|
3381
3052
|
`;
|
|
3382
3053
|
async function writeRouteFile(absPath, relPath, content, opts) {
|
|
3383
|
-
if (
|
|
3054
|
+
if (existsSync8(absPath) && !opts.force) return;
|
|
3384
3055
|
await writeFile(absPath, content, "utf-8");
|
|
3385
3056
|
}
|
|
3386
3057
|
async function installNextUILintRoutes(opts) {
|
|
3387
|
-
const baseRel =
|
|
3388
|
-
const baseAbs =
|
|
3389
|
-
await mkdir(
|
|
3058
|
+
const baseRel = join8(opts.appRoot, "api", ".uilint");
|
|
3059
|
+
const baseAbs = join8(opts.projectPath, baseRel);
|
|
3060
|
+
await mkdir(join8(baseAbs, "source"), { recursive: true });
|
|
3390
3061
|
await writeRouteFile(
|
|
3391
|
-
|
|
3392
|
-
|
|
3062
|
+
join8(baseAbs, "source", "route.ts"),
|
|
3063
|
+
join8(baseRel, "source", "route.ts"),
|
|
3393
3064
|
DEV_SOURCE_ROUTE_TS,
|
|
3394
3065
|
opts
|
|
3395
3066
|
);
|
|
3396
|
-
await mkdir(
|
|
3067
|
+
await mkdir(join8(baseAbs, "screenshots"), { recursive: true });
|
|
3397
3068
|
await writeRouteFile(
|
|
3398
|
-
|
|
3399
|
-
|
|
3069
|
+
join8(baseAbs, "screenshots", "route.ts"),
|
|
3070
|
+
join8(baseRel, "screenshots", "route.ts"),
|
|
3400
3071
|
SCREENSHOT_ROUTE_TS,
|
|
3401
3072
|
opts
|
|
3402
3073
|
);
|
|
3403
3074
|
}
|
|
3075
|
+
async function uninstallNextUILintRoutes(options) {
|
|
3076
|
+
const { projectPath, appRoot } = options;
|
|
3077
|
+
const { rm } = await import("fs/promises");
|
|
3078
|
+
const baseAbs = join8(projectPath, appRoot, "api", ".uilint");
|
|
3079
|
+
try {
|
|
3080
|
+
if (existsSync8(baseAbs)) {
|
|
3081
|
+
await rm(baseAbs, { recursive: true, force: true });
|
|
3082
|
+
}
|
|
3083
|
+
return { success: true };
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
return {
|
|
3086
|
+
success: false,
|
|
3087
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3404
3091
|
|
|
3405
3092
|
// src/utils/prettier.ts
|
|
3406
|
-
import { existsSync as
|
|
3093
|
+
import { existsSync as existsSync9, utimesSync } from "fs";
|
|
3407
3094
|
import { spawn } from "child_process";
|
|
3408
|
-
import { join as
|
|
3095
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
3409
3096
|
function getPrettierPath(projectPath) {
|
|
3410
|
-
const localPath =
|
|
3411
|
-
if (
|
|
3097
|
+
const localPath = join9(projectPath, "node_modules", ".bin", "prettier");
|
|
3098
|
+
if (existsSync9(localPath)) return localPath;
|
|
3412
3099
|
let dir = projectPath;
|
|
3413
3100
|
for (let i = 0; i < 10; i++) {
|
|
3414
|
-
const binPath =
|
|
3415
|
-
if (
|
|
3416
|
-
const parent =
|
|
3101
|
+
const binPath = join9(dir, "node_modules", ".bin", "prettier");
|
|
3102
|
+
if (existsSync9(binPath)) return binPath;
|
|
3103
|
+
const parent = dirname3(dir);
|
|
3417
3104
|
if (parent === dir) break;
|
|
3418
3105
|
dir = parent;
|
|
3419
3106
|
}
|
|
@@ -3544,7 +3231,7 @@ function touchFiles(filePaths) {
|
|
|
3544
3231
|
const now = /* @__PURE__ */ new Date();
|
|
3545
3232
|
for (const filePath of filePaths) {
|
|
3546
3233
|
try {
|
|
3547
|
-
if (
|
|
3234
|
+
if (existsSync9(filePath)) {
|
|
3548
3235
|
utimesSync(filePath, now, now);
|
|
3549
3236
|
}
|
|
3550
3237
|
} catch {
|
|
@@ -3565,7 +3252,7 @@ async function executeAction(action, options) {
|
|
|
3565
3252
|
wouldDo: `Create directory: ${action.path}`
|
|
3566
3253
|
};
|
|
3567
3254
|
}
|
|
3568
|
-
if (!
|
|
3255
|
+
if (!existsSync10(action.path)) {
|
|
3569
3256
|
mkdirSync(action.path, { recursive: true });
|
|
3570
3257
|
}
|
|
3571
3258
|
return { action, success: true };
|
|
@@ -3578,11 +3265,11 @@ async function executeAction(action, options) {
|
|
|
3578
3265
|
wouldDo: `Create file: ${action.path}${action.permissions ? ` (mode: ${action.permissions.toString(8)})` : ""}`
|
|
3579
3266
|
};
|
|
3580
3267
|
}
|
|
3581
|
-
const dir =
|
|
3582
|
-
if (!
|
|
3268
|
+
const dir = dirname4(action.path);
|
|
3269
|
+
if (!existsSync10(dir)) {
|
|
3583
3270
|
mkdirSync(dir, { recursive: true });
|
|
3584
3271
|
}
|
|
3585
|
-
|
|
3272
|
+
writeFileSync4(action.path, action.content, "utf-8");
|
|
3586
3273
|
if (action.permissions) {
|
|
3587
3274
|
chmodSync(action.path, action.permissions);
|
|
3588
3275
|
}
|
|
@@ -3597,18 +3284,18 @@ async function executeAction(action, options) {
|
|
|
3597
3284
|
};
|
|
3598
3285
|
}
|
|
3599
3286
|
let existing = {};
|
|
3600
|
-
if (
|
|
3287
|
+
if (existsSync10(action.path)) {
|
|
3601
3288
|
try {
|
|
3602
|
-
existing = JSON.parse(
|
|
3289
|
+
existing = JSON.parse(readFileSync8(action.path, "utf-8"));
|
|
3603
3290
|
} catch {
|
|
3604
3291
|
}
|
|
3605
3292
|
}
|
|
3606
3293
|
const merged = deepMerge(existing, action.merge);
|
|
3607
|
-
const dir =
|
|
3608
|
-
if (!
|
|
3294
|
+
const dir = dirname4(action.path);
|
|
3295
|
+
if (!existsSync10(dir)) {
|
|
3609
3296
|
mkdirSync(dir, { recursive: true });
|
|
3610
3297
|
}
|
|
3611
|
-
|
|
3298
|
+
writeFileSync4(action.path, JSON.stringify(merged, null, 2), "utf-8");
|
|
3612
3299
|
return { action, success: true };
|
|
3613
3300
|
}
|
|
3614
3301
|
case "delete_file": {
|
|
@@ -3619,7 +3306,7 @@ async function executeAction(action, options) {
|
|
|
3619
3306
|
wouldDo: `Delete file: ${action.path}`
|
|
3620
3307
|
};
|
|
3621
3308
|
}
|
|
3622
|
-
if (
|
|
3309
|
+
if (existsSync10(action.path)) {
|
|
3623
3310
|
unlinkSync(action.path);
|
|
3624
3311
|
}
|
|
3625
3312
|
return { action, success: true };
|
|
@@ -3632,12 +3319,12 @@ async function executeAction(action, options) {
|
|
|
3632
3319
|
wouldDo: `Append to file: ${action.path}`
|
|
3633
3320
|
};
|
|
3634
3321
|
}
|
|
3635
|
-
if (
|
|
3636
|
-
const content =
|
|
3322
|
+
if (existsSync10(action.path)) {
|
|
3323
|
+
const content = readFileSync8(action.path, "utf-8");
|
|
3637
3324
|
if (action.ifNotContains && content.includes(action.ifNotContains)) {
|
|
3638
3325
|
return { action, success: true };
|
|
3639
3326
|
}
|
|
3640
|
-
|
|
3327
|
+
writeFileSync4(action.path, content + action.content, "utf-8");
|
|
3641
3328
|
}
|
|
3642
3329
|
return { action, success: true };
|
|
3643
3330
|
}
|
|
@@ -3656,6 +3343,25 @@ async function executeAction(action, options) {
|
|
|
3656
3343
|
case "install_next_routes": {
|
|
3657
3344
|
return await executeInstallNextRoutes(action, options);
|
|
3658
3345
|
}
|
|
3346
|
+
// Uninstall actions
|
|
3347
|
+
case "remove_eslint": {
|
|
3348
|
+
return await executeRemoveEslint(action, options);
|
|
3349
|
+
}
|
|
3350
|
+
case "remove_react": {
|
|
3351
|
+
return await executeRemoveReact(action, options);
|
|
3352
|
+
}
|
|
3353
|
+
case "remove_next_config": {
|
|
3354
|
+
return await executeRemoveNextConfig(action, options);
|
|
3355
|
+
}
|
|
3356
|
+
case "remove_vite_config": {
|
|
3357
|
+
return await executeRemoveViteConfig(action, options);
|
|
3358
|
+
}
|
|
3359
|
+
case "remove_next_routes": {
|
|
3360
|
+
return await executeRemoveNextRoutes(action, options);
|
|
3361
|
+
}
|
|
3362
|
+
case "remove_directory": {
|
|
3363
|
+
return await executeRemoveDirectory(action, options);
|
|
3364
|
+
}
|
|
3659
3365
|
default: {
|
|
3660
3366
|
const _exhaustive = action;
|
|
3661
3367
|
return {
|
|
@@ -3781,6 +3487,117 @@ async function executeInstallNextRoutes(action, options) {
|
|
|
3781
3487
|
});
|
|
3782
3488
|
return { action, success: true };
|
|
3783
3489
|
}
|
|
3490
|
+
async function executeRemoveEslint(action, options) {
|
|
3491
|
+
const { dryRun = false } = options;
|
|
3492
|
+
if (dryRun) {
|
|
3493
|
+
return {
|
|
3494
|
+
action,
|
|
3495
|
+
success: true,
|
|
3496
|
+
wouldDo: `Remove uilint ESLint rules from: ${action.configPath}`
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
const result = await uninstallEslintPlugin({
|
|
3500
|
+
projectPath: action.packagePath
|
|
3501
|
+
});
|
|
3502
|
+
return {
|
|
3503
|
+
action,
|
|
3504
|
+
success: result.success,
|
|
3505
|
+
error: result.error,
|
|
3506
|
+
modifiedFiles: result.modifiedFiles
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3509
|
+
async function executeRemoveReact(action, options) {
|
|
3510
|
+
const { dryRun = false } = options;
|
|
3511
|
+
if (dryRun) {
|
|
3512
|
+
return {
|
|
3513
|
+
action,
|
|
3514
|
+
success: true,
|
|
3515
|
+
wouldDo: `Remove <uilint-devtools /> from: ${action.projectPath}`
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
3518
|
+
const result = await uninstallReactUILintOverlay({
|
|
3519
|
+
projectPath: action.projectPath,
|
|
3520
|
+
appRoot: action.appRoot,
|
|
3521
|
+
mode: action.mode
|
|
3522
|
+
});
|
|
3523
|
+
return {
|
|
3524
|
+
action,
|
|
3525
|
+
success: result.success,
|
|
3526
|
+
error: result.error,
|
|
3527
|
+
modifiedFiles: result.modifiedFiles
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
async function executeRemoveNextConfig(action, options) {
|
|
3531
|
+
const { dryRun = false } = options;
|
|
3532
|
+
if (dryRun) {
|
|
3533
|
+
return {
|
|
3534
|
+
action,
|
|
3535
|
+
success: true,
|
|
3536
|
+
wouldDo: `Remove jsx-loc-plugin from next.config: ${action.projectPath}`
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
const result = await uninstallJsxLocPlugin({
|
|
3540
|
+
projectPath: action.projectPath
|
|
3541
|
+
});
|
|
3542
|
+
return {
|
|
3543
|
+
action,
|
|
3544
|
+
success: result.success,
|
|
3545
|
+
error: result.error,
|
|
3546
|
+
modifiedFiles: result.modifiedFiles
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
async function executeRemoveViteConfig(action, options) {
|
|
3550
|
+
const { dryRun = false } = options;
|
|
3551
|
+
if (dryRun) {
|
|
3552
|
+
return {
|
|
3553
|
+
action,
|
|
3554
|
+
success: true,
|
|
3555
|
+
wouldDo: `Remove jsx-loc-plugin from vite.config: ${action.projectPath}`
|
|
3556
|
+
};
|
|
3557
|
+
}
|
|
3558
|
+
const result = await uninstallViteJsxLocPlugin({
|
|
3559
|
+
projectPath: action.projectPath
|
|
3560
|
+
});
|
|
3561
|
+
return {
|
|
3562
|
+
action,
|
|
3563
|
+
success: result.success,
|
|
3564
|
+
error: result.error,
|
|
3565
|
+
modifiedFiles: result.modifiedFiles
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
async function executeRemoveNextRoutes(action, options) {
|
|
3569
|
+
const { dryRun = false } = options;
|
|
3570
|
+
if (dryRun) {
|
|
3571
|
+
return {
|
|
3572
|
+
action,
|
|
3573
|
+
success: true,
|
|
3574
|
+
wouldDo: `Remove Next.js API routes: ${action.projectPath}`
|
|
3575
|
+
};
|
|
3576
|
+
}
|
|
3577
|
+
const result = await uninstallNextUILintRoutes({
|
|
3578
|
+
projectPath: action.projectPath,
|
|
3579
|
+
appRoot: action.appRoot
|
|
3580
|
+
});
|
|
3581
|
+
return {
|
|
3582
|
+
action,
|
|
3583
|
+
success: result.success,
|
|
3584
|
+
error: result.error
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
async function executeRemoveDirectory(action, options) {
|
|
3588
|
+
const { dryRun = false } = options;
|
|
3589
|
+
if (dryRun) {
|
|
3590
|
+
return {
|
|
3591
|
+
action,
|
|
3592
|
+
success: true,
|
|
3593
|
+
wouldDo: `Remove directory: ${action.path}`
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
if (existsSync10(action.path)) {
|
|
3597
|
+
rmSync(action.path, { recursive: true, force: true });
|
|
3598
|
+
}
|
|
3599
|
+
return { action, success: true };
|
|
3600
|
+
}
|
|
3784
3601
|
function deepMerge(target, source) {
|
|
3785
3602
|
const result = { ...target };
|
|
3786
3603
|
for (const key of Object.keys(source)) {
|
|
@@ -4013,7 +3830,7 @@ async function execute(plan, options = {}) {
|
|
|
4013
3830
|
import { ruleRegistry as ruleRegistry3 } from "uilint-eslint";
|
|
4014
3831
|
|
|
4015
3832
|
// src/commands/install/installers/genstyleguide.ts
|
|
4016
|
-
import { join as
|
|
3833
|
+
import { join as join10 } from "path";
|
|
4017
3834
|
var genstyleguideInstaller = {
|
|
4018
3835
|
id: "genstyleguide",
|
|
4019
3836
|
name: "/genstyleguide command",
|
|
@@ -4023,7 +3840,7 @@ var genstyleguideInstaller = {
|
|
|
4023
3840
|
return true;
|
|
4024
3841
|
},
|
|
4025
3842
|
getTargets(project) {
|
|
4026
|
-
const commandPath =
|
|
3843
|
+
const commandPath = join10(project.cursorDir.path, "commands", "genstyleguide.md");
|
|
4027
3844
|
const isInstalled = project.commands.genstyleguide;
|
|
4028
3845
|
return [
|
|
4029
3846
|
{
|
|
@@ -4036,7 +3853,7 @@ var genstyleguideInstaller = {
|
|
|
4036
3853
|
},
|
|
4037
3854
|
plan(targets, config, project) {
|
|
4038
3855
|
const actions = [];
|
|
4039
|
-
const commandsDir =
|
|
3856
|
+
const commandsDir = join10(project.cursorDir.path, "commands");
|
|
4040
3857
|
if (!project.cursorDir.exists) {
|
|
4041
3858
|
actions.push({
|
|
4042
3859
|
type: "create_directory",
|
|
@@ -4049,7 +3866,7 @@ var genstyleguideInstaller = {
|
|
|
4049
3866
|
});
|
|
4050
3867
|
actions.push({
|
|
4051
3868
|
type: "create_file",
|
|
4052
|
-
path:
|
|
3869
|
+
path: join10(commandsDir, "genstyleguide.md"),
|
|
4053
3870
|
content: GENSTYLEGUIDE_COMMAND_MD
|
|
4054
3871
|
});
|
|
4055
3872
|
return {
|
|
@@ -4075,12 +3892,21 @@ var genstyleguideInstaller = {
|
|
|
4075
3892
|
type: "complete",
|
|
4076
3893
|
message: "Installed /genstyleguide command"
|
|
4077
3894
|
};
|
|
3895
|
+
},
|
|
3896
|
+
planUninstall(targets, project) {
|
|
3897
|
+
const actions = [];
|
|
3898
|
+
const commandPath = join10(project.cursorDir.path, "commands", "genstyleguide.md");
|
|
3899
|
+
actions.push({
|
|
3900
|
+
type: "delete_file",
|
|
3901
|
+
path: commandPath
|
|
3902
|
+
});
|
|
3903
|
+
return { actions };
|
|
4078
3904
|
}
|
|
4079
3905
|
};
|
|
4080
3906
|
|
|
4081
3907
|
// src/commands/install/installers/skill.ts
|
|
4082
|
-
import { existsSync as
|
|
4083
|
-
import { join as
|
|
3908
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3909
|
+
import { join as join11 } from "path";
|
|
4084
3910
|
var skillInstaller = {
|
|
4085
3911
|
id: "skill",
|
|
4086
3912
|
name: "UI Consistency Agent skill",
|
|
@@ -4090,9 +3916,9 @@ var skillInstaller = {
|
|
|
4090
3916
|
return true;
|
|
4091
3917
|
},
|
|
4092
3918
|
getTargets(project) {
|
|
4093
|
-
const skillsDir =
|
|
4094
|
-
const skillMdPath =
|
|
4095
|
-
const isInstalled =
|
|
3919
|
+
const skillsDir = join11(project.cursorDir.path, "skills", "ui-consistency-enforcer");
|
|
3920
|
+
const skillMdPath = join11(skillsDir, "SKILL.md");
|
|
3921
|
+
const isInstalled = existsSync11(skillMdPath);
|
|
4096
3922
|
return [
|
|
4097
3923
|
{
|
|
4098
3924
|
id: "ui-consistency-skill",
|
|
@@ -4111,21 +3937,21 @@ var skillInstaller = {
|
|
|
4111
3937
|
path: project.cursorDir.path
|
|
4112
3938
|
});
|
|
4113
3939
|
}
|
|
4114
|
-
const skillsDir =
|
|
3940
|
+
const skillsDir = join11(project.cursorDir.path, "skills");
|
|
4115
3941
|
actions.push({
|
|
4116
3942
|
type: "create_directory",
|
|
4117
3943
|
path: skillsDir
|
|
4118
3944
|
});
|
|
4119
3945
|
try {
|
|
4120
3946
|
const skill = loadSkill("ui-consistency-enforcer");
|
|
4121
|
-
const skillDir =
|
|
3947
|
+
const skillDir = join11(skillsDir, skill.name);
|
|
4122
3948
|
actions.push({
|
|
4123
3949
|
type: "create_directory",
|
|
4124
3950
|
path: skillDir
|
|
4125
3951
|
});
|
|
4126
3952
|
for (const file of skill.files) {
|
|
4127
|
-
const filePath =
|
|
4128
|
-
const fileDir =
|
|
3953
|
+
const filePath = join11(skillDir, file.relativePath);
|
|
3954
|
+
const fileDir = join11(
|
|
4129
3955
|
skillDir,
|
|
4130
3956
|
file.relativePath.split("/").slice(0, -1).join("/")
|
|
4131
3957
|
);
|
|
@@ -4178,11 +4004,20 @@ var skillInstaller = {
|
|
|
4178
4004
|
error: error instanceof Error ? error.message : String(error)
|
|
4179
4005
|
};
|
|
4180
4006
|
}
|
|
4007
|
+
},
|
|
4008
|
+
planUninstall(targets, project) {
|
|
4009
|
+
const actions = [];
|
|
4010
|
+
const skillDir = join11(project.cursorDir.path, "skills", "ui-consistency-enforcer");
|
|
4011
|
+
actions.push({
|
|
4012
|
+
type: "remove_directory",
|
|
4013
|
+
path: skillDir
|
|
4014
|
+
});
|
|
4015
|
+
return { actions };
|
|
4181
4016
|
}
|
|
4182
4017
|
};
|
|
4183
4018
|
|
|
4184
4019
|
// src/commands/install/installers/eslint.ts
|
|
4185
|
-
import { join as
|
|
4020
|
+
import { join as join12 } from "path";
|
|
4186
4021
|
import { ruleRegistry as ruleRegistry2, getRulesByCategory as getRulesByCategory2 } from "uilint-eslint";
|
|
4187
4022
|
function getUpgradeInfo(configuredRuleIds) {
|
|
4188
4023
|
const configuredSet = new Set(configuredRuleIds);
|
|
@@ -4358,7 +4193,7 @@ var eslintInstaller = {
|
|
|
4358
4193
|
for (const target of targets) {
|
|
4359
4194
|
const pkgInfo = project.packages.find((p) => p.path === target.path);
|
|
4360
4195
|
if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
|
|
4361
|
-
const rulesDir =
|
|
4196
|
+
const rulesDir = join12(target.path, ".uilint", "rules");
|
|
4362
4197
|
actions.push({
|
|
4363
4198
|
type: "create_directory",
|
|
4364
4199
|
path: rulesDir
|
|
@@ -4376,7 +4211,7 @@ var eslintInstaller = {
|
|
|
4376
4211
|
hasExistingRules: pkgInfo.hasUilintRules
|
|
4377
4212
|
});
|
|
4378
4213
|
}
|
|
4379
|
-
const gitignorePath =
|
|
4214
|
+
const gitignorePath = join12(project.workspaceRoot, ".gitignore");
|
|
4380
4215
|
actions.push({
|
|
4381
4216
|
type: "append_to_file",
|
|
4382
4217
|
path: gitignorePath,
|
|
@@ -4407,6 +4242,24 @@ var eslintInstaller = {
|
|
|
4407
4242
|
type: "complete",
|
|
4408
4243
|
message: `ESLint plugin installed in ${targets.length} package(s)`
|
|
4409
4244
|
};
|
|
4245
|
+
},
|
|
4246
|
+
planUninstall(targets, project) {
|
|
4247
|
+
const actions = [];
|
|
4248
|
+
for (const target of targets) {
|
|
4249
|
+
const pkgInfo = project.packages.find((p) => p.path === target.path);
|
|
4250
|
+
if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
|
|
4251
|
+
actions.push({
|
|
4252
|
+
type: "remove_eslint",
|
|
4253
|
+
packagePath: target.path,
|
|
4254
|
+
configPath: pkgInfo.eslintConfigPath
|
|
4255
|
+
});
|
|
4256
|
+
const rulesDir = join12(target.path, ".uilint", "rules");
|
|
4257
|
+
actions.push({
|
|
4258
|
+
type: "remove_directory",
|
|
4259
|
+
path: rulesDir
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
return { actions };
|
|
4410
4263
|
}
|
|
4411
4264
|
};
|
|
4412
4265
|
|
|
@@ -4425,8 +4278,7 @@ var viteOverlayInstaller = {
|
|
|
4425
4278
|
label: app.projectPath.split("/").pop() || app.projectPath,
|
|
4426
4279
|
path: app.projectPath,
|
|
4427
4280
|
hint: "React + Vite",
|
|
4428
|
-
isInstalled:
|
|
4429
|
-
// TODO: Detect if already installed
|
|
4281
|
+
isInstalled: app.hasUilintOverlay
|
|
4430
4282
|
}));
|
|
4431
4283
|
},
|
|
4432
4284
|
plan(targets, config, project) {
|
|
@@ -4485,6 +4337,27 @@ var viteOverlayInstaller = {
|
|
|
4485
4337
|
type: "complete",
|
|
4486
4338
|
message: "Vite overlay installed"
|
|
4487
4339
|
};
|
|
4340
|
+
},
|
|
4341
|
+
planUninstall(targets, project) {
|
|
4342
|
+
const actions = [];
|
|
4343
|
+
if (targets.length === 0) return { actions };
|
|
4344
|
+
const target = targets[0];
|
|
4345
|
+
const appInfo = project.viteApps.find(
|
|
4346
|
+
(app) => app.projectPath === target.path
|
|
4347
|
+
);
|
|
4348
|
+
if (!appInfo) return { actions };
|
|
4349
|
+
const { projectPath, detection } = appInfo;
|
|
4350
|
+
actions.push({
|
|
4351
|
+
type: "remove_react",
|
|
4352
|
+
projectPath,
|
|
4353
|
+
appRoot: detection.entryRoot,
|
|
4354
|
+
mode: "vite"
|
|
4355
|
+
});
|
|
4356
|
+
actions.push({
|
|
4357
|
+
type: "remove_vite_config",
|
|
4358
|
+
projectPath
|
|
4359
|
+
});
|
|
4360
|
+
return { actions };
|
|
4488
4361
|
}
|
|
4489
4362
|
};
|
|
4490
4363
|
|
|
@@ -4566,23 +4439,41 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4566
4439
|
InstallApp,
|
|
4567
4440
|
{
|
|
4568
4441
|
projectPromise,
|
|
4569
|
-
onComplete: async (selections, eslintRules, injectionPointConfig) => {
|
|
4442
|
+
onComplete: async (selections, eslintRules, injectionPointConfig, uninstallSelections) => {
|
|
4570
4443
|
const project = await projectPromise;
|
|
4571
4444
|
const choices = selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig);
|
|
4572
|
-
|
|
4573
|
-
|
|
4445
|
+
const hasInstalls = choices.items.length > 0;
|
|
4446
|
+
const hasUninstalls = uninstallSelections && uninstallSelections.length > 0;
|
|
4447
|
+
if (!hasInstalls && !hasUninstalls) {
|
|
4448
|
+
console.log("\nNo changes selected");
|
|
4574
4449
|
process.exit(0);
|
|
4575
4450
|
}
|
|
4576
4451
|
const { createPlan } = await import("./plan-SIXVCXCK.js");
|
|
4577
4452
|
const plan = createPlan(project, choices, { force: options.force });
|
|
4453
|
+
if (hasUninstalls && uninstallSelections) {
|
|
4454
|
+
for (const selection of uninstallSelections) {
|
|
4455
|
+
if (!selection.selected || selection.targets.length === 0) continue;
|
|
4456
|
+
const { installer, targets } = selection;
|
|
4457
|
+
if (installer.planUninstall) {
|
|
4458
|
+
const uninstallPlan = installer.planUninstall(targets, project);
|
|
4459
|
+
plan.actions = [...uninstallPlan.actions, ...plan.actions];
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4578
4463
|
const result = await execute(plan, {
|
|
4579
4464
|
...executeOptions,
|
|
4580
4465
|
projectPath: project.projectPath
|
|
4581
4466
|
});
|
|
4582
4467
|
if (result.success) {
|
|
4583
|
-
|
|
4468
|
+
if (hasInstalls && hasUninstalls) {
|
|
4469
|
+
console.log("\n\u2713 Changes applied successfully!");
|
|
4470
|
+
} else if (hasUninstalls) {
|
|
4471
|
+
console.log("\n\u2713 Uninstallation completed successfully!");
|
|
4472
|
+
} else {
|
|
4473
|
+
console.log("\n\u2713 Installation completed successfully!");
|
|
4474
|
+
}
|
|
4584
4475
|
} else {
|
|
4585
|
-
console.log("\n\u26A0
|
|
4476
|
+
console.log("\n\u26A0 Operation completed with errors");
|
|
4586
4477
|
}
|
|
4587
4478
|
process.exit(result.success ? 0 : 1);
|
|
4588
4479
|
},
|
|
@@ -4598,4 +4489,4 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4598
4489
|
export {
|
|
4599
4490
|
installUI
|
|
4600
4491
|
};
|
|
4601
|
-
//# sourceMappingURL=install-ui-
|
|
4492
|
+
//# sourceMappingURL=install-ui-TXV7A34M.js.map
|