uilint 0.2.21 → 0.2.22

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.
@@ -137,8 +137,11 @@ function ProjectSelector({
137
137
  // src/commands/install/components/MultiSelect.tsx
138
138
  import { useState as useState3, useCallback } from "react";
139
139
  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 }) {
140
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
141
+ function StatusIndicator({ status, isSelected, isMarkedForUninstall }) {
142
+ if (isMarkedForUninstall) {
143
+ return /* @__PURE__ */ jsx3(Text3, { color: "red", children: "\u2717" });
144
+ }
142
145
  if (status === "installed") {
143
146
  return /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713" });
144
147
  }
@@ -153,7 +156,10 @@ function StatusIndicator({ status, isSelected }) {
153
156
  }
154
157
  return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u25CB" });
155
158
  }
156
- function StatusLabel({ status }) {
159
+ function StatusLabel({ status, isMarkedForUninstall }) {
160
+ if (isMarkedForUninstall) {
161
+ return /* @__PURE__ */ jsx3(Text3, { color: "red", dimColor: true, children: "uninstall" });
162
+ }
157
163
  if (status === "installed") {
158
164
  return /* @__PURE__ */ jsx3(Text3, { color: "green", dimColor: true, children: "installed" });
159
165
  }
@@ -177,6 +183,7 @@ function ConfigSelector({
177
183
  items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
178
184
  );
179
185
  });
186
+ const [markedForUninstall, setMarkedForUninstall] = useState3(/* @__PURE__ */ new Set());
180
187
  const categories = Array.from(new Set(items.map((item) => item.category)));
181
188
  const itemsByCategory = /* @__PURE__ */ new Map();
182
189
  for (const cat of categories) {
@@ -185,8 +192,19 @@ function ConfigSelector({
185
192
  const flatItems = items;
186
193
  const handleToggle = useCallback(() => {
187
194
  const item = flatItems[cursor];
188
- const canToggle = item && !item.disabled && item.status !== "installed";
189
- if (!canToggle) return;
195
+ if (!item || item.disabled) return;
196
+ if (item.status === "installed") {
197
+ setMarkedForUninstall((prev) => {
198
+ const next = new Set(prev);
199
+ if (next.has(item.id)) {
200
+ next.delete(item.id);
201
+ } else {
202
+ next.add(item.id);
203
+ }
204
+ return next;
205
+ });
206
+ return;
207
+ }
190
208
  setSelected((prev) => {
191
209
  const next = new Set(prev);
192
210
  if (next.has(item.id)) {
@@ -205,7 +223,7 @@ function ConfigSelector({
205
223
  } else if (input === " ") {
206
224
  handleToggle();
207
225
  } else if (key.return) {
208
- onSubmit(Array.from(selected));
226
+ onSubmit(Array.from(selected), Array.from(markedForUninstall));
209
227
  } else if (input === "q" || key.escape) {
210
228
  onCancel?.();
211
229
  exit();
@@ -215,8 +233,10 @@ function ConfigSelector({
215
233
  items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
216
234
  )
217
235
  );
236
+ setMarkedForUninstall(/* @__PURE__ */ new Set());
218
237
  } else if (input === "n") {
219
238
  setSelected(/* @__PURE__ */ new Set());
239
+ setMarkedForUninstall(/* @__PURE__ */ new Set());
220
240
  }
221
241
  });
222
242
  let globalIndex = 0;
@@ -234,20 +254,21 @@ function ConfigSelector({
234
254
  const itemIndex = globalIndex++;
235
255
  const isCursor = itemIndex === cursor;
236
256
  const isItemSelected = selected.has(item.id);
237
- const isDisabled = item.disabled || item.status === "installed";
257
+ const isItemMarkedForUninstall = markedForUninstall.has(item.id);
258
+ const isDisabled = item.disabled === true;
238
259
  return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 2, children: [
239
260
  /* @__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 }) }),
261
+ /* @__PURE__ */ jsx3(Box2, { width: 2, children: /* @__PURE__ */ jsx3(StatusIndicator, { status: item.status, isSelected: isItemSelected, isMarkedForUninstall: isItemMarkedForUninstall }) }),
241
262
  /* @__PURE__ */ jsx3(Box2, { width: 28, children: /* @__PURE__ */ jsx3(
242
263
  Text3,
243
264
  {
244
- color: isDisabled ? void 0 : isCursor ? "cyan" : void 0,
245
- dimColor: isDisabled,
265
+ color: isItemMarkedForUninstall ? "red" : isDisabled ? void 0 : isCursor ? "cyan" : void 0,
266
+ dimColor: isDisabled && !isItemMarkedForUninstall,
246
267
  children: item.label
247
268
  }
248
269
  ) }),
249
270
  /* @__PURE__ */ jsx3(Box2, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint || "" }) }),
250
- /* @__PURE__ */ jsx3(StatusLabel, { status: item.status })
271
+ /* @__PURE__ */ jsx3(StatusLabel, { status: item.status, isMarkedForUninstall: isItemMarkedForUninstall })
251
272
  ] }, item.id);
252
273
  })
253
274
  ] }, category);
@@ -273,10 +294,11 @@ function ConfigSelector({
273
294
  ] }) }),
