react-apps-ui 1.0.3 → 1.0.5

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.
Files changed (3) hide show
  1. package/dist/index.js +88 -8
  2. package/package.json +1 -1
  3. package/src/index.ts +149 -12
package/dist/index.js CHANGED
@@ -61,11 +61,10 @@ program
61
61
  const hasSrcDir = fs_1.default.existsSync(path_1.default.join(process.cwd(), "src"));
62
62
  // 4. Loop through the files and write them smartly
63
63
  for (const file of themeComponent.files) {
64
- // DEFENSIVE FALLBACK: If 'target' is missing from the JSON, use the file.name
65
- // This ensures path.join() never receives 'undefined' again.
66
- const safeTarget = file.target || file.name;
64
+ // A. Force the target to be a string so it can never be undefined
65
+ const safeTarget = String(file.target || file.name || "unknown-file");
67
66
  let finalTargetPath = path_1.default.join(process.cwd(), safeTarget);
68
- // SMART ROUTING: If they use 'src/' and it's not a root config file, put it inside src/
67
+ //B. Safely check includes on the guaranteed string
69
68
  const isRootConfig = safeTarget.includes("tailwind") ||
70
69
  safeTarget.endsWith(".config.js") ||
71
70
  safeTarget.endsWith(".json");
@@ -81,10 +80,17 @@ program
81
80
  if (file.type === "css" ||
82
81
  finalTargetPath.endsWith(".css")) {
83
82
  console.log(chalk_1.default.yellow(` ⚠️ ${file.target} already exists. Appending theme variables safely...`));
84
- const existingContent = fs_1.default.readFileSync(finalTargetPath, "utf-8");
85
- // Only append if it doesn't already contain our theme variables to prevent infinite duplication
86
- if (!existingContent.includes("--background:")) {
87
- fs_1.default.appendFileSync(finalTargetPath, `\n/* React Apps UI Theme */\n${file.content}`, "utf-8");
83
+ try {
84
+ // C. Read the file and forcefully convert it to a String
85
+ const rawContent = fs_1.default.readFileSync(finalTargetPath, "utf-8");
86
+ const safeExistingContent = String(rawContent || "");
87
+ // D. Safely check includes (This line can no longer crash!)
88
+ if (!safeExistingContent.includes("--background:")) {
89
+ fs_1.default.appendFileSync(finalTargetPath, `\n/* React Apps UI Theme */\n${file.content || ""}`, "utf-8");
90
+ }
91
+ }
92
+ catch (err) {
93
+ console.log(chalk_1.default.red(` ❌ Error reading or appending to ${safeTarget}`));
88
94
  }
89
95
  }
90
96
  else {
@@ -105,4 +111,78 @@ program
105
111
  console.error(error);
106
112
  }
107
113
  });
