uilint 0.2.22 → 0.2.26

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.
@@ -1,20 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  confirm,
4
+ detectCoverageSetup,
4
5
  detectNextAppRouter,
6
+ findEslintConfigFile,
5
7
  findNextAppRouterProjects,
8
+ getEslintConfigFilename,
9
+ getUilintEslintConfigInfoFromSource,
10
+ injectCoverageConfig,
11
+ installEslintPlugin,
6
12
  log,
7
13
  multiselect,
8
14
  note,
9
15
  pc,
10
- select
11
- } from "./chunk-FRNXXIEM.js";
16
+ select,
17
+ uninstallEslintPlugin
18
+ } from "./chunk-VNANPKR2.js";
12
19
  import {
13
20
  GENSTYLEGUIDE_COMMAND_MD,
21
+ loadSkill,
22
+ toInstallSpecifier
23
+ } from "./chunk-TWUDB36F.js";
24
+ import {
14
25
  detectPackageManager,
15
26
  installDependencies,
16
- loadSkill
17
- } from "./chunk-PBEKMDUH.js";
27
+ runTestsWithCoverage
28
+ } from "./chunk-P4I4RKBY.js";
18
29
 
19
30
  // src/commands/install-ui.tsx
20
31
  import { render } from "ink";
@@ -841,7 +852,7 @@ var nextOverlayInstaller = {
841
852
  label: app.projectPath.split("/").pop() || app.projectPath,
842
853
  path: app.projectPath,
843
854
  hint: "App Router",
844
- isInstalled: false
855
+ isInstalled: app.hasUilintOverlay
845
856
  }));
846
857
  },