274
295
  /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text3, { children: [
275
296
  /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: selected.size }),
276
- /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
277
- " item",
278
- selected.size !== 1 ? "s" : "",
279
- " selected"
297
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " to install" }),
298
+ markedForUninstall.size > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
299
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ", " }),
300
+ /* @__PURE__ */ jsx3(Text3, { color: "red", children: markedForUninstall.size }),
301
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " to uninstall" })
280
302
  ] })
281
303
  ] }) })
282
304
  ] });
@@ -892,6 +914,32 @@ var nextOverlayInstaller = {
892
914
  type: "complete",
893
915
  message: "Next.js overlay installed"
894
916
  };
917
+ },
918
+ planUninstall(targets, project) {
919
+ const actions = [];
920
+ if (targets.length === 0) return { actions };
921
+ const target = targets[0];
922
+ const appInfo = project.nextApps.find(
923
+ (app) => app.projectPath === target.path
924
+ );
925
+ if (!appInfo) return { actions };
926
+ const { projectPath, detection } = appInfo;
927
+ actions.push({
928
+ type: "remove_react",
929
+ projectPath,
930
+ appRoot: detection.appRoot,
931
+ mode: "next"
932
+ });
933
+ actions.push({
934
+ type: "remove_next_config",
935
+ projectPath
936
+ });
937
+ actions.push({
938
+ type: "remove_next_routes",
939
+ projectPath,
940
+ appRoot: detection.appRoot
941
+ });
942
+ return { actions };
895
943
  }
896
944
  };
897
945
 