114
+ // --- THE ADD COMMAND ---
115
+ program
116
+ .command("add [components...]")
117
+ .description("Add components to your project")
118
+ .action(async (components) => {
119
+ if (!components || components.length === 0) {
120
+ console.log(chalk_1.default.red("Please specify at least one component to add. (e.g., npx react-apps-ui add action-button)"));
121
+ return;
122
+ }
123
+ // 1. SMART AUTO-DETECT: Check if they are using NativeWind by looking for the preset or config
124
+ const hasTailwind = fs_1.default.existsSync(path_1.default.join(process.cwd(), "tailwind.config.js")) ||
125
+ fs_1.default.existsSync(path_1.default.join(process.cwd(), "theme/tailwind-preset.js")) ||
126
+ fs_1.default.existsSync(path_1.default.join(process.cwd(), "src/theme/tailwind-preset.js"));
127
+ const engine = hasTailwind ? "mobile-nativewind" : "mobile-stylesheet";
128
+ console.log(chalk_1.default.dim(`\nDetected engine: ${engine}`));
129
+ const manifestUrl = `${REGISTRY_URL}/${engine}.json`;
130
+ try {
131
+ // 2. Fetch the massive JSON database
132
+ const res = await fetch(manifestUrl);
133
+ if (!res.ok)
134
+ throw new Error(`Failed to fetch registry: ${res.statusText}`);
135
+ const registry = await res.json();
136
+ // 3. RECURSIVE DEPENDENCY RESOLUTION
137
+ // If action-button needs wave-dots-loader, this automatically grabs it!
138
+ const componentsToAdd = new Set();
139
+ const resolveDependencies = (compName) => {
140
+ if (componentsToAdd.has(compName))
141
+ return; // Prevent infinite loops
142
+ componentsToAdd.add(compName);
143
+ const compData = registry.find((c) => c.name === compName);
144
+ if (compData && compData.registryDependencies) {
145
+ compData.registryDependencies.forEach(resolveDependencies);
146
+ }
147
+ };
148
+ // Run the resolver for every component the user typed in the terminal
149
+ components.forEach(resolveDependencies);
150
+ // We don't need to reinstall the theme if it was pulled as a dependency
151
+ componentsToAdd.delete("theme");
152
+ console.log(chalk_1.default.blue(`\nInstalling: ${Array.from(componentsToAdd).join(", ")}...`));
153
+ const hasSrcDir = fs_1.default.existsSync(path_1.default.join(process.cwd(), "src"));
154
+ // 4. WRITE THE FILES
155
+ for (const compName of componentsToAdd) {
156
+ const compData = registry.find((c) => c.name === compName);
157
+ if (!compData) {
158
+ console.log(chalk_1.default.red(` ❌ Component '${compName}' not found in registry.`));
159
+ continue;
160
+ }
161
+ for (const file of compData.files) {
162
+ const safeTarget = String(file.target || file.name || "unknown-file");
163
+ let finalTargetPath = path_1.default.join(process.cwd(), safeTarget);
164
+ if (hasSrcDir) {
165
+ finalTargetPath = path_1.default.join(process.cwd(), "src", safeTarget);
166
+ }
167
+ // Ensure the folder exists
168
+ fs_1.default.mkdirSync(path_1.default.dirname(finalTargetPath), {
169
+ recursive: true,
170
+ });
171
+ // Safe Write
172
+ if (!fs_1.default.existsSync(finalTargetPath)) {
173
+ fs_1.default.writeFileSync(finalTargetPath, file.content || "", "utf-8");
174
+ console.log(chalk_1.default.green(` Created ${safeTarget}`));
175
+ }
176
+ else {
177
+ console.log(chalk_1.default.yellow(` ⚠️ ${safeTarget} already exists. Skipping.`));
178
+ }
179
+ }
180
+ }
181
+ console.log(chalk_1.default.blue("\n🎉 Components installed successfully!"));
182
+ }
183
+ catch (error) {
184
+ console.log(chalk_1.default.red(`\nFailed to add components:`));
185
+ console.error(error);
186
+ }
187
+ });
108
188
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-apps-ui",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "The React Native UI CLI",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -75,16 +75,18 @@ program
75
75
 
76
76
  // 4. Loop through the files and write them smartly
