uilint 0.2.14 → 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/index.js CHANGED
@@ -2805,7 +2805,7 @@ program.command("update").description("Update existing style guide with new styl
2805
2805
  });
2806
2806
  });
2807
2807
  program.command("install").description("Install UILint integration").option("--force", "Overwrite existing configuration files").action(async (options) => {
2808
- const { installUI } = await import("./install-ui-H2KOQ6SP.js");
2808
+ const { installUI } = await import("./install-ui-73AINMZ5.js");
2809
2809
  await installUI({ force: options.force });
2810
2810
  });
2811
2811
  program.command("serve").description("Start WebSocket server for real-time UI linting").option("-p, --port <number>", "Port to listen on", "9234").action(async (options) => {
@@ -1698,11 +1698,11 @@ import {
1698
1698
  unlinkSync,
1699
1699
  chmodSync
1700
1700
  } from "fs";
1701
- import { dirname as dirname3 } from "path";
1701
+ import { dirname as dirname4 } from "path";
1702
1702
 
1703
1703
  // src/utils/react-inject.ts
1704
1704
  import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
1705
- import { join as join5 } from "path";
1705
+ import { join as join5, relative as relative3 } from "path";
1706
1706
  import { parseModule as parseModule2, generateCode as generateCode2 } from "magicast";
1707
1707
  function getDefaultCandidates(projectPath, appRoot) {
1708
1708
  const viteMainCandidates = [
@@ -1759,6 +1759,32 @@ function walkAst(node, visit) {
1759
1759
  }
1760
1760
  }
1761
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
+ }
1762
1788
  function hasUILintDevtoolsJsx(program) {
1763
1789
  let found = false;
1764
1790
  walkAst(program, (node) => {
@@ -1852,7 +1878,204 @@ function ensureSideEffectImport(program, from) {
1852
1878
  program.body.splice(insertAt, 0, importDecl);
1853
1879
  return { changed: true };
1854
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
+ }
1855
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
+ }
1856
2079
  const candidates = getDefaultCandidates(opts.projectPath, opts.appRoot);
1857
2080
  if (!candidates.length) {
1858
2081
  throw new Error(
@@ -2742,7 +2965,7 @@ async function installNextUILintRoutes(opts) {
2742
2965
  // src/utils/prettier.ts
2743
2966
  import { existsSync as existsSync9 } from "fs";
2744
2967
  import { spawn } from "child_process";
2745
- import { join as join9, dirname as dirname2 } from "path";
2968
+ import { join as join9, dirname as dirname3 } from "path";
2746
2969
  function getPrettierPath(projectPath) {
2747
2970
  const localPath = join9(projectPath, "node_modules", ".bin", "prettier");
2748
2971
  if (existsSync9(localPath)) return localPath;
@@ -2750,7 +2973,7 @@ function getPrettierPath(projectPath) {
2750
2973
  for (let i = 0; i < 10; i++) {
2751
2974
  const binPath = join9(dir, "node_modules", ".bin", "prettier");
2752
2975
  if (existsSync9(binPath)) return binPath;
2753
- const parent = dirname2(dir);
2976
+ const parent = dirname3(dir);
2754
2977
  if (parent === dir) break;
2755
2978
  dir = parent;
2756
2979
  }
@@ -2904,7 +3127,7 @@ async function executeAction(action, options) {
2904
3127
  wouldDo: `Create file: ${action.path}${action.permissions ? ` (mode: ${action.permissions.toString(8)})` : ""}`
2905
3128
  };
2906
3129
  }
2907
- const dir = dirname3(action.path);
3130
+ const dir = dirname4(action.path);
2908
3131
  if (!existsSync10(dir)) {
2909
3132
  mkdirSync(dir, { recursive: true });
2910
3133
  }
@@ -2930,7 +3153,7 @@ async function executeAction(action, options) {
2930
3153
  }
2931
3154
  }
2932
3155
  const merged = deepMerge(existing, action.merge);
2933
- const dir = dirname3(action.path);
3156
+ const dir = dirname4(action.path);
2934
3157
  if (!existsSync10(dir)) {
2935
3158
  mkdirSync(dir, { recursive: true });
2936
3159
  }
@@ -3024,11 +3247,12 @@ async function executeInjectEslint(action, options) {
3024
3247
  }
3025
3248
  async function executeInjectReact(action, options) {
3026
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}`;
3027
3251
  if (dryRun) {
3028
3252
  return {
3029
3253
  action,
3030
3254
  success: true,
3031
- wouldDo: `Inject <uilint-devtools /> into React app: ${action.projectPath}`
3255
+ wouldDo: dryRunDescription
3032
3256
  };
3033
3257
  }
3034
3258
  const result = await installReactUILintOverlay({
@@ -3036,7 +3260,10 @@ async function executeInjectReact(action, options) {
3036
3260
  appRoot: action.appRoot,
3037
3261
  mode: action.mode,
3038
3262
  force: false,
3039
- // Auto-select first choice for execute phase
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)
3040
3267
  confirmFileChoice: async (choices) => choices[0]
3041
3268
  });
3042
3269
  const success = result.modified || result.alreadyConfigured === true;
@@ -3719,6 +3946,143 @@ var eslintInstaller = {
3719
3946
  }
3720
3947
  };
3721
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
+
3722
4086
  // src/commands/install/installers/next-overlay.ts
3723
4087
  var nextOverlayInstaller = {
3724
4088
  id: "next",
@@ -3729,21 +4093,95 @@ var nextOverlayInstaller = {
3729
4093
  return project.nextApps.length > 0;
3730
4094
  },
3731
4095
  getTargets(project) {
3732
- return project.nextApps.map((app) => ({
3733
- id: `next-${app.projectPath}`,
3734
- label: app.projectPath.split("/").pop() || app.projectPath,
3735
- path: app.projectPath,
3736
- hint: "App Router",
3737
- isInstalled: false
3738
- // TODO: Detect if already installed
3739
- }));
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;
3740
4176
  },
3741
4177
  plan(targets, config, project) {
3742
4178
  const actions = [];
3743
4179
  const dependencies = [];
3744
4180
  if (targets.length === 0) return { actions, dependencies };
3745
4181
  const target = targets[0];
3746
- const appInfo = project.nextApps.find((app) => app.projectPath === target.path);
4182
+ const appInfo = project.nextApps.find(
4183
+ (app) => app.projectPath === target.path
4184
+ );
3747
4185
  if (!appInfo) return { actions, dependencies };
3748
4186
  const { projectPath, detection } = appInfo;
3749
4187
  actions.push({
@@ -3760,7 +4198,9 @@ var nextOverlayInstaller = {
3760
4198
  type: "inject_react",
3761
4199
  projectPath,
3762
4200
  appRoot: detection.appRoot,
3763
- mode: "next"
4201
+ mode: "next",
4202
+ targetFile: target.targetFile,
4203
+ createProviders: target.createProviders
3764
4204
  });
3765
4205
  actions.push({
3766
4206
  type: "inject_next_config",
@@ -3785,10 +4225,11 @@ var nextOverlayInstaller = {
3785
4225
  message: "Installing dependencies",
3786
4226
  detail: "\u2192 uilint-react, uilint-core, jsx-loc-plugin"
3787
4227
  };
4228
+ const injectDetail = target.createProviders ? "\u2192 Creating providers.tsx" : target.targetFile ? `\u2192 ${target.hint || "client component"}` : "\u2192 <uilint-devtools /> in root layout";
3788
4229
  yield {
3789
4230
  type: "progress",
3790
4231
  message: "Injecting devtools component",
3791
- detail: "\u2192 <uilint-devtools /> in root layout"
4232
+ detail: injectDetail
3792
4233
  };
3793
4234
  yield {
3794
4235
  type: "progress",
@@ -3962,7 +4403,7 @@ async function installUI(options = {}, executeOptions = {}) {
3962
4403
  console.log("\nNo items selected for installation");
3963
4404
  process.exit(0);
3964
4405
  }
3965
- const { createPlan } = await import("./plan-G43256ML.js");
4406
+ const { createPlan } = await import("./plan-VPDTICSY.js");
3966
4407
  const plan = createPlan(project, choices, { force: options.force });
3967
4408
  const result = await execute(plan, {
3968
4409
  ...executeOptions,
@@ -3987,4 +4428,4 @@ async function installUI(options = {}, executeOptions = {}) {
3987
4428
  export {
3988
4429
  installUI
3989
4430
  };
3990
- //# sourceMappingURL=install-ui-H2KOQ6SP.js.map
4431
+ //# sourceMappingURL=install-ui-73AINMZ5.js.map