@@ -1029,6 +1077,7 @@ function InstallApp({
1029
1077
  const [selections, setSelections] = useState6([]);
1030
1078
  const [configItems, setConfigItems] = useState6([]);
1031
1079
  const [selectedFeatureIds, setSelectedFeatureIds] = useState6([]);
1080
+ const [uninstallFeatureIds, setUninstallFeatureIds] = useState6([]);
1032
1081
  const [error, setError] = useState6(null);
1033
1082
  const [injectionPoints, setInjectionPoints] = useState6([]);
1034
1083
  const [selectedInjectionPoint, setSelectedInjectionPoint] = useState6(void 0);
@@ -1086,8 +1135,9 @@ function InstallApp({
1086
1135
  setPhase("select-project");
1087
1136
  }
1088
1137
  };
1089
- const handleFeatureSubmit = (selectedIds) => {
1138
+ const handleFeatureSubmit = (selectedIds, uninstallIds) => {
1090
1139
  setSelectedFeatureIds(selectedIds);
1140
+ setUninstallFeatureIds(uninstallIds);
1091
1141
  const nextSelected = selectedIds.some((id) => id.startsWith("next:"));
1092
1142
  if (nextSelected && project && selectedProject) {
1093
1143
  const appInfo = project.nextApps.find(
@@ -1143,6 +1193,7 @@ function InstallApp({
1143
1193
  };
1144
1194
  const finishInstallation = (selectedIds, eslintRules, injectionConfig) => {
1145
1195
  const selectedSet = new Set(selectedIds);
1196
+ const uninstallSet = new Set(uninstallFeatureIds);
1146
1197
  const updatedSelections = selections.map((sel) => {
1147
1198
  const selectedTargets = sel.targets.filter(
1148
1199
  (t) => selectedSet.has(`${sel.installer.id}:${t.id}`)
@@ -1153,8 +1204,18 @@ function InstallApp({
1153
1204
  selected: selectedTargets.length > 0
1154
1205
  };
1155
1206
  });
1207
+ const uninstallSelections = selections.map((sel) => {
1208
+ const uninstallTargets = sel.targets.filter(
1209
+ (t) => uninstallSet.has(`${sel.installer.id}:${t.id}`)
1210
+ );
1211
+ return {
1212
+ ...sel,
1213
+ targets: uninstallTargets,
1214
+ selected: uninstallTargets.length > 0
1215
+ };
1216
+ }).filter((sel) => sel.selected);
1156
1217
  setSelections(updatedSelections);
1157
- onComplete(updatedSelections, eslintRules, injectionConfig);
1218
+ onComplete(updatedSelections, eslintRules, injectionConfig, uninstallSelections.length > 0 ? uninstallSelections : void 0);
1158
1219
  };
1159
1220
  const handleCancel = () => {
1160
1221
  exit();
@@ -2038,6 +2099,65 @@ async function installEslintPlugin(opts) {
2038
2099
  configured: getUilintEslintConfigInfoFromSource(updated).configured
2039
2100
  };
2040
2101
  }
2102
+ async function uninstallEslintPlugin(options) {
2103
+ const { projectPath } = options;
2104
+ const configPath = findEslintConfigFile(projectPath);
2105
+ if (!configPath) {
2106
+ return {
2107
+ success: true,
2108
+ // Nothing to uninstall
2109
+ modifiedFiles: []
2110
+ };
2111
+ }
2112
+ try {
2113
+ const original = readFileSync4(configPath, "utf-8");
2114
+ let updated = original.replace(
2115
+ /^import\s+\{[^}]*\}\s+from\s+["'][^"']*\.uilint\/rules[^"']*["'];?\s*$/gm,
2116
+ ""
2117
+ );
2118
+ updated = updated.replace(
2119
+ /^import\s+\w+\s+from\s+["'][^"']*\.uilint\/rules[^"']*["'];?\s*$/gm,
2120
+ ""
2121
+ );
2122
+ updated = updated.replace(
2123
+ /^import\s+\{[^}]*\}\s+from\s+["']uilint-eslint["'];?\s*$/gm,
2124
+ ""
2125
+ );
2126
+ updated = updated.replace(
2127
+ /^const\s+\{[^}]*createRule[^}]*\}\s*=\s*require\s*\(\s*["']uilint-eslint["']\s*\)\s*;?\s*$/gm,
2128
+ ""
2129
+ );
2130
+ updated = updated.replace(
2131
+ /["']uilint\/[^"']+["']\s*:\s*["'][^"']+["']\s*,?\s*/g,
2132
+ ""
2133
+ );
2134
+ updated = updated.replace(
2135
+ /["']uilint\/[^"']+["']\s*:\s*\[[^\]]*\]\s*,?\s*/g,
2136
+ ""
2137
+ );
2138
+ updated = updated.replace(
2139
+ /\{\s*plugins:\s*\{\s*uilint:\s*\{[^}]*\}[^}]*\}[^}]*rules:\s*\{[^}]*\}[^}]*\}\s*,?\s*/gs,
2140
+ ""
2141
+ );
2142
+ updated = updated.replace(/\n{3,}/g, "\n\n");
2143
+ if (updated !== original) {
2144
+ writeFileSync(configPath, updated, "utf-8");
2145
+ return {
2146
+ success: true,
2147
+ modifiedFiles: [configPath]
2148
+ };
2149
+ }
2150
+ return {
2151
+ success: true,
2152
+ modifiedFiles: []
2153
+ };
2154
+ } catch (error) {
2155
+ return {
2156
+ success: false,
2157
+ error: error instanceof Error ? error.message : String(error)
2158
+ };
2159
+ }
2160
+ }
2041
2161
 
2042
2162
  // src/commands/install/analyze.ts
2043
2163
  async function analyze(projectPath = process.cwd()) {
@@ -2127,7 +2247,8 @@ import {
2127
2247
  writeFileSync as writeFileSync5,
2128
2248
  readFileSync as readFileSync9,
2129
2249
  unlinkSync,
2130
- chmodSync
2250
+ chmodSync,
2251
+ rmSync
2131
2252
  } from "fs";
2132
2253
  import { dirname as dirname5 } from "path";
2133
2254
 
@@ -2559,6 +2680,43 @@ async function installReactUILintOverlay(opts) {
2559
2680
  modifiedFiles: modified ? [absTarget] : []
2560
2681
  };
2561
2682
  }
2683
+ async function uninstallReactUILintOverlay(options) {
2684
+ const { projectPath, appRoot, mode = "next" } = options;
2685
+ const candidates = getDefaultCandidates(projectPath, appRoot);
2686
+ const modifiedFiles = [];
2687
+ for (const candidate of candidates) {
2688
+ const absPath = join6(projectPath, candidate);
2689
+ if (!existsSync6(absPath)) continue;
2690
+ try {
2691
+ const original = readFileSync6(absPath, "utf-8");
2692
+ let updated = original.replace(
2693
+ /^import\s+["']uilint-react\/devtools["'];?\s*$/gm,
2694
+ ""
2695
+ );
2696
+ updated = updated.replace(
2697
+ /^import\s+\{[^}]*UILintProvider[^}]*\}\s+from\s+["']uilint-react["'];?\s*$/gm,
2698
+ ""
2699
+ );
2700
+ updated = updated.replace(/<uilint-devtools\s*\/>/g, "");
2701
+ updated = updated.replace(/<uilint-devtools><\/uilint-devtools>/g, "");
2702
+ updated = updated.replace(/<uilint-devtools\s*>\s*<\/uilint-devtools>/g, "");
2703
+ updated = updated.replace(
2704
+ /<UILintProvider[^>]*>([\s\S]*?)<\/UILintProvider>/g,
2705
+ "$1"
2706
+ );
2707
+ updated = updated.replace(/\n{3,}/g, "\n\n");
2708
+ if (updated !== original) {
2709
+ writeFileSync2(absPath, updated, "utf-8");
2710
+ modifiedFiles.push(absPath);
2711
+ }
2712
+ } catch {
2713
+ }
2714
+ }
2715
+ return {
2716
+ success: true,
2717
+ modifiedFiles
2718
+ };
2719
+ }
2562
2720
 
2563
2721
  // src/utils/next-config-inject.ts
2564
2722
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
@@ -2709,6 +2867,52 @@ async function installJsxLocPlugin(opts) {
2709
2867
  }
2710
2868
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
2711
2869
  }
2870
+ async function uninstallJsxLocPlugin(options) {
2871
+ const { projectPath } = options;
2872
+ const configPath = findNextConfigFile(projectPath);
2873
+ if (!configPath) {
2874
+ return {
2875
+ success: true,
2876
+ modifiedFiles: []
2877
+ };
2878
+ }
2879
+ try {
2880
+ const original = readFileSync7(configPath, "utf-8");
2881
+ let updated = original.replace(
2882
+ /^import\s+\{[^}]*withJsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/next["'];?\s*$/gm,
2883
+ ""
2884
+ );
2885
+ updated = updated.replace(
2886
+ /^const\s+\{[^}]*withJsxLoc[^}]*\}\s*=\s*require\s*\(\s*["']jsx-loc-plugin\/next["']\s*\)\s*;?\s*$/gm,
2887
+ ""
2888
+ );
2889
+ updated = updated.replace(
2890
+ /export\s+default\s+withJsxLoc\s*\(\s*([\s\S]*?)\s*\)\s*;?/g,
2891
+ "export default $1;"
2892
+ );
2893
+ updated = updated.replace(
2894
+ /module\.exports\s*=\s*withJsxLoc\s*\(\s*([\s\S]*?)\s*\)\s*;?/g,
2895
+ "module.exports = $1;"
2896
+ );
2897
+ updated = updated.replace(/\n{3,}/g, "\n\n");
2898
+ if (updated !== original) {
2899
+ writeFileSync3(configPath, updated, "utf-8");
2900
+ return {
2901
+ success: true,
2902
+ modifiedFiles: [configPath]
2903
+ };
2904
+ }
2905
+ return {
2906
+ success: true,
2907
+ modifiedFiles: []
2908
+ };
2909
+ } catch (error) {
2910
+ return {
2911
+ success: false,
2912
+ error: error instanceof Error ? error.message : String(error)
2913
+ };
2914
+ }
2915
+ }
2712
2916
 
2713
2917
  // src/utils/vite-config-inject.ts
2714
2918
  import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
@@ -2920,6 +3124,49 @@ async function installViteJsxLocPlugin(opts) {
2920
3124
  }
2921
3125
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
2922
3126
  }
3127
+ async function uninstallViteJsxLocPlugin(options) {
3128
+ const { projectPath } = options;
3129
+ const configPath = findViteConfigFile2(projectPath);
3130
+ if (!configPath) {
3131
+ return {
3132
+ success: true,
3133
+ modifiedFiles: []
3134
+ };
3135
+ }
3136
+ try {
3137
+ const original = readFileSync8(configPath, "utf-8");
3138
+ let updated = original.replace(
3139
+ /^import\s+\{[^}]*jsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/vite["'];?\s*$/gm,
3140
+ ""
3141
+ );
3142
+ updated = updated.replace(
3143
+ /^import\s+jsxLoc\s+from\s+["']jsx-loc-plugin\/vite["'];?\s*$/gm,
3144
+ ""
3145
+ );
3146
+ updated = updated.replace(
3147
+ /^const\s+\{[^}]*jsxLoc[^}]*\}\s*=\s*require\s*\(\s*["']jsx-loc-plugin\/vite["']\s*\)\s*;?\s*$/gm,
3148
+ ""
3149
+ );
3150
+ updated = updated.replace(/jsxLoc\s*\(\s*\)\s*,?\s*/g, "");
3151
+ updated = updated.replace(/\n{3,}/g, "\n\n");
3152
+ if (updated !== original) {
3153
+ writeFileSync4(configPath, updated, "utf-8");
3154
+ return {
3155
+ success: true,
3156
+ modifiedFiles: [configPath]
3157
+ };
3158
+ }
3159
+ return {
3160
+ success: true,
3161
+ modifiedFiles: []
3162
+ };
3163
+ } catch (error) {
3164
+ return {
3165
+ success: false,
3166
+ error: error instanceof Error ? error.message : String(error)
3167
+ };
3168
+ }
3169
+ }
2923
3170
 
2924
3171
  // src/utils/next-routes.ts
2925
3172
  import { existsSync as existsSync9 } from "fs";
@@ -3401,6 +3648,22 @@ async function installNextUILintRoutes(opts) {
3401
3648
  opts
3402
3649
  );
3403
3650
  }
3651
+ async function uninstallNextUILintRoutes(options) {
3652
+ const { projectPath, appRoot } = options;
3653
+ const { rm } = await import("fs/promises");
3654
+ const baseAbs = join9(projectPath, appRoot, "api", ".uilint");
3655
+ try {
3656
+ if (existsSync9(baseAbs)) {
3657
+ await rm(baseAbs, { recursive: true, force: true });
3658
+ }
3659
+ return { success: true };
3660
+ } catch (error) {
3661
+ return {
3662
+ success: false,
3663
+ error: error instanceof Error ? error.message : String(error)
3664
+ };
3665
+ }
3666
+ }
3404
3667
 
3405
3668
  // src/utils/prettier.ts
3406
3669
  import { existsSync as existsSync10, utimesSync } from "fs";
@@ -3656,6 +3919,25 @@ async function executeAction(action, options) {
3656
3919
  case "install_next_routes": {
3657
3920
  return await executeInstallNextRoutes(action, options);
3658
3921
  }
3922
+ // Uninstall actions
3923
+ case "remove_eslint": {
3924
+ return await executeRemoveEslint(action, options);
3925
+ }
3926
+ case "remove_react": {
3927
+ return await executeRemoveReact(action, options);
3928
+ }
3929
+ case "remove_next_config": {
3930
+ return await executeRemoveNextConfig(action, options);
3931
+ }
3932
+ case "remove_vite_config": {
3933
+ return await executeRemoveViteConfig(action, options);
3934
+ }
3935
+ case "remove_next_routes": {
3936
+ return await executeRemoveNextRoutes(action, options);
3937
+ }
3938
+ case "remove_directory": {
3939
+ return await executeRemoveDirectory(action, options);
3940
+ }
3659
3941
  default: {
3660
3942
  const _exhaustive = action;
3661
3943
  return {
@@ -3781,6 +4063,117 @@ async function executeInstallNextRoutes(action, options) {
3781
4063
  });
3782
4064
  return { action, success: true };
3783
4065
  }
4066
+ async function executeRemoveEslint(action, options) {
4067
+ const { dryRun = false } = options;
4068
+ if (dryRun) {
4069
+ return {
4070
+ action,
4071
+ success: true,
4072
+ wouldDo: `Remove uilint ESLint rules from: ${action.configPath}`
4073
+ };
4074
+ }
4075
+ const result = await uninstallEslintPlugin({
4076
+ projectPath: action.packagePath
4077
+ });
4078
+ return {
4079
+ action,
4080
+ success: result.success,
4081
+ error: result.error,
4082
+ modifiedFiles: result.modifiedFiles
4083
+ };
4084
+ }
4085
+ async function executeRemoveReact(action, options) {
4086
+ const { dryRun = false } = options;
4087
+ if (dryRun) {
4088
+ return {
4089
+ action,
4090
+ success: true,
4091
+ wouldDo: `Remove <uilint-devtools /> from: ${action.projectPath}`
4092
+ };
4093
+ }
4094
+ const result = await uninstallReactUILintOverlay({
4095
+ projectPath: action.projectPath,
4096
+ appRoot: action.appRoot,
4097
+ mode: action.mode
4098
+ });
4099
+ return {
4100
+ action,
4101
+ success: result.success,
4102
+ error: result.error,
4103
+ modifiedFiles: result.modifiedFiles
4104
+ };
4105
+ }
4106
+ async function executeRemoveNextConfig(action, options) {
4107
+ const { dryRun = false } = options;
4108
+ if (dryRun) {
4109
+ return {
4110
+ action,
4111
+ success: true,
4112
+ wouldDo: `Remove jsx-loc-plugin from next.config: ${action.projectPath}`
4113
+ };
4114
+ }
4115
+ const result = await uninstallJsxLocPlugin({
4116
+ projectPath: action.projectPath
4117
+ });
4118
+ return {
4119
+ action,
4120
+ success: result.success,
4121
+ error: result.error,
4122
+ modifiedFiles: result.modifiedFiles
4123
+ };
4124
+ }
4125
+ async function executeRemoveViteConfig(action, options) {
4126
+ const { dryRun = false } = options;
4127
+ if (dryRun) {
4128
+ return {
4129
+ action,
4130
+ success: true,
4131
+ wouldDo: `Remove jsx-loc-plugin from vite.config: ${action.projectPath}`
4132
+ };
4133
+ }
4134
+ const result = await uninstallViteJsxLocPlugin({
4135
+ projectPath: action.projectPath
4136
+ });
4137
+ return {
4138
+ action,
4139
+ success: result.success,
4140
+ error: result.error,
4141
+ modifiedFiles: result.modifiedFiles
4142
+ };
4143
+ }
4144
+ async function executeRemoveNextRoutes(action, options) {
4145
+ const { dryRun = false } = options;
4146
+ if (dryRun) {
4147
+ return {
4148
+ action,
4149
+ success: true,
4150
+ wouldDo: `Remove Next.js API routes: ${action.projectPath}`
4151
+ };
4152
+ }
4153
+ const result = await uninstallNextUILintRoutes({
4154
+ projectPath: action.projectPath,
4155
+ appRoot: action.appRoot
4156
+ });
4157
+ return {
4158
+ action,
4159
+ success: result.success,
4160
+ error: result.error
4161
+ };
4162
+ }
4163
+ async function executeRemoveDirectory(action, options) {
4164
+ const { dryRun = false } = options;
4165
+ if (dryRun) {
4166
+ return {
4167
+ action,
4168
+ success: true,
4169
+ wouldDo: `Remove directory: ${action.path}`
4170
+ };
4171
+ }
4172
+ if (existsSync11(action.path)) {
4173
+ rmSync(action.path, { recursive: true, force: true });
4174
+ }
4175
+ return { action, success: true };
4176
+ }
3784
4177
  function deepMerge(target, source) {
3785
4178
  const result = { ...target };
3786
4179
  for (const key of Object.keys(source)) {
@@ -4075,6 +4468,15 @@ var genstyleguideInstaller = {
4075
4468
  type: "complete",
4076
4469
  message: "Installed /genstyleguide command"
4077
4470
  };
4471
+ },
4472
+ planUninstall(targets, project) {
4473
+ const actions = [];
4474
+ const commandPath = join11(project.cursorDir.path, "commands", "genstyleguide.md");
4475
+ actions.push({
4476
+ type: "delete_file",
4477
+ path: commandPath
4478
+ });
4479
+ return { actions };
4078
4480
  }
4079
4481
  };
4080
4482
 
@@ -4178,6 +4580,15 @@ var skillInstaller = {
4178
4580
  error: error instanceof Error ? error.message : String(error)
4179
4581
  };
4180
4582
  }
4583
+ },
4584
+ planUninstall(targets, project) {
4585
+ const actions = [];
4586
+ const skillDir = join12(project.cursorDir.path, "skills", "ui-consistency-enforcer");
4587
+ actions.push({
4588
+ type: "remove_directory",
4589
+ path: skillDir
4590
+ });
4591
+ return { actions };
4181
4592
  }
4182
4593
  };
4183
4594
 
@@ -4407,6 +4818,24 @@ var eslintInstaller = {
4407
4818
  type: "complete",
4408
4819
  message: `ESLint plugin installed in ${targets.length} package(s)`
4409
4820
  };
4821
+ },
4822
+ planUninstall(targets, project) {
4823
+ const actions = [];
4824
+ for (const target of targets) {
4825
+ const pkgInfo = project.packages.find((p) => p.path === target.path);
4826
+ if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
4827
+ actions.push({
4828
+ type: "remove_eslint",
4829
+ packagePath: target.path,
4830
+ configPath: pkgInfo.eslintConfigPath
4831
+ });
4832
+ const rulesDir = join13(target.path, ".uilint", "rules");
4833
+ actions.push({
4834
+ type: "remove_directory",
4835
+ path: rulesDir
4836
+ });
4837
+ }
4838
+ return { actions };
4410
4839
  }
4411
4840
  };
