uilint 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EYCSOCU4.js → chunk-FRNXXIEM.js} +1 -32
- package/dist/{chunk-EYCSOCU4.js.map → chunk-FRNXXIEM.js.map} +1 -1
- package/dist/{chunk-2RNDQVEK.js → chunk-PBEKMDUH.js} +68 -13
- package/dist/chunk-PBEKMDUH.js.map +1 -0
- package/dist/index.js +16 -7
- package/dist/index.js.map +1 -1
- package/dist/{install-ui-ITXPOUVQ.js → install-ui-73AINMZ5.js} +602 -243
- package/dist/install-ui-73AINMZ5.js.map +1 -0
- package/dist/{plan-PX7FFJ25.js → plan-VPDTICSY.js} +9 -8
- package/dist/plan-VPDTICSY.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-2RNDQVEK.js.map +0 -1
- package/dist/install-ui-ITXPOUVQ.js.map +0 -1
- package/dist/plan-PX7FFJ25.js.map +0 -1
|
@@ -7,13 +7,14 @@ import {
|
|
|
7
7
|
multiselect,
|
|
8
8
|
note,
|
|
9
9
|
pc,
|
|
10
|
-
printBox,
|
|
11
10
|
select
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-FRNXXIEM.js";
|
|
13
12
|
import {
|
|
14
13
|
GENSTYLEGUIDE_COMMAND_MD,
|
|
14
|
+
detectPackageManager,
|
|
15
|
+
installDependencies,
|
|
15
16
|
loadSkill
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-PBEKMDUH.js";
|
|
17
18
|
|
|
18
19
|
// src/commands/install-ui.tsx
|
|
19
20
|
import { render } from "ink";
|
|
@@ -141,6 +142,9 @@ function StatusIndicator({ status, isSelected }) {
|
|
|
141
142
|
if (status === "installed") {
|
|
142
143
|
return /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713" });
|
|
143
144
|
}
|
|
145
|
+
if (status === "upgradeable") {
|
|
146
|
+
return isSelected ? /* @__PURE__ */ jsx3(Text3, { color: "blue", children: "\u2B06" }) : /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713" });
|
|
147
|
+
}
|
|
144
148
|
if (isSelected || status === "selected") {
|
|
145
149
|
return /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "\u25C9" });
|
|
146
150
|
}
|
|
@@ -153,6 +157,9 @@ function StatusLabel({ status }) {
|
|
|
153
157
|
if (status === "installed") {
|
|
154
158
|
return /* @__PURE__ */ jsx3(Text3, { color: "green", dimColor: true, children: "installed" });
|
|
155
159
|
}
|
|
160
|
+
if (status === "upgradeable") {
|
|
161
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "blue", dimColor: true, children: "upgrade available" });
|
|
162
|
+
}
|
|
156
163
|
if (status === "partial") {
|
|
157
164
|
return /* @__PURE__ */ jsx3(Text3, { color: "yellow", dimColor: true, children: "partial" });
|
|
158
165
|
}
|
|
@@ -178,7 +185,8 @@ function ConfigSelector({
|
|
|
178
185
|
const flatItems = items;
|
|
179
186
|
const handleToggle = useCallback(() => {
|
|
180
187
|
const item = flatItems[cursor];
|
|
181
|
-
|
|
188
|
+
const canToggle = item && !item.disabled && item.status !== "installed";
|
|
189
|
+
if (!canToggle) return;
|
|
182
190
|
setSelected((prev) => {
|
|
183
191
|
const next = new Set(prev);
|
|
184
192
|
if (next.has(item.id)) {
|
|
@@ -519,6 +527,15 @@ function getAllInstallers() {
|
|
|
519
527
|
|
|
520
528
|
// src/commands/install/components/InstallApp.tsx
|
|
521
529
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
530
|
+
function getTargetStatus(target) {
|
|
531
|
+
if (!target.isInstalled) {
|
|
532
|
+
return "not_installed";
|
|
533
|
+
}
|
|
534
|
+
if (target.canUpgrade) {
|
|
535
|
+
return "upgradeable";
|
|
536
|
+
}
|
|
537
|
+
return "installed";
|
|
538
|
+
}
|
|
522
539
|
function buildConfigItemsForProject(project, selectedProject, selections) {
|
|
523
540
|
const items = [];
|
|
524
541
|
for (const selection of selections) {
|
|
@@ -546,7 +563,7 @@ function buildConfigItemsForProject(project, selectedProject, selections) {
|
|
|
546
563
|
id: `${installer.id}:${target.id}`,
|
|
547
564
|
label: installer.name,
|
|
548
565
|
hint: target.hint,
|
|
549
|
-
status: target
|
|
566
|
+
status: getTargetStatus(target),
|
|
550
567
|
category: info.category,
|
|
551
568
|
categoryIcon: info.icon
|
|
552
569
|
});
|
|
@@ -571,7 +588,7 @@ function buildGlobalConfigItems(selections) {
|
|
|
571
588
|
id: `${installer.id}:${target.id}`,
|
|
572
589
|
label: installer.name,
|
|
573
590
|
hint: target.hint,
|
|
574
|
-
status: target
|
|
591
|
+
status: getTargetStatus(target),
|
|
575
592
|
category: info.category,
|
|
576
593
|
categoryIcon: info.icon
|
|
577
594
|
});
|
|
@@ -653,11 +670,13 @@ function InstallApp({
|
|
|
653
670
|
const installers2 = getAllInstallers();
|
|
654
671
|
const initialSelections = installers2.filter((installer) => installer.isApplicable(proj)).map((installer) => {
|
|
655
672
|
const targets = installer.getTargets(proj);
|
|
656
|
-
const
|
|
673
|
+
const actionableTargets = targets.filter(
|
|
674
|
+
(t) => !t.isInstalled || t.canUpgrade
|
|
675
|
+
);
|
|
657
676
|
return {
|
|
658
677
|
installer,
|
|
659
678
|
targets,
|
|
660
|
-
selected:
|
|
679
|
+
selected: actionableTargets.length > 0
|
|
661
680
|
};
|
|
662
681
|
});
|
|
663
682
|
setSelections(initialSelections);
|
|
@@ -798,8 +817,8 @@ function InstallApp({
|
|
|
798
817
|
}
|
|
799
818
|
|
|
800
819
|
// src/commands/install/analyze.ts
|
|
801
|
-
import { existsSync as
|
|
802
|
-
import { join as
|
|
820
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
821
|
+
import { join as join4 } from "path";
|
|
803
822
|
import { findWorkspaceRoot as findWorkspaceRoot2 } from "uilint-core/node";
|
|
804
823
|
|
|
805
824
|
// src/utils/vite-detect.ts
|
|
@@ -1043,86 +1062,16 @@ function findPackages(rootDir, options) {
|
|
|
1043
1062
|
});
|
|
1044
1063
|
}
|
|
1045
1064
|
|
|
1046
|
-
// src/utils/package-manager.ts
|
|
1047
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1048
|
-
import { spawn } from "child_process";
|
|
1049
|
-
import { dirname, join as join3 } from "path";
|
|
1050
|
-
function detectExecutionPackageManager() {
|
|
1051
|
-
const userAgent = process.env.npm_config_user_agent;
|
|
1052
|
-
if (userAgent) {
|
|
1053
|
-
if (userAgent.startsWith("pnpm/")) return "pnpm";
|
|
1054
|
-
if (userAgent.startsWith("yarn/")) return "yarn";
|
|
1055
|
-
if (userAgent.startsWith("bun/")) return "bun";
|
|
1056
|
-
if (userAgent.startsWith("npm/")) return "npm";
|
|
1057
|
-
}
|
|
1058
|
-
const execPath = process.env.npm_execpath;
|
|
1059
|
-
if (execPath) {
|
|
1060
|
-
if (execPath.includes("pnpm")) return "pnpm";
|
|
1061
|
-
if (execPath.includes("yarn")) return "yarn";
|
|
1062
|
-
if (execPath.includes("bun")) return "bun";
|
|
1063
|
-
if (execPath.includes("npm")) return "npm";
|
|
1064
|
-
}
|
|
1065
|
-
return null;
|
|
1066
|
-
}
|
|
1067
|
-
function detectPackageManager(projectPath) {
|
|
1068
|
-
let dir = projectPath;
|
|
1069
|
-
for (; ; ) {
|
|
1070
|
-
if (existsSync3(join3(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
1071
|
-
if (existsSync3(join3(dir, "pnpm-workspace.yaml"))) return "pnpm";
|
|
1072
|
-
if (existsSync3(join3(dir, "yarn.lock"))) return "yarn";
|
|
1073
|
-
if (existsSync3(join3(dir, "bun.lockb"))) return "bun";
|
|
1074
|
-
if (existsSync3(join3(dir, "bun.lock"))) return "bun";
|
|
1075
|
-
if (existsSync3(join3(dir, "package-lock.json"))) return "npm";
|
|
1076
|
-
const parent = dirname(dir);
|
|
1077
|
-
if (parent === dir) break;
|
|
1078
|
-
dir = parent;
|
|
1079
|
-
}
|
|
1080
|
-
return "npm";
|
|
1081
|
-
}
|
|
1082
|
-
function spawnAsync(command, args, cwd) {
|
|
1083
|
-
return new Promise((resolve, reject) => {
|
|
1084
|
-
const child = spawn(command, args, {
|
|
1085
|
-
cwd,
|
|
1086
|
-
stdio: "inherit",
|
|
1087
|
-
shell: process.platform === "win32"
|
|
1088
|
-
});
|
|
1089
|
-
child.on("error", reject);
|
|
1090
|
-
child.on("close", (code) => {
|
|
1091
|
-
if (code === 0) resolve();
|
|
1092
|
-
else
|
|
1093
|
-
reject(new Error(`${command} ${args.join(" ")} exited with ${code}`));
|
|
1094
|
-
});
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
async function installDependencies(pm, projectPath, packages) {
|
|
1098
|
-
if (!packages.length) return;
|
|
1099
|
-
switch (pm) {
|
|
1100
|
-
case "pnpm":
|
|
1101
|
-
await spawnAsync("pnpm", ["add", ...packages], projectPath);
|
|
1102
|
-
return;
|
|
1103
|
-
case "yarn":
|
|
1104
|
-
await spawnAsync("yarn", ["add", ...packages], projectPath);
|
|
1105
|
-
return;
|
|
1106
|
-
case "bun":
|
|
1107
|
-
await spawnAsync("bun", ["add", ...packages], projectPath);
|
|
1108
|
-
return;
|
|
1109
|
-
case "npm":
|
|
1110
|
-
default:
|
|
1111
|
-
await spawnAsync("npm", ["install", "--save", ...packages], projectPath);
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
1065
|
// src/utils/eslint-config-inject.ts
|
|
1117
|
-
import { existsSync as
|
|
1118
|
-
import { join as
|
|
1066
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1067
|
+
import { join as join3, relative as relative2, dirname } from "path";
|
|
1119
1068
|
import { parseExpression, parseModule, generateCode } from "magicast";
|
|
1120
1069
|
import { findWorkspaceRoot } from "uilint-core/node";
|
|
1121
1070
|
var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
|
|
1122
1071
|
function findEslintConfigFile(projectPath) {
|
|
1123
1072
|
for (const ext of CONFIG_EXTENSIONS) {
|
|
1124
|
-
const configPath =
|
|
1125
|
-
if (
|
|
1073
|
+
const configPath = join3(projectPath, `eslint.config${ext}`);
|
|
1074
|
+
if (existsSync3(configPath)) {
|
|
1126
1075
|
return configPath;
|
|
1127
1076
|
}
|
|
1128
1077
|
}
|
|
@@ -1345,8 +1294,8 @@ function chooseUniqueIdentifier(base, used) {
|
|
|
1345
1294
|
function addLocalRuleImportsAst(mod, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
|
|
1346
1295
|
const importNames = /* @__PURE__ */ new Map();
|
|
1347
1296
|
let changed = false;
|
|
1348
|
-
const configDir =
|
|
1349
|
-
const rulesDir =
|
|
1297
|
+
const configDir = dirname(configPath);
|
|
1298
|
+
const rulesDir = join3(rulesRoot, ".uilint", "rules");
|
|
1350
1299
|
const relativeRulesPath = relative2(configDir, rulesDir).replace(/\\/g, "/");
|
|
1351
1300
|
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1352
1301
|
const used = collectTopLevelBindings(mod.$ast);
|
|
@@ -1373,8 +1322,8 @@ function addLocalRuleRequiresAst(program, selectedRules, configPath, rulesRoot,
|
|
|
1373
1322
|
if (!program || program.type !== "Program") {
|
|
1374
1323
|
return { importNames, changed };
|
|
1375
1324
|
}
|
|
1376
|
-
const configDir =
|
|
1377
|
-
const rulesDir =
|
|
1325
|
+
const configDir = dirname(configPath);
|
|
1326
|
+
const rulesDir = join3(rulesRoot, ".uilint", "rules");
|
|
1378
1327
|
const relativeRulesPath = relative2(configDir, rulesDir).replace(/\\/g, "/");
|
|
1379
1328
|
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1380
1329
|
const used = collectTopLevelBindings(program);
|
|
@@ -1585,10 +1534,10 @@ async function installEslintPlugin(opts) {
|
|
|
1585
1534
|
};
|
|
1586
1535
|
}
|
|
1587
1536
|
let modifiedAst = false;
|
|
1588
|
-
const localRulesDir =
|
|
1537
|
+
const localRulesDir = join3(opts.projectPath, ".uilint", "rules");
|
|
1589
1538
|
const workspaceRoot = findWorkspaceRoot(opts.projectPath);
|
|
1590
|
-
const workspaceRulesDir =
|
|
1591
|
-
const rulesRoot =
|
|
1539
|
+
const workspaceRulesDir = join3(workspaceRoot, ".uilint", "rules");
|
|
1540
|
+
const rulesRoot = existsSync3(localRulesDir) ? opts.projectPath : workspaceRoot;
|
|
1592
1541
|
const isTypeScriptConfig = configPath.endsWith(".ts");
|
|
1593
1542
|
let fileExtension = isTypeScriptConfig ? "" : ".js";
|
|
1594
1543
|
let ruleImportNames;
|
|
@@ -1663,12 +1612,12 @@ async function installEslintPlugin(opts) {
|
|
|
1663
1612
|
async function analyze(projectPath = process.cwd()) {
|
|
1664
1613
|
const workspaceRoot = findWorkspaceRoot2(projectPath);
|
|
1665
1614
|
const packageManager = detectPackageManager(projectPath);
|
|
1666
|
-
const cursorDir =
|
|
1667
|
-
const cursorDirExists =
|
|
1668
|
-
const styleguidePath =
|
|
1669
|
-
const styleguideExists =
|
|
1670
|
-
const commandsDir =
|
|
1671
|
-
const genstyleguideExists =
|
|
1615
|
+
const cursorDir = join4(projectPath, ".cursor");
|
|
1616
|
+
const cursorDirExists = existsSync4(cursorDir);
|
|
1617
|
+
const styleguidePath = join4(projectPath, ".uilint", "styleguide.md");
|
|
1618
|
+
const styleguideExists = existsSync4(styleguidePath);
|
|
1619
|
+
const commandsDir = join4(cursorDir, "commands");
|
|
1620
|
+
const genstyleguideExists = existsSync4(join4(commandsDir, "genstyleguide.md"));
|
|
1672
1621
|
const nextApps = [];
|
|
1673
1622
|
const directDetection = detectNextAppRouter(projectPath);
|
|
1674
1623
|
if (directDetection) {
|
|
@@ -1742,7 +1691,7 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
1742
1691
|
|
|
1743
1692
|
// src/commands/install/execute.ts
|
|
1744
1693
|
import {
|
|
1745
|
-
existsSync as
|
|
1694
|
+
existsSync as existsSync10,
|
|
1746
1695
|
mkdirSync,
|
|
1747
1696
|
writeFileSync as writeFileSync5,
|
|
1748
1697
|
readFileSync as readFileSync8,
|
|
@@ -1752,39 +1701,39 @@ import {
|
|
|
1752
1701
|
import { dirname as dirname4 } from "path";
|
|
1753
1702
|
|
|
1754
1703
|
// src/utils/react-inject.ts
|
|
1755
|
-
import { existsSync as
|
|
1756
|
-
import { join as
|
|
1704
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
1705
|
+
import { join as join5, relative as relative3 } from "path";
|
|
1757
1706
|
import { parseModule as parseModule2, generateCode as generateCode2 } from "magicast";
|
|
1758
1707
|
function getDefaultCandidates(projectPath, appRoot) {
|
|
1759
1708
|
const viteMainCandidates = [
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1709
|
+
join5(appRoot, "main.tsx"),
|
|
1710
|
+
join5(appRoot, "main.jsx"),
|
|
1711
|
+
join5(appRoot, "main.ts"),
|
|
1712
|
+
join5(appRoot, "main.js")
|
|
1764
1713
|
];
|
|
1765
1714
|
const existingViteMain = viteMainCandidates.filter(
|
|
1766
|
-
(rel) =>
|
|
1715
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
1767
1716
|
);
|
|
1768
1717
|
if (existingViteMain.length > 0) return existingViteMain;
|
|
1769
|
-
const viteAppCandidates = [
|
|
1718
|
+
const viteAppCandidates = [join5(appRoot, "App.tsx"), join5(appRoot, "App.jsx")];
|
|
1770
1719
|
const existingViteApp = viteAppCandidates.filter(
|
|
1771
|
-
(rel) =>
|
|
1720
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
1772
1721
|
);
|
|
1773
1722
|
if (existingViteApp.length > 0) return existingViteApp;
|
|
1774
1723
|
const layoutCandidates = [
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1724
|
+
join5(appRoot, "layout.tsx"),
|
|
1725
|
+
join5(appRoot, "layout.jsx"),
|
|
1726
|
+
join5(appRoot, "layout.ts"),
|
|
1727
|
+
join5(appRoot, "layout.js")
|
|
1779
1728
|
];
|
|
1780
1729
|
const existingLayouts = layoutCandidates.filter(
|
|
1781
|
-
(rel) =>
|
|
1730
|
+
(rel) => existsSync5(join5(projectPath, rel))
|
|
1782
1731
|
);
|
|
1783
1732
|
if (existingLayouts.length > 0) {
|
|
1784
1733
|
return existingLayouts;
|
|
1785
1734
|
}
|
|
1786
|
-
const pageCandidates = [
|
|
1787
|
-
return pageCandidates.filter((rel) =>
|
|
1735
|
+
const pageCandidates = [join5(appRoot, "page.tsx"), join5(appRoot, "page.jsx")];
|
|
1736
|
+
return pageCandidates.filter((rel) => existsSync5(join5(projectPath, rel)));
|
|
1788
1737
|
}
|
|
1789
1738
|
function isUseClientDirective(stmt) {
|
|
1790
1739
|
return stmt?.type === "ExpressionStatement" && stmt.expression?.type === "StringLiteral" && stmt.expression.value === "use client";
|
|
@@ -1810,6 +1759,32 @@ function walkAst(node, visit) {
|
|
|
1810
1759
|
}
|
|
1811
1760
|
}
|
|
1812
1761
|
}
|
|
1762
|
+
function ensureNamedImport(program, from, name) {
|
|
1763
|
+
if (!program || program.type !== "Program") return { changed: false };
|
|
1764
|
+
const existing = findImportDeclaration(program, from);
|
|
1765
|
+
if (existing) {
|
|
1766
|
+
const has = (existing.specifiers ?? []).some(
|
|
1767
|
+
(s) => s?.type === "ImportSpecifier" && (s.imported?.name === name || s.imported?.value === name)
|
|
1768
|
+
);
|
|
1769
|
+
if (has) return { changed: false };
|
|
1770
|
+
const spec = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0]?.specifiers?.[0];
|
|
1771
|
+
if (!spec) return { changed: false };
|
|
1772
|
+
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
1773
|
+
return { changed: true };
|
|
1774
|
+
}
|
|
1775
|
+
const importDecl = parseModule2(`import { ${name} } from "${from}";`).$ast.body?.[0];
|
|
1776
|
+
if (!importDecl) return { changed: false };
|
|
1777
|
+
const body = program.body ?? [];
|
|
1778
|
+
let insertAt = 0;
|
|
1779
|
+
while (insertAt < body.length && isUseClientDirective(body[insertAt])) {
|
|
1780
|
+
insertAt++;
|
|
1781
|
+
}
|
|
1782
|
+
while (insertAt < body.length && body[insertAt]?.type === "ImportDeclaration") {
|
|
1783
|
+
insertAt++;
|
|
1784
|
+
}
|
|
1785
|
+
program.body.splice(insertAt, 0, importDecl);
|
|
1786
|
+
return { changed: true };
|
|
1787
|
+
}
|
|
1813
1788
|
function hasUILintDevtoolsJsx(program) {
|
|
1814
1789
|
let found = false;
|
|
1815
1790
|
walkAst(program, (node) => {
|
|
@@ -1903,7 +1878,204 @@ function ensureSideEffectImport(program, from) {
|
|
|
1903
1878
|
program.body.splice(insertAt, 0, importDecl);
|
|
1904
1879
|
return { changed: true };
|
|
1905
1880
|
}
|
|
1881
|
+
function addDevtoolsToClientComponent(program) {
|
|
1882
|
+
if (!program || program.type !== "Program") return { changed: false };
|
|
1883
|
+
if (hasUILintDevtoolsJsx(program)) return { changed: false };
|
|
1884
|
+
const devtoolsMod = parseModule2(
|
|
1885
|
+
"const __uilint_devtools = (<uilint-devtools />);"
|
|
1886
|
+
);
|
|
1887
|
+
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
1888
|
+
if (!devtoolsJsx || devtoolsJsx.type !== "JSXElement")
|
|
1889
|
+
return { changed: false };
|
|
1890
|
+
let added = false;
|
|
1891
|
+
walkAst(program, (node) => {
|
|
1892
|
+
if (added) return;
|
|
1893
|
+
if (node.type !== "JSXElement" && node.type !== "JSXFragment") return;
|
|
1894
|
+
const children = node.children ?? [];
|
|
1895
|
+
const childrenIndex = children.findIndex(
|
|
1896
|
+
(child) => child?.type === "JSXExpressionContainer" && child.expression?.type === "Identifier" && child.expression.name === "children"
|
|
1897
|
+
);
|
|
1898
|
+
if (childrenIndex !== -1) {
|
|
1899
|
+
children.splice(childrenIndex + 1, 0, devtoolsJsx);
|
|
1900
|
+
added = true;
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
if (added) return { changed: true };
|
|
1904
|
+
walkAst(program, (node) => {
|
|
1905
|
+
if (added) return;
|
|
1906
|
+
if (node.type !== "ReturnStatement") return;
|
|
1907
|
+
const arg = node.argument;
|
|
1908
|
+
if (!arg) return;
|
|
1909
|
+
if (arg.type !== "JSXElement" && arg.type !== "JSXFragment") return;
|
|
1910
|
+
const fragmentMod = parseModule2("const __fragment = (<></>);");
|
|
1911
|
+
const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
1912
|
+
if (!fragmentJsx) return;
|
|
1913
|
+
fragmentJsx.children = [arg, devtoolsJsx];
|
|
1914
|
+
node.argument = fragmentJsx;
|
|
1915
|
+
added = true;
|
|
1916
|
+
});
|
|
1917
|
+
if (!added) {
|
|
1918
|
+
throw new Error(
|
|
1919
|
+
"Could not find a suitable location to add devtools. Expected a component with JSX return or {children}."
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
return { changed: true };
|
|
1923
|
+
}
|
|
1924
|
+
function generateProvidersContent(isTypeScript) {
|
|
1925
|
+
const ext = isTypeScript ? "tsx" : "jsx";
|
|
1926
|
+
const typeAnnotation = isTypeScript ? ": { children: React.ReactNode }" : "";
|
|
1927
|
+
return `"use client";
|
|
1928
|
+
|
|
1929
|
+
import React from "react";
|
|
1930
|
+
import "uilint-react/devtools";
|
|
1931
|
+
|
|
1932
|
+
export function Providers({ children }${typeAnnotation}) {
|
|
1933
|
+
return (
|
|
1934
|
+
<>
|
|
1935
|
+
{children}
|
|
1936
|
+
<uilint-devtools />
|
|
1937
|
+
</>
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
`;
|
|
1941
|
+
}
|
|
1942
|
+
function wrapChildrenWithProviders(program, providersImportPath) {
|
|
1943
|
+
if (!program || program.type !== "Program") return { changed: false };
|
|
1944
|
+
let hasProvidersImport = false;
|
|
1945
|
+
for (const stmt of program.body ?? []) {
|
|
1946
|
+
if (stmt?.type !== "ImportDeclaration") continue;
|
|
1947
|
+
if (stmt.source?.value === providersImportPath) {
|
|
1948
|
+
hasProvidersImport = true;
|
|
1949
|
+
break;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
if (!hasProvidersImport) {
|
|
1953
|
+
const importRes = ensureNamedImport(program, providersImportPath, "Providers");
|
|
1954
|
+
if (!importRes.changed) return { changed: false };
|
|
1955
|
+
}
|
|
1956
|
+
let wrapped = false;
|
|
1957
|
+
walkAst(program, (node) => {
|
|
1958
|
+
if (wrapped) return;
|
|
1959
|
+
if (node.type !== "JSXElement" && node.type !== "JSXFragment") return;
|
|
1960
|
+
const children = node.children ?? [];
|
|
1961
|
+
const childrenIndex = children.findIndex(
|
|
1962
|
+
(child) => child?.type === "JSXExpressionContainer" && child.expression?.type === "Identifier" && child.expression.name === "children"
|
|
1963
|
+
);
|
|
1964
|
+
if (childrenIndex === -1) return;
|
|
1965
|
+
const providersMod = parseModule2(
|
|
1966
|
+
"const __providers = (<Providers>{children}</Providers>);"
|
|
1967
|
+
);
|
|
1968
|
+
const providersJsx = providersMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
1969
|
+
if (!providersJsx) return;
|
|
1970
|
+
children[childrenIndex] = providersJsx;
|
|
1971
|
+
wrapped = true;
|
|
1972
|
+
});
|
|
1973
|
+
if (!wrapped) {
|
|
1974
|
+
throw new Error(
|
|
1975
|
+
"Could not find {children} in layout to wrap with Providers."
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
return { changed: true };
|
|
1979
|
+
}
|
|
1980
|
+
function findLayoutFile(projectPath, appRoot) {
|
|
1981
|
+
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
1982
|
+
for (const ext of extensions) {
|
|
1983
|
+
const layoutPath = join5(projectPath, appRoot, `layout${ext}`);
|
|
1984
|
+
if (existsSync5(layoutPath)) return layoutPath;
|
|
1985
|
+
}
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
async function createProvidersAndModifyLayout(projectPath, appRoot) {
|
|
1989
|
+
const layoutPath = findLayoutFile(projectPath, appRoot);
|
|
1990
|
+
if (!layoutPath) {
|
|
1991
|
+
throw new Error(`Could not find layout file in ${appRoot}`);
|
|
1992
|
+
}
|
|
1993
|
+
const isTypeScript = layoutPath.endsWith(".tsx") || layoutPath.endsWith(".ts");
|
|
1994
|
+
const providersExt = isTypeScript ? ".tsx" : ".jsx";
|
|
1995
|
+
const providersPath = join5(projectPath, appRoot, `providers${providersExt}`);
|
|
1996
|
+
if (existsSync5(providersPath)) {
|
|
1997
|
+
throw new Error(
|
|
1998
|
+
`providers${providersExt} already exists. Please select it from the list instead.`
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
const providersContent = generateProvidersContent(isTypeScript);
|
|
2002
|
+
writeFileSync2(providersPath, providersContent, "utf-8");
|
|
2003
|
+
const layoutContent = readFileSync5(layoutPath, "utf-8");
|
|
2004
|
+
let layoutMod;
|
|
2005
|
+
try {
|
|
2006
|
+
layoutMod = parseModule2(layoutContent);
|
|
2007
|
+
} catch {
|
|
2008
|
+
throw new Error(
|
|
2009
|
+
`Unable to parse ${relative3(projectPath, layoutPath)} as JavaScript/TypeScript.`
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
const layoutProgram = layoutMod.$ast;
|
|
2013
|
+
const wrapRes = wrapChildrenWithProviders(layoutProgram, "./providers");
|
|
2014
|
+
if (wrapRes.changed) {
|
|
2015
|
+
const updatedLayout = generateCode2(layoutMod).code;
|
|
2016
|
+
writeFileSync2(layoutPath, updatedLayout, "utf-8");
|
|
2017
|
+
}
|
|
2018
|
+
return {
|
|
2019
|
+
providersFile: relative3(projectPath, providersPath),
|
|
2020
|
+
layoutFile: relative3(projectPath, layoutPath),
|
|
2021
|
+
modified: true
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
1906
2024
|
async function installReactUILintOverlay(opts) {
|
|
2025
|
+
if (opts.createProviders) {
|
|
2026
|
+
const result = await createProvidersAndModifyLayout(
|
|
2027
|
+
opts.projectPath,
|
|
2028
|
+
opts.appRoot
|
|
2029
|
+
);
|
|
2030
|
+
return {
|
|
2031
|
+
targetFile: result.providersFile,
|
|
2032
|
+
modified: result.modified,
|
|
2033
|
+
createdFile: result.providersFile,
|
|
2034
|
+
layoutModified: result.layoutFile
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
if (opts.targetFile) {
|
|
2038
|
+
const absTarget2 = opts.targetFile;
|
|
2039
|
+
const relTarget = relative3(opts.projectPath, absTarget2);
|
|
2040
|
+
if (!existsSync5(absTarget2)) {
|
|
2041
|
+
throw new Error(`Target file not found: ${relTarget}`);
|
|
2042
|
+
}
|
|
2043
|
+
const original2 = readFileSync5(absTarget2, "utf-8");
|
|
2044
|
+
let mod2;
|
|
2045
|
+
try {
|
|
2046
|
+
mod2 = parseModule2(original2);
|
|
2047
|
+
} catch {
|
|
2048
|
+
throw new Error(
|
|
2049
|
+
`Unable to parse ${relTarget} as JavaScript/TypeScript. Please update it manually.`
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
const program2 = mod2.$ast;
|
|
2053
|
+
const hasDevtoolsImport2 = !!findImportDeclaration(program2, "uilint-react/devtools");
|
|
2054
|
+
const hasOldImport2 = !!findImportDeclaration(program2, "uilint-react");
|
|
2055
|
+
const alreadyConfigured2 = (hasDevtoolsImport2 || hasOldImport2) && hasUILintDevtoolsJsx(program2);
|
|
2056
|
+
if (alreadyConfigured2) {
|
|
2057
|
+
return {
|
|
2058
|
+
targetFile: relTarget,
|
|
2059
|
+
modified: false,
|
|
2060
|
+
alreadyConfigured: true
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
let changed2 = false;
|
|
2064
|
+
const importRes2 = ensureSideEffectImport(program2, "uilint-react/devtools");
|
|
2065
|
+
if (importRes2.changed) changed2 = true;
|
|
2066
|
+
const addRes2 = addDevtoolsToClientComponent(program2);
|
|
2067
|
+
if (addRes2.changed) changed2 = true;
|
|
2068
|
+
const updated2 = changed2 ? generateCode2(mod2).code : original2;
|
|
2069
|
+
const modified2 = updated2 !== original2;
|
|
2070
|
+
if (modified2) {
|
|
2071
|
+
writeFileSync2(absTarget2, updated2, "utf-8");
|
|
2072
|
+
}
|
|
2073
|
+
return {
|
|
2074
|
+
targetFile: relTarget,
|
|
2075
|
+
modified: modified2,
|
|
2076
|
+
alreadyConfigured: false
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
1907
2079
|
const candidates = getDefaultCandidates(opts.projectPath, opts.appRoot);
|
|
1908
2080
|
if (!candidates.length) {
|
|
1909
2081
|
throw new Error(
|
|
@@ -1916,7 +2088,7 @@ async function installReactUILintOverlay(opts) {
|
|
|
1916
2088
|
} else {
|
|
1917
2089
|
chosen = candidates[0];
|
|
1918
2090
|
}
|
|
1919
|
-
const absTarget =
|
|
2091
|
+
const absTarget = join5(opts.projectPath, chosen);
|
|
1920
2092
|
const original = readFileSync5(absTarget, "utf-8");
|
|
1921
2093
|
let mod;
|
|
1922
2094
|
try {
|
|
@@ -1949,14 +2121,14 @@ async function installReactUILintOverlay(opts) {
|
|
|
1949
2121
|
}
|
|
1950
2122
|
|
|
1951
2123
|
// src/utils/next-config-inject.ts
|
|
1952
|
-
import { existsSync as
|
|
1953
|
-
import { join as
|
|
2124
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2125
|
+
import { join as join6 } from "path";
|
|
1954
2126
|
import { parseModule as parseModule3, generateCode as generateCode3 } from "magicast";
|
|
1955
2127
|
var CONFIG_EXTENSIONS2 = [".ts", ".mjs", ".js", ".cjs"];
|
|
1956
2128
|
function findNextConfigFile(projectPath) {
|
|
1957
2129
|
for (const ext of CONFIG_EXTENSIONS2) {
|
|
1958
|
-
const configPath =
|
|
1959
|
-
if (
|
|
2130
|
+
const configPath = join6(projectPath, `next.config${ext}`);
|
|
2131
|
+
if (existsSync6(configPath)) {
|
|
1960
2132
|
return configPath;
|
|
1961
2133
|
}
|
|
1962
2134
|
}
|
|
@@ -2099,14 +2271,14 @@ async function installJsxLocPlugin(opts) {
|
|
|
2099
2271
|
}
|
|
2100
2272
|
|
|
2101
2273
|
// src/utils/vite-config-inject.ts
|
|
2102
|
-
import { existsSync as
|
|
2103
|
-
import { join as
|
|
2274
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2275
|
+
import { join as join7 } from "path";
|
|
2104
2276
|
import { parseModule as parseModule4, generateCode as generateCode4 } from "magicast";
|
|
2105
2277
|
var CONFIG_EXTENSIONS3 = [".ts", ".mjs", ".js", ".cjs"];
|
|
2106
2278
|
function findViteConfigFile2(projectPath) {
|
|
2107
2279
|
for (const ext of CONFIG_EXTENSIONS3) {
|
|
2108
|
-
const configPath =
|
|
2109
|
-
if (
|
|
2280
|
+
const configPath = join7(projectPath, `vite.config${ext}`);
|
|
2281
|
+
if (existsSync7(configPath)) return configPath;
|
|
2110
2282
|
}
|
|
2111
2283
|
return null;
|
|
2112
2284
|
}
|
|
@@ -2310,9 +2482,9 @@ async function installViteJsxLocPlugin(opts) {
|
|
|
2310
2482
|
}
|
|
2311
2483
|
|
|
2312
2484
|
// src/utils/next-routes.ts
|
|
2313
|
-
import { existsSync as
|
|
2485
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2314
2486
|
import { mkdir, writeFile } from "fs/promises";
|
|
2315
|
-
import { join as
|
|
2487
|
+
import { join as join8 } from "path";
|
|
2316
2488
|
var DEV_SOURCE_ROUTE_TS = `/**
|
|
2317
2489
|
* Dev-only API route for fetching source files
|
|
2318
2490
|
*
|
|
@@ -2768,39 +2940,39 @@ export async function GET(request: NextRequest) {
|
|
|
2768
2940
|
}
|
|
2769
2941
|
`;
|
|
2770
2942
|
async function writeRouteFile(absPath, relPath, content, opts) {
|
|
2771
|
-
if (
|
|
2943
|
+
if (existsSync8(absPath) && !opts.force) return;
|
|
2772
2944
|
await writeFile(absPath, content, "utf-8");
|
|
2773
2945
|
}
|
|
2774
2946
|
async function installNextUILintRoutes(opts) {
|
|
2775
|
-
const baseRel =
|
|
2776
|
-
const baseAbs =
|
|
2777
|
-
await mkdir(
|
|
2947
|
+
const baseRel = join8(opts.appRoot, "api", ".uilint");
|
|
2948
|
+
const baseAbs = join8(opts.projectPath, baseRel);
|
|
2949
|
+
await mkdir(join8(baseAbs, "source"), { recursive: true });
|
|
2778
2950
|
await writeRouteFile(
|
|
2779
|
-
|
|
2780
|
-
|
|
2951
|
+
join8(baseAbs, "source", "route.ts"),
|
|
2952
|
+
join8(baseRel, "source", "route.ts"),
|
|
2781
2953
|
DEV_SOURCE_ROUTE_TS,
|
|
2782
2954
|
opts
|
|
2783
2955
|
);
|
|
2784
|
-
await mkdir(
|
|
2956
|
+
await mkdir(join8(baseAbs, "screenshots"), { recursive: true });
|
|
2785
2957
|
await writeRouteFile(
|
|
2786
|
-
|
|
2787
|
-
|
|
2958
|
+
join8(baseAbs, "screenshots", "route.ts"),
|
|
2959
|
+
join8(baseRel, "screenshots", "route.ts"),
|
|
2788
2960
|
SCREENSHOT_ROUTE_TS,
|
|
2789
2961
|
opts
|
|
2790
2962
|
);
|
|
2791
2963
|
}
|
|
2792
2964
|
|
|
2793
2965
|
// src/utils/prettier.ts
|
|
2794
|
-
import { existsSync as
|
|
2795
|
-
import { spawn
|
|
2796
|
-
import { join as
|
|
2966
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2967
|
+
import { spawn } from "child_process";
|
|
2968
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
2797
2969
|
function getPrettierPath(projectPath) {
|
|
2798
|
-
const localPath =
|
|
2799
|
-
if (
|
|
2970
|
+
const localPath = join9(projectPath, "node_modules", ".bin", "prettier");
|
|
2971
|
+
if (existsSync9(localPath)) return localPath;
|
|
2800
2972
|
let dir = projectPath;
|
|
2801
2973
|
for (let i = 0; i < 10; i++) {
|
|
2802
|
-
const binPath =
|
|
2803
|
-
if (
|
|
2974
|
+
const binPath = join9(dir, "node_modules", ".bin", "prettier");
|
|
2975
|
+
if (existsSync9(binPath)) return binPath;
|
|
2804
2976
|
const parent = dirname3(dir);
|
|
2805
2977
|
if (parent === dir) break;
|
|
2806
2978
|
dir = parent;
|
|
@@ -2827,7 +2999,7 @@ async function formatWithPrettier(filePath, projectPath) {
|
|
|
2827
2999
|
const runner = getPmRunner(pm);
|
|
2828
3000
|
return new Promise((resolve) => {
|
|
2829
3001
|
const args = [...runner.args, "prettier", "--write", filePath];
|
|
2830
|
-
const child =
|
|
3002
|
+
const child = spawn(runner.command, args, {
|
|
2831
3003
|
cwd: projectPath,
|
|
2832
3004
|
stdio: "pipe",
|
|
2833
3005
|
shell: process.platform === "win32"
|
|
@@ -2849,7 +3021,7 @@ async function formatWithPrettier(filePath, projectPath) {
|
|
|
2849
3021
|
});
|
|
2850
3022
|
}
|
|
2851
3023
|
return new Promise((resolve) => {
|
|
2852
|
-
const child =
|
|
3024
|
+
const child = spawn(prettierPath, ["--write", filePath], {
|
|
2853
3025
|
cwd: projectPath,
|
|
2854
3026
|
stdio: "pipe",
|
|
2855
3027
|
shell: process.platform === "win32"
|
|
@@ -2882,7 +3054,7 @@ async function formatFilesWithPrettier(filePaths, projectPath) {
|
|
|
2882
3054
|
const runner = getPmRunner(pm);
|
|
2883
3055
|
return new Promise((resolve) => {
|
|
2884
3056
|
const args = [...runner.args, "prettier", "--write", ...filePaths];
|
|
2885
|
-
const child =
|
|
3057
|
+
const child = spawn(runner.command, args, {
|
|
2886
3058
|
cwd: projectPath,
|
|
2887
3059
|
stdio: "pipe",
|
|
2888
3060
|
shell: process.platform === "win32"
|
|
@@ -2900,7 +3072,7 @@ async function formatFilesWithPrettier(filePaths, projectPath) {
|
|
|
2900
3072
|
});
|
|
2901
3073
|
}
|
|
2902
3074
|
return new Promise((resolve) => {
|
|
2903
|
-
const child =
|
|
3075
|
+
const child = spawn(prettierPath, ["--write", ...filePaths], {
|
|
2904
3076
|
cwd: projectPath,
|
|
2905
3077
|
stdio: "pipe",
|
|
2906
3078
|
shell: process.platform === "win32"
|
|
@@ -2942,7 +3114,7 @@ async function executeAction(action, options) {
|
|
|
2942
3114
|
wouldDo: `Create directory: ${action.path}`
|
|
2943
3115
|
};
|
|
2944
3116
|
}
|
|
2945
|
-
if (!
|
|
3117
|
+
if (!existsSync10(action.path)) {
|
|
2946
3118
|
mkdirSync(action.path, { recursive: true });
|
|
2947
3119
|
}
|
|
2948
3120
|
return { action, success: true };
|
|
@@ -2956,7 +3128,7 @@ async function executeAction(action, options) {
|
|
|
2956
3128
|
};
|
|
2957
3129
|
}
|
|
2958
3130
|
const dir = dirname4(action.path);
|
|
2959
|
-
if (!
|
|
3131
|
+
if (!existsSync10(dir)) {
|
|
2960
3132
|
mkdirSync(dir, { recursive: true });
|
|
2961
3133
|
}
|
|
2962
3134
|
writeFileSync5(action.path, action.content, "utf-8");
|
|
@@ -2974,7 +3146,7 @@ async function executeAction(action, options) {
|
|
|
2974
3146
|
};
|
|
2975
3147
|
}
|
|
2976
3148
|
let existing = {};
|
|
2977
|
-
if (
|
|
3149
|
+
if (existsSync10(action.path)) {
|
|
2978
3150
|
try {
|
|
2979
3151
|
existing = JSON.parse(readFileSync8(action.path, "utf-8"));
|
|
2980
3152
|
} catch {
|
|
@@ -2982,7 +3154,7 @@ async function executeAction(action, options) {
|
|
|
2982
3154
|
}
|
|
2983
3155
|
const merged = deepMerge(existing, action.merge);
|
|
2984
3156
|
const dir = dirname4(action.path);
|
|
2985
|
-
if (!
|
|
3157
|
+
if (!existsSync10(dir)) {
|
|
2986
3158
|
mkdirSync(dir, { recursive: true });
|
|
2987
3159
|
}
|
|
2988
3160
|
writeFileSync5(action.path, JSON.stringify(merged, null, 2), "utf-8");
|
|
@@ -2996,7 +3168,7 @@ async function executeAction(action, options) {
|
|
|
2996
3168
|
wouldDo: `Delete file: ${action.path}`
|
|
2997
3169
|
};
|
|
2998
3170
|
}
|
|
2999
|
-
if (
|
|
3171
|
+
if (existsSync10(action.path)) {
|
|
3000
3172
|
unlinkSync(action.path);
|
|
3001
3173
|
}
|
|
3002
3174
|
return { action, success: true };
|
|
@@ -3009,7 +3181,7 @@ async function executeAction(action, options) {
|
|
|
3009
3181
|
wouldDo: `Append to file: ${action.path}`
|
|
3010
3182
|
};
|
|
3011
3183
|
}
|
|
3012
|
-
if (
|
|
3184
|
+
if (existsSync10(action.path)) {
|
|
3013
3185
|
const content = readFileSync8(action.path, "utf-8");
|
|
3014
3186
|
if (action.ifNotContains && content.includes(action.ifNotContains)) {
|
|
3015
3187
|
return { action, success: true };
|
|
@@ -3075,11 +3247,12 @@ async function executeInjectEslint(action, options) {
|
|
|
3075
3247
|
}
|
|
3076
3248
|
async function executeInjectReact(action, options) {
|
|
3077
3249
|
const { dryRun = false } = options;
|
|
3250
|
+
const dryRunDescription = action.createProviders ? `Create providers.tsx and inject <uilint-devtools /> in: ${action.projectPath}` : action.targetFile ? `Inject <uilint-devtools /> into: ${action.targetFile}` : `Inject <uilint-devtools /> into React app: ${action.projectPath}`;
|
|
3078
3251
|
if (dryRun) {
|
|
3079
3252
|
return {
|
|
3080
3253
|
action,
|
|
3081
3254
|
success: true,
|
|
3082
|
-
wouldDo:
|
|
3255
|
+
wouldDo: dryRunDescription
|
|
3083
3256
|
};
|
|
3084
3257
|
}
|
|
3085
3258
|
const result = await installReactUILintOverlay({
|
|
@@ -3087,7 +3260,10 @@ async function executeInjectReact(action, options) {
|
|
|
3087
3260
|
appRoot: action.appRoot,
|
|
3088
3261
|
mode: action.mode,
|
|
3089
3262
|
force: false,
|
|
3090
|
-
//
|
|
3263
|
+
// Pass through targetFile and createProviders from the action
|
|
3264
|
+
targetFile: action.targetFile,
|
|
3265
|
+
createProviders: action.createProviders,
|
|
3266
|
+
// Auto-select first choice for execute phase (fallback if no targetFile)
|
|
3091
3267
|
confirmFileChoice: async (choices) => choices[0]
|
|
3092
3268
|
});
|
|
3093
3269
|
const success = result.modified || result.alreadyConfigured === true;
|
|
@@ -3371,11 +3547,9 @@ async function execute(plan, options = {}) {
|
|
|
3371
3547
|
|
|
3372
3548
|
// src/commands/install-ui.tsx
|
|
3373
3549
|
import { ruleRegistry as ruleRegistry3 } from "uilint-eslint";
|
|
3374
|
-
import { existsSync as existsSync13 } from "fs";
|
|
3375
|
-
import { join as join14 } from "path";
|
|
3376
3550
|
|
|
3377
3551
|
// src/commands/install/installers/genstyleguide.ts
|
|
3378
|
-
import { join as
|
|
3552
|
+
import { join as join10 } from "path";
|
|
3379
3553
|
var genstyleguideInstaller = {
|
|
3380
3554
|
id: "genstyleguide",
|
|
3381
3555
|
name: "/genstyleguide command",
|
|
@@ -3385,7 +3559,7 @@ var genstyleguideInstaller = {
|
|
|
3385
3559
|
return true;
|
|
3386
3560
|
},
|
|
3387
3561
|
getTargets(project) {
|
|
3388
|
-
const commandPath =
|
|
3562
|
+
const commandPath = join10(project.cursorDir.path, "commands", "genstyleguide.md");
|
|
3389
3563
|
const isInstalled = project.commands.genstyleguide;
|
|
3390
3564
|
return [
|
|
3391
3565
|
{
|
|
@@ -3398,7 +3572,7 @@ var genstyleguideInstaller = {
|
|
|
3398
3572
|
},
|
|
3399
3573
|
plan(targets, config, project) {
|
|
3400
3574
|
const actions = [];
|
|
3401
|
-
const commandsDir =
|
|
3575
|
+
const commandsDir = join10(project.cursorDir.path, "commands");
|
|
3402
3576
|
if (!project.cursorDir.exists) {
|
|
3403
3577
|
actions.push({
|
|
3404
3578
|
type: "create_directory",
|
|
@@ -3411,7 +3585,7 @@ var genstyleguideInstaller = {
|
|
|
3411
3585
|
});
|
|
3412
3586
|
actions.push({
|
|
3413
3587
|
type: "create_file",
|
|
3414
|
-
path:
|
|
3588
|
+
path: join10(commandsDir, "genstyleguide.md"),
|
|
3415
3589
|
content: GENSTYLEGUIDE_COMMAND_MD
|
|
3416
3590
|
});
|
|
3417
3591
|
return {
|
|
@@ -3441,8 +3615,8 @@ var genstyleguideInstaller = {
|
|
|
3441
3615
|
};
|
|
3442
3616
|
|
|
3443
3617
|
// src/commands/install/installers/skill.ts
|
|
3444
|
-
import { existsSync as
|
|
3445
|
-
import { join as
|
|
3618
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3619
|
+
import { join as join11 } from "path";
|
|
3446
3620
|
var skillInstaller = {
|
|
3447
3621
|
id: "skill",
|
|
3448
3622
|
name: "UI Consistency Agent skill",
|
|
@@ -3452,9 +3626,9 @@ var skillInstaller = {
|
|
|
3452
3626
|
return true;
|
|
3453
3627
|
},
|
|
3454
3628
|
getTargets(project) {
|
|
3455
|
-
const skillsDir =
|
|
3456
|
-
const skillMdPath =
|
|
3457
|
-
const isInstalled =
|
|
3629
|
+
const skillsDir = join11(project.cursorDir.path, "skills", "ui-consistency-enforcer");
|
|
3630
|
+
const skillMdPath = join11(skillsDir, "SKILL.md");
|
|
3631
|
+
const isInstalled = existsSync11(skillMdPath);
|
|
3458
3632
|
return [
|
|
3459
3633
|
{
|
|
3460
3634
|
id: "ui-consistency-skill",
|
|
@@ -3473,21 +3647,21 @@ var skillInstaller = {
|
|
|
3473
3647
|
path: project.cursorDir.path
|
|
3474
3648
|
});
|
|
3475
3649
|
}
|
|
3476
|
-
const skillsDir =
|
|
3650
|
+
const skillsDir = join11(project.cursorDir.path, "skills");
|
|
3477
3651
|
actions.push({
|
|
3478
3652
|
type: "create_directory",
|
|
3479
3653
|
path: skillsDir
|
|
3480
3654
|
});
|
|
3481
3655
|
try {
|
|
3482
3656
|
const skill = loadSkill("ui-consistency-enforcer");
|
|
3483
|
-
const skillDir =
|
|
3657
|
+
const skillDir = join11(skillsDir, skill.name);
|
|
3484
3658
|
actions.push({
|
|
3485
3659
|
type: "create_directory",
|
|
3486
3660
|
path: skillDir
|
|
3487
3661
|
});
|
|
3488
3662
|
for (const file of skill.files) {
|
|
3489
|
-
const filePath =
|
|
3490
|
-
const fileDir =
|
|
3663
|
+
const filePath = join11(skillDir, file.relativePath);
|
|
3664
|
+
const fileDir = join11(
|
|
3491
3665
|
skillDir,
|
|
3492
3666
|
file.relativePath.split("/").slice(0, -1).join("/")
|
|
3493
3667
|
);
|
|
@@ -3544,8 +3718,21 @@ var skillInstaller = {
|
|
|
3544
3718
|
};
|
|
3545
3719
|
|
|
3546
3720
|
// src/commands/install/installers/eslint.ts
|
|
3547
|
-
import { join as
|
|
3721
|
+
import { join as join12 } from "path";
|
|
3548
3722
|
import { ruleRegistry as ruleRegistry2, getRulesByCategory as getRulesByCategory2 } from "uilint-eslint";
|
|
3723
|
+
function getUpgradeInfo(configuredRuleIds) {
|
|
3724
|
+
const configuredSet = new Set(configuredRuleIds);
|
|
3725
|
+
const allRuleIds = ruleRegistry2.map((r) => r.id);
|
|
3726
|
+
const missingRules = allRuleIds.filter((id) => !configuredSet.has(id));
|
|
3727
|
+
if (missingRules.length === 0) {
|
|
3728
|
+
return void 0;
|
|
3729
|
+
}
|
|
3730
|
+
const summary = missingRules.length === 1 ? "1 new rule available" : `${missingRules.length} new rules available`;
|
|
3731
|
+
return {
|
|
3732
|
+
missingRules,
|
|
3733
|
+
summary
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3549
3736
|
function toInstallSpecifier(pkgName) {
|
|
3550
3737
|
return pkgName;
|
|
3551
3738
|
}
|
|
@@ -3566,13 +3753,22 @@ var eslintInstaller = {
|
|
|
3566
3753
|
return project.packages.some((pkg) => pkg.eslintConfigPath !== null);
|
|
3567
3754
|
},
|
|
3568
3755
|
getTargets(project) {
|
|
3569
|
-
return project.packages.filter((pkg) => pkg.eslintConfigPath !== null).map((pkg) =>
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3756
|
+
return project.packages.filter((pkg) => pkg.eslintConfigPath !== null).map((pkg) => {
|
|
3757
|
+
const upgradeInfo = pkg.hasUilintRules ? getUpgradeInfo(pkg.configuredRuleIds) : void 0;
|
|
3758
|
+
let hint = pkg.eslintConfigFilename || "ESLint config detected";
|
|
3759
|
+
if (upgradeInfo?.summary) {
|
|
3760
|
+
hint = `${hint} \xB7 ${upgradeInfo.summary}`;
|
|
3761
|
+
}
|
|
3762
|
+
return {
|
|
3763
|
+
id: `eslint-${pkg.name}`,
|
|
3764
|
+
label: pkg.name,
|
|
3765
|
+
path: pkg.path,
|
|
3766
|
+
hint,
|
|
3767
|
+
isInstalled: pkg.hasUilintRules,
|
|
3768
|
+
canUpgrade: upgradeInfo !== void 0,
|
|
3769
|
+
upgradeInfo
|
|
3770
|
+
};
|
|
3771
|
+
});
|
|
3576
3772
|
},
|
|
3577
3773
|
async configure(targets, project) {
|
|
3578
3774
|
const staticRules = getRulesByCategory2("static");
|
|
@@ -3698,14 +3894,14 @@ var eslintInstaller = {
|
|
|
3698
3894
|
for (const target of targets) {
|
|
3699
3895
|
const pkgInfo = project.packages.find((p) => p.path === target.path);
|
|
3700
3896
|
if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
|
|
3701
|
-
const rulesDir =
|
|
3897
|
+
const rulesDir = join12(target.path, ".uilint", "rules");
|
|
3702
3898
|
actions.push({
|
|
3703
3899
|
type: "create_directory",
|
|
3704
3900
|
path: rulesDir
|
|
3705
3901
|
});
|
|
3706
3902
|
dependencies.push({
|
|
3707
3903
|
packagePath: target.path,
|
|
3708
|
-
packageManager:
|
|
3904
|
+
packageManager: detectPackageManager(target.path),
|
|
3709
3905
|
packages: [toInstallSpecifier("uilint-eslint"), "typescript-eslint"]
|
|
3710
3906
|
});
|
|
3711
3907
|
actions.push({
|
|
@@ -3716,7 +3912,7 @@ var eslintInstaller = {
|
|
|
3716
3912
|
hasExistingRules: pkgInfo.hasUilintRules
|
|
3717
3913
|
});
|
|
3718
3914
|
}
|
|
3719
|
-
const gitignorePath =
|
|
3915
|
+
const gitignorePath = join12(project.workspaceRoot, ".gitignore");
|
|
3720
3916
|
actions.push({
|
|
3721
3917
|
type: "append_to_file",
|
|
3722
3918
|
path: gitignorePath,
|
|
@@ -3750,6 +3946,143 @@ var eslintInstaller = {
|
|
|
3750
3946
|
}
|
|
3751
3947
|
};
|
|
3752
3948
|
|
|
3949
|
+
// src/utils/client-boundary-tracer.ts
|
|
3950
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
3951
|
+
import { join as join13, dirname as dirname5, relative as relative4 } from "path";
|
|
3952
|
+
import { parseModule as parseModule5 } from "magicast";
|
|
3953
|
+
function hasUseClientDirective(filePath) {
|
|
3954
|
+
try {
|
|
3955
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
3956
|
+
const mod = parseModule5(content);
|
|
3957
|
+
const program = mod.$ast;
|
|
3958
|
+
if (!program || program.type !== "Program") return false;
|
|
3959
|
+
const firstStmt = program.body?.[0];
|
|
3960
|
+
return firstStmt?.type === "ExpressionStatement" && firstStmt.expression?.type === "StringLiteral" && (firstStmt.expression.value === "use client" || firstStmt.expression.value === "use client");
|
|
3961
|
+
} catch {
|
|
3962
|
+
return false;
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
function extractImports(program) {
|
|
3966
|
+
const imports = [];
|
|
3967
|
+
if (!program || program.type !== "Program") return imports;
|
|
3968
|
+
for (const stmt of program.body ?? []) {
|
|
3969
|
+
if (stmt?.type !== "ImportDeclaration") continue;
|
|
3970
|
+
const source = stmt.source?.value;
|
|
3971
|
+
if (typeof source !== "string") continue;
|
|
3972
|
+
if (!source.startsWith(".") && !source.startsWith("@/") && !source.startsWith("~/")) {
|
|
3973
|
+
continue;
|
|
3974
|
+
}
|
|
3975
|
+
const specifiers = [];
|
|
3976
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
3977
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
3978
|
+
specifiers.push("default");
|
|
3979
|
+
} else if (spec.type === "ImportSpecifier") {
|
|
3980
|
+
const name = spec.imported?.name ?? spec.imported?.value;
|
|
3981
|
+
if (name) specifiers.push(name);
|
|
3982
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
3983
|
+
specifiers.push("*");
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
imports.push({ source, specifiers });
|
|
3987
|
+
}
|
|
3988
|
+
return imports;
|
|
3989
|
+
}
|
|
3990
|
+
function resolveImportPath(importSource, fromFile, projectPath) {
|
|
3991
|
+
const fromDir = dirname5(fromFile);
|
|
3992
|
+
let basePath;
|
|
3993
|
+
if (importSource.startsWith("@/")) {
|
|
3994
|
+
const withoutAlias = importSource.slice(2);
|
|
3995
|
+
const srcPath = join13(projectPath, "src", withoutAlias);
|
|
3996
|
+
const rootPath = join13(projectPath, withoutAlias);
|
|
3997
|
+
basePath = existsSync12(dirname5(srcPath)) ? srcPath : rootPath;
|
|
3998
|
+
} else if (importSource.startsWith("~/")) {
|
|
3999
|
+
basePath = join13(projectPath, importSource.slice(2));
|
|
4000
|
+
} else if (importSource.startsWith(".")) {
|
|
4001
|
+
basePath = join13(fromDir, importSource);
|
|
4002
|
+
} else {
|
|
4003
|
+
return null;
|
|
4004
|
+
}
|
|
4005
|
+
const extensions = [".tsx", ".ts", ".jsx", ".js"];
|
|
4006
|
+
for (const ext of extensions) {
|
|
4007
|
+
const fullPath = basePath + ext;
|
|
4008
|
+
if (existsSync12(fullPath)) return fullPath;
|
|
4009
|
+
}
|
|
4010
|
+
for (const ext of extensions) {
|
|
4011
|
+
const indexPath = join13(basePath, `index${ext}`);
|
|
4012
|
+
if (existsSync12(indexPath)) return indexPath;
|
|
4013
|
+
}
|
|
4014
|
+
if (existsSync12(basePath)) return basePath;
|
|
4015
|
+
return null;
|
|
4016
|
+
}
|
|
4017
|
+
function findLayoutFile2(projectPath, appRoot) {
|
|
4018
|
+
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
4019
|
+
for (const ext of extensions) {
|
|
4020
|
+
const layoutPath = join13(projectPath, appRoot, `layout${ext}`);
|
|
4021
|
+
if (existsSync12(layoutPath)) return layoutPath;
|
|
4022
|
+
}
|
|
4023
|
+
return null;
|
|
4024
|
+
}
|
|
4025
|
+
function traceClientBoundaries(projectPath, appRoot) {
|
|
4026
|
+
const layoutFile = findLayoutFile2(projectPath, appRoot);
|
|
4027
|
+
if (!layoutFile) {
|
|
4028
|
+
return null;
|
|
4029
|
+
}
|
|
4030
|
+
const layoutIsClient = hasUseClientDirective(layoutFile);
|
|
4031
|
+
const layoutRelative = relative4(projectPath, layoutFile);
|
|
4032
|
+
if (layoutIsClient) {
|
|
4033
|
+
return {
|
|
4034
|
+
layoutIsClient: true,
|
|
4035
|
+
clientBoundaries: [],
|
|
4036
|
+
layoutFile,
|
|
4037
|
+
layoutRelative
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
let program;
|
|
4041
|
+
try {
|
|
4042
|
+
const content = readFileSync9(layoutFile, "utf-8");
|
|
4043
|
+
const mod = parseModule5(content);
|
|
4044
|
+
program = mod.$ast;
|
|
4045
|
+
} catch {
|
|
4046
|
+
return {
|
|
4047
|
+
layoutIsClient: false,
|
|
4048
|
+
clientBoundaries: [],
|
|
4049
|
+
layoutFile,
|
|
4050
|
+
layoutRelative
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
const imports = extractImports(program);
|
|
4054
|
+
const clientBoundaries = [];
|
|
4055
|
+
for (const imp of imports) {
|
|
4056
|
+
const resolvedPath = resolveImportPath(imp.source, layoutFile, projectPath);
|
|
4057
|
+
if (!resolvedPath) continue;
|
|
4058
|
+
if (hasUseClientDirective(resolvedPath)) {
|
|
4059
|
+
clientBoundaries.push({
|
|
4060
|
+
filePath: resolvedPath,
|
|
4061
|
+
relativePath: relative4(projectPath, resolvedPath),
|
|
4062
|
+
componentNames: imp.specifiers,
|
|
4063
|
+
importSource: imp.source
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
return {
|
|
4068
|
+
layoutIsClient: false,
|
|
4069
|
+
clientBoundaries,
|
|
4070
|
+
layoutFile,
|
|
4071
|
+
layoutRelative
|
|
4072
|
+
};
|
|
4073
|
+
}
|
|
4074
|
+
function providersFileExists(projectPath, appRoot) {
|
|
4075
|
+
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
4076
|
+
const names = ["providers", "Providers"];
|
|
4077
|
+
for (const name of names) {
|
|
4078
|
+
for (const ext of extensions) {
|
|
4079
|
+
const providersPath = join13(projectPath, appRoot, `${name}${ext}`);
|
|
4080
|
+
if (existsSync12(providersPath)) return providersPath;
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
return null;
|
|
4084
|
+
}
|
|
4085
|
+
|
|
3753
4086
|
// src/commands/install/installers/next-overlay.ts
|
|
3754
4087
|
var nextOverlayInstaller = {
|
|
3755
4088
|
id: "next",
|
|
@@ -3760,21 +4093,95 @@ var nextOverlayInstaller = {
|
|
|
3760
4093
|
return project.nextApps.length > 0;
|
|
3761
4094
|
},
|
|
3762
4095
|
getTargets(project) {
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
4096
|
+
const targets = [];
|
|
4097
|
+
for (const app of project.nextApps) {
|
|
4098
|
+
const traceResult = traceClientBoundaries(
|
|
4099
|
+
app.projectPath,
|
|
4100
|
+
app.detection.appRoot
|
|
4101
|
+
);
|
|
4102
|
+
if (!traceResult) {
|
|
4103
|
+
targets.push({
|
|
4104
|
+
id: `next-${app.projectPath}`,
|
|
4105
|
+
label: app.projectPath.split("/").pop() || app.projectPath,
|
|
4106
|
+
path: app.projectPath,
|
|
4107
|
+
hint: "App Router (no layout found)",
|
|
4108
|
+
isInstalled: false
|
|
4109
|
+
});
|
|
4110
|
+
continue;
|
|
4111
|
+
}
|
|
4112
|
+
if (traceResult.layoutIsClient) {
|
|
4113
|
+
targets.push({
|
|
4114
|
+
id: `next-${app.projectPath}`,
|
|
4115
|
+
label: app.projectPath.split("/").pop() || app.projectPath,
|
|
4116
|
+
path: app.projectPath,
|
|
4117
|
+
hint: "App Router",
|
|
4118
|
+
isInstalled: false,
|
|
4119
|
+
targetFile: traceResult.layoutFile
|
|
4120
|
+
});
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
const existingProviders = providersFileExists(
|
|
4124
|
+
app.projectPath,
|
|
4125
|
+
app.detection.appRoot
|
|
4126
|
+
);
|
|
4127
|
+
if (!existingProviders) {
|
|
4128
|
+
targets.push({
|
|
4129
|
+
id: `next-${app.projectPath}-create-providers`,
|
|
4130
|
+
label: `${app.projectPath.split("/").pop() || app.projectPath}`,
|
|
4131
|
+
path: app.projectPath,
|
|
4132
|
+
hint: "Create providers.tsx (Recommended)",
|
|
4133
|
+
isInstalled: false,
|
|
4134
|
+
createProviders: true
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
for (const boundary of traceResult.clientBoundaries) {
|
|
4138
|
+
const componentNames = boundary.componentNames.length > 0 ? boundary.componentNames.join(", ") : "default";
|
|
4139
|
+
targets.push({
|
|
4140
|
+
id: `next-${app.projectPath}-${boundary.relativePath}`,
|
|
4141
|
+
label: `${app.projectPath.split("/").pop() || app.projectPath}`,
|
|
4142
|
+
path: app.projectPath,
|
|
4143
|
+
hint: `${boundary.relativePath} (${componentNames})`,
|
|
4144
|
+
isInstalled: false,
|
|
4145
|
+
targetFile: boundary.filePath
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
if (existingProviders) {
|
|
4149
|
+
const relativePath = existingProviders.replace(app.projectPath + "/", "").replace(app.projectPath, "");
|
|
4150
|
+
const alreadyListed = traceResult.clientBoundaries.some(
|
|
4151
|
+
(b) => b.filePath === existingProviders
|
|
4152
|
+
);
|
|
4153
|
+
if (!alreadyListed) {
|
|
4154
|
+
targets.push({
|
|
4155
|
+
id: `next-${app.projectPath}-existing-providers`,
|
|
4156
|
+
label: `${app.projectPath.split("/").pop() || app.projectPath}`,
|
|
4157
|
+
path: app.projectPath,
|
|
4158
|
+
hint: `${relativePath} (existing)`,
|
|
4159
|
+
isInstalled: false,
|
|
4160
|
+
targetFile: existingProviders
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
if (targets.filter((t) => t.path === app.projectPath).length === 0) {
|
|
4165
|
+
targets.push({
|
|
4166
|
+
id: `next-${app.projectPath}-create-providers`,
|
|
4167
|
+
label: `${app.projectPath.split("/").pop() || app.projectPath}`,
|
|
4168
|
+
path: app.projectPath,
|
|
4169
|
+
hint: "Create providers.tsx",
|
|
4170
|
+
isInstalled: false,
|
|
4171
|
+
createProviders: true
|
|
4172
|
+
});
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
return targets;
|
|
3771
4176
|
},
|
|
3772
4177
|
plan(targets, config, project) {
|
|
3773
4178
|
const actions = [];
|
|
3774
4179
|
const dependencies = [];
|
|
3775
4180
|
if (targets.length === 0) return { actions, dependencies };
|
|
3776
4181
|
const target = targets[0];
|
|
3777
|
-
const appInfo = project.nextApps.find(
|
|
4182
|
+
const appInfo = project.nextApps.find(
|
|
4183
|
+
(app) => app.projectPath === target.path
|
|
4184
|
+
);
|
|
3778
4185
|
if (!appInfo) return { actions, dependencies };
|
|
3779
4186
|
const { projectPath, detection } = appInfo;
|
|
3780
4187
|
actions.push({
|
|
@@ -3791,7 +4198,9 @@ var nextOverlayInstaller = {
|
|
|
3791
4198
|
type: "inject_react",
|
|
3792
4199
|
projectPath,
|
|
3793
4200
|
appRoot: detection.appRoot,
|
|
3794
|
-
mode: "next"
|
|
4201
|
+
mode: "next",
|
|
4202
|
+
targetFile: target.targetFile,
|
|
4203
|
+
createProviders: target.createProviders
|
|
3795
4204
|
});
|
|
3796
4205
|
actions.push({
|
|
3797
4206
|
type: "inject_next_config",
|
|
@@ -3816,10 +4225,11 @@ var nextOverlayInstaller = {
|
|
|
3816
4225
|
message: "Installing dependencies",
|
|
3817
4226
|
detail: "\u2192 uilint-react, uilint-core, jsx-loc-plugin"
|
|
3818
4227
|
};
|
|
4228
|
+
const injectDetail = target.createProviders ? "\u2192 Creating providers.tsx" : target.targetFile ? `\u2192 ${target.hint || "client component"}` : "\u2192 <uilint-devtools /> in root layout";
|
|
3819
4229
|
yield {
|
|
3820
4230
|
type: "progress",
|
|
3821
4231
|
message: "Injecting devtools component",
|
|
3822
|
-
detail:
|
|
4232
|
+
detail: injectDetail
|
|
3823
4233
|
};
|
|
3824
4234
|
yield {
|
|
3825
4235
|
type: "progress",
|
|
@@ -3973,56 +4383,6 @@ function selectionsToUserChoices(selections, project, eslintRules) {
|
|
|
3973
4383
|
function isInteractiveTerminal() {
|
|
3974
4384
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
3975
4385
|
}
|
|
3976
|
-
function getRecommendedCommand(pm) {
|
|
3977
|
-
switch (pm) {
|
|
3978
|
-
case "pnpm":
|
|
3979
|
-
return "pnpm dlx uilint install";
|
|
3980
|
-
case "yarn":
|
|
3981
|
-
return "yarn dlx uilint install";
|
|
3982
|
-
case "bun":
|
|
3983
|
-
return "bunx uilint install";
|
|
3984
|
-
case "npm":
|
|
3985
|
-
default:
|
|
3986
|
-
return "npx uilint install";
|
|
3987
|
-
}
|
|
3988
|
-
}
|
|
3989
|
-
async function warnOnPackageManagerMismatch(projectPromise) {
|
|
3990
|
-
const executionPm = detectExecutionPackageManager();
|
|
3991
|
-
if (!executionPm) return;
|
|
3992
|
-
const project = await projectPromise;
|
|
3993
|
-
const packageManagers = /* @__PURE__ */ new Set();
|
|
3994
|
-
const hasRootPackageJson = existsSync13(join14(project.projectPath, "package.json"));
|
|
3995
|
-
if (hasRootPackageJson) {
|
|
3996
|
-
packageManagers.add(project.packageManager);
|
|
3997
|
-
} else {
|
|
3998
|
-
for (const pkg of project.packages) {
|
|
3999
|
-
const pm = detectPackageManager(pkg.path);
|
|
4000
|
-
packageManagers.add(pm);
|
|
4001
|
-
}
|
|
4002
|
-
}
|
|
4003
|
-
const specificPackageManagers = Array.from(packageManagers).filter(
|
|
4004
|
-
(pm) => pm !== "npm" || hasRootPackageJson
|
|
4005
|
-
);
|
|
4006
|
-
if (specificPackageManagers.length === 0) return;
|
|
4007
|
-
if (specificPackageManagers.includes(executionPm)) return;
|
|
4008
|
-
const primaryPm = specificPackageManagers[0];
|
|
4009
|
-
const recommendedCmd = getRecommendedCommand(primaryPm);
|
|
4010
|
-
const pmList = specificPackageManagers.length === 1 ? specificPackageManagers[0] : specificPackageManagers.join(", ");
|
|
4011
|
-
printBox(
|
|
4012
|
-
"Package manager mismatch detected",
|
|
4013
|
-
[
|
|
4014
|
-
`This project uses ${pmList}, but you ran this command with ${executionPm}.`,
|
|
4015
|
-
"",
|
|
4016
|
-
`Running with ${executionPm} may create unwanted lockfiles.`,
|
|
4017
|
-
"",
|
|
4018
|
-
"Recommended:",
|
|
4019
|
-
` ${recommendedCmd}`,
|
|
4020
|
-
"",
|
|
4021
|
-
"Continuing anyway..."
|
|
4022
|
-
],
|
|
4023
|
-
"warning"
|
|
4024
|
-
);
|
|
4025
|
-
}
|
|
4026
4386
|
async function installUI(options = {}, executeOptions = {}) {
|
|
4027
4387
|
const projectPath = process.cwd();
|
|
4028
4388
|
if (!isInteractiveTerminal()) {
|
|
@@ -4031,7 +4391,6 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4031
4391
|
process.exit(1);
|
|
4032
4392
|
}
|
|
4033
4393
|
const projectPromise = analyze(projectPath);
|
|
4034
|
-
await warnOnPackageManagerMismatch(projectPromise);
|
|
4035
4394
|
const { waitUntilExit } = render(
|
|
4036
4395
|
/* @__PURE__ */ jsx6(
|
|
4037
4396
|
InstallApp,
|
|
@@ -4044,7 +4403,7 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4044
4403
|
console.log("\nNo items selected for installation");
|
|
4045
4404
|
process.exit(0);
|
|
4046
4405
|
}
|
|
4047
|
-
const { createPlan } = await import("./plan-
|
|
4406
|
+
const { createPlan } = await import("./plan-VPDTICSY.js");
|
|
4048
4407
|
const plan = createPlan(project, choices, { force: options.force });
|
|
4049
4408
|
const result = await execute(plan, {
|
|
4050
4409
|
...executeOptions,
|
|
@@ -4069,4 +4428,4 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4069
4428
|
export {
|
|
4070
4429
|
installUI
|
|
4071
4430
|
};
|
|
4072
|
-
//# sourceMappingURL=install-ui-
|
|
4431
|
+
//# sourceMappingURL=install-ui-73AINMZ5.js.map
|