uilint 0.2.16 → 0.2.18
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/index.js +1 -1
- package/dist/{install-ui-BBIO2E5O.js → install-ui-FE5AA75P.js} +669 -532
- package/dist/install-ui-FE5AA75P.js.map +1 -0
- package/dist/{plan-VPDTICSY.js → plan-SIXVCXCK.js} +22 -12
- package/dist/plan-SIXVCXCK.js.map +1 -0
- package/package.json +3 -3
- package/dist/install-ui-BBIO2E5O.js.map +0 -1
- package/dist/plan-VPDTICSY.js.map +0 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
import { render } from "ink";
|
|
21
21
|
|
|
22
22
|
// src/commands/install/components/InstallApp.tsx
|
|
23
|
-
import { useState as
|
|
24
|
-
import { Box as
|
|
23
|
+
import { useState as useState6, useEffect as useEffect2 } from "react";
|
|
24
|
+
import { Box as Box5, Text as Text6, useApp as useApp5, useInput as useInput5 } from "ink";
|
|
25
25
|
|
|
26
26
|
// src/commands/install/components/Spinner.tsx
|
|
27
27
|
import { useState, useEffect } from "react";
|
|
@@ -512,6 +512,69 @@ function RuleSelector({
|
|
|
512
512
|
] });
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
+
// src/commands/install/components/InjectionPointSelector.tsx
|
|
516
|
+
import { useState as useState5 } from "react";
|
|
517
|
+
import { Box as Box4, Text as Text5, useInput as useInput4, useApp as useApp4 } from "ink";
|
|
518
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
519
|
+
function InjectionPointSelector({
|
|
520
|
+
points,
|
|
521
|
+
onSubmit,
|
|
522
|
+
onBack,
|
|
523
|
+
onCancel
|
|
524
|
+
}) {
|
|
525
|
+
const { exit } = useApp4();
|
|
526
|
+
const recommendedIndex = points.findIndex((p) => p.recommended);
|
|
527
|
+
const [cursor, setCursor] = useState5(recommendedIndex >= 0 ? recommendedIndex : 0);
|
|
528
|
+
useInput4((input, key) => {
|
|
529
|
+
if (key.upArrow) {
|
|
530
|
+
setCursor((prev) => prev > 0 ? prev - 1 : points.length - 1);
|
|
531
|
+
} else if (key.downArrow) {
|
|
532
|
+
setCursor((prev) => prev < points.length - 1 ? prev + 1 : 0);
|
|
533
|
+
} else if (key.return) {
|
|
534
|
+
const selected = points[cursor];
|
|
535
|
+
if (selected) {
|
|
536
|
+
onSubmit(selected);
|
|
537
|
+
}
|
|
538
|
+
} else if (input === "b" || key.leftArrow) {
|
|
539
|
+
onBack?.();
|
|
540
|
+
} else if (input === "q" || key.escape) {
|
|
541
|
+
onCancel?.();
|
|
542
|
+
exit();
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
546
|
+
/* @__PURE__ */ jsx5(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Where should the devtools component be injected?" }) }),
|
|
547
|
+
points.map((point, index) => {
|
|
548
|
+
const isCursor = index === cursor;
|
|
549
|
+
return /* @__PURE__ */ jsxs4(Box4, { paddingLeft: 1, children: [
|
|
550
|
+
/* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
551
|
+
/* @__PURE__ */ jsx5(Box4, { width: 2, children: /* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u25C9" : "\u25CB" }) }),
|
|
552
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
553
|
+
/* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: point.label }),
|
|
554
|
+
point.hint && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
555
|
+
" (",
|
|
556
|
+
point.hint,
|
|
557
|
+
")"
|
|
558
|
+
] })
|
|
559
|
+
] })
|
|
560
|
+
] }, point.id);
|
|
561
|
+
}),
|
|
562
|
+
/* @__PURE__ */ jsx5(Box4, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
563
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2191\u2193" }),
|
|
564
|
+
" navigate",
|
|
565
|
+
" ",
|
|
566
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "enter" }),
|
|
567
|
+
" select",
|
|
568
|
+
" ",
|
|
569
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "b" }),
|
|
570
|
+
" back",
|
|
571
|
+
" ",
|
|
572
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "q" }),
|
|
573
|
+
" quit"
|
|
574
|
+
] }) })
|
|
575
|
+
] });
|
|
576
|
+
}
|
|
577
|
+
|
|
515
578
|
// src/commands/install/installers/registry.ts
|
|
516
579
|
var installers = [];
|
|
517
580
|
function registerInstaller(installer) {
|
|
@@ -525,8 +588,315 @@ function getAllInstallers() {
|
|
|
525
588
|
return installers;
|
|
526
589
|
}
|
|
527
590
|
|
|
591
|
+
// src/utils/client-boundary-tracer.ts
|
|
592
|
+
import { existsSync, readFileSync } from "fs";
|
|
593
|
+
import { join, dirname, relative } from "path";
|
|
594
|
+
import { parseModule } from "magicast";
|
|
595
|
+
function hasUseClientDirective(filePath) {
|
|
596
|
+
try {
|
|
597
|
+
const content = readFileSync(filePath, "utf-8");
|
|
598
|
+
const mod = parseModule(content);
|
|
599
|
+
const program = mod.$ast;
|
|
600
|
+
if (!program || program.type !== "Program") return false;
|
|
601
|
+
const directives = program.directives ?? [];
|
|
602
|
+
for (const directive of directives) {
|
|
603
|
+
if (directive?.type === "Directive" && directive.value?.type === "DirectiveLiteral" && directive.value.value === "use client") {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const firstStmt = program.body?.[0];
|
|
608
|
+
if (firstStmt?.type === "ExpressionStatement" && (firstStmt.expression?.type === "StringLiteral" || firstStmt.expression?.type === "Literal") && firstStmt.expression.value === "use client") {
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
return false;
|
|
612
|
+
} catch {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function extractImports(program) {
|
|
617
|
+
const imports = [];
|
|
618
|
+
if (!program || program.type !== "Program") return imports;
|
|
619
|
+
for (const stmt of program.body ?? []) {
|
|
620
|
+
if (stmt?.type !== "ImportDeclaration") continue;
|
|
621
|
+
const source = stmt.source?.value;
|
|
622
|
+
if (typeof source !== "string") continue;
|
|
623
|
+
if (!source.startsWith(".") && !source.startsWith("@/") && !source.startsWith("~/")) {
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const specifiers = [];
|
|
627
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
628
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
629
|
+
specifiers.push("default");
|
|
630
|
+
} else if (spec.type === "ImportSpecifier") {
|
|
631
|
+
const name = spec.imported?.name ?? spec.imported?.value;
|
|
632
|
+
if (name) specifiers.push(name);
|
|
633
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
634
|
+
specifiers.push("*");
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
imports.push({ source, specifiers });
|
|
638
|
+
}
|
|
639
|
+
return imports;
|
|
640
|
+
}
|
|
641
|
+
function resolveImportPath(importSource, fromFile, projectPath) {
|
|
642
|
+
const fromDir = dirname(fromFile);
|
|
643
|
+
let basePath;
|
|
644
|
+
if (importSource.startsWith("@/")) {
|
|
645
|
+
const withoutAlias = importSource.slice(2);
|
|
646
|
+
const srcPath = join(projectPath, "src", withoutAlias);
|
|
647
|
+
const rootPath = join(projectPath, withoutAlias);
|
|
648
|
+
basePath = existsSync(dirname(srcPath)) ? srcPath : rootPath;
|
|
649
|
+
} else if (importSource.startsWith("~/")) {
|
|
650
|
+
basePath = join(projectPath, importSource.slice(2));
|
|
651
|
+
} else if (importSource.startsWith(".")) {
|
|
652
|
+
basePath = join(fromDir, importSource);
|
|
653
|
+
} else {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
const extensions = [".tsx", ".ts", ".jsx", ".js"];
|
|
657
|
+
for (const ext of extensions) {
|
|
658
|
+
const fullPath = basePath + ext;
|
|
659
|
+
if (existsSync(fullPath)) return fullPath;
|
|
660
|
+
}
|
|
661
|
+
for (const ext of extensions) {
|
|
662
|
+
const indexPath = join(basePath, `index${ext}`);
|
|
663
|
+
if (existsSync(indexPath)) return indexPath;
|
|
664
|
+
}
|
|
665
|
+
if (existsSync(basePath)) return basePath;
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
function findLayoutFile(projectPath, appRoot) {
|
|
669
|
+
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
670
|
+
for (const ext of extensions) {
|
|
671
|
+
const layoutPath = join(projectPath, appRoot, `layout${ext}`);
|
|
672
|
+
if (existsSync(layoutPath)) return layoutPath;
|
|
673
|
+
}
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
function traceClientBoundaries(projectPath, appRoot) {
|
|
677
|
+
const layoutFile = findLayoutFile(projectPath, appRoot);
|
|
678
|
+
if (!layoutFile) {
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
const layoutIsClient = hasUseClientDirective(layoutFile);
|
|
682
|
+
const layoutRelative = relative(projectPath, layoutFile);
|
|
683
|
+
if (layoutIsClient) {
|
|
684
|
+
return {
|
|
685
|
+
layoutIsClient: true,
|
|
686
|
+
clientBoundaries: [],
|
|
687
|
+
layoutFile,
|
|
688
|
+
layoutRelative
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
let program;
|
|
692
|
+
try {
|
|
693
|
+
const content = readFileSync(layoutFile, "utf-8");
|
|
694
|
+
const mod = parseModule(content);
|
|
695
|
+
program = mod.$ast;
|
|
696
|
+
} catch {
|
|
697
|
+
return {
|
|
698
|
+
layoutIsClient: false,
|
|
699
|
+
clientBoundaries: [],
|
|
700
|
+
layoutFile,
|
|
701
|
+
layoutRelative
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const imports = extractImports(program);
|
|
705
|
+
const clientBoundaries = [];
|
|
706
|
+
for (const imp of imports) {
|
|
707
|
+
const resolvedPath = resolveImportPath(imp.source, layoutFile, projectPath);
|
|
708
|
+
if (!resolvedPath) continue;
|
|
709
|
+
if (hasUseClientDirective(resolvedPath)) {
|
|
710
|
+
clientBoundaries.push({
|
|
711
|
+
filePath: resolvedPath,
|
|
712
|
+
relativePath: relative(projectPath, resolvedPath),
|
|
713
|
+
componentNames: imp.specifiers,
|
|
714
|
+
importSource: imp.source
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
layoutIsClient: false,
|
|
720
|
+
clientBoundaries,
|
|
721
|
+
layoutFile,
|
|
722
|
+
layoutRelative
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function providersFileExists(projectPath, appRoot) {
|
|
726
|
+
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
727
|
+
const names = ["providers", "Providers"];
|
|
728
|
+
for (const name of names) {
|
|
729
|
+
for (const ext of extensions) {
|
|
730
|
+
const providersPath = join(projectPath, appRoot, `${name}${ext}`);
|
|
731
|
+
if (existsSync(providersPath)) return providersPath;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/commands/install/installers/next-overlay.ts
|
|
738
|
+
function getInjectionPoints(projectPath, appRoot) {
|
|
739
|
+
const points = [];
|
|
740
|
+
const traceResult = traceClientBoundaries(projectPath, appRoot);
|
|
741
|
+
if (!traceResult) {
|
|
742
|
+
points.push({
|
|
743
|
+
id: "create-providers",
|
|
744
|
+
label: "Create providers.tsx",
|
|
745
|
+
hint: "Recommended",
|
|
746
|
+
createProviders: true,
|
|
747
|
+
recommended: true
|
|
748
|
+
});
|
|
749
|
+
return points;
|
|
750
|
+
}
|
|
751
|
+
if (traceResult.layoutIsClient) {
|
|
752
|
+
points.push({
|
|
753
|
+
id: "layout",
|
|
754
|
+
label: "Root layout",
|
|
755
|
+
hint: "Already a client component",
|
|
756
|
+
filePath: traceResult.layoutFile,
|
|
757
|
+
recommended: true
|
|
758
|
+
});
|
|
759
|
+
return points;
|
|
760
|
+
}
|
|
761
|
+
const existingProviders = providersFileExists(projectPath, appRoot);
|
|
762
|
+
if (!existingProviders) {
|
|
763
|
+
points.push({
|
|
764
|
+
id: "create-providers",
|
|
765
|
+
label: "Create providers.tsx",
|
|
766
|
+
hint: "Recommended",
|
|
767
|
+
createProviders: true,
|
|
768
|
+
recommended: true
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
for (const boundary of traceResult.clientBoundaries) {
|
|
772
|
+
const componentNames = boundary.componentNames.length > 0 ? boundary.componentNames.join(", ") : "default";
|
|
773
|
+
points.push({
|
|
774
|
+
id: boundary.relativePath,
|
|
775
|
+
label: boundary.relativePath,
|
|
776
|
+
hint: componentNames,
|
|
777
|
+
filePath: boundary.filePath,
|
|
778
|
+
// Mark existing providers as recommended if it exists
|
|
779
|
+
recommended: existingProviders === boundary.filePath
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
if (existingProviders) {
|
|
783
|
+
const relativePath = existingProviders.replace(projectPath + "/", "").replace(projectPath, "");
|
|
784
|
+
const alreadyListed = traceResult.clientBoundaries.some(
|
|
785
|
+
(b) => b.filePath === existingProviders
|
|
786
|
+
);
|
|
787
|
+
if (!alreadyListed) {
|
|
788
|
+
points.push({
|
|
789
|
+
id: "existing-providers",
|
|
790
|
+
label: relativePath,
|
|
791
|
+
hint: "Existing providers",
|
|
792
|
+
filePath: existingProviders,
|
|
793
|
+
recommended: true
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (points.length === 0) {
|
|
798
|
+
points.push({
|
|
799
|
+
id: "create-providers",
|
|
800
|
+
label: "Create providers.tsx",
|
|
801
|
+
hint: "Recommended",
|
|
802
|
+
createProviders: true,
|
|
803
|
+
recommended: true
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
return points;
|
|
807
|
+
}
|
|
808
|
+
var nextOverlayInstaller = {
|
|
809
|
+
id: "next",
|
|
810
|
+
name: "Next.js overlay",
|
|
811
|
+
description: "Alt+Click UI inspector for Next.js App Router",
|
|
812
|
+
icon: "\u{1F537}",
|
|
813
|
+
isApplicable(project) {
|
|
814
|
+
return project.nextApps.length > 0;
|
|
815
|
+
},
|
|
816
|
+
getTargets(project) {
|
|
817
|
+
return project.nextApps.map((app) => ({
|
|
818
|
+
id: `next-${app.projectPath}`,
|
|
819
|
+
label: app.projectPath.split("/").pop() || app.projectPath,
|
|
820
|
+
path: app.projectPath,
|
|
821
|
+
hint: "App Router",
|
|
822
|
+
isInstalled: false
|
|
823
|
+
}));
|
|
824
|
+
},
|
|
825
|
+
plan(targets, config, project) {
|
|
826
|
+
const actions = [];
|
|
827
|
+
const dependencies = [];
|
|
828
|
+
if (targets.length === 0) return { actions, dependencies };
|
|
829
|
+
const target = targets[0];
|
|
830
|
+
const appInfo = project.nextApps.find(
|
|
831
|
+
(app) => app.projectPath === target.path
|
|
832
|
+
);
|
|
833
|
+
if (!appInfo) return { actions, dependencies };
|
|
834
|
+
const { projectPath, detection } = appInfo;
|
|
835
|
+
const nextConfig = config;
|
|
836
|
+
const targetFile = nextConfig.selectedTargetFile;
|
|
837
|
+
const createProviders = nextConfig.createProviders;
|
|
838
|
+
actions.push({
|
|
839
|
+
type: "install_next_routes",
|
|
840
|
+
projectPath,
|
|
841
|
+
appRoot: detection.appRoot
|
|
842
|
+
});
|
|
843
|
+
dependencies.push({
|
|
844
|
+
packagePath: projectPath,
|
|
845
|
+
packageManager: project.packageManager,
|
|
846
|
+
packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
|
|
847
|
+
});
|
|
848
|
+
actions.push({
|
|
849
|
+
type: "inject_react",
|
|
850
|
+
projectPath,
|
|
851
|
+
appRoot: detection.appRoot,
|
|
852
|
+
mode: "next",
|
|
853
|
+
targetFile,
|
|
854
|
+
createProviders
|
|
855
|
+
});
|
|
856
|
+
actions.push({
|
|
857
|
+
type: "inject_next_config",
|
|
858
|
+
projectPath
|
|
859
|
+
});
|
|
860
|
+
return { actions, dependencies };
|
|
861
|
+
},
|
|
862
|
+
async *execute(targets, config, project) {
|
|
863
|
+
if (targets.length === 0) return;
|
|
864
|
+
const target = targets[0];
|
|
865
|
+
const nextConfig = config;
|
|
866
|
+
yield {
|
|
867
|
+
type: "start",
|
|
868
|
+
message: "Installing Next.js overlay"
|
|
869
|
+
};
|
|
870
|
+
yield {
|
|
871
|
+
type: "progress",
|
|
872
|
+
message: `Installing in ${target.label}`,
|
|
873
|
+
detail: "\u2192 Adding API routes"
|
|
874
|
+
};
|
|
875
|
+
yield {
|
|
876
|
+
type: "progress",
|
|
877
|
+
message: "Installing dependencies",
|
|
878
|
+
detail: "\u2192 uilint-react, uilint-core, jsx-loc-plugin"
|
|
879
|
+
};
|
|
880
|
+
const injectDetail = nextConfig.createProviders ? "\u2192 Creating providers.tsx" : nextConfig.selectedTargetFile ? `\u2192 ${nextConfig.selectedTargetFile.split("/").pop() || "client component"}` : "\u2192 <uilint-devtools /> in root layout";
|
|
881
|
+
yield {
|
|
882
|
+
type: "progress",
|
|
883
|
+
message: "Injecting devtools component",
|
|
884
|
+
detail: injectDetail
|
|
885
|
+
};
|
|
886
|
+
yield {
|
|
887
|
+
type: "progress",
|
|
888
|
+
message: "Configuring jsx-loc-plugin",
|
|
889
|
+
detail: "\u2192 next.config.js"
|
|
890
|
+
};
|
|
891
|
+
yield {
|
|
892
|
+
type: "complete",
|
|
893
|
+
message: "Next.js overlay installed"
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
|
|
528
898
|
// src/commands/install/components/InstallApp.tsx
|
|
529
|
-
import { jsx as
|
|
899
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
530
900
|
function getTargetStatus(target) {
|
|
531
901
|
if (!target.isInstalled) {
|
|
532
902
|
return "not_installed";
|
|
@@ -597,10 +967,10 @@ function buildGlobalConfigItems(selections) {
|
|
|
597
967
|
return items;
|
|
598
968
|
}
|
|
599
969
|
function Header({ subtitle }) {
|
|
600
|
-
return /* @__PURE__ */
|
|
601
|
-
/* @__PURE__ */
|
|
602
|
-
/* @__PURE__ */
|
|
603
|
-
subtitle && /* @__PURE__ */
|
|
970
|
+
return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
971
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "\u25C6 UILint" }),
|
|
972
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " v0.5.0" }),
|
|
973
|
+
subtitle && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
604
974
|
" \xB7 ",
|
|
605
975
|
subtitle
|
|
606
976
|
] })
|
|
@@ -614,22 +984,22 @@ function FeatureConfig({
|
|
|
614
984
|
onBack,
|
|
615
985
|
onCancel
|
|
616
986
|
}) {
|
|
617
|
-
|
|
987
|
+
useInput5((input, key) => {
|
|
618
988
|
if ((input === "b" || key.leftArrow) && canGoBack) {
|
|
619
989
|
onBack();
|
|
620
990
|
}
|
|
621
991
|
});
|
|
622
|
-
return /* @__PURE__ */
|
|
623
|
-
selectedProject && /* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
992
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
993
|
+
selectedProject && /* @__PURE__ */ jsxs5(Box5, { marginBottom: 1, children: [
|
|
994
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
|
|
995
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name }),
|
|
996
|
+
/* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
627
997
|
" (",
|
|
628
998
|
selectedProject.hint,
|
|
629
999
|
")"
|
|
630
1000
|
] })
|
|
631
1001
|
] }),
|
|
632
|
-
/* @__PURE__ */
|
|
1002
|
+
/* @__PURE__ */ jsx6(
|
|
633
1003
|
ConfigSelector,
|
|
634
1004
|
{
|
|
635
1005
|
items: configItems,
|
|
@@ -637,11 +1007,11 @@ function FeatureConfig({
|
|
|
637
1007
|
onCancel
|
|
638
1008
|
}
|
|
639
1009
|
),
|
|
640
|
-
canGoBack && /* @__PURE__ */
|
|
1010
|
+
canGoBack && /* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
641
1011
|
"Press ",
|
|
642
|
-
/* @__PURE__ */
|
|
1012
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "b" }),
|
|
643
1013
|
" or ",
|
|
644
|
-
/* @__PURE__ */
|
|
1014
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2190" }),
|
|
645
1015
|
" to select a different project"
|
|
646
1016
|
] }) })
|
|
647
1017
|
] });
|
|
@@ -651,16 +1021,19 @@ function InstallApp({
|
|
|
651
1021
|
onComplete,
|
|
652
1022
|
onError
|
|
653
1023
|
}) {
|
|
654
|
-
const { exit } =
|
|
655
|
-
const [phase, setPhase] =
|
|
656
|
-
const [project, setProject] =
|
|
657
|
-
const [detectedProjects, setDetectedProjects] =
|
|
658
|
-
const [selectedProject, setSelectedProject] =
|
|
659
|
-
const [selections, setSelections] =
|
|
660
|
-
const [configItems, setConfigItems] =
|
|
661
|
-
const [selectedFeatureIds, setSelectedFeatureIds] =
|
|
662
|
-
const [error, setError] =
|
|
1024
|
+
const { exit } = useApp5();
|
|
1025
|
+
const [phase, setPhase] = useState6("scanning");
|
|
1026
|
+
const [project, setProject] = useState6(null);
|
|
1027
|
+
const [detectedProjects, setDetectedProjects] = useState6([]);
|
|
1028
|
+
const [selectedProject, setSelectedProject] = useState6(null);
|
|
1029
|
+
const [selections, setSelections] = useState6([]);
|
|
1030
|
+
const [configItems, setConfigItems] = useState6([]);
|
|
1031
|
+
const [selectedFeatureIds, setSelectedFeatureIds] = useState6([]);
|
|
1032
|
+
const [error, setError] = useState6(null);
|
|
1033
|
+
const [injectionPoints, setInjectionPoints] = useState6([]);
|
|
1034
|
+
const [selectedInjectionPoint, setSelectedInjectionPoint] = useState6(void 0);
|
|
663
1035
|
const isEslintSelected = selectedFeatureIds.some((id) => id.startsWith("eslint:"));
|
|
1036
|
+
const isNextSelected = selectedFeatureIds.some((id) => id.startsWith("next:"));
|
|
664
1037
|
useEffect2(() => {
|
|
665
1038
|
if (phase !== "scanning") return;
|
|
666
1039
|
projectPromise.then((proj) => {
|
|
@@ -715,20 +1088,60 @@ function InstallApp({
|
|
|
715
1088
|
};
|
|
716
1089
|
const handleFeatureSubmit = (selectedIds) => {
|
|
717
1090
|
setSelectedFeatureIds(selectedIds);
|
|
1091
|
+
const nextSelected = selectedIds.some((id) => id.startsWith("next:"));
|
|
1092
|
+
if (nextSelected && project && selectedProject) {
|
|
1093
|
+
const appInfo = project.nextApps.find(
|
|
1094
|
+
(app) => app.projectPath === selectedProject.path
|
|
1095
|
+
);
|
|
1096
|
+
if (appInfo) {
|
|
1097
|
+
const points = getInjectionPoints(appInfo.projectPath, appInfo.detection.appRoot);
|
|
1098
|
+
if (points.length === 1) {
|
|
1099
|
+
const point = points[0];
|
|
1100
|
+
setSelectedInjectionPoint({
|
|
1101
|
+
targetFile: point.filePath,
|
|
1102
|
+
createProviders: point.createProviders
|
|
1103
|
+
});
|
|
1104
|
+
proceedAfterInjectionPoint(selectedIds, {
|
|
1105
|
+
targetFile: point.filePath,
|
|
1106
|
+
createProviders: point.createProviders
|
|
1107
|
+
});
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
if (points.length > 1) {
|
|
1111
|
+
setInjectionPoints(points);
|
|
1112
|
+
setPhase("configure-injection-point");
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
proceedAfterInjectionPoint(selectedIds, void 0);
|
|
1118
|
+
};
|
|
1119
|
+
const proceedAfterInjectionPoint = (selectedIds, injectionConfig) => {
|
|
718
1120
|
const eslintSelected = selectedIds.some((id) => id.startsWith("eslint:"));
|
|
719
1121
|
if (eslintSelected) {
|
|
720
1122
|
setPhase("configure-eslint");
|
|
721
1123
|
} else {
|
|
722
|
-
finishInstallation(selectedIds, void 0);
|
|
1124
|
+
finishInstallation(selectedIds, void 0, injectionConfig);
|
|
723
1125
|
}
|
|
724
1126
|
};
|
|
1127
|
+
const handleInjectionPointSubmit = (point) => {
|
|
1128
|
+
const config = {
|
|
1129
|
+
targetFile: point.filePath,
|
|
1130
|
+
createProviders: point.createProviders
|
|
1131
|
+
};
|
|
1132
|
+
setSelectedInjectionPoint(config);
|
|
1133
|
+
proceedAfterInjectionPoint(selectedFeatureIds, config);
|
|
1134
|
+
};
|
|
1135
|
+
const handleBackFromInjectionPoint = () => {
|
|
1136
|
+
setPhase("configure-features");
|
|
1137
|
+
};
|
|
725
1138
|
const handleRuleSubmit = (configuredRules) => {
|
|
726
|
-
finishInstallation(selectedFeatureIds, configuredRules);
|
|
1139
|
+
finishInstallation(selectedFeatureIds, configuredRules, selectedInjectionPoint);
|
|
727
1140
|
};
|
|
728
1141
|
const handleBackToFeatures = () => {
|
|
729
1142
|
setPhase("configure-features");
|
|
730
1143
|
};
|
|
731
|
-
const finishInstallation = (selectedIds, eslintRules) => {
|
|
1144
|
+
const finishInstallation = (selectedIds, eslintRules, injectionConfig) => {
|
|
732
1145
|
const selectedSet = new Set(selectedIds);
|
|
733
1146
|
const updatedSelections = selections.map((sel) => {
|
|
734
1147
|
const selectedTargets = sel.targets.filter(
|
|
@@ -741,33 +1154,33 @@ function InstallApp({
|
|
|
741
1154
|
};
|
|
742
1155
|
});
|
|
743
1156
|
setSelections(updatedSelections);
|
|
744
|
-
onComplete(updatedSelections, eslintRules);
|
|
1157
|
+
onComplete(updatedSelections, eslintRules, injectionConfig);
|
|
745
1158
|
};
|
|
746
1159
|
const handleCancel = () => {
|
|
747
1160
|
exit();
|
|
748
1161
|
};
|
|
749
1162
|
if (phase === "scanning") {
|
|
750
|
-
return /* @__PURE__ */
|
|
751
|
-
/* @__PURE__ */
|
|
752
|
-
/* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
1163
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1164
|
+
/* @__PURE__ */ jsx6(Header, { subtitle: "Install" }),
|
|
1165
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1166
|
+
/* @__PURE__ */ jsx6(Spinner, {}),
|
|
1167
|
+
/* @__PURE__ */ jsx6(Text6, { children: " Scanning project..." })
|
|
755
1168
|
] })
|
|
756
1169
|
] });
|
|
757
1170
|
}
|
|
758
1171
|
if (phase === "error") {
|
|
759
|
-
return /* @__PURE__ */
|
|
760
|
-
/* @__PURE__ */
|
|
761
|
-
/* @__PURE__ */
|
|
762
|
-
/* @__PURE__ */
|
|
763
|
-
/* @__PURE__ */
|
|
1172
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1173
|
+
/* @__PURE__ */ jsx6(Header, {}),
|
|
1174
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1175
|
+
/* @__PURE__ */ jsx6(Text6, { color: "red", children: "\u2717 " }),
|
|
1176
|
+
/* @__PURE__ */ jsx6(Text6, { color: "red", children: error?.message || "An unknown error occurred" })
|
|
764
1177
|
] })
|
|
765
1178
|
] });
|
|
766
1179
|
}
|
|
767
1180
|
if (phase === "select-project") {
|
|
768
|
-
return /* @__PURE__ */
|
|
769
|
-
/* @__PURE__ */
|
|
770
|
-
/* @__PURE__ */
|
|
1181
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1182
|
+
/* @__PURE__ */ jsx6(Header, { subtitle: "Install" }),
|
|
1183
|
+
/* @__PURE__ */ jsx6(
|
|
771
1184
|
ProjectSelector,
|
|
772
1185
|
{
|
|
773
1186
|
projects: detectedProjects,
|
|
@@ -778,9 +1191,9 @@ function InstallApp({
|
|
|
778
1191
|
] });
|
|
779
1192
|
}
|
|
780
1193
|
if (phase === "configure-features" && project && configItems.length > 0) {
|
|
781
|
-
return /* @__PURE__ */
|
|
782
|
-
/* @__PURE__ */
|
|
783
|
-
/* @__PURE__ */
|
|
1194
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1195
|
+
/* @__PURE__ */ jsx6(Header, { subtitle: "Features" }),
|
|
1196
|
+
/* @__PURE__ */ jsx6(
|
|
784
1197
|
FeatureConfig,
|
|
785
1198
|
{
|
|
786
1199
|
selectedProject,
|
|
@@ -793,14 +1206,32 @@ function InstallApp({
|
|
|
793
1206
|
)
|
|
794
1207
|
] });
|
|
795
1208
|
}
|
|
1209
|
+
if (phase === "configure-injection-point" && injectionPoints.length > 0) {
|
|
1210
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1211
|
+
/* @__PURE__ */ jsx6(Header, { subtitle: "Injection Point" }),
|
|
1212
|
+
selectedProject && /* @__PURE__ */ jsxs5(Box5, { marginBottom: 1, children: [
|
|
1213
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
|
|
1214
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name })
|
|
1215
|
+
] }),
|
|
1216
|
+
/* @__PURE__ */ jsx6(
|
|
1217
|
+
InjectionPointSelector,
|
|
1218
|
+
{
|
|
1219
|
+
points: injectionPoints,
|
|
1220
|
+
onSubmit: handleInjectionPointSubmit,
|
|
1221
|
+
onBack: handleBackFromInjectionPoint,
|
|
1222
|
+
onCancel: handleCancel
|
|
1223
|
+
}
|
|
1224
|
+
)
|
|
1225
|
+
] });
|
|
1226
|
+
}
|
|
796
1227
|
if (phase === "configure-eslint") {
|
|
797
|
-
return /* @__PURE__ */
|
|
798
|
-
/* @__PURE__ */
|
|
799
|
-
selectedProject && /* @__PURE__ */
|
|
800
|
-
/* @__PURE__ */
|
|
801
|
-
/* @__PURE__ */
|
|
1228
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1229
|
+
/* @__PURE__ */ jsx6(Header, { subtitle: "ESLint Rules" }),
|
|
1230
|
+
selectedProject && /* @__PURE__ */ jsxs5(Box5, { marginBottom: 1, children: [
|
|
1231
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
|
|
1232
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name })
|
|
802
1233
|
] }),
|
|
803
|
-
/* @__PURE__ */
|
|
1234
|
+
/* @__PURE__ */ jsx6(
|
|
804
1235
|
RuleSelector,
|
|
805
1236
|
{
|
|
806
1237
|
onSubmit: handleRuleSubmit,
|
|
@@ -810,33 +1241,33 @@ function InstallApp({
|
|
|
810
1241
|
)
|
|
811
1242
|
] });
|
|
812
1243
|
}
|
|
813
|
-
return /* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
/* @__PURE__ */
|
|
1244
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1245
|
+
/* @__PURE__ */ jsx6(Header, {}),
|
|
1246
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading..." })
|
|
816
1247
|
] });
|
|
817
1248
|
}
|
|
818
1249
|
|
|
819
1250
|
// src/commands/install/analyze.ts
|
|
820
|
-
import { existsSync as
|
|
821
|
-
import { join as
|
|
1251
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
1252
|
+
import { join as join5 } from "path";
|
|
822
1253
|
import { findWorkspaceRoot as findWorkspaceRoot2 } from "uilint-core/node";
|
|
823
1254
|
|
|
824
1255
|
// src/utils/vite-detect.ts
|
|
825
|
-
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
826
|
-
import { join } from "path";
|
|
1256
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
1257
|
+
import { join as join2 } from "path";
|
|
827
1258
|
var VITE_CONFIG_EXTS = [".ts", ".mjs", ".js", ".cjs"];
|
|
828
1259
|
function findViteConfigFile(projectPath) {
|
|
829
1260
|
for (const ext of VITE_CONFIG_EXTS) {
|
|
830
1261
|
const rel = `vite.config${ext}`;
|
|
831
|
-
if (
|
|
1262
|
+
if (existsSync2(join2(projectPath, rel))) return rel;
|
|
832
1263
|
}
|
|
833
1264
|
return null;
|
|
834
1265
|
}
|
|
835
1266
|
function looksLikeReactPackage(projectPath) {
|
|
836
1267
|
try {
|
|
837
|
-
const pkgPath =
|
|
838
|
-
if (!
|
|
839
|
-
const pkg = JSON.parse(
|
|
1268
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
1269
|
+
if (!existsSync2(pkgPath)) return false;
|
|
1270
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
840
1271
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
841
1272
|
return "react" in deps || "react-dom" in deps;
|
|
842
1273
|
} catch {
|
|
@@ -844,7 +1275,7 @@ function looksLikeReactPackage(projectPath) {
|
|
|
844
1275
|
}
|
|
845
1276
|
}
|
|
846
1277
|
function fileExists(projectPath, relPath) {
|
|
847
|
-
return
|
|
1278
|
+
return existsSync2(join2(projectPath, relPath));
|
|
848
1279
|
}
|
|
849
1280
|
function detectViteReact(projectPath) {
|
|
850
1281
|
const configFile = findViteConfigFile(projectPath);
|
|
@@ -853,17 +1284,17 @@ function detectViteReact(projectPath) {
|
|
|
853
1284
|
const entryRoot = "src";
|
|
854
1285
|
const candidates = [];
|
|
855
1286
|
const entryCandidates = [
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1287
|
+
join2(entryRoot, "main.tsx"),
|
|
1288
|
+
join2(entryRoot, "main.jsx"),
|
|
1289
|
+
join2(entryRoot, "main.ts"),
|
|
1290
|
+
join2(entryRoot, "main.js")
|
|
860
1291
|
];
|
|
861
1292
|
for (const rel of entryCandidates) {
|
|
862
1293
|
if (fileExists(projectPath, rel)) candidates.push(rel);
|
|
863
1294
|
}
|
|
864
1295
|
const fallbackCandidates = [
|
|
865
|
-
|
|
866
|
-
|
|
1296
|
+
join2(entryRoot, "App.tsx"),
|
|
1297
|
+
join2(entryRoot, "App.jsx")
|
|
867
1298
|
];
|
|
868
1299
|
for (const rel of fallbackCandidates) {
|
|
869
1300
|
if (!candidates.includes(rel) && fileExists(projectPath, rel)) {
|
|
@@ -872,7 +1303,7 @@ function detectViteReact(projectPath) {
|
|
|
872
1303
|
}
|
|
873
1304
|
return {
|
|
874
1305
|
configFile,
|
|
875
|
-
configFileAbs:
|
|
1306
|
+
configFileAbs: join2(projectPath, configFile),
|
|
876
1307
|
entryRoot,
|
|
877
1308
|
candidates
|
|
878
1309
|
};
|
|
@@ -917,7 +1348,7 @@ function findViteReactProjects(rootDir, options) {
|
|
|
917
1348
|
if (!ent.isDirectory) continue;
|
|
918
1349
|
if (ignoreDirs.has(ent.name)) continue;
|
|
919
1350
|
if (ent.name.startsWith(".") && ent.name !== ".") continue;
|
|
920
|
-
walk(
|
|
1351
|
+
walk(join2(dir, ent.name), depth + 1);
|
|
921
1352
|
}
|
|
922
1353
|
}
|
|
923
1354
|
walk(rootDir, 0);
|
|
@@ -925,8 +1356,8 @@ function findViteReactProjects(rootDir, options) {
|
|
|
925
1356
|
}
|
|
926
1357
|
|
|
927
1358
|
// src/utils/package-detect.ts
|
|
928
|
-
import { existsSync as
|
|
929
|
-
import { join as
|
|
1359
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
|
|
1360
|
+
import { join as join3, relative as relative2 } from "path";
|
|
930
1361
|
var DEFAULT_IGNORE_DIRS2 = /* @__PURE__ */ new Set([
|
|
931
1362
|
"node_modules",
|
|
932
1363
|
".git",
|
|
@@ -971,7 +1402,7 @@ function isFrontendPackage(pkgJson) {
|
|
|
971
1402
|
return FRONTEND_INDICATORS.some((pkg) => pkg in deps);
|
|
972
1403
|
}
|
|
973
1404
|
function isTypeScriptPackage(dir, pkgJson) {
|
|
974
|
-
if (
|
|
1405
|
+
if (existsSync3(join3(dir, "tsconfig.json"))) {
|
|
975
1406
|
return true;
|
|
976
1407
|
}
|
|
977
1408
|
const deps = {
|
|
@@ -982,7 +1413,7 @@ function isTypeScriptPackage(dir, pkgJson) {
|
|
|
982
1413
|
return true;
|
|
983
1414
|
}
|
|
984
1415
|
for (const configFile of ESLINT_CONFIG_FILES) {
|
|
985
|
-
if (configFile.endsWith(".ts") &&
|
|
1416
|
+
if (configFile.endsWith(".ts") && existsSync3(join3(dir, configFile))) {
|
|
986
1417
|
return true;
|
|
987
1418
|
}
|
|
988
1419
|
}
|
|
@@ -990,14 +1421,14 @@ function isTypeScriptPackage(dir, pkgJson) {
|
|
|
990
1421
|
}
|
|
991
1422
|
function hasEslintConfig(dir) {
|
|
992
1423
|
for (const file of ESLINT_CONFIG_FILES) {
|
|
993
|
-
if (
|
|
1424
|
+
if (existsSync3(join3(dir, file))) {
|
|
994
1425
|
return true;
|
|
995
1426
|
}
|
|
996
1427
|
}
|
|
997
1428
|
try {
|
|
998
|
-
const pkgPath =
|
|
999
|
-
if (
|
|
1000
|
-
const pkg = JSON.parse(
|
|
1429
|
+
const pkgPath = join3(dir, "package.json");
|
|
1430
|
+
if (existsSync3(pkgPath)) {
|
|
1431
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1001
1432
|
if (pkg.eslintConfig) return true;
|
|
1002
1433
|
}
|
|
1003
1434
|
} catch {
|
|
@@ -1010,14 +1441,14 @@ function findPackages(rootDir, options) {
|
|
|
1010
1441
|
const results = [];
|
|
1011
1442
|
const visited = /* @__PURE__ */ new Set();
|
|
1012
1443
|
function processPackage(dir, isRoot) {
|
|
1013
|
-
const pkgPath =
|
|
1014
|
-
if (!
|
|
1444
|
+
const pkgPath = join3(dir, "package.json");
|
|
1445
|
+
if (!existsSync3(pkgPath)) return null;
|
|
1015
1446
|
try {
|
|
1016
|
-
const pkg = JSON.parse(
|
|
1017
|
-
const name = pkg.name ||
|
|
1447
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1448
|
+
const name = pkg.name || relative2(rootDir, dir) || ".";
|
|
1018
1449
|
return {
|
|
1019
1450
|
path: dir,
|
|
1020
|
-
displayPath:
|
|
1451
|
+
displayPath: relative2(rootDir, dir) || ".",
|
|
1021
1452
|
name,
|
|
1022
1453
|
hasEslintConfig: hasEslintConfig(dir),
|
|
1023
1454
|
isFrontend: isFrontendPackage(pkg),
|
|
@@ -1049,7 +1480,7 @@ function findPackages(rootDir, options) {
|
|
|
1049
1480
|
if (!ent.isDirectory) continue;
|
|
1050
1481
|
if (ignoreDirs.has(ent.name)) continue;
|
|
1051
1482
|
if (ent.name.startsWith(".")) continue;
|
|
1052
|
-
walk(
|
|
1483
|
+
walk(join3(dir, ent.name), depth + 1);
|
|
1053
1484
|
}
|
|
1054
1485
|
}
|
|
1055
1486
|
walk(rootDir, 0);
|
|
@@ -1063,15 +1494,15 @@ function findPackages(rootDir, options) {
|
|
|
1063
1494
|
}
|
|
1064
1495
|
|
|
1065
1496
|
// src/utils/eslint-config-inject.ts
|
|
1066
|
-
import { existsSync as
|
|
1067
|
-
import { join as
|
|
1068
|
-
import { parseExpression, parseModule, generateCode } from "magicast";
|
|
1497
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
1498
|
+
import { join as join4, relative as relative3, dirname as dirname2 } from "path";
|
|
1499
|
+
import { parseExpression, parseModule as parseModule2, generateCode } from "magicast";
|
|
1069
1500
|
import { findWorkspaceRoot } from "uilint-core/node";
|
|
1070
1501
|
var CONFIG_EXTENSIONS = [".ts", ".mjs", ".js", ".cjs"];
|
|
1071
1502
|
function findEslintConfigFile(projectPath) {
|
|
1072
1503
|
for (const ext of CONFIG_EXTENSIONS) {
|
|
1073
|
-
const configPath =
|
|
1074
|
-
if (
|
|
1504
|
+
const configPath = join4(projectPath, `eslint.config${ext}`);
|
|
1505
|
+
if (existsSync4(configPath)) {
|
|
1075
1506
|
return configPath;
|
|
1076
1507
|
}
|
|
1077
1508
|
}
|
|
@@ -1294,9 +1725,9 @@ function chooseUniqueIdentifier(base, used) {
|
|
|
1294
1725
|
function addLocalRuleImportsAst(mod, selectedRules, configPath, rulesRoot, fileExtension = ".js") {
|
|
1295
1726
|
const importNames = /* @__PURE__ */ new Map();
|
|
1296
1727
|
let changed = false;
|
|
1297
|
-
const configDir =
|
|
1298
|
-
const rulesDir =
|
|
1299
|
-
const relativeRulesPath =
|
|
1728
|
+
const configDir = dirname2(configPath);
|
|
1729
|
+
const rulesDir = join4(rulesRoot, ".uilint", "rules");
|
|
1730
|
+
const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
|
|
1300
1731
|
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1301
1732
|
const used = collectTopLevelBindings(mod.$ast);
|
|
1302
1733
|
for (const rule of selectedRules) {
|
|
@@ -1322,9 +1753,9 @@ function addLocalRuleRequiresAst(program, selectedRules, configPath, rulesRoot,
|
|
|
1322
1753
|
if (!program || program.type !== "Program") {
|
|
1323
1754
|
return { importNames, changed };
|
|
1324
1755
|
}
|
|
1325
|
-
const configDir =
|
|
1326
|
-
const rulesDir =
|
|
1327
|
-
const relativeRulesPath =
|
|
1756
|
+
const configDir = dirname2(configPath);
|
|
1757
|
+
const rulesDir = join4(rulesRoot, ".uilint", "rules");
|
|
1758
|
+
const relativeRulesPath = relative3(configDir, rulesDir).replace(/\\/g, "/");
|
|
1328
1759
|
const normalizedRulesPath = relativeRulesPath.startsWith("./") || relativeRulesPath.startsWith("../") ? relativeRulesPath : `./${relativeRulesPath}`;
|
|
1329
1760
|
const used = collectTopLevelBindings(program);
|
|
1330
1761
|
for (const rule of selectedRules) {
|
|
@@ -1335,7 +1766,7 @@ function addLocalRuleRequiresAst(program, selectedRules, configPath, rulesRoot,
|
|
|
1335
1766
|
importNames.set(rule.id, importName);
|
|
1336
1767
|
used.add(importName);
|
|
1337
1768
|
const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;
|
|
1338
|
-
const stmtMod =
|
|
1769
|
+
const stmtMod = parseModule2(
|
|
1339
1770
|
`const ${importName} = require("${rulePath}");`
|
|
1340
1771
|
);
|
|
1341
1772
|
const stmt = stmtMod.$ast.body?.[0];
|
|
@@ -1384,7 +1815,7 @@ ${rulesPropsCode}
|
|
|
1384
1815
|
}
|
|
1385
1816
|
function getUilintEslintConfigInfoFromSourceAst(source) {
|
|
1386
1817
|
try {
|
|
1387
|
-
const mod =
|
|
1818
|
+
const mod = parseModule2(source);
|
|
1388
1819
|
const found = findExportedConfigArrayExpression(mod);
|
|
1389
1820
|
if (!found) {
|
|
1390
1821
|
return {
|
|
@@ -1482,7 +1913,7 @@ async function installEslintPlugin(opts) {
|
|
|
1482
1913
|
};
|
|
1483
1914
|
}
|
|
1484
1915
|
const configFilename = getEslintConfigFilename(configPath);
|
|
1485
|
-
const original =
|
|
1916
|
+
const original = readFileSync4(configPath, "utf-8");
|
|
1486
1917
|
const isCommonJS = configPath.endsWith(".cjs");
|
|
1487
1918
|
const ast = getUilintEslintConfigInfoFromSourceAst(original);
|
|
1488
1919
|
if ("error" in ast) {
|
|
@@ -1534,10 +1965,10 @@ async function installEslintPlugin(opts) {
|
|
|
1534
1965
|
};
|
|
1535
1966
|
}
|
|
1536
1967
|
let modifiedAst = false;
|
|
1537
|
-
const localRulesDir =
|
|
1968
|
+
const localRulesDir = join4(opts.projectPath, ".uilint", "rules");
|
|
1538
1969
|
const workspaceRoot = findWorkspaceRoot(opts.projectPath);
|
|
1539
|
-
const workspaceRulesDir =
|
|
1540
|
-
const rulesRoot =
|
|
1970
|
+
const workspaceRulesDir = join4(workspaceRoot, ".uilint", "rules");
|
|
1971
|
+
const rulesRoot = existsSync4(localRulesDir) ? opts.projectPath : workspaceRoot;
|
|
1541
1972
|
const isTypeScriptConfig = configPath.endsWith(".ts");
|
|
1542
1973
|
let fileExtension = isTypeScriptConfig ? "" : ".js";
|
|
1543
1974
|
let ruleImportNames;
|
|
@@ -1575,7 +2006,7 @@ async function installEslintPlugin(opts) {
|
|
|
1575
2006
|
});
|
|
1576
2007
|
modifiedAst = true;
|
|
1577
2008
|
} else {
|
|
1578
|
-
const stmtMod =
|
|
2009
|
+
const stmtMod = parseModule2(
|
|
1579
2010
|
`const { createRule } = require("uilint-eslint");`
|
|
1580
2011
|
);
|
|
1581
2012
|
const stmt = stmtMod.$ast.body?.[0];
|
|
@@ -1612,12 +2043,12 @@ async function installEslintPlugin(opts) {
|
|
|
1612
2043
|
async function analyze(projectPath = process.cwd()) {
|
|
1613
2044
|
const workspaceRoot = findWorkspaceRoot2(projectPath);
|
|
1614
2045
|
const packageManager = detectPackageManager(projectPath);
|
|
1615
|
-
const cursorDir =
|
|
1616
|
-
const cursorDirExists =
|
|
1617
|
-
const styleguidePath =
|
|
1618
|
-
const styleguideExists =
|
|
1619
|
-
const commandsDir =
|
|
1620
|
-
const genstyleguideExists =
|
|
2046
|
+
const cursorDir = join5(projectPath, ".cursor");
|
|
2047
|
+
const cursorDirExists = existsSync5(cursorDir);
|
|
2048
|
+
const styleguidePath = join5(projectPath, ".uilint", "styleguide.md");
|
|
2049
|
+
const styleguideExists = existsSync5(styleguidePath);
|
|
2050
|
+
const commandsDir = join5(cursorDir, "commands");
|
|
2051
|
+
const genstyleguideExists = existsSync5(join5(commandsDir, "genstyleguide.md"));
|
|
1621
2052
|
const nextApps = [];
|
|
1622
2053
|
const directDetection = detectNextAppRouter(projectPath);
|
|
1623
2054
|
if (directDetection) {
|
|
@@ -1653,7 +2084,7 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
1653
2084
|
if (eslintConfigPath) {
|
|
1654
2085
|
eslintConfigFilename = getEslintConfigFilename(eslintConfigPath);
|
|
1655
2086
|
try {
|
|
1656
|
-
const source =
|
|
2087
|
+
const source = readFileSync5(eslintConfigPath, "utf-8");
|
|
1657
2088
|
const info = getUilintEslintConfigInfoFromSource(source);
|
|
1658
2089
|
hasRules = info.configuredRuleIds.size > 0;
|
|
1659
2090
|
configuredRuleIds = Array.from(info.configuredRuleIds);
|
|
@@ -1691,49 +2122,49 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
1691
2122
|
|
|
1692
2123
|
// src/commands/install/execute.ts
|
|
1693
2124
|
import {
|
|
1694
|
-
existsSync as
|
|
2125
|
+
existsSync as existsSync11,
|
|
1695
2126
|
mkdirSync,
|
|
1696
2127
|
writeFileSync as writeFileSync5,
|
|
1697
|
-
readFileSync as
|
|
2128
|
+
readFileSync as readFileSync9,
|
|
1698
2129
|
unlinkSync,
|
|
1699
2130
|
chmodSync
|
|
1700
2131
|
} from "fs";
|
|
1701
|
-
import { dirname as
|
|
2132
|
+
import { dirname as dirname5 } from "path";
|
|
1702
2133
|
|
|
1703
2134
|
// src/utils/react-inject.ts
|
|
1704
|
-
import { existsSync as
|
|
1705
|
-
import { join as
|
|
1706
|
-
import { parseModule as
|
|
2135
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
2136
|
+
import { join as join6, relative as relative4 } from "path";
|
|
2137
|
+
import { parseModule as parseModule3, generateCode as generateCode2 } from "magicast";
|
|
1707
2138
|
function getDefaultCandidates(projectPath, appRoot) {
|
|
1708
2139
|
const viteMainCandidates = [
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
2140
|
+
join6(appRoot, "main.tsx"),
|
|
2141
|
+
join6(appRoot, "main.jsx"),
|
|
2142
|
+
join6(appRoot, "main.ts"),
|
|
2143
|
+
join6(appRoot, "main.js")
|
|
1713
2144
|
];
|
|
1714
2145
|
const existingViteMain = viteMainCandidates.filter(
|
|
1715
|
-
(rel) =>
|
|
2146
|
+
(rel) => existsSync6(join6(projectPath, rel))
|
|
1716
2147
|
);
|
|
1717
2148
|
if (existingViteMain.length > 0) return existingViteMain;
|
|
1718
|
-
const viteAppCandidates = [
|
|
2149
|
+
const viteAppCandidates = [join6(appRoot, "App.tsx"), join6(appRoot, "App.jsx")];
|
|
1719
2150
|
const existingViteApp = viteAppCandidates.filter(
|
|
1720
|
-
(rel) =>
|
|
2151
|
+
(rel) => existsSync6(join6(projectPath, rel))
|
|
1721
2152
|
);
|
|
1722
2153
|
if (existingViteApp.length > 0) return existingViteApp;
|
|
1723
2154
|
const layoutCandidates = [
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
2155
|
+
join6(appRoot, "layout.tsx"),
|
|
2156
|
+
join6(appRoot, "layout.jsx"),
|
|
2157
|
+
join6(appRoot, "layout.ts"),
|
|
2158
|
+
join6(appRoot, "layout.js")
|
|
1728
2159
|
];
|
|
1729
2160
|
const existingLayouts = layoutCandidates.filter(
|
|
1730
|
-
(rel) =>
|
|
2161
|
+
(rel) => existsSync6(join6(projectPath, rel))
|
|
1731
2162
|
);
|
|
1732
2163
|
if (existingLayouts.length > 0) {
|
|
1733
2164
|
return existingLayouts;
|
|
1734
2165
|
}
|
|
1735
|
-
const pageCandidates = [
|
|
1736
|
-
return pageCandidates.filter((rel) =>
|
|
2166
|
+
const pageCandidates = [join6(appRoot, "page.tsx"), join6(appRoot, "page.jsx")];
|
|
2167
|
+
return pageCandidates.filter((rel) => existsSync6(join6(projectPath, rel)));
|
|
1737
2168
|
}
|
|
1738
2169
|
function isUseClientDirective(stmt) {
|
|
1739
2170
|
return stmt?.type === "ExpressionStatement" && stmt.expression?.type === "StringLiteral" && stmt.expression.value === "use client";
|
|
@@ -1767,12 +2198,12 @@ function ensureNamedImport(program, from, name) {
|
|
|
1767
2198
|
(s) => s?.type === "ImportSpecifier" && (s.imported?.name === name || s.imported?.value === name)
|
|
1768
2199
|
);
|
|
1769
2200
|
if (has) return { changed: false };
|
|
1770
|
-
const spec =
|
|
2201
|
+
const spec = parseModule3(`import { ${name} } from "${from}";`).$ast.body?.[0]?.specifiers?.[0];
|
|
1771
2202
|
if (!spec) return { changed: false };
|
|
1772
2203
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
1773
2204
|
return { changed: true };
|
|
1774
2205
|
}
|
|
1775
|
-
const importDecl =
|
|
2206
|
+
const importDecl = parseModule3(`import { ${name} } from "${from}";`).$ast.body?.[0];
|
|
1776
2207
|
if (!importDecl) return { changed: false };
|
|
1777
2208
|
const body = program.body ?? [];
|
|
1778
2209
|
let insertAt = 0;
|
|
@@ -1802,7 +2233,7 @@ function hasUILintDevtoolsJsx(program) {
|
|
|
1802
2233
|
function addDevtoolsElementNextJs(program) {
|
|
1803
2234
|
if (!program || program.type !== "Program") return { changed: false };
|
|
1804
2235
|
if (hasUILintDevtoolsJsx(program)) return { changed: false };
|
|
1805
|
-
const devtoolsMod =
|
|
2236
|
+
const devtoolsMod = parseModule3(
|
|
1806
2237
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
1807
2238
|
);
|
|
1808
2239
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -1840,12 +2271,12 @@ function addDevtoolsElementVite(program) {
|
|
|
1840
2271
|
const arg0 = node.arguments?.[0];
|
|
1841
2272
|
if (!arg0) return;
|
|
1842
2273
|
if (arg0.type !== "JSXElement" && arg0.type !== "JSXFragment") return;
|
|
1843
|
-
const devtoolsMod =
|
|
2274
|
+
const devtoolsMod = parseModule3(
|
|
1844
2275
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
1845
2276
|
);
|
|
1846
2277
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
1847
2278
|
if (!devtoolsJsx) return;
|
|
1848
|
-
const fragmentMod =
|
|
2279
|
+
const fragmentMod = parseModule3(
|
|
1849
2280
|
"const __fragment = <></>;"
|
|
1850
2281
|
);
|
|
1851
2282
|
const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -1865,7 +2296,7 @@ function ensureSideEffectImport(program, from) {
|
|
|
1865
2296
|
if (!program || program.type !== "Program") return { changed: false };
|
|
1866
2297
|
const existing = findImportDeclaration(program, from);
|
|
1867
2298
|
if (existing) return { changed: false };
|
|
1868
|
-
const importDecl =
|
|
2299
|
+
const importDecl = parseModule3(`import "${from}";`).$ast.body?.[0];
|
|
1869
2300
|
if (!importDecl) return { changed: false };
|
|
1870
2301
|
const body = program.body ?? [];
|
|
1871
2302
|
let insertAt = 0;
|
|
@@ -1881,7 +2312,7 @@ function ensureSideEffectImport(program, from) {
|
|
|
1881
2312
|
function addDevtoolsToClientComponent(program) {
|
|
1882
2313
|
if (!program || program.type !== "Program") return { changed: false };
|
|
1883
2314
|
if (hasUILintDevtoolsJsx(program)) return { changed: false };
|
|
1884
|
-
const devtoolsMod =
|
|
2315
|
+
const devtoolsMod = parseModule3(
|
|
1885
2316
|
"const __uilint_devtools = <uilint-devtools />;"
|
|
1886
2317
|
);
|
|
1887
2318
|
const devtoolsJsx = devtoolsMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -1907,7 +2338,7 @@ function addDevtoolsToClientComponent(program) {
|
|
|
1907
2338
|
const arg = node.argument;
|
|
1908
2339
|
if (!arg) return;
|
|
1909
2340
|
if (arg.type !== "JSXElement" && arg.type !== "JSXFragment") return;
|
|
1910
|
-
const fragmentMod =
|
|
2341
|
+
const fragmentMod = parseModule3("const __fragment = <></>;");
|
|
1911
2342
|
const fragmentJsx = fragmentMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
1912
2343
|
if (!fragmentJsx) return;
|
|
1913
2344
|
fragmentJsx.children = [arg, devtoolsJsx];
|
|
@@ -1962,7 +2393,7 @@ function wrapChildrenWithProviders(program, providersImportPath) {
|
|
|
1962
2393
|
(child) => child?.type === "JSXExpressionContainer" && child.expression?.type === "Identifier" && child.expression.name === "children"
|
|
1963
2394
|
);
|
|
1964
2395
|
if (childrenIndex === -1) return;
|
|
1965
|
-
const providersMod =
|
|
2396
|
+
const providersMod = parseModule3(
|
|
1966
2397
|
"const __providers = <Providers>{children}</Providers>;"
|
|
1967
2398
|
);
|
|
1968
2399
|
const providersJsx = providersMod.$ast.body?.[0]?.declarations?.[0]?.init ?? null;
|
|
@@ -1977,36 +2408,36 @@ function wrapChildrenWithProviders(program, providersImportPath) {
|
|
|
1977
2408
|
}
|
|
1978
2409
|
return { changed: true };
|
|
1979
2410
|
}
|
|
1980
|
-
function
|
|
2411
|
+
function findLayoutFile2(projectPath, appRoot) {
|
|
1981
2412
|
const extensions = [".tsx", ".jsx", ".ts", ".js"];
|
|
1982
2413
|
for (const ext of extensions) {
|
|
1983
|
-
const layoutPath =
|
|
1984
|
-
if (
|
|
2414
|
+
const layoutPath = join6(projectPath, appRoot, `layout${ext}`);
|
|
2415
|
+
if (existsSync6(layoutPath)) return layoutPath;
|
|
1985
2416
|
}
|
|
1986
2417
|
return null;
|
|
1987
2418
|
}
|
|
1988
2419
|
async function createProvidersAndModifyLayout(projectPath, appRoot) {
|
|
1989
|
-
const layoutPath =
|
|
2420
|
+
const layoutPath = findLayoutFile2(projectPath, appRoot);
|
|
1990
2421
|
if (!layoutPath) {
|
|
1991
2422
|
throw new Error(`Could not find layout file in ${appRoot}`);
|
|
1992
2423
|
}
|
|
1993
2424
|
const isTypeScript = layoutPath.endsWith(".tsx") || layoutPath.endsWith(".ts");
|
|
1994
2425
|
const providersExt = isTypeScript ? ".tsx" : ".jsx";
|
|
1995
|
-
const providersPath =
|
|
1996
|
-
if (
|
|
2426
|
+
const providersPath = join6(projectPath, appRoot, `providers${providersExt}`);
|
|
2427
|
+
if (existsSync6(providersPath)) {
|
|
1997
2428
|
throw new Error(
|
|
1998
2429
|
`providers${providersExt} already exists. Please select it from the list instead.`
|
|
1999
2430
|
);
|
|
2000
2431
|
}
|
|
2001
2432
|
const providersContent = generateProvidersContent(isTypeScript);
|
|
2002
2433
|
writeFileSync2(providersPath, providersContent, "utf-8");
|
|
2003
|
-
const layoutContent =
|
|
2434
|
+
const layoutContent = readFileSync6(layoutPath, "utf-8");
|
|
2004
2435
|
let layoutMod;
|
|
2005
2436
|
try {
|
|
2006
|
-
layoutMod =
|
|
2437
|
+
layoutMod = parseModule3(layoutContent);
|
|
2007
2438
|
} catch {
|
|
2008
2439
|
throw new Error(
|
|
2009
|
-
`Unable to parse ${
|
|
2440
|
+
`Unable to parse ${relative4(projectPath, layoutPath)} as JavaScript/TypeScript.`
|
|
2010
2441
|
);
|
|
2011
2442
|
}
|
|
2012
2443
|
const layoutProgram = layoutMod.$ast;
|
|
@@ -2016,8 +2447,8 @@ async function createProvidersAndModifyLayout(projectPath, appRoot) {
|
|
|
2016
2447
|
writeFileSync2(layoutPath, updatedLayout, "utf-8");
|
|
2017
2448
|
}
|
|
2018
2449
|
return {
|
|
2019
|
-
providersFile:
|
|
2020
|
-
layoutFile:
|
|
2450
|
+
providersFile: relative4(projectPath, providersPath),
|
|
2451
|
+
layoutFile: relative4(projectPath, layoutPath),
|
|
2021
2452
|
modified: true
|
|
2022
2453
|
};
|
|
2023
2454
|
}
|
|
@@ -2036,14 +2467,14 @@ async function installReactUILintOverlay(opts) {
|
|
|
2036
2467
|
}
|
|
2037
2468
|
if (opts.targetFile) {
|
|
2038
2469
|
const absTarget2 = opts.targetFile;
|
|
2039
|
-
const relTarget =
|
|
2040
|
-
if (!
|
|
2470
|
+
const relTarget = relative4(opts.projectPath, absTarget2);
|
|
2471
|
+
if (!existsSync6(absTarget2)) {
|
|
2041
2472
|
throw new Error(`Target file not found: ${relTarget}`);
|
|
2042
2473
|
}
|
|
2043
|
-
const original2 =
|
|
2474
|
+
const original2 = readFileSync6(absTarget2, "utf-8");
|
|
2044
2475
|
let mod2;
|
|
2045
2476
|
try {
|
|
2046
|
-
mod2 =
|
|
2477
|
+
mod2 = parseModule3(original2);
|
|
2047
2478
|
} catch {
|
|
2048
2479
|
throw new Error(
|
|
2049
2480
|
`Unable to parse ${relTarget} as JavaScript/TypeScript. Please update it manually.`
|
|
@@ -2088,11 +2519,11 @@ async function installReactUILintOverlay(opts) {
|
|
|
2088
2519
|
} else {
|
|
2089
2520
|
chosen = candidates[0];
|
|
2090
2521
|
}
|
|
2091
|
-
const absTarget =
|
|
2092
|
-
const original =
|
|
2522
|
+
const absTarget = join6(opts.projectPath, chosen);
|
|
2523
|
+
const original = readFileSync6(absTarget, "utf-8");
|
|
2093
2524
|
let mod;
|
|
2094
2525
|
try {
|
|
2095
|
-
mod =
|
|
2526
|
+
mod = parseModule3(original);
|
|
2096
2527
|
} catch {
|
|
2097
2528
|
throw new Error(
|
|
2098
2529
|
`Unable to parse ${chosen} as JavaScript/TypeScript. Please update it manually.`
|
|
@@ -2121,14 +2552,14 @@ async function installReactUILintOverlay(opts) {
|
|
|
2121
2552
|
}
|
|
2122
2553
|
|
|
2123
2554
|
// src/utils/next-config-inject.ts
|
|
2124
|
-
import { existsSync as
|
|
2125
|
-
import { join as
|
|
2126
|
-
import { parseModule as
|
|
2555
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
2556
|
+
import { join as join7 } from "path";
|
|
2557
|
+
import { parseModule as parseModule4, generateCode as generateCode3 } from "magicast";
|
|
2127
2558
|
var CONFIG_EXTENSIONS2 = [".ts", ".mjs", ".js", ".cjs"];
|
|
2128
2559
|
function findNextConfigFile(projectPath) {
|
|
2129
2560
|
for (const ext of CONFIG_EXTENSIONS2) {
|
|
2130
|
-
const configPath =
|
|
2131
|
-
if (
|
|
2561
|
+
const configPath = join7(projectPath, `next.config${ext}`);
|
|
2562
|
+
if (existsSync7(configPath)) {
|
|
2132
2563
|
return configPath;
|
|
2133
2564
|
}
|
|
2134
2565
|
}
|
|
@@ -2154,12 +2585,12 @@ function ensureEsmWithJsxLocImport(program) {
|
|
|
2154
2585
|
(sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "withJsxLoc" || sp.imported?.value === "withJsxLoc")
|
|
2155
2586
|
);
|
|
2156
2587
|
if (has) return { changed: false };
|
|
2157
|
-
const spec =
|
|
2588
|
+
const spec = parseModule4('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0]?.specifiers?.[0];
|
|
2158
2589
|
if (!spec) return { changed: false };
|
|
2159
2590
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
2160
2591
|
return { changed: true };
|
|
2161
2592
|
}
|
|
2162
|
-
const importDecl =
|
|
2593
|
+
const importDecl = parseModule4('import { withJsxLoc } from "jsx-loc-plugin";').$ast.body?.[0];
|
|
2163
2594
|
if (!importDecl) return { changed: false };
|
|
2164
2595
|
const body = program.body ?? [];
|
|
2165
2596
|
let insertAt = 0;
|
|
@@ -2182,7 +2613,7 @@ function ensureCjsWithJsxLocRequire(program) {
|
|
|
2182
2613
|
return isIdentifier2(p.key, "withJsxLoc");
|
|
2183
2614
|
});
|
|
2184
2615
|
if (has) return { changed: false };
|
|
2185
|
-
const prop =
|
|
2616
|
+
const prop = parseModule4('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
|
|
2186
2617
|
if (!prop) return { changed: false };
|
|
2187
2618
|
decl.id.properties = [...decl.id.properties ?? [], prop];
|
|
2188
2619
|
return { changed: true };
|
|
@@ -2191,7 +2622,7 @@ function ensureCjsWithJsxLocRequire(program) {
|
|
|
2191
2622
|
}
|
|
2192
2623
|
}
|
|
2193
2624
|
}
|
|
2194
|
-
const reqDecl =
|
|
2625
|
+
const reqDecl = parseModule4('const { withJsxLoc } = require("jsx-loc-plugin");').$ast.body?.[0];
|
|
2195
2626
|
if (!reqDecl) return { changed: false };
|
|
2196
2627
|
program.body.unshift(reqDecl);
|
|
2197
2628
|
return { changed: true };
|
|
@@ -2241,10 +2672,10 @@ async function installJsxLocPlugin(opts) {
|
|
|
2241
2672
|
return { configFile: null, modified: false };
|
|
2242
2673
|
}
|
|
2243
2674
|
const configFilename = getNextConfigFilename(configPath);
|
|
2244
|
-
const original =
|
|
2675
|
+
const original = readFileSync7(configPath, "utf-8");
|
|
2245
2676
|
let mod;
|
|
2246
2677
|
try {
|
|
2247
|
-
mod =
|
|
2678
|
+
mod = parseModule4(original);
|
|
2248
2679
|
} catch {
|
|
2249
2680
|
return { configFile: configFilename, modified: false };
|
|
2250
2681
|
}
|
|
@@ -2271,14 +2702,14 @@ async function installJsxLocPlugin(opts) {
|
|
|
2271
2702
|
}
|
|
2272
2703
|
|
|
2273
2704
|
// src/utils/vite-config-inject.ts
|
|
2274
|
-
import { existsSync as
|
|
2275
|
-
import { join as
|
|
2276
|
-
import { parseModule as
|
|
2705
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
2706
|
+
import { join as join8 } from "path";
|
|
2707
|
+
import { parseModule as parseModule5, generateCode as generateCode4 } from "magicast";
|
|
2277
2708
|
var CONFIG_EXTENSIONS3 = [".ts", ".mjs", ".js", ".cjs"];
|
|
2278
2709
|
function findViteConfigFile2(projectPath) {
|
|
2279
2710
|
for (const ext of CONFIG_EXTENSIONS3) {
|
|
2280
|
-
const configPath =
|
|
2281
|
-
if (
|
|
2711
|
+
const configPath = join8(projectPath, `vite.config${ext}`);
|
|
2712
|
+
if (existsSync8(configPath)) return configPath;
|
|
2282
2713
|
}
|
|
2283
2714
|
return null;
|
|
2284
2715
|
}
|
|
@@ -2372,12 +2803,12 @@ function ensureEsmJsxLocImport(program) {
|
|
|
2372
2803
|
(sp) => sp?.type === "ImportSpecifier" && (sp.imported?.name === "jsxLoc" || sp.imported?.value === "jsxLoc")
|
|
2373
2804
|
);
|
|
2374
2805
|
if (has) return { changed: false };
|
|
2375
|
-
const spec =
|
|
2806
|
+
const spec = parseModule5('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0]?.specifiers?.[0];
|
|
2376
2807
|
if (!spec) return { changed: false };
|
|
2377
2808
|
existing.specifiers = [...existing.specifiers ?? [], spec];
|
|
2378
2809
|
return { changed: true };
|
|
2379
2810
|
}
|
|
2380
|
-
const importDecl =
|
|
2811
|
+
const importDecl = parseModule5('import { jsxLoc } from "jsx-loc-plugin/vite";').$ast.body?.[0];
|
|
2381
2812
|
if (!importDecl) return { changed: false };
|
|
2382
2813
|
const body = program.body ?? [];
|
|
2383
2814
|
let insertAt = 0;
|
|
@@ -2400,7 +2831,7 @@ function ensureCjsJsxLocRequire(program) {
|
|
|
2400
2831
|
return isIdentifier3(p.key, "jsxLoc");
|
|
2401
2832
|
});
|
|
2402
2833
|
if (has) return { changed: false };
|
|
2403
|
-
const prop =
|
|
2834
|
+
const prop = parseModule5('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0]?.declarations?.[0]?.id?.properties?.[0];
|
|
2404
2835
|
if (!prop) return { changed: false };
|
|
2405
2836
|
decl.id.properties = [...decl.id.properties ?? [], prop];
|
|
2406
2837
|
return { changed: true };
|
|
@@ -2409,7 +2840,7 @@ function ensureCjsJsxLocRequire(program) {
|
|
|
2409
2840
|
}
|
|
2410
2841
|
}
|
|
2411
2842
|
}
|
|
2412
|
-
const reqDecl =
|
|
2843
|
+
const reqDecl = parseModule5('const { jsxLoc } = require("jsx-loc-plugin/vite");').$ast.body?.[0];
|
|
2413
2844
|
if (!reqDecl) return { changed: false };
|
|
2414
2845
|
program.body.unshift(reqDecl);
|
|
2415
2846
|
return { changed: true };
|
|
@@ -2426,7 +2857,7 @@ function pluginsHasJsxLoc(arr) {
|
|
|
2426
2857
|
function ensurePluginsContainsJsxLoc(configObj) {
|
|
2427
2858
|
const pluginsProp = getObjectProperty(configObj, "plugins");
|
|
2428
2859
|
if (!pluginsProp) {
|
|
2429
|
-
const prop =
|
|
2860
|
+
const prop = parseModule5("export default { plugins: [jsxLoc()] };").$ast.body?.find((s) => s.type === "ExportDefaultDeclaration")?.declaration?.properties?.find((p) => {
|
|
2430
2861
|
const k = p?.key;
|
|
2431
2862
|
return k?.type === "Identifier" && k.name === "plugins" || isStringLiteral3(k) && k.value === "plugins";
|
|
2432
2863
|
});
|
|
@@ -2438,12 +2869,12 @@ function ensurePluginsContainsJsxLoc(configObj) {
|
|
|
2438
2869
|
if (!value) return { changed: false };
|
|
2439
2870
|
if (value.type === "ArrayExpression") {
|
|
2440
2871
|
if (pluginsHasJsxLoc(value)) return { changed: false };
|
|
2441
|
-
const jsxLocCall2 =
|
|
2872
|
+
const jsxLocCall2 = parseModule5("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
|
|
2442
2873
|
if (!jsxLocCall2) return { changed: false };
|
|
2443
2874
|
value.elements.push(jsxLocCall2);
|
|
2444
2875
|
return { changed: true };
|
|
2445
2876
|
}
|
|
2446
|
-
const jsxLocCall =
|
|
2877
|
+
const jsxLocCall = parseModule5("const __x = jsxLoc();").$ast.body?.[0]?.declarations?.[0]?.init;
|
|
2447
2878
|
if (!jsxLocCall) return { changed: false };
|
|
2448
2879
|
const spread = { type: "SpreadElement", argument: value };
|
|
2449
2880
|
pluginsProp.value = { type: "ArrayExpression", elements: [spread, jsxLocCall] };
|
|
@@ -2453,11 +2884,11 @@ async function installViteJsxLocPlugin(opts) {
|
|
|
2453
2884
|
const configPath = findViteConfigFile2(opts.projectPath);
|
|
2454
2885
|
if (!configPath) return { configFile: null, modified: false };
|
|
2455
2886
|
const configFilename = getViteConfigFilename(configPath);
|
|
2456
|
-
const original =
|
|
2887
|
+
const original = readFileSync8(configPath, "utf-8");
|
|
2457
2888
|
const isCjs = configPath.endsWith(".cjs");
|
|
2458
2889
|
let mod;
|
|
2459
2890
|
try {
|
|
2460
|
-
mod =
|
|
2891
|
+
mod = parseModule5(original);
|
|
2461
2892
|
} catch {
|
|
2462
2893
|
return { configFile: configFilename, modified: false };
|
|
2463
2894
|
}
|
|
@@ -2482,9 +2913,9 @@ async function installViteJsxLocPlugin(opts) {
|
|
|
2482
2913
|
}
|
|
2483
2914
|
|
|
2484
2915
|
// src/utils/next-routes.ts
|
|
2485
|
-
import { existsSync as
|
|
2916
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2486
2917
|
import { mkdir, writeFile } from "fs/promises";
|
|
2487
|
-
import { join as
|
|
2918
|
+
import { join as join9 } from "path";
|
|
2488
2919
|
var DEV_SOURCE_ROUTE_TS = `/**
|
|
2489
2920
|
* Dev-only API route for fetching source files
|
|
2490
2921
|
*
|
|
@@ -2940,40 +3371,40 @@ export async function GET(request: NextRequest) {
|
|
|
2940
3371
|
}
|
|
2941
3372
|
`;
|
|
2942
3373
|
async function writeRouteFile(absPath, relPath, content, opts) {
|
|
2943
|
-
if (
|
|
3374
|
+
if (existsSync9(absPath) && !opts.force) return;
|
|
2944
3375
|
await writeFile(absPath, content, "utf-8");
|
|
2945
3376
|
}
|
|
2946
3377
|
async function installNextUILintRoutes(opts) {
|
|
2947
|
-
const baseRel =
|
|
2948
|
-
const baseAbs =
|
|
2949
|
-
await mkdir(
|
|
3378
|
+
const baseRel = join9(opts.appRoot, "api", ".uilint");
|
|
3379
|
+
const baseAbs = join9(opts.projectPath, baseRel);
|
|
3380
|
+
await mkdir(join9(baseAbs, "source"), { recursive: true });
|
|
2950
3381
|
await writeRouteFile(
|
|
2951
|
-
|
|
2952
|
-
|
|
3382
|
+
join9(baseAbs, "source", "route.ts"),
|
|
3383
|
+
join9(baseRel, "source", "route.ts"),
|
|
2953
3384
|
DEV_SOURCE_ROUTE_TS,
|
|
2954
3385
|
opts
|
|
2955
3386
|
);
|
|
2956
|
-
await mkdir(
|
|
3387
|
+
await mkdir(join9(baseAbs, "screenshots"), { recursive: true });
|
|
2957
3388
|
await writeRouteFile(
|
|
2958
|
-
|
|
2959
|
-
|
|
3389
|
+
join9(baseAbs, "screenshots", "route.ts"),
|
|
3390
|
+
join9(baseRel, "screenshots", "route.ts"),
|
|
2960
3391
|
SCREENSHOT_ROUTE_TS,
|
|
2961
3392
|
opts
|
|
2962
3393
|
);
|
|
2963
3394
|
}
|
|
2964
3395
|
|
|
2965
3396
|
// src/utils/prettier.ts
|
|
2966
|
-
import { existsSync as
|
|
3397
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2967
3398
|
import { spawn } from "child_process";
|
|
2968
|
-
import { join as
|
|
3399
|
+
import { join as join10, dirname as dirname4 } from "path";
|
|
2969
3400
|
function getPrettierPath(projectPath) {
|
|
2970
|
-
const localPath =
|
|
2971
|
-
if (
|
|
3401
|
+
const localPath = join10(projectPath, "node_modules", ".bin", "prettier");
|
|
3402
|
+
if (existsSync10(localPath)) return localPath;
|
|
2972
3403
|
let dir = projectPath;
|
|
2973
3404
|
for (let i = 0; i < 10; i++) {
|
|
2974
|
-
const binPath =
|
|
2975
|
-
if (
|
|
2976
|
-
const parent =
|
|
3405
|
+
const binPath = join10(dir, "node_modules", ".bin", "prettier");
|
|
3406
|
+
if (existsSync10(binPath)) return binPath;
|
|
3407
|
+
const parent = dirname4(dir);
|
|
2977
3408
|
if (parent === dir) break;
|
|
2978
3409
|
dir = parent;
|
|
2979
3410
|
}
|
|
@@ -3114,7 +3545,7 @@ async function executeAction(action, options) {
|
|
|
3114
3545
|
wouldDo: `Create directory: ${action.path}`
|
|
3115
3546
|
};
|
|
3116
3547
|
}
|
|
3117
|
-
if (!
|
|
3548
|
+
if (!existsSync11(action.path)) {
|
|
3118
3549
|
mkdirSync(action.path, { recursive: true });
|
|
3119
3550
|
}
|
|
3120
3551
|
return { action, success: true };
|
|
@@ -3127,8 +3558,8 @@ async function executeAction(action, options) {
|
|
|
3127
3558
|
wouldDo: `Create file: ${action.path}${action.permissions ? ` (mode: ${action.permissions.toString(8)})` : ""}`
|
|
3128
3559
|
};
|
|
3129
3560
|
}
|
|
3130
|
-
const dir =
|
|
3131
|
-
if (!
|
|
3561
|
+
const dir = dirname5(action.path);
|
|
3562
|
+
if (!existsSync11(dir)) {
|
|
3132
3563
|
mkdirSync(dir, { recursive: true });
|
|
3133
3564
|
}
|
|
3134
3565
|
writeFileSync5(action.path, action.content, "utf-8");
|
|
@@ -3146,15 +3577,15 @@ async function executeAction(action, options) {
|
|
|
3146
3577
|
};
|
|
3147
3578
|
}
|
|
3148
3579
|
let existing = {};
|
|
3149
|
-
if (
|
|
3580
|
+
if (existsSync11(action.path)) {
|
|
3150
3581
|
try {
|
|
3151
|
-
existing = JSON.parse(
|
|
3582
|
+
existing = JSON.parse(readFileSync9(action.path, "utf-8"));
|
|
3152
3583
|
} catch {
|
|
3153
3584
|
}
|
|
3154
3585
|
}
|
|
3155
3586
|
const merged = deepMerge(existing, action.merge);
|
|
3156
|
-
const dir =
|
|
3157
|
-
if (!
|
|
3587
|
+
const dir = dirname5(action.path);
|
|
3588
|
+
if (!existsSync11(dir)) {
|
|
3158
3589
|
mkdirSync(dir, { recursive: true });
|
|
3159
3590
|
}
|
|
3160
3591
|
writeFileSync5(action.path, JSON.stringify(merged, null, 2), "utf-8");
|
|
@@ -3168,7 +3599,7 @@ async function executeAction(action, options) {
|
|
|
3168
3599
|
wouldDo: `Delete file: ${action.path}`
|
|
3169
3600
|
};
|
|
3170
3601
|
}
|
|
3171
|
-
if (
|
|
3602
|
+
if (existsSync11(action.path)) {
|
|
3172
3603
|
unlinkSync(action.path);
|
|
3173
3604
|
}
|
|
3174
3605
|
return { action, success: true };
|
|
@@ -3181,8 +3612,8 @@ async function executeAction(action, options) {
|
|
|
3181
3612
|
wouldDo: `Append to file: ${action.path}`
|
|
3182
3613
|
};
|
|
3183
3614
|
}
|
|
3184
|
-
if (
|
|
3185
|
-
const content =
|
|
3615
|
+
if (existsSync11(action.path)) {
|
|
3616
|
+
const content = readFileSync9(action.path, "utf-8");
|
|
3186
3617
|
if (action.ifNotContains && content.includes(action.ifNotContains)) {
|
|
3187
3618
|
return { action, success: true };
|
|
3188
3619
|
}
|
|
@@ -3549,7 +3980,7 @@ async function execute(plan, options = {}) {
|
|
|
3549
3980
|
import { ruleRegistry as ruleRegistry3 } from "uilint-eslint";
|
|
3550
3981
|
|
|
3551
3982
|
// src/commands/install/installers/genstyleguide.ts
|
|
3552
|
-
import { join as
|
|
3983
|
+
import { join as join11 } from "path";
|
|
3553
3984
|
var genstyleguideInstaller = {
|
|
3554
3985
|
id: "genstyleguide",
|
|
3555
3986
|
name: "/genstyleguide command",
|
|
@@ -3559,7 +3990,7 @@ var genstyleguideInstaller = {
|
|
|
3559
3990
|
return true;
|
|
3560
3991
|
},
|
|
3561
3992
|
getTargets(project) {
|
|
3562
|
-
const commandPath =
|
|
3993
|
+
const commandPath = join11(project.cursorDir.path, "commands", "genstyleguide.md");
|
|
3563
3994
|
const isInstalled = project.commands.genstyleguide;
|
|
3564
3995
|
return [
|
|
3565
3996
|
{
|
|
@@ -3572,7 +4003,7 @@ var genstyleguideInstaller = {
|
|
|
3572
4003
|
},
|
|
3573
4004
|
plan(targets, config, project) {
|
|
3574
4005
|
const actions = [];
|
|
3575
|
-
const commandsDir =
|
|
4006
|
+
const commandsDir = join11(project.cursorDir.path, "commands");
|
|
3576
4007
|
if (!project.cursorDir.exists) {
|
|
3577
4008
|
actions.push({
|
|
3578
4009
|
type: "create_directory",
|
|
@@ -3585,7 +4016,7 @@ var genstyleguideInstaller = {
|
|
|
3585
4016
|
});
|
|
3586
4017
|
actions.push({
|
|
3587
4018
|
type: "create_file",
|
|
3588
|
-
path:
|
|
4019
|
+
path: join11(commandsDir, "genstyleguide.md"),
|
|
3589
4020
|
content: GENSTYLEGUIDE_COMMAND_MD
|
|
3590
4021
|
});
|
|
3591
4022
|
return {
|
|
@@ -3615,8 +4046,8 @@ var genstyleguideInstaller = {
|
|
|
3615
4046
|
};
|
|
3616
4047
|
|
|
3617
4048
|
// src/commands/install/installers/skill.ts
|
|
3618
|
-
import { existsSync as
|
|
3619
|
-
import { join as
|
|
4049
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4050
|
+
import { join as join12 } from "path";
|
|
3620
4051
|
var skillInstaller = {
|
|
3621
4052
|
id: "skill",
|
|
3622
4053
|
name: "UI Consistency Agent skill",
|
|
@@ -3626,9 +4057,9 @@ var skillInstaller = {
|
|
|
3626
4057
|
return true;
|
|
3627
4058
|
},
|
|
3628
4059
|
getTargets(project) {
|
|
3629
|
-
const skillsDir =
|
|
3630
|
-
const skillMdPath =
|
|
3631
|
-
const isInstalled =
|
|
4060
|
+
const skillsDir = join12(project.cursorDir.path, "skills", "ui-consistency-enforcer");
|
|
4061
|
+
const skillMdPath = join12(skillsDir, "SKILL.md");
|
|
4062
|
+
const isInstalled = existsSync12(skillMdPath);
|
|
3632
4063
|
return [
|
|
3633
4064
|
{
|
|
3634
4065
|
id: "ui-consistency-skill",
|
|
@@ -3647,21 +4078,21 @@ var skillInstaller = {
|
|
|
3647
4078
|
path: project.cursorDir.path
|
|
3648
4079
|
});
|
|
3649
4080
|
}
|
|
3650
|
-
const skillsDir =
|
|
4081
|
+
const skillsDir = join12(project.cursorDir.path, "skills");
|
|
3651
4082
|
actions.push({
|
|
3652
4083
|
type: "create_directory",
|
|
3653
4084
|
path: skillsDir
|
|
3654
4085
|
});
|
|
3655
4086
|
try {
|
|
3656
4087
|
const skill = loadSkill("ui-consistency-enforcer");
|
|
3657
|
-
const skillDir =
|
|
4088
|
+
const skillDir = join12(skillsDir, skill.name);
|
|
3658
4089
|
actions.push({
|
|
3659
4090
|
type: "create_directory",
|
|
3660
4091
|
path: skillDir
|
|
3661
4092
|
});
|
|
3662
4093
|
for (const file of skill.files) {
|
|
3663
|
-
const filePath =
|
|
3664
|
-
const fileDir =
|
|
4094
|
+
const filePath = join12(skillDir, file.relativePath);
|
|
4095
|
+
const fileDir = join12(
|
|
3665
4096
|
skillDir,
|
|
3666
4097
|
file.relativePath.split("/").slice(0, -1).join("/")
|
|
3667
4098
|
);
|
|
@@ -3718,7 +4149,7 @@ var skillInstaller = {
|
|
|
3718
4149
|
};
|
|
3719
4150
|
|
|
3720
4151
|
// src/commands/install/installers/eslint.ts
|
|
3721
|
-
import { join as
|
|
4152
|
+
import { join as join13 } from "path";
|
|
3722
4153
|
import { ruleRegistry as ruleRegistry2, getRulesByCategory as getRulesByCategory2 } from "uilint-eslint";
|
|
3723
4154
|
function getUpgradeInfo(configuredRuleIds) {
|
|
3724
4155
|
const configuredSet = new Set(configuredRuleIds);
|
|
@@ -3894,7 +4325,7 @@ var eslintInstaller = {
|
|
|
3894
4325
|
for (const target of targets) {
|
|
3895
4326
|
const pkgInfo = project.packages.find((p) => p.path === target.path);
|
|
3896
4327
|
if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
|
|
3897
|
-
const rulesDir =
|
|
4328
|
+
const rulesDir = join13(target.path, ".uilint", "rules");
|
|
3898
4329
|
actions.push({
|
|
3899
4330
|
type: "create_directory",
|
|
3900
4331
|
path: rulesDir
|
|
@@ -3912,7 +4343,7 @@ var eslintInstaller = {
|
|
|
3912
4343
|
hasExistingRules: pkgInfo.hasUilintRules
|
|
3913
4344
|
});
|
|
3914
4345
|
}
|
|
3915
|
-
const gitignorePath =
|
|
4346
|
+
const gitignorePath = join13(project.workspaceRoot, ".gitignore");
|
|
3916
4347
|
actions.push({
|
|
3917
4348
|
type: "append_to_file",
|
|
3918
4349
|
path: gitignorePath,
|
|
@@ -3946,303 +4377,6 @@ var eslintInstaller = {
|
|
|
3946
4377
|
}
|
|
3947
4378
|
};
|
|
3948
4379
|
|
|
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
|
-
|
|
4086
|
-
// src/commands/install/installers/next-overlay.ts
|
|
4087
|
-
var nextOverlayInstaller = {
|
|
4088
|
-
id: "next",
|
|
4089
|
-
name: "Next.js overlay",
|
|
4090
|
-
description: "Alt+Click UI inspector for Next.js App Router",
|
|
4091
|
-
icon: "\u{1F537}",
|
|
4092
|
-
isApplicable(project) {
|
|
4093
|
-
return project.nextApps.length > 0;
|
|
4094
|
-
},
|
|
4095
|
-
getTargets(project) {
|
|
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;
|
|
4176
|
-
},
|
|
4177
|
-
plan(targets, config, project) {
|
|
4178
|
-
const actions = [];
|
|
4179
|
-
const dependencies = [];
|
|
4180
|
-
if (targets.length === 0) return { actions, dependencies };
|
|
4181
|
-
const target = targets[0];
|
|
4182
|
-
const appInfo = project.nextApps.find(
|
|
4183
|
-
(app) => app.projectPath === target.path
|
|
4184
|
-
);
|
|
4185
|
-
if (!appInfo) return { actions, dependencies };
|
|
4186
|
-
const { projectPath, detection } = appInfo;
|
|
4187
|
-
actions.push({
|
|
4188
|
-
type: "install_next_routes",
|
|
4189
|
-
projectPath,
|
|
4190
|
-
appRoot: detection.appRoot
|
|
4191
|
-
});
|
|
4192
|
-
dependencies.push({
|
|
4193
|
-
packagePath: projectPath,
|
|
4194
|
-
packageManager: project.packageManager,
|
|
4195
|
-
packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
|
|
4196
|
-
});
|
|
4197
|
-
actions.push({
|
|
4198
|
-
type: "inject_react",
|
|
4199
|
-
projectPath,
|
|
4200
|
-
appRoot: detection.appRoot,
|
|
4201
|
-
mode: "next",
|
|
4202
|
-
targetFile: target.targetFile,
|
|
4203
|
-
createProviders: target.createProviders
|
|
4204
|
-
});
|
|
4205
|
-
actions.push({
|
|
4206
|
-
type: "inject_next_config",
|
|
4207
|
-
projectPath
|
|
4208
|
-
});
|
|
4209
|
-
return { actions, dependencies };
|
|
4210
|
-
},
|
|
4211
|
-
async *execute(targets, config, project) {
|
|
4212
|
-
if (targets.length === 0) return;
|
|
4213
|
-
const target = targets[0];
|
|
4214
|
-
yield {
|
|
4215
|
-
type: "start",
|
|
4216
|
-
message: "Installing Next.js overlay"
|
|
4217
|
-
};
|
|
4218
|
-
yield {
|
|
4219
|
-
type: "progress",
|
|
4220
|
-
message: `Installing in ${target.label}`,
|
|
4221
|
-
detail: "\u2192 Adding API routes"
|
|
4222
|
-
};
|
|
4223
|
-
yield {
|
|
4224
|
-
type: "progress",
|
|
4225
|
-
message: "Installing dependencies",
|
|
4226
|
-
detail: "\u2192 uilint-react, uilint-core, jsx-loc-plugin"
|
|
4227
|
-
};
|
|
4228
|
-
const injectDetail = target.createProviders ? "\u2192 Creating providers.tsx" : target.targetFile ? `\u2192 ${target.hint || "client component"}` : "\u2192 <uilint-devtools /> in root layout";
|
|
4229
|
-
yield {
|
|
4230
|
-
type: "progress",
|
|
4231
|
-
message: "Injecting devtools component",
|
|
4232
|
-
detail: injectDetail
|
|
4233
|
-
};
|
|
4234
|
-
yield {
|
|
4235
|
-
type: "progress",
|
|
4236
|
-
message: "Configuring jsx-loc-plugin",
|
|
4237
|
-
detail: "\u2192 next.config.js"
|
|
4238
|
-
};
|
|
4239
|
-
yield {
|
|
4240
|
-
type: "complete",
|
|
4241
|
-
message: "Next.js overlay installed"
|
|
4242
|
-
};
|
|
4243
|
-
}
|
|
4244
|
-
};
|
|
4245
|
-
|
|
4246
4380
|
// src/commands/install/installers/vite-overlay.ts
|
|
4247
4381
|
var viteOverlayInstaller = {
|
|
4248
4382
|
id: "vite",
|
|
@@ -4329,8 +4463,8 @@ registerInstaller(nextOverlayInstaller);
|
|
|
4329
4463
|
registerInstaller(viteOverlayInstaller);
|
|
4330
4464
|
|
|
4331
4465
|
// src/commands/install-ui.tsx
|
|
4332
|
-
import { jsx as
|
|
4333
|
-
function selectionsToUserChoices(selections, project, eslintRules) {
|
|
4466
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
4467
|
+
function selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig) {
|
|
4334
4468
|
const items = [];
|
|
4335
4469
|
const choices = { items };
|
|
4336
4470
|
for (const selection of selections) {
|
|
@@ -4361,7 +4495,10 @@ function selectionsToUserChoices(selections, project, eslintRules) {
|
|
|
4361
4495
|
if (appInfo) {
|
|
4362
4496
|
choices.next = {
|
|
4363
4497
|
projectPath: appInfo.projectPath,
|
|
4364
|
-
detection: appInfo.detection
|
|
4498
|
+
detection: appInfo.detection,
|
|
4499
|
+
// Use injection point from follow-up UI selection
|
|
4500
|
+
targetFile: injectionPointConfig?.targetFile,
|
|
4501
|
+
createProviders: injectionPointConfig?.createProviders
|
|
4365
4502
|
};
|
|
4366
4503
|
}
|
|
4367
4504
|
} else if (installer.id === "vite") {
|
|
@@ -4392,18 +4529,18 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4392
4529
|
}
|
|
4393
4530
|
const projectPromise = analyze(projectPath);
|
|
4394
4531
|
const { waitUntilExit } = render(
|
|
4395
|
-
/* @__PURE__ */
|
|
4532
|
+
/* @__PURE__ */ jsx7(
|
|
4396
4533
|
InstallApp,
|
|
4397
4534
|
{
|
|
4398
4535
|
projectPromise,
|
|
4399
|
-
onComplete: async (selections, eslintRules) => {
|
|
4536
|
+
onComplete: async (selections, eslintRules, injectionPointConfig) => {
|
|
4400
4537
|
const project = await projectPromise;
|
|
4401
|
-
const choices = selectionsToUserChoices(selections, project, eslintRules);
|
|
4538
|
+
const choices = selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig);
|
|
4402
4539
|
if (choices.items.length === 0) {
|
|
4403
4540
|
console.log("\nNo items selected for installation");
|
|
4404
4541
|
process.exit(0);
|
|
4405
4542
|
}
|
|
4406
|
-
const { createPlan } = await import("./plan-
|
|
4543
|
+
const { createPlan } = await import("./plan-SIXVCXCK.js");
|
|
4407
4544
|
const plan = createPlan(project, choices, { force: options.force });
|
|
4408
4545
|
const result = await execute(plan, {
|
|
4409
4546
|
...executeOptions,
|
|
@@ -4428,4 +4565,4 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
4428
4565
|
export {
|
|
4429
4566
|
installUI
|
|
4430
4567
|
};
|
|
4431
|
-
//# sourceMappingURL=install-ui-
|
|
4568
|
+
//# sourceMappingURL=install-ui-FE5AA75P.js.map
|