77
77
  for (const file of themeComponent.files) {
78
- // DEFENSIVE FALLBACK: If 'target' is missing from the JSON, use the file.name
79
- // This ensures path.join() never receives 'undefined' again.
80
- const safeTarget = file.target || file.name;
78
+ // A. Force the target to be a string so it can never be undefined
79
+ const safeTarget = String(
80
+ file.target || file.name || "unknown-file",
81
+ );
81
82
  let finalTargetPath = path.join(process.cwd(), safeTarget);
82
83
 
83
- // SMART ROUTING: If they use 'src/' and it's not a root config file, put it inside src/
84
+ //B. Safely check includes on the guaranteed string
84
85
  const isRootConfig =
85
86
  safeTarget.includes("tailwind") ||
86
87
  safeTarget.endsWith(".config.js") ||
87
88
  safeTarget.endsWith(".json");
89
+
88
90
  if (hasSrcDir && !isRootConfig) {
89
91
  finalTargetPath = path.join(
90
92
  process.cwd(),
@@ -109,18 +111,33 @@ program
109
111
  ` ⚠️ ${file.target} already exists. Appending theme variables safely...`,
110
112
  ),
111
113
  );
112
- const existingContent = fs.readFileSync(
113
- finalTargetPath,
114
- "utf-8",
115
- );
116
114
 
117
- // Only append if it doesn't already contain our theme variables to prevent infinite duplication
118
- if (!existingContent.includes("--background:")) {
119
- fs.appendFileSync(
115
+ try {
116
+ // C. Read the file and forcefully convert it to a String
117
+ const rawContent = fs.readFileSync(
120
118
  finalTargetPath,
121
- `\n/* React Apps UI Theme */\n${file.content}`,
122
119
  "utf-8",
123
120
  );
121
+ const safeExistingContent = String(
122
+ rawContent || "",
123
+ );
124
+
125
+ // D. Safely check includes (This line can no longer crash!)
126
+ if (
127
+ !safeExistingContent.includes("--background:")
128
+ ) {
129
+ fs.appendFileSync(
130
+ finalTargetPath,
131
+ `\n/* React Apps UI Theme */\n${file.content || ""}`,
132
+ "utf-8",
133
+ );
134
+ }
135
+ } catch (err) {
136
+ console.log(
137
+ chalk.red(
138
+ ` ❌ Error reading or appending to ${safeTarget}`,
139
+ ),
140
+ );
124
141
  }
125
142
  } else {
126
143
  console.log(
@@ -148,4 +165,124 @@ program
148
165
  }
149
166
  });
150
167
 
168
+ // --- THE ADD COMMAND ---
169
+ program
170
+ .command("add [components...]")
171
+ .description("Add components to your project")
172
+ .action(async (components: string[]) => {
173
+ if (!components || components.length === 0) {
174
+ console.log(
175
+ chalk.red(
176
+ "Please specify at least one component to add. (e.g., npx react-apps-ui add action-button)",
177
+ ),
178
+ );
179
+ return;
180
+ }
181
+
182
+ // 1. SMART AUTO-DETECT: Check if they are using NativeWind by looking for the preset or config
183
+ const hasTailwind =
184
+ fs.existsSync(path.join(process.cwd(), "tailwind.config.js")) ||
185
+ fs.existsSync(
186
+ path.join(process.cwd(), "theme/tailwind-preset.js"),
187
+ ) ||
188
+ fs.existsSync(
189
+ path.join(process.cwd(), "src/theme/tailwind-preset.js"),
190
+ );
191
+
192
+ const engine = hasTailwind ? "mobile-nativewind" : "mobile-stylesheet";
193
+
194
+ console.log(chalk.dim(`\nDetected engine: ${engine}`));
195
+ const manifestUrl = `${REGISTRY_URL}/${engine}.json`;
196
+
197
+ try {
198
+ // 2. Fetch the massive JSON database
199
+ const res = await fetch(manifestUrl);
200
+ if (!res.ok)
201
+ throw new Error(`Failed to fetch registry: ${res.statusText}`);
202
+ const registry = await res.json();
203
+
204
+ // 3. RECURSIVE DEPENDENCY RESOLUTION
205
+ // If action-button needs wave-dots-loader, this automatically grabs it!
206
+ const componentsToAdd = new Set<string>();
207
+
208
+ const resolveDependencies = (compName: string) => {
209
+ if (componentsToAdd.has(compName)) return; // Prevent infinite loops
210
+ componentsToAdd.add(compName);
211
+
212
+ const compData = registry.find((c: any) => c.name === compName);
213
+ if (compData && compData.registryDependencies) {
214
+ compData.registryDependencies.forEach(resolveDependencies);
215
+ }
216
+ };
217
+
218
+ // Run the resolver for every component the user typed in the terminal
219
+ components.forEach(resolveDependencies);
220
+
221
+ // We don't need to reinstall the theme if it was pulled as a dependency
222
+ componentsToAdd.delete("theme");
223
+
224
+ console.log(
225
+ chalk.blue(
226
+ `\nInstalling: ${Array.from(componentsToAdd).join(", ")}...`,
227
+ ),
228
+ );
229
+
230
+ const hasSrcDir = fs.existsSync(path.join(process.cwd(), "src"));
231
+
232
+ // 4. WRITE THE FILES
233
+ for (const compName of componentsToAdd) {
234
+ const compData = registry.find((c: any) => c.name === compName);
235
+ if (!compData) {
236
+ console.log(
237
+ chalk.red(
238
+ ` ❌ Component '${compName}' not found in registry.`,
239
+ ),
240
+ );
241
+ continue;
242
+ }
243
+
244
+ for (const file of compData.files) {
245
+ const safeTarget = String(
246
+ file.target || file.name || "unknown-file",
247
+ );
248
+ let finalTargetPath = path.join(process.cwd(), safeTarget);
249
+
250
+ if (hasSrcDir) {
251
+ finalTargetPath = path.join(
252
+ process.cwd(),
253
+ "src",
254
+ safeTarget,
255
+ );
256
+ }
257
+
258
+ // Ensure the folder exists
259
+ fs.mkdirSync(path.dirname(finalTargetPath), {
260
+ recursive: true,
261
+ });
262
+
263
+ // Safe Write
264
+ if (!fs.existsSync(finalTargetPath)) {
265
+ fs.writeFileSync(
266
+ finalTargetPath,
267
+ file.content || "",
268
+ "utf-8",
269
+ );
270
+ console.log(chalk.green(` Created ${safeTarget}`));
271
+ } else {
272
+ console.log(
273
+ chalk.yellow(
274
+ ` ⚠️ ${safeTarget} already exists. Skipping.`,
275
+ ),
276
+ );
277
+ }
278
+ }
279
+ }
280
+
281
+ console.log(chalk.blue("\n🎉 Components installed successfully!"));
282
+ } catch (error) {
283
+ console.log(chalk.red(`\nFailed to add components:`));
284
+ console.error(error);
285
+ }
286
+ });
287
+
151
288
  program.parse();