847
858
  plan(targets, config, project) {
@@ -865,7 +876,19 @@ var nextOverlayInstaller = {
865
876
  dependencies.push({
866
877
  packagePath: projectPath,
867
878
  packageManager: project.packageManager,
868
- packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
879
+ packages: [
880
+ toInstallSpecifier("uilint-react", {
881
+ preferWorkspaceProtocol: project.packageManager === "pnpm",
882
+ workspaceRoot: project.workspaceRoot,
883
+ targetProjectPath: projectPath
884
+ }),
885
+ toInstallSpecifier("uilint-core", {
886
+ preferWorkspaceProtocol: project.packageManager === "pnpm",
887
+ workspaceRoot: project.workspaceRoot,
888
+ targetProjectPath: projectPath
889
+ }),
890
+ "jsx-loc-plugin"
891
+ ]
869
892
  });
870
893
  actions.push({
871
894
  type: "inject_react",
@@ -1309,9 +1332,9 @@ function InstallApp({
1309
1332
  }
1310
1333
 
1311
1334
  // src/commands/install/analyze.ts
1312
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1313
- import { join as join5 } from "path";
1314
- import { findWorkspaceRoot as findWorkspaceRoot2 } from "uilint-core/node";
1335
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1336
+ import { join as join4 } from "path";
1337
+ import { findWorkspaceRoot } from "uilint-core/node";
1315
1338
 
1316
1339
  // src/utils/vite-detect.ts
1317
1340
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
@@ -1554,644 +1577,63 @@ function findPackages(rootDir, options) {
1554
1577
  });
1555
1578
  }
1556
1579
 
1557
- // src/utils/eslint-config-inject.ts
1558
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync } from "fs";
1559
- import { join as join4, relative as relative3, dirname as dirname2 } from "path";
1560
- import { parseExpression, parseModule as parseModule2, generateCode } from "magicast";
1561
- import { findWorkspaceRoot } from "uilint-core/node";
1562
- var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
1563
- function findEslintConfigFile(projectPath) {
1564
- for (const ext of CONFIG_EXTENSIONS) {
1565
- const configPath = join4(projectPath, `eslint.config${ext}`);
1566
- if (existsSync4(configPath)) {
1567
- return configPath;
1568
- }
1569
- }
1570
- return null;
1571
- }
1572
- function getEslintConfigFilename(configPath) {
1573
- const parts = configPath.split("/");
1574
- return parts[parts.length - 1] || "eslint.config.mjs";
1575
- }
1576
- function isIdentifier(node, name) {
1577
- return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
1578
- }
1579
- function isStringLiteral(node) {
1580
- return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
1581
- }
1582
- function getObjectPropertyValue(obj, keyName) {
1583
- if (!obj || obj.type !== "ObjectExpression") return null;
1584
- for (const prop of obj.properties ?? []) {
1585
- if (!prop) continue;
1586
- if (prop.type === "ObjectProperty" || prop.type === "Property") {
1587
- const key = prop.key;
1588
- const keyMatch = key?.type === "Identifier" && key.name === keyName || isStringLiteral(key) && key.value === keyName;
1589
- if (keyMatch) return prop.value;
1590
- }
1591
- }
1592
- return null;
1593
- }
1594
- function hasSpreadProperties(obj) {
1595
- if (!obj || obj.type !== "ObjectExpression") return false;
1596
- return (obj.properties ?? []).some(
1597
- (p) => p && (p.type === "SpreadElement" || p.type === "SpreadProperty")
1598
- );
1599
- }
1600
- var IGNORED_AST_KEYS = /* @__PURE__ */ new Set([
1601
- "loc",
1602
- "start",
1603
- "end",
1604
- "extra",
1605
- "leadingComments",
1606
- "trailingComments",
1607
- "innerComments"
1608
- ]);
1609
- function normalizeAstForCompare(node) {
1610
- if (node === null) return null;
1611
- if (node === void 0) return void 0;
1612
- if (typeof node !== "object") return node;
1613
- if (Array.isArray(node)) return node.map(normalizeAstForCompare);
1614
- const out = {};
1615
- const keys = Object.keys(node).filter((k) => !IGNORED_AST_KEYS.has(k)).sort();
1616
- for (const k of keys) {
1617
- if (k.startsWith("$")) continue;
1618
- out[k] = normalizeAstForCompare(node[k]);
1619
- }
1620
- return out;
1621
- }
1622
- function astEquivalent(a, b) {
1623
- try {
1624
- return JSON.stringify(normalizeAstForCompare(a)) === JSON.stringify(normalizeAstForCompare(b));
1625
- } catch {
1626
- return false;
1627
- }
1628
- }
1629
- function collectUilintRuleIdsFromRulesObject(rulesObj) {
1630
- const ids = /* @__PURE__ */ new Set();
1631
- if (!rulesObj || rulesObj.type !== "ObjectExpression") return ids;
1632
- for (const prop of rulesObj.properties ?? []) {
1633
- if (!prop) continue;
1634
- if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
1635
- const key = prop.key;
1636
- if (!isStringLiteral(key)) continue;
1637
- const val = key.value;
1638
- if (typeof val !== "string") continue;
1639
- if (val.startsWith("uilint/")) {
1640
- ids.add(val.slice("uilint/".length));
1641
- }
1642
- }
1643
- return ids;
1644
- }
1645
- function findExportedConfigArrayExpression(mod) {
1646
- function unwrapExpression2(expr) {
1647
- let e = expr;
1648
- while (e) {
1649
- if (e.type === "TSAsExpression" || e.type === "TSNonNullExpression") {
1650
- e = e.expression;
1651
- continue;
1652
- }
1653
- if (e.type === "TSSatisfiesExpression") {
1654
- e = e.expression;
1655
- continue;
1656
- }
1657
- if (e.type === "ParenthesizedExpression") {
1658
- e = e.expression;
1659
- continue;
1660
- }
1661
- break;
1662
- }
1663
- return e;
1664
- }
1665
- function resolveTopLevelIdentifierToArrayExpr(program2, name) {
1666
- if (!program2 || program2.type !== "Program") return null;
1667
- for (const stmt of program2.body ?? []) {
1668
- if (stmt?.type !== "VariableDeclaration") continue;
1669
- for (const decl of stmt.declarations ?? []) {
1670
- const id = decl?.id;
1671
- if (!isIdentifier(id, name)) continue;
1672
- const init = unwrapExpression2(decl?.init);
1673
- if (!init) return null;
1674
- if (init.type === "ArrayExpression") return init;
1675
- if (init.type === "CallExpression" && isIdentifier(init.callee, "defineConfig") && unwrapExpression2(init.arguments?.[0])?.type === "ArrayExpression") {
1676
- return unwrapExpression2(init.arguments?.[0]);
1677
- }
1678
- return null;
1679
- }
1680
- }
1681
- return null;
1682
- }
1683
- const program = mod?.$ast;
1684
- if (program && program.type === "Program") {
1685
- for (const stmt of program.body ?? []) {
1686
- if (!stmt || stmt.type !== "ExportDefaultDeclaration") continue;
1687
- const decl = unwrapExpression2(stmt.declaration);
1688
- if (!decl) break;
1689
- if (decl.type === "ArrayExpression") {
1690
- return { kind: "esm", arrayExpr: decl, program };
1691
- }
1692
- if (decl.type === "CallExpression" && isIdentifier(decl.callee, "defineConfig") && unwrapExpression2(decl.arguments?.[0])?.type === "ArrayExpression") {
1693
- return {
1694
- kind: "esm",
1695
- arrayExpr: unwrapExpression2(decl.arguments?.[0]),
1696
- program
1697
- };
1698
- }
1699
- if (decl.type === "Identifier" && typeof decl.name === "string") {
1700
- const resolved = resolveTopLevelIdentifierToArrayExpr(
1701
- program,
1702
- decl.name
1703
- );
1704
- if (resolved) return { kind: "esm", arrayExpr: resolved, program };
1705
- }
1706
- break;
1707
- }
1708
- }
1709
- if (!program || program.type !== "Program") return null;
1710
- for (const stmt of program.body ?? []) {
1711
- if (!stmt || stmt.type !== "ExpressionStatement") continue;
1712
- const expr = stmt.expression;
1713
- if (!expr || expr.type !== "AssignmentExpression") continue;
1714
- const left = expr.left;
1715
- const right = expr.right;
1716
- const isModuleExports = left?.type === "MemberExpression" && isIdentifier(left.object, "module") && isIdentifier(left.property, "exports");
1717
- if (!isModuleExports) continue;
1718
- if (right?.type === "ArrayExpression") {
1719
- return { kind: "cjs", arrayExpr: right, program };
1720
- }
1721
- if (right?.type === "CallExpression" && isIdentifier(right.callee, "defineConfig") && right.arguments?.[0]?.type === "ArrayExpression") {
1722
- return { kind: "cjs", arrayExpr: right.arguments[0], program };
1723
- }
1724
- if (right?.type === "Identifier" && typeof right.name === "string") {
1725
- const resolved = resolveTopLevelIdentifierToArrayExpr(
1726
- program,
1727
- right.name
1728
- );
1729
- if (resolved) return { kind: "cjs", arrayExpr: resolved, program };
1730
- }
1731
- }
1732
- return null;
1733
- }
1734
- function collectConfiguredUilintRuleIdsFromConfigArray(arrayExpr) {
1735
- const ids = /* @__PURE__ */ new Set();
1736
- if (!arrayExpr || arrayExpr.type !== "ArrayExpression") return ids;
1737
- for (const el of arrayExpr.elements ?? []) {
1738
- if (!el || el.type !== "ObjectExpression") continue;
1739
- const rules = getObjectPropertyValue(el, "rules");
1740
- for (const id of collectUilintRuleIdsFromRulesObject(rules)) ids.add(id);
1741
- }
1742
- return ids;
1743
- }
1744
- function findExistingUilintRulesObject(arrayExpr) {
1745
- if (!arrayExpr || arrayExpr.type !== "ArrayExpression") {
1746
- return { configObj: null, rulesObj: null, safeToMutate: false };
1747
- }
1748
- for (const el of arrayExpr.elements ?? []) {
1749
- if (!el || el.type !== "ObjectExpression") continue;
1750
- const plugins = getObjectPropertyValue(el, "plugins");
1751
- const rules = getObjectPropertyValue(el, "rules");
1752
- const hasUilintPlugin = plugins?.type === "ObjectExpression" && getObjectPropertyValue(plugins, "uilint") !== null;
1753
- const uilintIds = collectUilintRuleIdsFromRulesObject(rules);
1754
- const hasUilintRules = uilintIds.size > 0;
1755
- if (!hasUilintPlugin && !hasUilintRules) continue;
1756
- const safe = rules?.type === "ObjectExpression" && !hasSpreadProperties(rules);
1757
- return { configObj: el, rulesObj: rules, safeToMutate: safe };
1758
- }
1759
- return { configObj: null, rulesObj: null, safeToMutate: false };
1760
- }
1761
- function collectTopLevelBindings(program) {
1762
- const names = /* @__PURE__ */ new Set();
1763
- if (!program || program.type !== "Program") return names;
1764
- for (const stmt of program.body ?? []) {
1765
- if (stmt?.type === "VariableDeclaration") {
1766
- for (const decl of stmt.declarations ?? []) {
1767
- const id = decl?.id;
1768
- if (id?.type === "Identifier" && typeof id.name === "string") {
1769
- names.add(id.name);
1770
- }
1771
- }
1772
- } else if (stmt?.type === "FunctionDeclaration") {
1773
- if (stmt.id?.type === "Identifier" && typeof stmt.id.name === "string") {
1774
- names.add(stmt.id.name);
1775
- }
1776
- }
1777
- }
1778
- return names;
1779
- }
1780
- function chooseUniqueIdentifier(base, used) {
1781
- if (!used.has(base)) return base;
1782
- let i = 2;
1783
- while (used.has(`${base}${i}`)) i++;
1784
- return `${base}${i}`;
1785
- }
1786
- function addLocalRuleImportsAst(mod, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
1787
- const importNames = /* @__PURE__ */ new Map();
1788
- let changed = false;
1789
- const configDir = dirname2(configPath);
1790
- const rulesDir = join4(rulesRoot, ".uilint", "rules");
1791
- const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
1792
- const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
1793
- const used = collectTopLevelBindings(mod.$ast);
1794
- for (const rule of selectedRules) {
1795
- const importName = chooseUniqueIdentifier(
1796
- `${rule.id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase())}Rule`,
1797
- used
1798
- );
1799
- importNames.set(rule.id, importName);
1800
- used.add(importName);
1801
- const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;
1802
- mod.imports.$add({
1803
- imported: "default",
1804
- local: importName,
1805
- from: rulePath
1806
- });
1807
- changed = true;
1808
- }
1809
- return { importNames, changed };
1810
- }
1811
- function addLocalRuleRequiresAst(program, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
1812
- const importNames = /* @__PURE__ */ new Map();
1813
- let changed = false;
1814
- if (!program || program.type !== "Program") {
1815
- return { importNames, changed };
1816
- }
1817
- const configDir = dirname2(configPath);
1818
- const rulesDir = join4(rulesRoot, ".uilint", "rules");
1819
- const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
1820
- const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
1821
- const used = collectTopLevelBindings(program);
1822
- for (const rule of selectedRules) {
1823
- const importName = chooseUniqueIdentifier(
1824
- `${rule.id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase())}Rule`,
1825
- used
1826
- );
1827
- importNames.set(rule.id, importName);
1828
- used.add(importName);
1829
- const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;
1830
- const stmtMod = parseModule2(
1831
- `const ${importName} = require("${rulePath}");`
1832
- );
1833
- const stmt = stmtMod.$ast.body?.[0];
1834
- if (stmt) {
1835
- let insertAt = 0;
1836
- const first = program.body?.[0];
1837
- if (first?.type === "ExpressionStatement" && first.expression?.type === "StringLiteral" && first.expression.value === "use strict") {
1838
- insertAt = 1;
1839
- }
1840
- program.body.splice(insertAt, 0, stmt);
1841
- changed = true;
1842
- }
1843
- }
1844
- return { importNames, changed };
1845
- }
1846
- function appendUilintConfigBlockToArray(arrayExpr, selectedRules, ruleImportNames) {
1847
- const pluginRulesCode = Array.from(ruleImportNames.entries()).map(([ruleId, importName]) => ` "${ruleId}": ${importName},`).join("\n");
1848
- const rulesPropsCode = selectedRules.map((r) => {
1849
- const ruleKey = `uilint/${r.id}`;
1850
- const valueCode = r.defaultOptions && r.defaultOptions.length > 0 ? `["${r.defaultSeverity}", ...${JSON.stringify(
1851
- r.defaultOptions,
1852
- null,
1853
- 2
1854
- )}]` : `"${r.defaultSeverity}"`;
1855
- return ` "${ruleKey}": ${valueCode},`;
1856
- }).join("\n");
1857
- const blockCode = `{
1858
- files: [
1859
- "src/**/*.{js,jsx,ts,tsx}",
1860
- "app/**/*.{js,jsx,ts,tsx}",
1861
- "pages/**/*.{js,jsx,ts,tsx}",
1862
- ],
1863
- plugins: {
1864
- uilint: {
1865
- rules: {
1866
- ${pluginRulesCode}
1867
- },
1868
- },
1869
- },
1870
- rules: {
1871
- ${rulesPropsCode}
1872
- },
1873
- }`;
1874
- const objExpr = parseExpression(blockCode).$ast;
1875
- arrayExpr.elements.push(objExpr);
1876
- }
1877
- function getUilintEslintConfigInfoFromSourceAst(source) {
1580
+ // src/commands/install/analyze.ts
1581
+ function safeParseJson(filePath) {
1878
1582
  try {
1879
- const mod = parseModule2(source);
1880
- const found = findExportedConfigArrayExpression(mod);
1881
- if (!found) {
1882
- return {
1883
- error: "Could not locate an exported ESLint flat config array (expected `export default [...]`, `export default defineConfig([...])`, `module.exports = [...]`, or `module.exports = defineConfig([...])`)."
1884
- };
1885
- }
1886
- const configuredRuleIds = collectConfiguredUilintRuleIdsFromConfigArray(
1887
- found.arrayExpr
1888
- );
1889
- const existingUilint = findExistingUilintRulesObject(found.arrayExpr);
1890
- const configured = configuredRuleIds.size > 0 || existingUilint.configObj !== null;
1891
- return {
1892
- info: { configuredRuleIds, configured },
1893
- mod,
1894
- arrayExpr: found.arrayExpr,
1895
- kind: found.kind
1896
- };
1583
+ const content = readFileSync4(filePath, "utf-8");
1584
+ return JSON.parse(content);
1897
1585
  } catch {
1898
- return {
1899
- error: "Unable to parse ESLint config as JavaScript. Please update it manually or simplify the config so it can be safely auto-modified."
1900
- };
1901
- }
1902
- }
1903
- function getUilintEslintConfigInfoFromSource(source) {
1904
- const ast = getUilintEslintConfigInfoFromSourceAst(source);
1905
- if ("error" in ast) {
1906
- const configuredRuleIds = extractConfiguredUilintRuleIds(source);
1907
- return {
1908
- configuredRuleIds,
1909
- configured: configuredRuleIds.size > 0
1910
- };
1911
- }
1912
- return ast.info;
1913
- }
1914
- function extractConfiguredUilintRuleIds(source) {
1915
- const ids = /* @__PURE__ */ new Set();
1916
- const re = /["']uilint\/([^"']+)["']\s*:/g;
1917
- for (const m of source.matchAll(re)) {
1918
- if (m[1]) ids.add(m[1]);
1919
- }
1920
- return ids;
1921
- }
1922
- function getMissingSelectedRules(selectedRules, configuredIds) {
1923
- return selectedRules.filter((r) => !configuredIds.has(r.id));
1924
- }
1925
- function buildDesiredRuleValueExpression(rule) {
1926
- if (rule.defaultOptions && rule.defaultOptions.length > 0) {
1927
- return `["${rule.defaultSeverity}", ...${JSON.stringify(
1928
- rule.defaultOptions,
1929
- null,
1930
- 2
1931
- )}]`;
1932
- }
1933
- return `"${rule.defaultSeverity}"`;
1934
- }
1935
- function collectUilintRuleValueNodesFromConfigArray(arrayExpr) {
1936
- const out = /* @__PURE__ */ new Map();
1937
- if (!arrayExpr || arrayExpr.type !== "ArrayExpression") return out;
1938
- for (const el of arrayExpr.elements ?? []) {
1939
- if (!el || el.type !== "ObjectExpression") continue;
1940
- const rules = getObjectPropertyValue(el, "rules");
1941
- if (!rules || rules.type !== "ObjectExpression") continue;
1942
- for (const prop of rules.properties ?? []) {
1943
- if (!prop) continue;
1944
- if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
1945
- const key = prop.key;
1946
- if (!isStringLiteral(key)) continue;
1947
- const k = key.value;
1948
- if (typeof k !== "string" || !k.startsWith("uilint/")) continue;
1949
- const id = k.slice("uilint/".length);
1950
- if (!out.has(id)) out.set(id, prop.value);
1951
- }
1952
- }
1953
- return out;
1954
- }
1955
- function getRulesNeedingUpdate(selectedRules, configuredIds, arrayExpr) {
1956
- const existingVals = collectUilintRuleValueNodesFromConfigArray(arrayExpr);
1957
- return selectedRules.filter((r) => {
1958
- if (!configuredIds.has(r.id)) return false;
1959
- const existing = existingVals.get(r.id);
1960
- if (!existing) return true;
1961
- const desiredExpr = buildDesiredRuleValueExpression(r);
1962
- const desiredAst = parseExpression(desiredExpr).$ast;
1963
- return !astEquivalent(existing, desiredAst);
1964
- });
1965
- }
1966
- async function installEslintPlugin(opts) {
1967
- const configPath = findEslintConfigFile(opts.projectPath);
1968
- if (!configPath) {
1969
- return {
1970
- configFile: null,
1971
- modified: false,
1972
- missingRuleIds: [],
1973
- configured: false
1974
- };
1975
- }
1976
- const configFilename = getEslintConfigFilename(configPath);
1977
- const original = readFileSync4(configPath, "utf-8");
1978
- const isCommonJS = configPath.endsWith(".cjs");
1979
- const ast = getUilintEslintConfigInfoFromSourceAst(original);
1980
- if ("error" in ast) {
1981
- return {
1982
- configFile: configFilename,
1983
- modified: false,
1984
- missingRuleIds: [],
1985
- configured: false,
1986
- error: ast.error
1987
- };
1988
- }
1989
- const { info, mod, arrayExpr, kind } = ast;
1990
- const configuredIds = info.configuredRuleIds;
1991
- const missingRules = getMissingSelectedRules(
1992
- opts.selectedRules,
1993
- configuredIds
1994
- );
1995
- const rulesToUpdate = getRulesNeedingUpdate(
1996
- opts.selectedRules,
1997
- configuredIds,
1998
- arrayExpr
1999
- );
2000
- let rulesToApply = [];
2001
- if (!info.configured) {
2002
- rulesToApply = opts.selectedRules;
2003
- } else {
2004
- rulesToApply = [...missingRules, ...rulesToUpdate];
2005
- if (missingRules.length > 0 && !opts.force) {
2006
- const ok = await opts.confirmAddMissingRules?.(
2007
- configFilename,
2008
- missingRules
2009
- );
2010
- if (!ok) {
2011
- return {
2012
- configFile: configFilename,
2013
- modified: false,
2014
- missingRuleIds: missingRules.map((r) => r.id),
2015
- configured: true
2016
- };
2017
- }
2018
- }
2019
- }
2020
- if (rulesToApply.length === 0) {
2021
- return {
2022
- configFile: configFilename,
2023
- modified: false,
2024
- missingRuleIds: missingRules.map((r) => r.id),
2025
- configured: info.configured
2026
- };
2027
- }
2028
- let modifiedAst = false;
2029
- const localRulesDir = join4(opts.projectPath, ".uilint", "rules");
2030
- const workspaceRoot = findWorkspaceRoot(opts.projectPath);
2031
- const workspaceRulesDir = join4(workspaceRoot, ".uilint", "rules");
2032
- const rulesRoot = existsSync4(localRulesDir) ? opts.projectPath : workspaceRoot;
2033
- const isTypeScriptConfig = configPath.endsWith(".ts");
2034
- let fileExtension = isTypeScriptConfig ? "" : ".js";
2035
- let ruleImportNames;
2036
- if (kind === "esm") {
2037
- const result = addLocalRuleImportsAst(
2038
- mod,
2039
- rulesToApply,
2040
- configPath,
2041
- rulesRoot,
2042
- fileExtension
2043
- );
2044
- ruleImportNames = result.importNames;
2045
- if (result.changed) modifiedAst = true;
2046
- } else {
2047
- const result = addLocalRuleRequiresAst(
2048
- mod.$ast,
2049
- rulesToApply,
2050
- configPath,
2051
- rulesRoot,
2052
- fileExtension
2053
- );
2054
- ruleImportNames = result.importNames;
2055
- if (result.changed) modifiedAst = true;
2056
- }
2057
- if (ruleImportNames && ruleImportNames.size > 0) {
2058
- appendUilintConfigBlockToArray(arrayExpr, rulesToApply, ruleImportNames);
2059
- modifiedAst = true;
2060
- }
2061
- if (!info.configured) {
2062
- if (kind === "esm") {
2063
- mod.imports.$add({
2064
- imported: "createRule",
2065
- local: "createRule",
2066
- from: "uilint-eslint"
2067
- });
2068
- modifiedAst = true;
2069
- } else {
2070
- const stmtMod = parseModule2(
2071
- `const { createRule } = require("uilint-eslint");`
2072
- );
2073
- const stmt = stmtMod.$ast.body?.[0];
2074
- if (stmt) {
2075
- let insertAt = 0;
2076
- const first = mod.$ast.body?.[0];
2077
- if (first?.type === "ExpressionStatement" && first.expression?.type === "StringLiteral" && first.expression.value === "use strict") {
2078
- insertAt = 1;
2079
- }
2080
- mod.$ast.body.splice(insertAt, 0, stmt);
2081
- modifiedAst = true;
2082
- }
2083
- }
2084
- }
2085
- const updated = modifiedAst ? generateCode(mod).code : original;
2086
- if (updated !== original) {
2087
- writeFileSync(configPath, updated, "utf-8");
2088
- return {
2089
- configFile: configFilename,
2090
- modified: true,
2091
- missingRuleIds: missingRules.map((r) => r.id),
2092
- configured: getUilintEslintConfigInfoFromSource(updated).configured
2093
- };
1586
+ return void 0;
2094
1587
  }
2095
- return {
2096
- configFile: configFilename,
2097
- modified: false,
2098
- missingRuleIds: missingRules.map((r) => r.id),
2099
- configured: getUilintEslintConfigInfoFromSource(updated).configured
2100
- };
2101
1588
  }
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
- }
1589
+ function hasUilintOverlayInstalled(projectPath) {
1590
+ const pkgPath = join4(projectPath, "package.json");
1591
+ const pkg = safeParseJson(pkgPath);
1592
+ if (!pkg) return false;
1593
+ return !!(pkg.dependencies?.["uilint-react"] || pkg.devDependencies?.["uilint-react"]);
2160
1594
  }
2161
-
2162
- // src/commands/install/analyze.ts
2163
1595
  async function analyze(projectPath = process.cwd()) {
2164
- const workspaceRoot = findWorkspaceRoot2(projectPath);
1596
+ const workspaceRoot = findWorkspaceRoot(projectPath);
2165
1597
  const packageManager = detectPackageManager(projectPath);
2166
- const cursorDir = join5(projectPath, ".cursor");
2167
- const cursorDirExists = existsSync5(cursorDir);
2168
- const styleguidePath = join5(projectPath, ".uilint", "styleguide.md");
2169
- const styleguideExists = existsSync5(styleguidePath);
2170
- const commandsDir = join5(cursorDir, "commands");
2171
- const genstyleguideExists = existsSync5(join5(commandsDir, "genstyleguide.md"));
1598
+ const cursorDir = join4(projectPath, ".cursor");
1599
+ const cursorDirExists = existsSync4(cursorDir);
1600
+ const styleguidePath = join4(projectPath, ".uilint", "styleguide.md");
1601
+ const styleguideExists = existsSync4(styleguidePath);
1602
+ const commandsDir = join4(cursorDir, "commands");
1603
+ const genstyleguideExists = existsSync4(join4(commandsDir, "genstyleguide.md"));
2172
1604
  const nextApps = [];
2173
1605
  const directDetection = detectNextAppRouter(projectPath);
2174
1606
  if (directDetection) {
2175
- nextApps.push({ projectPath, detection: directDetection });
1607
+ nextApps.push({
1608
+ projectPath,
1609
+ detection: directDetection,
1610
+ hasUilintOverlay: hasUilintOverlayInstalled(projectPath)
1611
+ });
2176
1612
  } else {
2177
1613
  const matches = findNextAppRouterProjects(workspaceRoot, { maxDepth: 5 });
2178
1614
  for (const match of matches) {
2179
1615
  nextApps.push({
2180
1616
  projectPath: match.projectPath,
2181
- detection: match.detection
1617
+ detection: match.detection,
1618
+ hasUilintOverlay: hasUilintOverlayInstalled(match.projectPath)
2182
1619
  });
2183
1620
  }
2184
1621
  }
2185
1622
  const viteApps = [];
2186
1623
  const directVite = detectViteReact(projectPath);
2187
1624
  if (directVite) {
2188
- viteApps.push({ projectPath, detection: directVite });
1625
+ viteApps.push({
1626
+ projectPath,
1627
+ detection: directVite,
1628
+ hasUilintOverlay: hasUilintOverlayInstalled(projectPath)
1629
+ });
2189
1630
  } else {
2190
1631
  const matches = findViteReactProjects(workspaceRoot, { maxDepth: 5 });
2191
1632
  for (const match of matches) {
2192
1633
  viteApps.push({
2193
1634
  projectPath: match.projectPath,
2194
- detection: match.detection
1635
+ detection: match.detection,
1636
+ hasUilintOverlay: hasUilintOverlayInstalled(match.projectPath)
2195
1637
  });
2196
1638
  }
2197
1639
  }
@@ -2204,7 +1646,7 @@ async function analyze(projectPath = process.cwd()) {
2204
1646
  if (eslintConfigPath) {
2205
1647
  eslintConfigFilename = getEslintConfigFilename(eslintConfigPath);
2206
1648
  try {
2207
- const source = readFileSync5(eslintConfigPath, "utf-8");
1649
+ const source = readFileSync4(eslintConfigPath, "utf-8");
2208
1650
  const info = getUilintEslintConfigInfoFromSource(source);
2209
1651
  hasRules = info.configuredRuleIds.size > 0;
2210
1652
  configuredRuleIds = Array.from(info.configuredRuleIds);
@@ -2242,50 +1684,50 @@ async function analyze(projectPath = process.cwd()) {
2242
1684
 
2243
1685
  // src/commands/install/execute.ts
2244
1686
  import {
2245
- existsSync as existsSync11,
1687
+ existsSync as existsSync10,
2246
1688
  mkdirSync,
2247
- writeFileSync as writeFileSync5,
2248
- readFileSync as readFileSync9,
1689
+ writeFileSync as writeFileSync4,
1690
+ readFileSync as readFileSync8,
2249
1691
  unlinkSync,
2250
1692
  chmodSync,
2251
1693
  rmSync
2252
1694
  } from "fs";
2253
- import { dirname as dirname5 } from "path";
1695
+ import { dirname as dirname4, join as join10 } from "path";
2254
1696
 
2255
1697
  // src/utils/react-inject.ts
2256
- import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
2257
- import { join as join6, relative as relative4 } from "path";
2258
- import { parseModule as parseModule3, generateCode as generateCode2 } from "magicast";
1698
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync } from "fs";
1699
+ import { join as join5, relative as relative3 } from "path";
1700
+ import { parseModule as parseModule2, generateCode } from "magicast";
2259
1701
  function getDefaultCandidates(projectPath, appRoot) {
2260
1702
  const viteMainCandidates = [
2261
- join6(appRoot, "main.tsx"),
2262
- join6(appRoot, "main.jsx"),
2263
- join6(appRoot, "main.ts"),
2264
- join6(appRoot, "main.js")
1703
+ join5(appRoot, "main.tsx"),
1704
+ join5(appRoot, "main.jsx"),
1705
+ join5(appRoot, "main.ts"),
1706
+ join5(appRoot, "main.js")
2265
1707
  ];
2266
1708
  const existingViteMain = viteMainCandidates.filter(
2267
- (rel) => existsSync6(join6(projectPath, rel))
1709
+ (rel) => existsSync5(join5(projectPath, rel))
2268
1710
  );
2269
1711
  if (existingViteMain.length > 0) return existingViteMain;
2270
- const viteAppCandidates = [join6(appRoot, "App.tsx"), join6(appRoot, "App.jsx")];
1712
+ const viteAppCandidates = [join5(appRoot, "App.tsx"), join5(appRoot, "App.jsx")];
2271
1713
  const existingViteApp = viteAppCandidates.filter(
2272
- (rel) => existsSync6(join6(projectPath, rel))
1714
+ (rel) => existsSync5(join5(projectPath, rel))
2273
1715
  );
2274
1716
  if (existingViteApp.length > 0) return existingViteApp;
2275
1717
  const layoutCandidates = [
2276
- join6(appRoot, "layout.tsx"),
2277
- join6(appRoot, "layout.jsx"),
2278
- join6(appRoot, "layout.ts"),
2279
- join6(appRoot, "layout.js")
1718
+ join5(appRoot, "layout.tsx"),
1719
+ join5(appRoot, "layout.jsx"),
1720
+ join5(appRoot, "layout.ts"),
1721
+ join5(appRoot, "layout.js")
2280
1722
  ];
2281
1723
  const existingLayouts = layoutCandidates.filter(
2282
- (rel) => existsSync6(join6(projectPath, rel))
1724
+ (rel) => existsSync5(join5(projectPath, rel))
2283
1725
  );
2284
1726
  if (existingLayouts.length > 0) {
2285
1727
  return existingLayouts;
2286
1728
  }
2287
- const pageCandidates = [join6(appRoot, "page.tsx"), join6(appRoot, "page.jsx")];
2288
- return pageCandidates.filter((rel) => existsSync6(join6(projectPath, rel)));
1729
+ const pageCandidates = [join5(appRoot, "page.tsx"), join5(appRoot, "page.jsx")];
1730
+ return pageCandidates.filter((rel) => existsSync5(join5(projectPath, rel)));
2289
1731
  }
2290
1732
  function isUseClientDirective(stmt) {
2291
1733
  return stmt?.type === "ExpressionStatement" && stmt.expression?.type === "StringLiteral" && stmt.expression.value === "use client";
@@ -2319,12 +1761,12 @@ function ensureNamedImport(program, from, name) {
2319
1761
  (s) => s?.type === "ImportSpecifier" && (s.imported?.name === name || s.imported?.value === name)
2320
1762
  );
2321
1763
  if (has) return { changed: false };
2322
- const spec = parseModule3(`import { ${name} } from "${from}";`).$ast.body?.[0]?.specifiers?.[0];
1764
+ const spec = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0]?.specifiers?.[0];
2323
1765
  if (!spec) return { changed: false };
2324
1766
  existing.specifiers = [...existing.specifiers ?? [], spec];
2325
1767
  return { changed: true };
2326
1768
  }
2327
- const importDecl = parseModule3(`import { ${name} } from "${from}";`).$ast.body?.[0];
1769
+ const importDecl = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0];
2328
1770
  if (!importDecl) return { changed: false };
2329
1771
  const body = program.body ?? [];
2330
1772
  let insertAt = 0;
@@ -2354,7 +1796,7 @@ function hasUILintDevtoolsJsx(program) {
2354
1796
  function addDevtoolsElementNextJs(program) {
2355
1797
  if (!program || program.type !== "Program") return { changed: false };
2356
1798
  if (hasUILintDevtoolsJsx(program)) return { changed: false };
2357
- const devtoolsMod = parseModule3(
1799
+ const devtoolsMod = parseModule2(
2358
1800
  "const __uilint_devtools = <uilint-devtools />;"
2359
1801
  );
2360
1802
  const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
@@ -2392,12 +1834,12 @@ function addDevtoolsElementVite(program) {
2392
1834
  const arg0 = node.arguments?.[0];
2393
1835
  if (!arg0) return;
2394
1836
  if (arg0.type !== "JSXElement" && arg0.type !== "JSXFragment") return;
2395
- const devtoolsMod = parseModule3(
1837
+ const devtoolsMod = parseModule2(
2396
1838
  "const __uilint_devtools = <uilint-devtools />;"
2397
1839
  );
2398
1840
  const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
2399
1841
  if (!devtoolsJsx) return;
2400
- const fragmentMod = parseModule3(
1842
+ const fragmentMod = parseModule2(
2401
1843
  "const __fragment = <></>;"
2402
1844
  );
2403
1845
  const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
@@ -2417,7 +1859,7 @@ function ensureSideEffectImport(program, from) {
2417
1859
  if (!program || program.type !== "Program") return { changed: false };
2418
1860
  const existing = findImportDeclaration(program, from);
2419
1861
  if (existing) return { changed: false };
2420
- const importDecl = parseModule3(`import "${from}";`).$ast.body?.[0];
1862
+ const importDecl = parseModule2(`import "${from}";`).$ast.body?.[0];
2421
1863
  if (!importDecl) return { changed: false };
2422
1864
  const body = program.body ?? [];
2423
1865
  let insertAt = 0;
@@ -2433,7 +1875,7 @@ function ensureSideEffectImport(program, from) {
2433
1875
  function addDevtoolsToClientComponent(program) {
2434
1876
  if (!program || program.type !== "Program") return { changed: false };
2435
1877
  if (hasUILintDevtoolsJsx(program)) return { changed: false };
2436
- const devtoolsMod = parseModule3(
1878
+ const devtoolsMod = parseModule2(
2437
1879
  "const __uilint_devtools = <uilint-devtools />;"
2438
1880
  );
2439
1881
  const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
@@ -2459,7 +1901,7 @@ function addDevtoolsToClientComponent(program) {
2459
1901
  const arg = node.argument;
2460
1902
  if (!arg) return;
2461
1903
  if (arg.type !== "JSXElement" && arg.type !== "JSXFragment") return;
2462
- const fragmentMod = parseModule3("const __fragment = <></>;");
1904
+ const fragmentMod = parseModule2("const __fragment = <></>;");
2463
1905
  const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
2464
1906
  if (!fragmentJsx) return;
2465
1907
  fragmentJsx.children = [arg, devtoolsJsx];
@@ -2514,7 +1956,7 @@ function wrapChildrenWithProviders(program, providersImportPath) {
2514
1956
  (child) => child?.type === "JSXExpressionContainer" && child.expression?.type === "Identifier" && child.expression.name === "children"
2515
1957
  );
2516
1958
  if (childrenIndex === -1) return;
2517
- const providersMod = parseModule3(
1959
+ const providersMod = parseModule2(
2518
1960
  "const __providers = <Providers>{children}</Providers>;"
2519
1961
  );
2520
1962
  const providersJsx = providersMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
@@ -2532,8 +1974,8 @@ function wrapChildrenWithProviders(program, providersImportPath) {
2532
1974
  function findLayoutFile2(projectPath, appRoot) {
2533
1975
  const extensions = [".tsx", ".jsx", ".ts", ".js"];
2534
1976
  for (const ext of extensions) {
2535
- const layoutPath = join6(projectPath, appRoot, `layout${ext}`);
2536
- if (existsSync6(layoutPath)) return layoutPath;
1977
+ const layoutPath = join5(projectPath, appRoot, `layout${ext}`);
1978
+ if (existsSync5(layoutPath)) return layoutPath;
2537
1979
  }
2538
1980
  return null;
2539
1981
  }
@@ -2544,32 +1986,32 @@ async function createProvidersAndModifyLayout(projectPath, appRoot) {
2544
1986
  }
2545
1987
  const isTypeScript = layoutPath.endsWith(".tsx") || layoutPath.endsWith(".ts");
2546
1988
  const providersExt = isTypeScript ? ".tsx" : ".jsx";
2547
- const providersPath = join6(projectPath, appRoot, `providers${providersExt}`);
2548
- if (existsSync6(providersPath)) {
1989
+ const providersPath = join5(projectPath, appRoot, `providers${providersExt}`);
1990
+ if (existsSync5(providersPath)) {
2549
1991
  throw new Error(
2550
1992
  `providers${providersExt} already exists. Please select it from the list instead.`
2551
1993
  );
2552
1994
  }
2553
1995
  const providersContent = generateProvidersContent(isTypeScript);
2554
- writeFileSync2(providersPath, providersContent, "utf-8");
2555
- const layoutContent = readFileSync6(layoutPath, "utf-8");
1996
+ writeFileSync(providersPath, providersContent, "utf-8");
1997
+ const layoutContent = readFileSync5(layoutPath, "utf-8");
2556
1998
  let layoutMod;
2557
1999
  try {
2558
- layoutMod = parseModule3(layoutContent);
2000
+ layoutMod = parseModule2(layoutContent);
2559
2001
  } catch {
2560
2002
  throw new Error(
2561
- `Unable to parse ${relative4(projectPath, layoutPath)} as JavaScript/TypeScript.`
2003
+ `Unable to parse ${relative3(projectPath, layoutPath)} as JavaScript/TypeScript.`
2562
2004
  );
2563
2005
  }
2564
2006
  const layoutProgram = layoutMod.$ast;
2565
2007
  const wrapRes = wrapChildrenWithProviders(layoutProgram, "./providers");
2566
2008
  if (wrapRes.changed) {
2567
- const updatedLayout = generateCode2(layoutMod).code;
2568
- writeFileSync2(layoutPath, updatedLayout, "utf-8");
2009
+ const updatedLayout = generateCode(layoutMod).code;
2010
+ writeFileSync(layoutPath, updatedLayout, "utf-8");
2569
2011
  }
2570
2012
  return {
2571
- providersFile: relative4(projectPath, providersPath),
2572
- layoutFile: relative4(projectPath, layoutPath),
2013
+ providersFile: relative3(projectPath, providersPath),
2014
+ layoutFile: relative3(projectPath, layoutPath),
2573
2015
  modified: true
2574
2016
  };
2575
2017
  }
@@ -2581,8 +2023,8 @@ async function installReactUILintOverlay(opts) {
2581
2023
  );
2582
2024
  const modifiedFiles = [];
2583
2025
  if (result.modified) {
2584
- modifiedFiles.push(join6(opts.projectPath, result.providersFile));
2585
- modifiedFiles.push(join6(opts.projectPath, result.layoutFile));
2026
+ modifiedFiles.push(join5(opts.projectPath, result.providersFile));
2027
+ modifiedFiles.push(join5(opts.projectPath, result.layoutFile));
2586
2028
  }
2587
2029
  return {
2588
2030
  targetFile: result.providersFile,
@@ -2594,14 +2036,14 @@ async function installReactUILintOverlay(opts) {
2594
2036
  }
2595
2037
  if (opts.targetFile) {
2596
2038
  const absTarget2 = opts.targetFile;
2597
- const relTarget = relative4(opts.projectPath, absTarget2);
2598
- if (!existsSync6(absTarget2)) {
2039
+ const relTarget = relative3(opts.projectPath, absTarget2);
2040
+ if (!existsSync5(absTarget2)) {
2599
2041
  throw new Error(`Target file not found: ${relTarget}`);
2600
2042
  }
2601
- const original2 = readFileSync6(absTarget2, "utf-8");
2043
+ const original2 = readFileSync5(absTarget2, "utf-8");
2602
2044
  let mod2;
2603
2045
  try {
2604
- mod2 = parseModule3(original2);
2046
+ mod2 = parseModule2(original2);
2605
2047
  } catch {
2606
2048
  throw new Error(
2607
2049
  `Unable to parse ${relTarget} as JavaScript/TypeScript. Please update it manually.`
@@ -2624,10 +2066,10 @@ async function installReactUILintOverlay(opts) {
2624
2066
  if (importRes2.changed) changed2 = true;
2625
2067
  const addRes2 = addDevtoolsToClientComponent(program2);
2626
2068
  if (addRes2.changed) changed2 = true;
2627
- const updated2 = changed2 ? generateCode2(mod2).code : original2;
2069
+ const updated2 = changed2 ? generateCode(mod2).code : original2;
2628
2070
  const modified2 = updated2 !== original2;
2629
2071
  if (modified2) {
2630
- writeFileSync2(absTarget2, updated2, "utf-8");
2072
+ writeFileSync(absTarget2, updated2, "utf-8");
2631
2073
  }
2632
2074
  return {
2633
2075
  targetFile: relTarget,
@@ -2648,11 +2090,11 @@ async function installReactUILintOverlay(opts) {
2648
2090
  } else {
2649
2091
  chosen = candidates[0];
2650
2092
  }
2651
- const absTarget = join6(opts.projectPath, chosen);
2652
- const original = readFileSync6(absTarget, "utf-8");
2093
+ const absTarget = join5(opts.projectPath, chosen);
2094
+ const original = readFileSync5(absTarget, "utf-8");
2653
2095
  let mod;
2654
2096
  try {
2655
- mod = parseModule3(original);
2097
+ mod = parseModule2(original);
2656
2098
  } catch {
2657
2099
  throw new Error(
2658
2100
  `Unable to parse ${chosen} as JavaScript/TypeScript. Please update it manually.`
@@ -2668,10 +2110,10 @@ async function installReactUILintOverlay(opts) {
2668
2110
  const mode = opts.mode ?? "next";
2669
2111
  const addRes = mode === "vite" ? addDevtoolsElementVite(program) : addDevtoolsElementNextJs(program);
2670
2112
  if (addRes.changed) changed = true;
2671
- const updated = changed ? generateCode2(mod).code : original;
2113
+ const updated = changed ? generateCode(mod).code : original;
2672
2114
  const modified = updated !== original;
2673
2115
  if (modified) {
2674
- writeFileSync2(absTarget, updated, "utf-8");
2116
+ writeFileSync(absTarget, updated, "utf-8");
2675
2117
  }
2676
2118
  return {
2677
2119
  targetFile: chosen,
@@ -2685,10 +2127,10 @@ async function uninstallReactUILintOverlay(options) {
2685
2127
  const candidates = getDefaultCandidates(projectPath, appRoot);
2686
2128
  const modifiedFiles = [];
2687
2129
  for (const candidate of candidates) {
2688
- const absPath = join6(projectPath, candidate);
2689
- if (!existsSync6(absPath)) continue;
2130
+ const absPath = join5(projectPath, candidate);
2131
+ if (!existsSync5(absPath)) continue;
2690
2132
  try {
2691
- const original = readFileSync6(absPath, "utf-8");
2133
+ const original = readFileSync5(absPath, "utf-8");
2692
2134
  let updated = original.replace(
2693
2135
  /^import\s+["']uilint-react\/devtools["'];?\s*$/gm,
2694
2136
  ""
@@ -2706,7 +2148,7 @@ async function uninstallReactUILintOverlay(options) {
2706
2148
  );
2707
2149
  updated = updated.replace(/\n{3,}/g, "\n\n");
2708
2150
  if (updated !== original) {
2709
- writeFileSync2(absPath, updated, "utf-8");
2151
+ writeFileSync(absPath, updated, "utf-8");
2710
2152
  modifiedFiles.push(absPath);
2711
2153
  }
2712
2154
  } catch {
@@ -2719,14 +2161,14 @@ async function uninstallReactUILintOverlay(options) {
2719
2161
  }
2720
2162
 
2721
2163
  // src/utils/next-config-inject.ts
2722
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
2723
- import { join as join7 } from "path";
2724
- import { parseModule as parseModule4, generateCode as generateCode3 } from "magicast";
2725
- var CONFIG_EXTENSIONS2 = [".ts", ".mjs", ".js", ".cjs"];
2164
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
2165
+ import { join as join6 } from "path";
2166
+ import { parseModule as parseModule3, generateCode as generateCode2 } from "magicast";
2167
+ var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
2726
2168
  function findNextConfigFile(projectPath) {
2727
- for (const ext of CONFIG_EXTENSIONS2) {
2728
- const configPath = join7(projectPath, `next.config${ext}`);
2729
- if (existsSync7(configPath)) {
2169
+ for (const ext of CONFIG_EXTENSIONS) {
2170
+ const configPath = join6(projectPath, `next.config${ext}`);
2171
+ if (existsSync6(configPath)) {
2730
2172
  return configPath;
2731
2173
  }
2732
2174
  }
@@ -2736,10 +2178,10 @@ function getNextConfigFilename(configPath) {
2736
2178
  const parts = configPath.split("/");
2737
2179
  return parts[parts.length - 1] || "next.config.ts";
2738
2180
  }
2739
- function isIdentifier2(node, name) {
2181
+ function isIdentifier(node, name) {
2740
2182
  return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
2741
2183
  }
2742
- function isStringLiteral2(node) {
2184
+ function isStringLiteral(node) {
2743
2185
  return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
2744
2186
  }
2745
2187
  function ensureEsmWithJsxLocImport(program) {
@@ -2752,12 +2194,12 @@ function ensureEsmWithJsxLocImport(program) {
2752
2194
  (sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "withJsxLoc" || sp.imported?.value === "withJsxLoc")
2753
2195
  );
2754
2196
  if (has) return { changed: false };
2755
- const spec = parseModule4('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0]?.specifiers?.[0];
2197
+ const spec = parseModule3('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0]?.specifiers?.[0];
2756
2198
  if (!spec) return { changed: false };
2757
2199
  existing.specifiers = [...existing.specifiers ?? [], spec];
2758
2200
  return { changed: true };
2759
2201
  }
2760
- const importDecl = parseModule4('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0];
2202
+ const importDecl = parseModule3('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0];
2761
2203
  if (!importDecl) return { changed: false };
2762
2204
  const body = program.body ?? [];
2763
2205
  let insertAt = 0;
@@ -2773,14 +2215,14 @@ function ensureCjsWithJsxLocRequire(program) {
2773
2215
  if (stmt?.type !== "VariableDeclaration") continue;
2774
2216
  for (const decl of stmt.declarations ?? []) {
2775
2217
  const init = decl?.init;
2776
- if (init?.type === "CallExpression" && isIdentifier2(init.callee, "require") && isStringLiteral2(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin") {
2218
+ if (init?.type === "CallExpression" && isIdentifier(init.callee, "require") && isStringLiteral(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin") {
2777
2219
  if (decl.id?.type === "ObjectPattern") {
2778
2220
  const has = (decl.id.properties ?? []).some((p) => {
2779
2221
  if (p?.type !== "ObjectProperty" && p?.type !== "Property") return false;
2780
- return isIdentifier2(p.key, "withJsxLoc");
2222
+ return isIdentifier(p.key, "withJsxLoc");
2781
2223
  });
2782
2224
  if (has) return { changed: false };
2783
- const prop = parseModule4('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
2225
+ const prop = parseModule3('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
2784
2226
  if (!prop) return { changed: false };
2785
2227
  decl.id.properties = [...decl.id.properties ?? [], prop];
2786
2228
  return { changed: true };
@@ -2789,7 +2231,7 @@ function ensureCjsWithJsxLocRequire(program) {
2789
2231
  }
2790
2232
  }
2791
2233
  }
2792
- const reqDecl = parseModule4('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0];
2234
+ const reqDecl = parseModule3('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0];
2793
2235
  if (!reqDecl) return { changed: false };
2794
2236
  program.body.unshift(reqDecl);
2795
2237
  return { changed: true };
@@ -2801,7 +2243,7 @@ function wrapEsmExportDefault(program) {
2801
2243
  );
2802
2244
  if (!exportDecl) return { changed: false };
2803
2245
  const decl = exportDecl.declaration;
2804
- if (decl?.type === "CallExpression" && isIdentifier2(decl.callee, "withJsxLoc")) {
2246
+ if (decl?.type === "CallExpression" && isIdentifier(decl.callee, "withJsxLoc")) {
2805
2247
  return { changed: false };
2806
2248
  }
2807
2249
  exportDecl.declaration = {
@@ -2819,9 +2261,9 @@ function wrapCjsModuleExports(program) {
2819
2261
  if (!expr || expr.type !== "AssignmentExpression") continue;
2820
2262
  const left = expr.left;
2821
2263
  const right = expr.right;
2822
- const isModuleExports = left?.type === "MemberExpression" && isIdentifier2(left.object, "module") && isIdentifier2(left.property, "exports");
2264
+ const isModuleExports = left?.type === "MemberExpression" && isIdentifier(left.object, "module") && isIdentifier(left.property, "exports");
2823
2265
  if (!isModuleExports) continue;
2824
- if (right?.type === "CallExpression" && isIdentifier2(right.callee, "withJsxLoc")) {
2266
+ if (right?.type === "CallExpression" && isIdentifier(right.callee, "withJsxLoc")) {
2825
2267
  return { changed: false };
2826
2268
  }
2827
2269
  expr.right = {
@@ -2839,10 +2281,10 @@ async function installJsxLocPlugin(opts) {
2839
2281
  return { configFile: null, modified: false, modifiedFiles: [] };
2840
2282
  }
2841
2283
  const configFilename = getNextConfigFilename(configPath);
2842
- const original = readFileSync7(configPath, "utf-8");
2284
+ const original = readFileSync6(configPath, "utf-8");
2843
2285
  let mod;
2844
2286
  try {
2845
- mod = parseModule4(original);
2287
+ mod = parseModule3(original);
2846
2288
  } catch {
2847
2289
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
2848
2290
  }
@@ -2860,9 +2302,9 @@ async function installJsxLocPlugin(opts) {
2860
2302
  const wrapRes = wrapEsmExportDefault(program);
2861
2303
  if (wrapRes.changed) changed = true;
2862
2304
  }
2863
- const updated = changed ? generateCode3(mod).code : original;
2305
+ const updated = changed ? generateCode2(mod).code : original;
2864
2306
  if (updated !== original) {
2865
- writeFileSync3(configPath, updated, "utf-8");
2307
+ writeFileSync2(configPath, updated, "utf-8");
2866
2308
  return { configFile: configFilename, modified: true, modifiedFiles: [configPath] };
2867
2309
  }
2868
2310
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
@@ -2877,7 +2319,7 @@ async function uninstallJsxLocPlugin(options) {
2877
2319
  };
2878
2320
  }
2879
2321
  try {
2880
- const original = readFileSync7(configPath, "utf-8");
2322
+ const original = readFileSync6(configPath, "utf-8");
2881
2323
  let updated = original.replace(
2882
2324
  /^import\s+\{[^}]*withJsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/next["'];?\s*$/gm,
2883
2325
  ""
@@ -2896,7 +2338,7 @@ async function uninstallJsxLocPlugin(options) {
2896
2338
  );
2897
2339
  updated = updated.replace(/\n{3,}/g, "\n\n");
2898
2340
  if (updated !== original) {
2899
- writeFileSync3(configPath, updated, "utf-8");
2341
+ writeFileSync2(configPath, updated, "utf-8");
2900
2342
  return {
2901
2343
  success: true,
2902
2344
  modifiedFiles: [configPath]
@@ -2915,14 +2357,14 @@ async function uninstallJsxLocPlugin(options) {
2915
2357
  }
2916
2358
 
2917
2359
  // src/utils/vite-config-inject.ts
2918
- import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
2919
- import { join as join8 } from "path";
2920
- import { parseModule as parseModule5, generateCode as generateCode4 } from "magicast";
2921
- var CONFIG_EXTENSIONS3 = [".ts", ".mjs", ".js", ".cjs"];
2360
+ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
2361
+ import { join as join7 } from "path";
2362
+ import { parseModule as parseModule4, generateCode as generateCode3 } from "magicast";
2363
+ var CONFIG_EXTENSIONS2 = [".ts", ".mjs", ".js", ".cjs"];
2922
2364
  function findViteConfigFile2(projectPath) {
2923
- for (const ext of CONFIG_EXTENSIONS3) {
2924
- const configPath = join8(projectPath, `vite.config${ext}`);
2925
- if (existsSync8(configPath)) return configPath;
2365
+ for (const ext of CONFIG_EXTENSIONS2) {
2366
+ const configPath = join7(projectPath, `vite.config${ext}`);
2367
+ if (existsSync7(configPath)) return configPath;
2926
2368
  }
2927
2369
  return null;
2928
2370
  }
@@ -2930,10 +2372,10 @@ function getViteConfigFilename(configPath) {
2930
2372
  const parts = configPath.split("/");
2931
2373
  return parts[parts.length - 1] || "vite.config.ts";
2932
2374
  }
2933
- function isIdentifier3(node, name) {
2375
+ function isIdentifier2(node, name) {
2934
2376
  return !!node && node.type === "Identifier" && (name ? node.name === name : typeof node.name === "string");
2935
2377
  }
2936
- function isStringLiteral3(node) {
2378
+ function isStringLiteral2(node) {
2937
2379
  return !!node && (node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string";
2938
2380
  }
2939
2381
  function unwrapExpression(expr) {
@@ -2965,7 +2407,7 @@ function findExportedConfigObjectExpression(mod) {
2965
2407
  if (decl.type === "ObjectExpression") {
2966
2408
  return { kind: "esm", objExpr: decl, program };
2967
2409
  }
2968
- if (decl.type === "CallExpression" && isIdentifier3(decl.callee, "defineConfig") && unwrapExpression(decl.arguments?.[0])?.type === "ObjectExpression") {
2410
+ if (decl.type === "CallExpression" && isIdentifier2(decl.callee, "defineConfig") && unwrapExpression(decl.arguments?.[0])?.type === "ObjectExpression") {
2969
2411
  return {
2970
2412
  kind: "esm",
2971
2413
  objExpr: unwrapExpression(decl.arguments?.[0]),
@@ -2980,12 +2422,12 @@ function findExportedConfigObjectExpression(mod) {
2980
2422
  if (!expr || expr.type !== "AssignmentExpression") continue;
2981
2423
  const left = expr.left;
2982
2424
  const right = unwrapExpression(expr.right);
2983
- const isModuleExports = left?.type === "MemberExpression" && isIdentifier3(left.object, "module") && isIdentifier3(left.property, "exports");
2425
+ const isModuleExports = left?.type === "MemberExpression" && isIdentifier2(left.object, "module") && isIdentifier2(left.property, "exports");
2984
2426
  if (!isModuleExports) continue;
2985
2427
  if (right?.type === "ObjectExpression") {
2986
2428
  return { kind: "cjs", objExpr: right, program };
2987
2429
  }
2988
- if (right?.type === "CallExpression" && isIdentifier3(right.callee, "defineConfig") && unwrapExpression(right.arguments?.[0])?.type === "ObjectExpression") {
2430
+ if (right?.type === "CallExpression" && isIdentifier2(right.callee, "defineConfig") && unwrapExpression(right.arguments?.[0])?.type === "ObjectExpression") {
2989
2431
  return {
2990
2432
  kind: "cjs",
2991
2433
  objExpr: unwrapExpression(right.arguments?.[0]),
@@ -3001,7 +2443,7 @@ function getObjectProperty(obj, keyName) {
3001
2443
  if (!prop) continue;
3002
2444
  if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
3003
2445
  const key = prop.key;
3004
- const keyMatch = key?.type === "Identifier" && key.name === keyName || isStringLiteral3(key) && key.value === keyName;
2446
+ const keyMatch = key?.type === "Identifier" && key.name === keyName || isStringLiteral2(key) && key.value === keyName;
3005
2447
  if (keyMatch) return prop;
3006
2448
  }
3007
2449
  return null;
@@ -3016,12 +2458,12 @@ function ensureEsmJsxLocImport(program) {
3016
2458
  (sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "jsxLoc" || sp.imported?.value === "jsxLoc")
3017
2459
  );
3018
2460
  if (has) return { changed: false };
3019
- const spec = parseModule5('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0]?.specifiers?.[0];
2461
+ const spec = parseModule4('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0]?.specifiers?.[0];
3020
2462
  if (!spec) return { changed: false };
3021
2463
  existing.specifiers = [...existing.specifiers ?? [], spec];
3022
2464
  return { changed: true };
3023
2465
  }
3024
- const importDecl = parseModule5('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0];
2466
+ const importDecl = parseModule4('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0];
3025
2467
  if (!importDecl) return { changed: false };
3026
2468
  const body = program.body ?? [];
3027
2469
  let insertAt = 0;
@@ -3037,14 +2479,14 @@ function ensureCjsJsxLocRequire(program) {
3037
2479
  if (stmt?.type !== "VariableDeclaration") continue;
3038
2480
  for (const decl of stmt.declarations ?? []) {
3039
2481
  const init = decl?.init;
3040
- if (init?.type === "CallExpression" && isIdentifier3(init.callee, "require") && isStringLiteral3(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin/vite") {
2482
+ if (init?.type === "CallExpression" && isIdentifier2(init.callee, "require") && isStringLiteral2(init.arguments?.[0]) && init.arguments[0].value === "jsx-loc-plugin/vite") {
3041
2483
  if (decl.id?.type === "ObjectPattern") {
3042
2484
  const has = (decl.id.properties ?? []).some((p) => {
3043
2485
  if (p?.type !== "ObjectProperty" && p?.type !== "Property") return false;
3044
- return isIdentifier3(p.key, "jsxLoc");
2486
+ return isIdentifier2(p.key, "jsxLoc");
3045
2487
  });
3046
2488
  if (has) return { changed: false };
3047
- const prop = parseModule5('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
2489
+ const prop = parseModule4('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
3048
2490
  if (!prop) return { changed: false };
3049
2491
  decl.id.properties = [...decl.id.properties ?? [], prop];
3050
2492
  return { changed: true };
@@ -3053,7 +2495,7 @@ function ensureCjsJsxLocRequire(program) {
3053
2495
  }
3054
2496
  }
3055
2497
  }
3056
- const reqDecl = parseModule5('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0];
2498
+ const reqDecl = parseModule4('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0];
3057
2499
  if (!reqDecl) return { changed: false };
3058
2500
  program.body.unshift(reqDecl);
3059
2501
  return { changed: true };
@@ -3063,16 +2505,16 @@ function pluginsHasJsxLoc(arr) {
3063
2505
  for (const el of arr.elements ?? []) {
3064
2506
  const e = unwrapExpression(el);
3065
2507
  if (!e) continue;
3066
- if (e.type === "CallExpression" && isIdentifier3(e.callee, "jsxLoc")) return true;
2508
+ if (e.type === "CallExpression" && isIdentifier2(e.callee, "jsxLoc")) return true;
3067
2509
  }
3068
2510
  return false;
3069
2511
  }
3070
2512
  function ensurePluginsContainsJsxLoc(configObj) {
3071
2513
  const pluginsProp = getObjectProperty(configObj, "plugins");
3072
2514
  if (!pluginsProp) {
3073
- const prop = parseModule5("export default { plugins: [jsxLoc()] };").$ast.body?.find((s) => s.type === "ExportDefaultDeclaration")?.declaration?.properties?.find((p) => {
2515
+ const prop = parseModule4("export default { plugins: [jsxLoc()] };").$ast.body?.find((s) => s.type === "ExportDefaultDeclaration")?.declaration?.properties?.find((p) => {
3074
2516
  const k = p?.key;
3075
- return k?.type === "Identifier" && k.name === "plugins" || isStringLiteral3(k) && k.value === "plugins";
2517
+ return k?.type === "Identifier" && k.name === "plugins" || isStringLiteral2(k) && k.value === "plugins";
3076
2518
  });
3077
2519
  if (!prop) return { changed: false };
3078
2520
  configObj.properties = [...configObj.properties ?? [], prop];
@@ -3082,12 +2524,12 @@ function ensurePluginsContainsJsxLoc(configObj) {
3082
2524
  if (!value) return { changed: false };
3083
2525
  if (value.type === "ArrayExpression") {
3084
2526
  if (pluginsHasJsxLoc(value)) return { changed: false };
3085
- const jsxLocCall2 = parseModule5("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
2527
+ const jsxLocCall2 = parseModule4("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
3086
2528
  if (!jsxLocCall2) return { changed: false };
3087
2529
  value.elements.push(jsxLocCall2);
3088
2530
  return { changed: true };
3089
2531
  }
3090
- const jsxLocCall = parseModule5("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
2532
+ const jsxLocCall = parseModule4("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
3091
2533
  if (!jsxLocCall) return { changed: false };
3092
2534
  const spread = { type: "SpreadElement", argument: value };
3093
2535
  pluginsProp.value = { type: "ArrayExpression", elements: [spread, jsxLocCall] };
@@ -3097,11 +2539,11 @@ async function installViteJsxLocPlugin(opts) {
3097
2539
  const configPath = findViteConfigFile2(opts.projectPath);
3098
2540
  if (!configPath) return { configFile: null, modified: false, modifiedFiles: [] };
3099
2541
  const configFilename = getViteConfigFilename(configPath);
3100
- const original = readFileSync8(configPath, "utf-8");
2542
+ const original = readFileSync7(configPath, "utf-8");
3101
2543
  const isCjs = configPath.endsWith(".cjs");
3102
2544
  let mod;
3103
2545
  try {
3104
- mod = parseModule5(original);
2546
+ mod = parseModule4(original);
3105
2547
  } catch {
3106
2548
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
3107
2549
  }
@@ -3117,9 +2559,9 @@ async function installViteJsxLocPlugin(opts) {
3117
2559
  }
3118
2560
  const pluginsRes = ensurePluginsContainsJsxLoc(found.objExpr);
3119
2561
  if (pluginsRes.changed) changed = true;
3120
- const updated = changed ? generateCode4(mod).code : original;
2562
+ const updated = changed ? generateCode3(mod).code : original;
3121
2563
  if (updated !== original) {
3122
- writeFileSync4(configPath, updated, "utf-8");
2564
+ writeFileSync3(configPath, updated, "utf-8");
3123
2565
  return { configFile: configFilename, modified: true, modifiedFiles: [configPath] };
3124
2566
  }
3125
2567
  return { configFile: configFilename, modified: false, modifiedFiles: [] };
@@ -3134,7 +2576,7 @@ async function uninstallViteJsxLocPlugin(options) {
3134
2576
  };
3135
2577
  }
3136
2578
  try {
3137
- const original = readFileSync8(configPath, "utf-8");
2579
+ const original = readFileSync7(configPath, "utf-8");
3138
2580
  let updated = original.replace(
3139
2581
  /^import\s+\{[^}]*jsxLoc[^}]*\}\s+from\s+["']jsx-loc-plugin\/vite["'];?\s*$/gm,
3140
2582
  ""
@@ -3150,7 +2592,7 @@ async function uninstallViteJsxLocPlugin(options) {
3150
2592
  updated = updated.replace(/jsxLoc\s*\(\s*\)\s*,?\s*/g, "");
3151
2593
  updated = updated.replace(/\n{3,}/g, "\n\n");
3152
2594
  if (updated !== original) {
3153
- writeFileSync4(configPath, updated, "utf-8");
2595
+ writeFileSync3(configPath, updated, "utf-8");
3154
2596
  return {
3155
2597
  success: true,
3156
2598
  modifiedFiles: [configPath]
@@ -3169,9 +2611,9 @@ async function uninstallViteJsxLocPlugin(options) {
3169
2611
  }
3170
2612
 
3171
2613
  // src/utils/next-routes.ts
3172
- import { existsSync as existsSync9 } from "fs";
2614
+ import { existsSync as existsSync8 } from "fs";
3173
2615
  import { mkdir, writeFile } from "fs/promises";
3174
- import { join as join9 } from "path";
2616
+ import { join as join8 } from "path";
3175
2617
  var DEV_SOURCE_ROUTE_TS = `/**
3176
2618
  * Dev-only API route for fetching source files
3177
2619
  *
@@ -3627,23 +3069,23 @@ export async function GET(request: NextRequest) {
3627
3069
  }
3628
3070
  `;
3629
3071
  async function writeRouteFile(absPath, relPath, content, opts) {
3630
- if (existsSync9(absPath) && !opts.force) return;
3072
+ if (existsSync8(absPath) && !opts.force) return;
3631
3073
  await writeFile(absPath, content, "utf-8");
3632
3074
  }
3633
3075
  async function installNextUILintRoutes(opts) {
3634
- const baseRel = join9(opts.appRoot, "api", ".uilint");
3635
- const baseAbs = join9(opts.projectPath, baseRel);
3636
- await mkdir(join9(baseAbs, "source"), { recursive: true });
3076
+ const baseRel = join8(opts.appRoot, "api", ".uilint");
3077
+ const baseAbs = join8(opts.projectPath, baseRel);
3078
+ await mkdir(join8(baseAbs, "source"), { recursive: true });
3637
3079
  await writeRouteFile(
3638
- join9(baseAbs, "source", "route.ts"),
3639
- join9(baseRel, "source", "route.ts"),
3080
+ join8(baseAbs, "source", "route.ts"),
3081
+ join8(baseRel, "source", "route.ts"),
3640
3082
  DEV_SOURCE_ROUTE_TS,
3641
3083
  opts
3642
3084
  );
3643
- await mkdir(join9(baseAbs, "screenshots"), { recursive: true });
3085
+ await mkdir(join8(baseAbs, "screenshots"), { recursive: true });
3644
3086
  await writeRouteFile(
3645
- join9(baseAbs, "screenshots", "route.ts"),
3646
- join9(baseRel, "screenshots", "route.ts"),
3087
+ join8(baseAbs, "screenshots", "route.ts"),
3088
+ join8(baseRel, "screenshots", "route.ts"),
3647
3089
  SCREENSHOT_ROUTE_TS,
3648
3090
  opts
3649
3091
  );
@@ -3651,9 +3093,9 @@ async function installNextUILintRoutes(opts) {
3651
3093
  async function uninstallNextUILintRoutes(options) {
3652
3094
  const { projectPath, appRoot } = options;
3653
3095
  const { rm } = await import("fs/promises");
3654
- const baseAbs = join9(projectPath, appRoot, "api", ".uilint");
3096
+ const baseAbs = join8(projectPath, appRoot, "api", ".uilint");
3655
3097
  try {
3656
- if (existsSync9(baseAbs)) {
3098
+ if (existsSync8(baseAbs)) {
3657
3099
  await rm(baseAbs, { recursive: true, force: true });
3658
3100
  }
3659
3101
  return { success: true };
@@ -3666,17 +3108,17 @@ async function uninstallNextUILintRoutes(options) {
3666
3108
  }
3667
3109
 
3668
3110
  // src/utils/prettier.ts
3669
- import { existsSync as existsSync10, utimesSync } from "fs";
3111
+ import { existsSync as existsSync9, utimesSync } from "fs";
3670
3112
  import { spawn } from "child_process";
3671
- import { join as join10, dirname as dirname4 } from "path";
3113
+ import { join as join9, dirname as dirname3 } from "path";
3672
3114
  function getPrettierPath(projectPath) {
3673
- const localPath = join10(projectPath, "node_modules", ".bin", "prettier");
3674
- if (existsSync10(localPath)) return localPath;
3115
+ const localPath = join9(projectPath, "node_modules", ".bin", "prettier");
3116
+ if (existsSync9(localPath)) return localPath;
3675
3117
  let dir = projectPath;
3676
3118
  for (let i = 0; i < 10; i++) {
3677
- const binPath = join10(dir, "node_modules", ".bin", "prettier");
3678
- if (existsSync10(binPath)) return binPath;
3679
- const parent = dirname4(dir);
3119
+ const binPath = join9(dir, "node_modules", ".bin", "prettier");
3120
+ if (existsSync9(binPath)) return binPath;
3121
+ const parent = dirname3(dir);
3680
3122
  if (parent === dir) break;
3681
3123
  dir = parent;
3682
3124
  }
@@ -3807,7 +3249,7 @@ function touchFiles(filePaths) {
3807
3249
  const now = /* @__PURE__ */ new Date();
3808
3250
  for (const filePath of filePaths) {
3809
3251
  try {
3810
- if (existsSync10(filePath)) {
3252
+ if (existsSync9(filePath)) {
3811
3253
  utimesSync(filePath, now, now);
3812
3254
  }
3813
3255
  } catch {
@@ -3816,6 +3258,7 @@ function touchFiles(filePaths) {
3816
3258
  }
3817
3259
 
3818
3260
  // src/commands/install/execute.ts
3261
+ import { findWorkspaceRoot as findWorkspaceRoot2 } from "uilint-core/node";
3819
3262
  async function executeAction(action, options) {
3820
3263
  const { dryRun = false } = options;
3821
3264
  try {
@@ -3828,7 +3271,7 @@ async function executeAction(action, options) {
3828
3271
  wouldDo: `Create directory: ${action.path}`
3829
3272
  };
3830
3273
  }
3831
- if (!existsSync11(action.path)) {
3274
+ if (!existsSync10(action.path)) {
3832
3275
  mkdirSync(action.path, { recursive: true });
3833
3276
  }
3834
3277
  return { action, success: true };
@@ -3841,11 +3284,11 @@ async function executeAction(action, options) {
3841
3284
  wouldDo: `Create file: ${action.path}${action.permissions ? ` (mode: ${action.permissions.toString(8)})` : ""}`
3842
3285
  };
3843
3286
  }
3844
- const dir = dirname5(action.path);
3845
- if (!existsSync11(dir)) {
3287
+ const dir = dirname4(action.path);
3288
+ if (!existsSync10(dir)) {
3846
3289
  mkdirSync(dir, { recursive: true });
3847
3290
  }
3848
- writeFileSync5(action.path, action.content, "utf-8");
3291
+ writeFileSync4(action.path, action.content, "utf-8");
3849
3292
  if (action.permissions) {
3850
3293
  chmodSync(action.path, action.permissions);
3851
3294
  }
@@ -3860,18 +3303,18 @@ async function executeAction(action, options) {
3860
3303
  };
3861
3304
  }
3862
3305
  let existing = {};
3863
- if (existsSync11(action.path)) {
3306
+ if (existsSync10(action.path)) {
3864
3307
  try {
3865
- existing = JSON.parse(readFileSync9(action.path, "utf-8"));
3308
+ existing = JSON.parse(readFileSync8(action.path, "utf-8"));
3866
3309
  } catch {
3867
3310
  }
3868
3311
  }
3869
3312
  const merged = deepMerge(existing, action.merge);
3870
- const dir = dirname5(action.path);
3871
- if (!existsSync11(dir)) {
3313
+ const dir = dirname4(action.path);
3314
+ if (!existsSync10(dir)) {
3872
3315
  mkdirSync(dir, { recursive: true });
3873
3316
  }
3874
- writeFileSync5(action.path, JSON.stringify(merged, null, 2), "utf-8");
3317
+ writeFileSync4(action.path, JSON.stringify(merged, null, 2), "utf-8");
3875
3318
  return { action, success: true };
3876
3319
  }
3877
3320
  case "delete_file": {
@@ -3882,7 +3325,7 @@ async function executeAction(action, options) {
3882
3325
  wouldDo: `Delete file: ${action.path}`
3883
3326
  };
3884
3327
  }
3885
- if (existsSync11(action.path)) {
3328
+ if (existsSync10(action.path)) {
3886
3329
  unlinkSync(action.path);
3887
3330
  }
3888
3331
  return { action, success: true };
@@ -3895,12 +3338,12 @@ async function executeAction(action, options) {
3895
3338
  wouldDo: `Append to file: ${action.path}`
3896
3339
  };
3897
3340
  }
3898
- if (existsSync11(action.path)) {
3899
- const content = readFileSync9(action.path, "utf-8");
3341
+ if (existsSync10(action.path)) {
3342
+ const content = readFileSync8(action.path, "utf-8");
3900
3343
  if (action.ifNotContains && content.includes(action.ifNotContains)) {
3901
3344
  return { action, success: true };
3902
3345
  }
3903
- writeFileSync5(action.path, content + action.content, "utf-8");
3346
+ writeFileSync4(action.path, content + action.content, "utf-8");
3904
3347
  }
3905
3348
  return { action, success: true };
3906
3349
  }
@@ -3916,6 +3359,9 @@ async function executeAction(action, options) {
3916
3359
  case "inject_vite_config": {
3917
3360
  return await executeInjectViteConfig(action, options);
3918
3361
  }
3362
+ case "inject_vitest_coverage": {
3363
+ return await executeInjectVitestCoverage(action, options);
3364
+ }
3919
3365
  case "install_next_routes": {
3920
3366
  return await executeInstallNextRoutes(action, options);
3921
3367
  }
@@ -4027,6 +3473,31 @@ async function executeInjectViteConfig(action, options) {
4027
3473
  modifiedFiles: result.modifiedFiles
4028
3474
  };
4029
3475
  }
3476
+ async function executeInjectVitestCoverage(action, options) {
3477
+ const { dryRun = false } = options;
3478
+ const setup = detectCoverageSetup(action.projectPath);
3479
+ const vitestConfigPath = action.vitestConfigPath || setup.vitestConfigPath;
3480
+ if (!vitestConfigPath) {
3481
+ return {
3482
+ action,
3483
+ success: true
3484
+ // Don't fail the install, coverage config is optional
3485
+ };
3486
+ }
3487
+ if (dryRun) {
3488
+ return {
3489
+ action,
3490
+ success: true,
3491
+ wouldDo: `Inject coverage config into vitest.config: ${vitestConfigPath}`
3492
+ };
3493
+ }
3494
+ const modified = injectCoverageConfig(vitestConfigPath);
3495
+ return {
3496
+ action,
3497
+ success: true,
3498
+ modifiedFiles: modified ? [vitestConfigPath] : void 0
3499
+ };
3500
+ }
4030
3501
  async function executeInjectNextConfig(action, options) {
4031
3502
  const { dryRun = false } = options;
4032
3503
  if (dryRun) {
@@ -4169,7 +3640,7 @@ async function executeRemoveDirectory(action, options) {
4169
3640
  wouldDo: `Remove directory: ${action.path}`
4170
3641
  };
4171
3642
  }
4172
- if (existsSync11(action.path)) {
3643
+ if (existsSync10(action.path)) {
4173
3644
  rmSync(action.path, { recursive: true, force: true });
4174
3645
  }
4175
3646
  return { action, success: true };
@@ -4316,6 +3787,36 @@ function getProjectPathFromActions(actionsPerformed) {
4316
3787
  }
4317
3788
  return void 0;
4318
3789
  }
3790
+ function normalizePnpmWorkspaceUilintSpecs(packagePath, packages) {
3791
+ const workspaceRoot = findWorkspaceRoot2(packagePath);
3792
+ if (!existsSync10(join10(workspaceRoot, "pnpm-workspace.yaml"))) return packages;
3793
+ const touchesUilint = packages.some((p) => p === "uilint-eslint" || p.startsWith("uilint-"));
3794
+ if (!touchesUilint) return packages;
3795
+ const pkgJsonPath = join10(packagePath, "package.json");
3796
+ if (!existsSync10(pkgJsonPath)) return packages;
3797
+ let pkg = null;
3798
+ try {
3799
+ pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
3800
+ } catch {
3801
+ return packages;
3802
+ }
3803
+ const wanted = ["uilint-core", "uilint-eslint", "uilint-react"];
3804
+ const present = /* @__PURE__ */ new Set();
3805
+ for (const name of wanted) {
3806
+ const range = pkg?.dependencies?.[name] ?? pkg?.devDependencies?.[name];
3807
+ if (typeof range === "string") {
3808
+ present.add(name);
3809
+ }
3810
+ }
3811
+ if (present.size === 0) return packages;
3812
+ const filtered = packages.filter(
3813
+ (p) => !/^uilint-(core|eslint|react)(@.+)?$/.test(p)
3814
+ );
3815
+ for (const name of present) {
3816
+ filtered.push(`${name}@workspace:*`);
3817
+ }
3818
+ return filtered;
3819
+ }
4319
3820
  async function execute(plan, options = {}) {
4320
3821
  const {
4321
3822
  dryRun = false,
@@ -4339,13 +3840,14 @@ async function execute(plan, options = {}) {
4339
3840
  continue;
4340
3841
  }
4341
3842
  try {
3843
+ const pkgs = dep.packageManager === "pnpm" ? normalizePnpmWorkspaceUilintSpecs(dep.packagePath, dep.packages) : dep.packages;
4342
3844
  await installDependencies2(
4343
3845
  dep.packageManager,
4344
3846
  dep.packagePath,
4345
- dep.packages
3847
+ pkgs
4346
3848
  );
4347
3849
  dependencyResults.push({
4348
- install: dep,
3850
+ install: { ...dep, packages: pkgs },
4349
3851
  success: true
4350
3852
  });
4351
3853
  } catch (error) {
@@ -4481,7 +3983,7 @@ var genstyleguideInstaller = {
4481
3983
  };
4482
3984
 
4483
3985
  // src/commands/install/installers/skill.ts
4484
- import { existsSync as existsSync12 } from "fs";
3986
+ import { existsSync as existsSync11 } from "fs";
4485
3987
  import { join as join12 } from "path";
4486
3988
  var skillInstaller = {
4487
3989
  id: "skill",
@@ -4494,7 +3996,7 @@ var skillInstaller = {
4494
3996
  getTargets(project) {
4495
3997
  const skillsDir = join12(project.cursorDir.path, "skills", "ui-consistency-enforcer");
4496
3998
  const skillMdPath = join12(skillsDir, "SKILL.md");
4497
- const isInstalled = existsSync12(skillMdPath);
3999
+ const isInstalled = existsSync11(skillMdPath);
4498
4000
  return [
4499
4001
  {
4500
4002
  id: "ui-consistency-skill",
@@ -4608,9 +4110,6 @@ function getUpgradeInfo(configuredRuleIds) {
4608
4110
  summary
4609
4111
  };
4610
4112
  }
4611
- function toInstallSpecifier(pkgName) {
4612
- return pkgName;
4613
- }
4614
4113
  function formatRuleOption(rule) {
4615
4114
  const severityBadge = rule.defaultSeverity === "error" ? pc.red("error") : pc.yellow("warn");
4616
4115
  return {
@@ -4747,6 +4246,9 @@ var eslintInstaller = {
4747
4246
  const warnCount = configuredRules.filter(
4748
4247
  (r) => r.severity === "warn"
4749
4248
  ).length;
4249
+ const hasSemanticDuplicates = configuredRules.some(
4250
+ (cr) => cr.rule.id === "no-semantic-duplicates"
4251
+ );
4750
4252
  log("");
4751
4253
  note(
4752
4254
  configuredRules.map(
@@ -4754,6 +4256,19 @@ var eslintInstaller = {
4754
4256
  ).join("\n"),
4755
4257
  `Selected ${configuredRules.length} rules (${errorCount} errors, ${warnCount} warnings)`
4756
4258
  );
4259
+ if (hasSemanticDuplicates) {
4260
+ log("");
4261
+ log(
4262
+ pc.yellow(
4263
+ "\u26A0\uFE0F The no-semantic-duplicates rule requires a semantic index."
4264
+ )
4265
+ );
4266
+ log(
4267
+ pc.dim(
4268
+ " Run 'uilint duplicates index' in each target app to build it."
4269
+ )
4270
+ );
4271
+ }
4757
4272
  return { configuredRules };
4758
4273
  },
4759
4274
  plan(targets, config, project) {
@@ -4777,7 +4292,14 @@ var eslintInstaller = {
4777
4292
  dependencies.push({
4778
4293
  packagePath: target.path,
4779
4294
  packageManager: detectPackageManager(target.path),
4780
- packages: [toInstallSpecifier("uilint-eslint"), "typescript-eslint"]
4295
+ packages: [
4296
+ toInstallSpecifier("uilint-eslint", {
4297
+ preferWorkspaceProtocol: project.packageManager === "pnpm",
4298
+ workspaceRoot: project.workspaceRoot,
4299
+ targetProjectPath: target.path
4300
+ }),
4301
+ "typescript-eslint"
4302
+ ]
4781
4303
  });
4782
4304
  actions.push({
4783
4305
  type: "inject_eslint",
@@ -4854,8 +4376,7 @@ var viteOverlayInstaller = {
4854
4376
  label: app.projectPath.split("/").pop() || app.projectPath,
4855
4377
  path: app.projectPath,
4856
4378
  hint: "React + Vite",
4857
- isInstalled: false
4858
- // TODO: Detect if already installed
4379
+ isInstalled: app.hasUilintOverlay
4859
4380
  }));
4860
4381
  },
4861
4382
  plan(targets, config, project) {
@@ -4863,13 +4384,27 @@ var viteOverlayInstaller = {
4863
4384
  const dependencies = [];
4864
4385
  if (targets.length === 0) return { actions, dependencies };
4865
4386
  const target = targets[0];
4866
- const appInfo = project.viteApps.find((app) => app.projectPath === target.path);
4387
+ const appInfo = project.viteApps.find(
4388
+ (app) => app.projectPath === target.path
4389
+ );
4867
4390
  if (!appInfo) return { actions, dependencies };
4868
4391
  const { projectPath, detection } = appInfo;
4869
4392
  dependencies.push({
4870
4393
  packagePath: projectPath,
4871
4394
  packageManager: project.packageManager,
4872
- packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
4395
+ packages: [
4396
+ toInstallSpecifier("uilint-react", {
4397
+ preferWorkspaceProtocol: project.packageManager === "pnpm",
4398
+ workspaceRoot: project.workspaceRoot,
4399
+ targetProjectPath: projectPath
4400
+ }),
4401
+ toInstallSpecifier("uilint-core", {
4402
+ preferWorkspaceProtocol: project.packageManager === "pnpm",
4403
+ workspaceRoot: project.workspaceRoot,
4404
+ targetProjectPath: projectPath
4405
+ }),
4406
+ "jsx-loc-plugin"
4407
+ ]
4873
4408
  });
4874
4409
  actions.push({
4875
4410
  type: "inject_react",
@@ -4947,6 +4482,105 @@ registerInstaller(viteOverlayInstaller);
4947
4482
 
4948
4483
  // src/commands/install-ui.tsx
4949
4484
  import { jsx as jsx7 } from "react/jsx-runtime";
4485
+ function limitList(items, max) {
4486
+ if (items.length <= max) return items;
4487
+ return [...items.slice(0, max), pc.dim(`\u2026and ${items.length - max} more`)];
4488
+ }
4489
+ function printInstallReport(result, testCoverageResult) {
4490
+ const failedDeps = result.dependencyResults.filter((r) => !r.success);
4491
+ const okDeps = result.dependencyResults.filter((r) => r.success);
4492
+ const failedActions = result.actionsPerformed.filter((r) => !r.success);
4493
+ const okActions = result.actionsPerformed.filter((r) => r.success);
4494
+ if (result.success) {
4495
+ console.log(`
4496
+ ${pc.green("\u2713")} Operation completed successfully`);
4497
+ } else {
4498
+ console.log(`
4499
+ ${pc.yellow("\u26A0")} Operation completed with errors`);
4500
+ }
4501
+ const { summary } = result;
4502
+ const installed = summary.installedItems.map((x) => String(x));
4503
+ const created = summary.filesCreated;
4504
+ const modified = summary.filesModified;
4505
+ const deleted = summary.filesDeleted;
4506
+ if (installed.length > 0) {
4507
+ console.log(`
4508
+ ${pc.bold("Installed:")}`);
4509
+ for (const item of installed) console.log(`- ${pc.green("\u2713")} ${item}`);
4510
+ }
4511
+ if (summary.eslintTargets.length > 0) {
4512
+ console.log(`
4513
+ ${pc.bold("ESLint configured:")}`);
4514
+ for (const t of summary.eslintTargets) {
4515
+ console.log(
4516
+ `- ${pc.green("\u2713")} ${t.displayName} ${pc.dim(`(${t.configFile})`)}`
4517
+ );
4518
+ }
4519
+ }
4520
+ if (created.length + modified.length + deleted.length > 0) {
4521
+ console.log(`
4522
+ ${pc.bold("Files:")}`);
4523
+ for (const p of limitList(created, 20))
4524
+ console.log(`- ${pc.green("+")} ${p}`);
4525
+ for (const p of limitList(modified, 20))
4526
+ console.log(`- ${pc.yellow("~")} ${p}`);
4527
+ for (const p of limitList(deleted, 20))
4528
+ console.log(`- ${pc.red("-")} ${p}`);
4529
+ }
4530
+ if (summary.dependenciesInstalled.length > 0) {
4531
+ console.log(`
4532
+ ${pc.bold("Dependencies installed:")}`);
4533
+ for (const d of summary.dependenciesInstalled) {
4534
+ console.log(
4535
+ `- ${pc.green("\u2713")} ${d.packagePath} ${pc.dim(`\u2190 ${d.packages.join(", ")}`)}`
4536
+ );
4537
+ }
4538
+ }
4539
+ if (testCoverageResult?.ran) {
4540
+ console.log(`
4541
+ ${pc.bold("Test coverage:")}`);
4542
+ if (testCoverageResult.success) {
4543
+ console.log(`- ${pc.green("\u2713")} Coverage data generated successfully`);
4544
+ } else {
4545
+ console.log(`- ${pc.yellow("\u26A0")} Tests ran with errors`);
4546
+ if (testCoverageResult.error) {
4547
+ console.log(pc.dim(` ${testCoverageResult.error.split("\n")[0]}`));
4548
+ }
4549
+ }
4550
+ }
4551
+ if (failedDeps.length > 0 || failedActions.length > 0) {
4552
+ console.log(`
4553
+ ${pc.bold(pc.red("Failures:"))}`);
4554
+ }
4555
+ if (failedDeps.length > 0) {
4556
+ console.log(`
4557
+ ${pc.bold("Dependency installs failed:")}`);
4558
+ for (const dep of failedDeps) {
4559
+ const pkgs = dep.install.packages.join(", ");
4560
+ console.log(
4561
+ `- ${pc.red("\u2717")} ${dep.install.packageManager} in ${dep.install.packagePath} ${pc.dim(`\u2190 ${pkgs}`)}`
4562
+ );
4563
+ if (dep.error) console.log(pc.dim(dep.error.split("\n").slice(0, 30).join("\n")));
4564
+ }
4565
+ }
4566
+ if (failedActions.length > 0) {
4567
+ console.log(`
4568
+ ${pc.bold("Actions failed:")}`);
4569
+ for (const a of failedActions) {
4570
+ const action = a.action;
4571
+ const type = String(action.type || "unknown");
4572
+ const pathish = typeof action.path === "string" && action.path || typeof action.projectPath === "string" && action.projectPath || typeof action.packagePath === "string" && action.packagePath || "";
4573
+ console.error(`- ${type}${pathish ? ` (${pathish})` : ""}`);
4574
+ if (a.error) console.error(` ${a.error}`);
4575
+ }
4576
+ }
4577
+ console.log(
4578
+ pc.dim(
4579
+ `
4580
+ Summary: ${okActions.length} action(s) ok, ${failedActions.length} failed \xB7 ${okDeps.length} dep install(s) ok, ${failedDeps.length} failed`
4581
+ )
4582
+ );
4583
+ }
4950
4584
  function selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig) {
4951
4585
  const items = [];
4952
4586
  const choices = { items };
@@ -5025,7 +4659,7 @@ async function installUI(options = {}, executeOptions = {}) {
5025
4659
  console.log("\nNo changes selected");
5026
4660
  process.exit(0);
5027
4661
  }
5028
- const { createPlan } = await import("./plan-SIXVCXCK.js");
4662
+ const { createPlan } = await import("./plan-5WHKVACB.js");
5029
4663
  const plan = createPlan(project, choices, { force: options.force });
5030
4664
  if (hasUninstalls && uninstallSelections) {
5031
4665
  for (const selection of uninstallSelections) {
@@ -5041,17 +4675,28 @@ async function installUI(options = {}, executeOptions = {}) {
5041
4675
  ...executeOptions,
5042
4676
  projectPath: project.projectPath
5043
4677
  });
5044
- if (result.success) {
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!");
4678
+ let testCoverageResult;
4679
+ if (result.success && eslintRules?.some((r) => r.rule.id === "require-test-coverage")) {
4680
+ const eslintTargetPaths = choices.eslint?.packagePaths ?? [];
4681
+ for (const targetPath of eslintTargetPaths) {
4682
+ const coverageSetup = detectCoverageSetup(targetPath);
4683
+ if (coverageSetup.hasVitest && coverageSetup.hasCoverageConfig) {
4684
+ console.log(`
4685
+ ${pc.blue("Running tests with coverage...")}`);
4686
+ const pm = detectPackageManager(targetPath);
4687
+ try {
4688
+ await runTestsWithCoverage(pm, targetPath);
4689
+ testCoverageResult = { ran: true, success: true };
4690
+ console.log(`${pc.green("\u2713")} Coverage data generated`);
4691
+ } catch (error) {
4692
+ const errorMsg = error instanceof Error ? error.message : String(error);
4693
+ testCoverageResult = { ran: true, success: false, error: errorMsg };
4694
+ console.log(`${pc.yellow("\u26A0")} Tests completed with errors`);
4695
+ }
4696
+ }
5051
4697
  }
5052
- } else {
5053
- console.log("\n\u26A0 Operation completed with errors");
5054
4698
  }
4699
+ printInstallReport(result, testCoverageResult);
5055
4700
  process.exit(result.success ? 0 : 1);
5056
4701
  },
5057
4702
  onError: (error) => {
@@ -5066,4 +4711,4 @@ async function installUI(options = {}, executeOptions = {}) {
5066
4711
  export {
5067
4712
  installUI
5068
4713
  };
5069
- //# sourceMappingURL=install-ui-HTVB5HDB.js.map
4714
+ //# sourceMappingURL=install-ui-CCZ3XJDE.js.map