4412
4841
 
@@ -4485,6 +4914,27 @@ var viteOverlayInstaller = {
4485
4914
  type: "complete",
4486
4915
  message: "Vite overlay installed"
4487
4916
  };
4917
+ },
4918
+ planUninstall(targets, project) {
4919
+ const actions = [];
4920
+ if (targets.length === 0) return { actions };
4921
+ const target = targets[0];
4922
+ const appInfo = project.viteApps.find(
4923
+ (app) => app.projectPath === target.path
4924
+ );
4925
+ if (!appInfo) return { actions };
4926
+ const { projectPath, detection } = appInfo;
4927
+ actions.push({
4928
+ type: "remove_react",
4929
+ projectPath,
4930
+ appRoot: detection.entryRoot,
4931
+ mode: "vite"
4932
+ });
4933
+ actions.push({
4934
+ type: "remove_vite_config",
4935
+ projectPath
4936
+ });
4937
+ return { actions };
4488
4938
  }
4489
4939
  };
4490
4940
 
@@ -4566,23 +5016,41 @@ async function installUI(options = {}, executeOptions = {}) {
4566
5016
  InstallApp,
4567
5017
  {
4568
5018
  projectPromise,
4569
- onComplete: async (selections, eslintRules, injectionPointConfig) => {
5019
+ onComplete: async (selections, eslintRules, injectionPointConfig, uninstallSelections) => {
4570
5020
  const project = await projectPromise;
4571
5021
  const choices = selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig);
4572
- if (choices.items.length === 0) {
4573
- console.log("\nNo items selected for installation");
5022
+ const hasInstalls = choices.items.length > 0;
5023
+ const hasUninstalls = uninstallSelections && uninstallSelections.length > 0;
5024
+ if (!hasInstalls && !hasUninstalls) {
5025
+ console.log("\nNo changes selected");
4574
5026
  process.exit(0);
4575
5027
  }
4576
5028
  const { createPlan } = await import("./plan-SIXVCXCK.js");
4577
5029
  const plan = createPlan(project, choices, { force: options.force });
5030
+ if (hasUninstalls && uninstallSelections) {
5031
+ for (const selection of uninstallSelections) {
5032
+ if (!selection.selected || selection.targets.length === 0) continue;
5033
+ const { installer, targets } = selection;
5034
+ if (installer.planUninstall) {
5035
+ const uninstallPlan = installer.planUninstall(targets, project);
5036
+ plan.actions = [...uninstallPlan.actions, ...plan.actions];
5037
+ }
5038
+ }
5039
+ }
4578
5040
  const result = await execute(plan, {
4579
5041
  ...executeOptions,
4580
5042
  projectPath: project.projectPath
4581
5043
  });
4582
5044
  if (result.success) {
4583
- console.log("\n\u2713 Installation completed successfully!");
5045
+ if (hasInstalls && hasUninstalls) {
5046
+ console.log("\n\u2713 Changes applied successfully!");
5047
+ } else if (hasUninstalls) {
5048
+ console.log("\n\u2713 Uninstallation completed successfully!");
5049
+ } else {
5050
+ console.log("\n\u2713 Installation completed successfully!");
5051
+ }
4584
5052
  } else {
4585
- console.log("\n\u26A0 Installation completed with errors");
5053
+ console.log("\n\u26A0 Operation completed with errors");
4586
5054
  }
4587
5055
  process.exit(result.success ? 0 : 1);
4588
5056
  },
@@ -4598,4 +5066,4 @@ async function installUI(options = {}, executeOptions = {}) {
4598
5066
  export {
4599
5067
  installUI
4600
5068
  };
4601
- //# sourceMappingURL=install-ui-2HMR6TZT.js.map
5069
+ //# sourceMappingURL=install-ui-HTVB5HDB.js.map