react-apps-ui 1.0.10 → 1.0.12
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/README.md +7 -7
- package/dist/index.js +73 -41
- package/package.json +1 -1
- package/src/index.ts +125 -73
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ Get up and running in a fresh project in seconds.
|
|
|
44
44
|
|
|
45
45
|
Run the `init` command at the root of your project. This interactive command lets you select your target platform (Web or Mobile) and styling engine, creates your base theme, and sets up your utility files.
|
|
46
46
|
|
|
47
|
-
```
|
|
47
|
+
```sh
|
|
48
48
|
npx react-apps-ui@latest init
|
|
49
49
|
```
|
|
50
50
|
|
|
@@ -52,7 +52,7 @@ npx react-apps-ui@latest init
|
|
|
52
52
|
|
|
53
53
|
Use the `add` command to pull a component into your project. The CLI will automatically fetch the exact code needed for your specific engine, grab any sub-dependencies, and install necessary NPM packages.
|
|
54
54
|
|
|
55
|
-
```
|
|
55
|
+
```sh
|
|
56
56
|
npx react-apps-ui@latest add action-button
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -64,11 +64,11 @@ The code is now yours! You will find it beautifully organized in your project fo
|
|
|
64
64
|
import { ActionButton } from "@/components/ui/action-button";
|
|
65
65
|
|
|
66
66
|
export default function App() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
return (
|
|
68
|
+
<ActionButton onPress={() => console.log("Pressed!")}>
|
|
69
|
+
Click Me
|
|
70
|
+
</ActionButton>
|
|
71
|
+
);
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,22 @@ program
|
|
|
18
18
|
.description("Add high-quality components to your React Native app")
|
|
19
19
|
.version("1.0.0");
|
|
20
20
|
// --- THE INIT COMMAND ---
|
|
21
|
+
// 1. Define the foundational architecture for each engine
|
|
22
|
+
const BASE_DEPS = {
|
|
23
|
+
"mobile-nativewind": {
|
|
24
|
+
npm: [
|
|
25
|
+
"class-variance-authority",
|
|
26
|
+
"tailwind-merge",
|
|
27
|
+
"clsx",
|
|
28
|
+
"lucide-react-native", // Added for the IconName type
|
|
29
|
+
],
|
|
30
|
+
registry: ["theme", "utils"], // Pulls your cn() utility and theme
|
|
31
|
+
},
|
|
32
|
+
"mobile-stylesheet": {
|
|
33
|
+
npm: ["lucide-react-native"], // Added for the IconName type
|
|
34
|
+
registry: ["theme", "utils"],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
21
37
|
program
|
|
22
38
|
.command("init")
|
|
23
39
|
.description("Configure your Expo project and install the ThemeProvider")
|
|
@@ -43,6 +59,8 @@ program
|
|
|
43
59
|
console.log(chalk_1.default.red("Initialization cancelled."));
|
|
44
60
|
return;
|
|
45
61
|
}
|
|
62
|
+
const engineKey = response.engine;
|
|
63
|
+
const setup = BASE_DEPS[engineKey];
|
|
46
64
|
// 2. USE THE REGISTRY_URL to fetch the correct JSON manifest
|
|
47
65
|
const manifestUrl = `${REGISTRY_URL}/${response.engine}.json`;
|
|
48
66
|
console.log(chalk_1.default.dim(`\nFetching registry from GitHub...`));
|
|
@@ -51,57 +69,71 @@ program
|
|
|
51
69
|
if (!res.ok)
|
|
52
70
|
throw new Error(`Failed to fetch registry: ${res.statusText}`);
|
|
53
71
|
const registry = await res.json();
|
|
54
|
-
// 3. Find the "theme" component inside the giant JSON
|
|
55
|
-
const themeComponent = registry.find((item) => item.name === "theme");
|
|
56
|
-
if (!themeComponent) {
|
|
57
|
-
console.log(chalk_1.default.red("Error: Could not find 'theme' in the registry."));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
console.log(chalk_1.default.green(`✓ Found theme provider. Installing...`));
|
|
61
72
|
// Check if the user's project uses a 'src' directory
|
|
62
73
|
const hasSrcDir = fs_1.default.existsSync(path_1.default.join(process.cwd(), "src"));
|
|
63
|
-
//
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const isRootConfig = safeTarget.includes("tailwind") ||
|
|
70
|
-
safeTarget.endsWith(".config.js") ||
|
|
71
|
-
safeTarget.endsWith(".json");
|
|
72
|
-
if (hasSrcDir && !isRootConfig) {
|
|
73
|
-
finalTargetPath = path_1.default.join(process.cwd(), "src", safeTarget);
|
|
74
|
+
// 3. Loop through the required BASE REGISTRY components (theme, utils, etc.)
|
|
75
|
+
for (const compName of setup.registry) {
|
|
76
|
+
const componentData = registry.find((item) => item.name === compName);
|
|
77
|
+
if (!componentData) {
|
|
78
|
+
console.log(chalk_1.default.red(`Error: Could not find '${compName}' in the registry.`));
|
|
79
|
+
continue;
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
console.log(chalk_1.default.green(`✓ Found ${compName}. Installing...`));
|
|
82
|
+
// 4. Loop through the files and write them smartly
|
|
83
|
+
for (const file of componentData.files) {
|
|
84
|
+
// A. Force the target to be a string so it can never be undefined
|
|
85
|
+
const safeTarget = String(file.target || file.name || "unknown-file");
|
|
86
|
+
let finalTargetPath = path_1.default.join(process.cwd(), safeTarget);
|
|
87
|
+
//B. Safely check includes on the guaranteed string
|
|
88
|
+
const isRootConfig = safeTarget.includes("tailwind") ||
|
|
89
|
+
safeTarget.endsWith(".config.js") ||
|
|
90
|
+
safeTarget.endsWith(".json");
|
|
91
|
+
if (hasSrcDir && !isRootConfig) {
|
|
92
|
+
finalTargetPath = path_1.default.join(process.cwd(), "src", safeTarget);
|
|
93
|
+
}
|
|
94
|
+
// Ensure the folder exists
|
|
95
|
+
fs_1.default.mkdirSync(path_1.default.dirname(finalTargetPath), {
|
|
96
|
+
recursive: true,
|
|
97
|
+
});
|
|
98
|
+
// SAFE WRITE: Check if the file already exists
|
|
99
|
+
if (fs_1.default.existsSync(finalTargetPath)) {
|
|
100
|
+
if (file.type === "css" ||
|
|
101
|
+
finalTargetPath.endsWith(".css")) {
|
|
102
|
+
console.log(chalk_1.default.yellow(` ⚠️ ${file.target} already exists. Appending theme variables safely...`));
|
|
103
|
+
try {
|
|
104
|
+
// C. Read the file and forcefully convert it to a String
|
|
105
|
+
const rawContent = fs_1.default.readFileSync(finalTargetPath, "utf-8");
|
|
106
|
+
const safeExistingContent = String(rawContent || "");
|
|
107
|
+
// D. Safely check includes (This line can no longer crash!)
|
|
108
|
+
if (!safeExistingContent.includes("--background:")) {
|
|
109
|
+
fs_1.default.appendFileSync(finalTargetPath, `\n/* React Apps UI Theme */\n${file.content || ""}`, "utf-8");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.log(chalk_1.default.red(` ❌ Error reading or appending to ${safeTarget}`));
|
|
91
114
|
}
|
|
92
115
|
}
|
|
93
|
-
|
|
94
|
-
console.log(chalk_1.default.
|
|
116
|
+
else {
|
|
117
|
+
console.log(chalk_1.default.yellow(` ⚠️ ${file.target} already exists. Skipping to prevent overwrite.`));
|
|
95
118
|
}
|
|
96
119
|
}
|
|
97
120
|
else {
|
|
98
|
-
|
|
121
|
+
// File doesn't exist, safe to write normally
|
|
122
|
+
fs_1.default.writeFileSync(finalTargetPath, file.content, "utf-8");
|
|
123
|
+
console.log(chalk_1.default.green(` Created ${safeTarget}`));
|
|
99
124
|
}
|
|
100
125
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
126
|
+
}
|
|
127
|
+
// 5. INSTALL FOUNDATIONAL NPM DEPENDENCIES
|
|
128
|
+
if (setup.npm.length > 0) {
|
|
129
|
+
const depList = setup.npm.join(" ");
|
|
130
|
+
console.log(chalk_1.default.blue(`\n📦 Installing foundational NPM dependencies: ${depList}...`));
|
|
131
|
+
try {
|
|
132
|
+
(0, child_process_1.execSync)(`npm install ${depList}`, { stdio: "inherit" });
|
|
133
|
+
console.log(chalk_1.default.green("✓ Dependencies installed successfully."));
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
console.log(chalk_1.default.red("❌ Failed to install dependencies. You may need to install them manually."));
|
|
105
137
|
}
|
|
106
138
|
}
|
|
107
139
|
console.log(chalk_1.default.blue("\n🎉 Initialization complete!"));
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -18,6 +18,24 @@ program
|
|
|
18
18
|
.version("1.0.0");
|
|
19
19
|
|
|
20
20
|
// --- THE INIT COMMAND ---
|
|
21
|
+
|
|
22
|
+
// 1. Define the foundational architecture for each engine
|
|
23
|
+
const BASE_DEPS: Record<string, { npm: string[]; registry: string[] }> = {
|
|
24
|
+
"mobile-nativewind": {
|
|
25
|
+
npm: [
|
|
26
|
+
"class-variance-authority",
|
|
27
|
+
"tailwind-merge",
|
|
28
|
+
"clsx",
|
|
29
|
+
"lucide-react-native", // Added for the IconName type
|
|
30
|
+
],
|
|
31
|
+
registry: ["theme", "utils"], // Pulls your cn() utility and theme
|
|
32
|
+
},
|
|
33
|
+
"mobile-stylesheet": {
|
|
34
|
+
npm: ["lucide-react-native"], // Added for the IconName type
|
|
35
|
+
registry: ["theme", "utils"],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
21
39
|
program
|
|
22
40
|
.command("init")
|
|
23
41
|
.description("Configure your Expo project and install the ThemeProvider")
|
|
@@ -46,6 +64,9 @@ program
|
|
|
46
64
|
return;
|
|
47
65
|
}
|
|
48
66
|
|
|
67
|
+
const engineKey = response.engine as keyof typeof BASE_DEPS;
|
|
68
|
+
const setup = BASE_DEPS[engineKey];
|
|
69
|
+
|
|
49
70
|
// 2. USE THE REGISTRY_URL to fetch the correct JSON manifest
|
|
50
71
|
const manifestUrl = `${REGISTRY_URL}/${response.engine}.json`;
|
|
51
72
|
console.log(chalk.dim(`\nFetching registry from GitHub...`));
|
|
@@ -56,101 +77,132 @@ program
|
|
|
56
77
|
throw new Error(`Failed to fetch registry: ${res.statusText}`);
|
|
57
78
|
|
|
58
79
|
const registry = await res.json();
|
|
59
|
-
|
|
60
|
-
// 3. Find the "theme" component inside the giant JSON
|
|
61
|
-
const themeComponent = registry.find(
|
|
62
|
-
(item: any) => item.name === "theme",
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
if (!themeComponent) {
|
|
66
|
-
console.log(
|
|
67
|
-
chalk.red("Error: Could not find 'theme' in the registry."),
|
|
68
|
-
);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
console.log(chalk.green(`✓ Found theme provider. Installing...`));
|
|
73
|
-
|
|
74
80
|
// Check if the user's project uses a 'src' directory
|
|
75
81
|
const hasSrcDir = fs.existsSync(path.join(process.cwd(), "src"));
|
|
76
82
|
|
|
77
|
-
//
|
|
78
|
-
for (const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
file.target || file.name || "unknown-file",
|
|
83
|
+
// 3. Loop through the required BASE REGISTRY components (theme, utils, etc.)
|
|
84
|
+
for (const compName of setup.registry) {
|
|
85
|
+
const componentData = registry.find(
|
|
86
|
+
(item: any) => item.name === compName,
|
|
82
87
|
);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
safeTarget.endsWith(".json");
|
|
90
|
-
|
|
91
|
-
if (hasSrcDir && !isRootConfig) {
|
|
92
|
-
finalTargetPath = path.join(
|
|
93
|
-
process.cwd(),
|
|
94
|
-
"src",
|
|
95
|
-
safeTarget,
|
|
88
|
+
|
|
89
|
+
if (!componentData) {
|
|
90
|
+
console.log(
|
|
91
|
+
chalk.red(
|
|
92
|
+
`Error: Could not find '${compName}' in the registry.`,
|
|
93
|
+
),
|
|
96
94
|
);
|
|
95
|
+
continue;
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
console.log(chalk.green(`✓ Found ${compName}. Installing...`));
|
|
99
|
+
|
|
100
|
+
// 4. Loop through the files and write them smartly
|
|
101
|
+
for (const file of componentData.files) {
|
|
102
|
+
// A. Force the target to be a string so it can never be undefined
|
|
103
|
+
const safeTarget = String(
|
|
104
|
+
file.target || file.name || "unknown-file",
|
|
105
|
+
);
|
|
106
|
+
let finalTargetPath = path.join(process.cwd(), safeTarget);
|
|
107
|
+
|
|
108
|
+
//B. Safely check includes on the guaranteed string
|
|
109
|
+
const isRootConfig =
|
|
110
|
+
safeTarget.includes("tailwind") ||
|
|
111
|
+
safeTarget.endsWith(".config.js") ||
|
|
112
|
+
safeTarget.endsWith(".json");
|
|
113
|
+
|
|
114
|
+
if (hasSrcDir && !isRootConfig) {
|
|
115
|
+
finalTargetPath = path.join(
|
|
116
|
+
process.cwd(),
|
|
117
|
+
"src",
|
|
118
|
+
safeTarget,
|
|
114
119
|
);
|
|
120
|
+
}
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
// Ensure the folder exists
|
|
123
|
+
fs.mkdirSync(path.dirname(finalTargetPath), {
|
|
124
|
+
recursive: true,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// SAFE WRITE: Check if the file already exists
|
|
128
|
+
if (fs.existsSync(finalTargetPath)) {
|
|
129
|
+
if (
|
|
130
|
+
file.type === "css" ||
|
|
131
|
+
finalTargetPath.endsWith(".css")
|
|
132
|
+
) {
|
|
133
|
+
console.log(
|
|
134
|
+
chalk.yellow(
|
|
135
|
+
` ⚠️ ${file.target} already exists. Appending theme variables safely...`,
|
|
136
|
+
),
|
|
124
137
|
);
|
|
125
138
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
) {
|
|
130
|
-
fs.appendFileSync(
|
|
139
|
+
try {
|
|
140
|
+
// C. Read the file and forcefully convert it to a String
|
|
141
|
+
const rawContent = fs.readFileSync(
|
|
131
142
|
finalTargetPath,
|
|
132
|
-
`\n/* React Apps UI Theme */\n${file.content || ""}`,
|
|
133
143
|
"utf-8",
|
|
134
144
|
);
|
|
145
|
+
const safeExistingContent = String(
|
|
146
|
+
rawContent || "",
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// D. Safely check includes (This line can no longer crash!)
|
|
150
|
+
if (
|
|
151
|
+
!safeExistingContent.includes(
|
|
152
|
+
"--background:",
|
|
153
|
+
)
|
|
154
|
+
) {
|
|
155
|
+
fs.appendFileSync(
|
|
156
|
+
finalTargetPath,
|
|
157
|
+
`\n/* React Apps UI Theme */\n${file.content || ""}`,
|
|
158
|
+
"utf-8",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.log(
|
|
163
|
+
chalk.red(
|
|
164
|
+
` ❌ Error reading or appending to ${safeTarget}`,
|
|
165
|
+
),
|
|
166
|
+
);
|
|
135
167
|
}
|
|
136
|
-
}
|
|
168
|
+
} else {
|
|
137
169
|
console.log(
|
|
138
|
-
chalk.
|
|
139
|
-
`
|
|
170
|
+
chalk.yellow(
|
|
171
|
+
` ⚠️ ${file.target} already exists. Skipping to prevent overwrite.`,
|
|
140
172
|
),
|
|
141
173
|
);
|
|
142
174
|
}
|
|
143
175
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
176
|
+
// File doesn't exist, safe to write normally
|
|
177
|
+
fs.writeFileSync(
|
|
178
|
+
finalTargetPath,
|
|
179
|
+
file.content,
|
|
180
|
+
"utf-8",
|
|
148
181
|
);
|
|
182
|
+
console.log(chalk.green(` Created ${safeTarget}`));
|
|
149
183
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 5. INSTALL FOUNDATIONAL NPM DEPENDENCIES
|
|
188
|
+
if (setup.npm.length > 0) {
|
|
189
|
+
const depList = setup.npm.join(" ");
|
|
190
|
+
console.log(
|
|
191
|
+
chalk.blue(
|
|
192
|
+
`\n📦 Installing foundational NPM dependencies: ${depList}...`,
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
try {
|
|
196
|
+
execSync(`npm install ${depList}`, { stdio: "inherit" });
|
|
197
|
+
console.log(
|
|
198
|
+
chalk.green("✓ Dependencies installed successfully."),
|
|
199
|
+
);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.log(
|
|
202
|
+
chalk.red(
|
|
203
|
+
"❌ Failed to install dependencies. You may need to install them manually.",
|
|
204
|
+
),
|
|
205
|
+
);
|
|
154
206
|
}
|
|
155
207
|
}
|
|
156
208
|
|