create-skateboard-app 1.0.0
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/LICENSE +21 -0
- package/README.md +61 -0
- package/bin/cli.js +426 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 stevederico
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Create Skateboard App
|
|
2
|
+
|
|
3
|
+
The fastest way to create a new [Skateboard](https://github.com/stevederico/skateboard) app.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-skateboard-app
|
|
9
|
+
cd my-app
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Interactive Mode (Default)
|
|
16
|
+
```bash
|
|
17
|
+
npx create-skateboard-app
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### With Project Name
|
|
21
|
+
```bash
|
|
22
|
+
npx create-skateboard-app my-app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What You Get
|
|
26
|
+
|
|
27
|
+
- âī¸ React v19
|
|
28
|
+
- đ¨ TailwindCSS v4
|
|
29
|
+
- đ§Š Shadcn/ui components
|
|
30
|
+
- ⥠Vite build tool
|
|
31
|
+
- đ Authentication ready
|
|
32
|
+
- đŗ Stripe integration
|
|
33
|
+
- đ Dark mode support
|
|
34
|
+
- đą Mobile responsive
|
|
35
|
+
- đŖī¸ React Router
|
|
36
|
+
- đĻ Modern JavaScript
|
|
37
|
+
|
|
38
|
+
## Features Included
|
|
39
|
+
|
|
40
|
+
- Sign up/Sign in pages
|
|
41
|
+
- Landing page
|
|
42
|
+
- Settings page with Home and Other views
|
|
43
|
+
- Legal pages (Privacy, Terms, EULA)
|
|
44
|
+
- 404 error handling
|
|
45
|
+
- Protected routes
|
|
46
|
+
- Mobile tab bar
|
|
47
|
+
- Customizable constants
|
|
48
|
+
|
|
49
|
+
## Requirements
|
|
50
|
+
|
|
51
|
+
- Node.js 22.5+
|
|
52
|
+
- npm or yarn
|
|
53
|
+
- git, curl, or npx (for template download)
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
Contributions are welcome! Please check out the [Skateboard repository](https://github.com/stevederico/skateboard) for more information.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import https from 'https';
|
|
7
|
+
import { createWriteStream } from 'fs';
|
|
8
|
+
import { createInterface } from 'readline';
|
|
9
|
+
|
|
10
|
+
// Simple colors using ANSI codes
|
|
11
|
+
const colors = {
|
|
12
|
+
green: '\x1b[32m',
|
|
13
|
+
red: '\x1b[31m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
blue: '\x1b[34m',
|
|
16
|
+
cyan: '\x1b[36m',
|
|
17
|
+
magenta: '\x1b[35m',
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
bold: '\x1b[1m'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function log(message, color = 'reset') {
|
|
23
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function error(message) {
|
|
27
|
+
log(`â ${message}`, 'red');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function success(message) {
|
|
31
|
+
log(`â
${message}`, 'green');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function info(message) {
|
|
35
|
+
log(`âšī¸ ${message}`, 'blue');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function checkCommand(command) {
|
|
39
|
+
try {
|
|
40
|
+
execSync(`which ${command}`, { stdio: 'pipe' });
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function downloadTemplate(projectName) {
|
|
48
|
+
// Try multiple methods in order of preference
|
|
49
|
+
const methods = [
|
|
50
|
+
{
|
|
51
|
+
name: 'degit',
|
|
52
|
+
check: () => checkCommand('npx'),
|
|
53
|
+
execute: () => execSync(`npx degit stevederico/skateboard ${projectName}`, {
|
|
54
|
+
stdio: 'pipe',
|
|
55
|
+
timeout: 30000
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'git clone',
|
|
60
|
+
check: () => checkCommand('git'),
|
|
61
|
+
execute: () => {
|
|
62
|
+
execSync(`git clone --depth 1 https://github.com/stevederico/skateboard.git ${projectName}`, {
|
|
63
|
+
stdio: 'pipe',
|
|
64
|
+
timeout: 30000
|
|
65
|
+
});
|
|
66
|
+
// Remove .git directory to avoid including git history
|
|
67
|
+
execSync(`rm -rf ${projectName}/.git`, { stdio: 'pipe' });
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'curl + unzip',
|
|
72
|
+
check: () => checkCommand('curl') && checkCommand('unzip'),
|
|
73
|
+
execute: () => {
|
|
74
|
+
execSync(`curl -L https://github.com/stevederico/skateboard/archive/refs/heads/master.zip -o temp.zip`, {
|
|
75
|
+
stdio: 'pipe',
|
|
76
|
+
timeout: 30000
|
|
77
|
+
});
|
|
78
|
+
execSync(`unzip -q temp.zip`, { stdio: 'pipe' });
|
|
79
|
+
execSync(`mv skateboard-master ${projectName}`, { stdio: 'pipe' });
|
|
80
|
+
execSync(`rm temp.zip`, { stdio: 'pipe' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
for (const method of methods) {
|
|
86
|
+
if (!method.check()) {
|
|
87
|
+
log(`${method.name} not available, skipping...`, 'yellow');
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
info(`Trying ${method.name}...`);
|
|
93
|
+
method.execute();
|
|
94
|
+
success(`Template downloaded via ${method.name}`);
|
|
95
|
+
return;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log(`${method.name} failed, trying next method...`, 'yellow');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error('All download methods failed. Please ensure you have git, curl, or npx available and check your internet connection.');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Interactive prompt functions
|
|
105
|
+
function ask(question, defaultValue = '') {
|
|
106
|
+
const rl = createInterface({
|
|
107
|
+
input: process.stdin,
|
|
108
|
+
output: process.stdout
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
const prompt = defaultValue
|
|
113
|
+
? `${colors.cyan}${question}${colors.reset} ${colors.yellow}(${defaultValue})${colors.reset}: `
|
|
114
|
+
: `${colors.cyan}${question}${colors.reset}: `;
|
|
115
|
+
|
|
116
|
+
rl.question(prompt, (answer) => {
|
|
117
|
+
rl.close();
|
|
118
|
+
resolve(answer.trim() || defaultValue);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function askChoice(question, choices, defaultChoice = 0) {
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
let currentChoice = defaultChoice;
|
|
126
|
+
|
|
127
|
+
const displayMenu = () => {
|
|
128
|
+
// Clear screen and show menu
|
|
129
|
+
console.clear();
|
|
130
|
+
log(`\n${colors.cyan}${question}${colors.reset}\n`);
|
|
131
|
+
choices.forEach((choice, index) => {
|
|
132
|
+
const marker = index === currentChoice ? 'â' : 'â';
|
|
133
|
+
const color = index === currentChoice ? 'green' : 'reset';
|
|
134
|
+
const highlight = index === currentChoice ? colors.bold : '';
|
|
135
|
+
log(` ${colors[color]}${highlight}${marker} ${choice.label}${colors.reset}`);
|
|
136
|
+
});
|
|
137
|
+
log(`\n${colors.yellow}Use â/â arrows to navigate, Enter to select${colors.reset}`);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
displayMenu();
|
|
141
|
+
|
|
142
|
+
// Enable raw mode to capture arrow keys
|
|
143
|
+
process.stdin.setRawMode(true);
|
|
144
|
+
process.stdin.resume();
|
|
145
|
+
process.stdin.setEncoding('utf8');
|
|
146
|
+
|
|
147
|
+
const handleKeypress = (key) => {
|
|
148
|
+
switch (key) {
|
|
149
|
+
case '\u001b[A': // Up arrow
|
|
150
|
+
currentChoice = currentChoice > 0 ? currentChoice - 1 : choices.length - 1;
|
|
151
|
+
displayMenu();
|
|
152
|
+
break;
|
|
153
|
+
case '\u001b[B': // Down arrow
|
|
154
|
+
currentChoice = currentChoice < choices.length - 1 ? currentChoice + 1 : 0;
|
|
155
|
+
displayMenu();
|
|
156
|
+
break;
|
|
157
|
+
case '\r': // Enter
|
|
158
|
+
case '\n':
|
|
159
|
+
process.stdin.setRawMode(false);
|
|
160
|
+
process.stdin.pause();
|
|
161
|
+
process.stdin.removeListener('data', handleKeypress);
|
|
162
|
+
resolve(choices[currentChoice]);
|
|
163
|
+
break;
|
|
164
|
+
case '\u0003': // Ctrl+C
|
|
165
|
+
process.exit(0);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
process.stdin.on('data', handleKeypress);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function askYesNo(question, defaultYes = true) {
|
|
175
|
+
const defaultText = defaultYes ? 'Y/n' : 'y/N';
|
|
176
|
+
const answer = await ask(`${question} (${defaultText})`, defaultYes ? 'y' : 'n');
|
|
177
|
+
return answer.toLowerCase().startsWith('y');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function collectProjectConfig(projectName) {
|
|
181
|
+
log(`\n${colors.bold}Let's configure your Skateboard app!${colors.reset}\n`);
|
|
182
|
+
|
|
183
|
+
// App name
|
|
184
|
+
const appName = await ask('App name', projectName.split('-').map(word =>
|
|
185
|
+
word.charAt(0).toUpperCase() + word.slice(1)
|
|
186
|
+
).join(' '));
|
|
187
|
+
|
|
188
|
+
// Tagline
|
|
189
|
+
const tagline = await ask('App tagline', 'Try Something New');
|
|
190
|
+
|
|
191
|
+
// App color selection
|
|
192
|
+
const colorChoices = [
|
|
193
|
+
{ label: 'đĩ Blue', value: 'blue' },
|
|
194
|
+
{ label: 'đ Green', value: 'green' },
|
|
195
|
+
{ label: 'đŖ Purple', value: 'purple' },
|
|
196
|
+
{ label: 'đ´ Red', value: 'red' },
|
|
197
|
+
{ label: 'đ Orange', value: 'orange' },
|
|
198
|
+
{ label: 'đĄ Yellow', value: 'yellow' },
|
|
199
|
+
{ label: '𩷠Pink', value: 'pink' },
|
|
200
|
+
{ label: 'đŠĩ Cyan', value: 'cyan' }
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
const selectedColor = await askChoice('Choose your app color:', colorChoices);
|
|
204
|
+
|
|
205
|
+
// App icon
|
|
206
|
+
const iconChoices = [
|
|
207
|
+
{ label: 'â Command', value: 'command' },
|
|
208
|
+
{ label: 'đ House', value: 'house' },
|
|
209
|
+
{ label: '⥠Zap', value: 'zap' },
|
|
210
|
+
{ label: 'đ Rocket', value: 'rocket' },
|
|
211
|
+
{ label: 'đ Diamond', value: 'diamond' },
|
|
212
|
+
{ label: 'đ¯ Target', value: 'target' },
|
|
213
|
+
{ label: 'đĨ Flame', value: 'flame' },
|
|
214
|
+
{ label: 'â Star', value: 'star' }
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const selectedIcon = await askChoice('Choose an app icon:', iconChoices);
|
|
218
|
+
|
|
219
|
+
// Backend URLs
|
|
220
|
+
const backendURL = await ask('Production backend URL', 'https://api.example.com');
|
|
221
|
+
const devBackendURL = await ask('Development backend URL', 'http://localhost:8000');
|
|
222
|
+
|
|
223
|
+
// App pages configuration
|
|
224
|
+
log(`\n${colors.cyan}Configure your app pages:${colors.reset}`);
|
|
225
|
+
const pages = [];
|
|
226
|
+
|
|
227
|
+
const addDefaultPages = await askYesNo('Add default pages (Home, Other)?', true);
|
|
228
|
+
if (addDefaultPages) {
|
|
229
|
+
pages.push(
|
|
230
|
+
{ title: 'Home', url: 'home', icon: 'house' },
|
|
231
|
+
{ title: 'Other', url: 'other', icon: 'inbox' }
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const addMorePages = await askYesNo('Add more custom pages?', false);
|
|
236
|
+
if (addMorePages) {
|
|
237
|
+
let addAnother = true;
|
|
238
|
+
while (addAnother) {
|
|
239
|
+
const pageTitle = await ask('Page title');
|
|
240
|
+
const pageUrl = await ask('Page URL', pageTitle.toLowerCase().replace(/\s+/g, '-'));
|
|
241
|
+
const pageIcon = await ask('Page icon (lucide icon name)', 'circle');
|
|
242
|
+
|
|
243
|
+
pages.push({
|
|
244
|
+
title: pageTitle,
|
|
245
|
+
url: pageUrl,
|
|
246
|
+
icon: pageIcon
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
addAnother = await askYesNo('Add another page?', false);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Company name (after pages configuration)
|
|
254
|
+
const companyName = await ask('Company name', 'Your Company');
|
|
255
|
+
|
|
256
|
+
// Installation preferences
|
|
257
|
+
const installDeps = await askYesNo('Install dependencies automatically?', true);
|
|
258
|
+
const initGit = await askYesNo('Initialize git repository?', true);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
companyName,
|
|
262
|
+
appName,
|
|
263
|
+
tagline,
|
|
264
|
+
appColor: selectedColor.value,
|
|
265
|
+
appIcon: selectedIcon.value,
|
|
266
|
+
backendURL,
|
|
267
|
+
devBackendURL,
|
|
268
|
+
pages,
|
|
269
|
+
installDeps,
|
|
270
|
+
initGit
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function showHelp() {
|
|
275
|
+
log(`
|
|
276
|
+
${colors.bold}đš Create Skateboard App${colors.reset}
|
|
277
|
+
|
|
278
|
+
${colors.cyan}Usage:${colors.reset}
|
|
279
|
+
npx create-skateboard-app
|
|
280
|
+
|
|
281
|
+
${colors.cyan}Arguments:${colors.reset}
|
|
282
|
+
project-name Optional project directory name (will prompt if not provided)
|
|
283
|
+
|
|
284
|
+
${colors.cyan}Options:${colors.reset}
|
|
285
|
+
--help, -h Show this help message
|
|
286
|
+
--version, -v Show version number
|
|
287
|
+
|
|
288
|
+
${colors.cyan}Examples:${colors.reset}
|
|
289
|
+
npx create-skateboard-app # Interactive mode
|
|
290
|
+
npx create-skateboard-app my-app # With project name
|
|
291
|
+
npx create-skateboard-app awesome-project # With custom name
|
|
292
|
+
`, 'reset');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function showVersion() {
|
|
296
|
+
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
297
|
+
log(`v${packageJson.version}`, 'green');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function main() {
|
|
301
|
+
// Get project name from command line
|
|
302
|
+
const args = process.argv.slice(2);
|
|
303
|
+
let projectName = args[0];
|
|
304
|
+
|
|
305
|
+
// Handle help and version flags
|
|
306
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
307
|
+
showHelp();
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
312
|
+
showVersion();
|
|
313
|
+
process.exit(0);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// If no project name provided, ask for it
|
|
317
|
+
if (!projectName) {
|
|
318
|
+
log(`\n${colors.bold}đš Welcome to Skateboard App Creator!${colors.reset}\n`);
|
|
319
|
+
projectName = await ask('What is the name of your project?', 'my-skateboard-app');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Validate project name
|
|
323
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
|
|
324
|
+
error('Project name can only contain letters, numbers, hyphens, and underscores');
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Check if directory already exists
|
|
329
|
+
if (existsSync(projectName)) {
|
|
330
|
+
error(`Directory '${projectName}' already exists`);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
log(`\n${colors.bold}đš Creating Skateboard app: ${projectName}${colors.reset}\n`);
|
|
336
|
+
|
|
337
|
+
// Step 1: Download the template with fallback methods
|
|
338
|
+
info('Downloading template...');
|
|
339
|
+
await downloadTemplate(projectName);
|
|
340
|
+
|
|
341
|
+
// Step 2: Collect user configuration
|
|
342
|
+
const config = await collectProjectConfig(projectName);
|
|
343
|
+
|
|
344
|
+
// Step 3: Update package.json
|
|
345
|
+
info('Updating package.json...');
|
|
346
|
+
const packageJsonPath = join(projectName, 'package.json');
|
|
347
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
348
|
+
packageJson.name = projectName;
|
|
349
|
+
packageJson.version = '0.1.0';
|
|
350
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
351
|
+
success('Package.json updated');
|
|
352
|
+
|
|
353
|
+
// Step 4: Update constants.json with user configuration
|
|
354
|
+
info('Configuring app settings...');
|
|
355
|
+
const constantsPath = join(projectName, 'src', 'constants.json');
|
|
356
|
+
if (existsSync(constantsPath)) {
|
|
357
|
+
const constants = JSON.parse(readFileSync(constantsPath, 'utf8'));
|
|
358
|
+
constants.companyName = config.companyName;
|
|
359
|
+
constants.appName = config.appName;
|
|
360
|
+
constants.tagline = config.tagline;
|
|
361
|
+
constants.appIcon = config.appIcon;
|
|
362
|
+
constants.backendURL = config.backendURL;
|
|
363
|
+
constants.devBackendURL = config.devBackendURL;
|
|
364
|
+
constants.pages = config.pages;
|
|
365
|
+
writeFileSync(constantsPath, JSON.stringify(constants, null, 4));
|
|
366
|
+
success('App configuration updated');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Step 5: Update app color in styles.css
|
|
370
|
+
info('Setting app color...');
|
|
371
|
+
const stylesPath = join(projectName, 'src', 'assets', 'styles.css');
|
|
372
|
+
if (existsSync(stylesPath)) {
|
|
373
|
+
let stylesContent = readFileSync(stylesPath, 'utf8');
|
|
374
|
+
// Replace the app color in the @theme block
|
|
375
|
+
stylesContent = stylesContent.replace(
|
|
376
|
+
/--color-app:\s*var\(--color-[^)]+\);/,
|
|
377
|
+
`--color-app: var(--color-${config.appColor}-500);`
|
|
378
|
+
);
|
|
379
|
+
writeFileSync(stylesPath, stylesContent);
|
|
380
|
+
success(`App color set to ${config.appColor}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Step 6: Install dependencies (if requested)
|
|
384
|
+
if (config.installDeps) {
|
|
385
|
+
info('Installing dependencies...');
|
|
386
|
+
execSync(`cd ${projectName} && npm install`, { stdio: 'inherit' });
|
|
387
|
+
success('Dependencies installed');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Step 7: Initialize git (if requested)
|
|
391
|
+
if (config.initGit) {
|
|
392
|
+
info('Initializing git repository...');
|
|
393
|
+
execSync(`cd ${projectName} && git init`, { stdio: 'pipe' });
|
|
394
|
+
success('Git repository initialized');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Success message
|
|
398
|
+
log(`\n${colors.bold}${colors.green}đ Success! Created ${config.appName}${colors.reset}\n`);
|
|
399
|
+
|
|
400
|
+
// Change to the new project directory
|
|
401
|
+
process.chdir(projectName);
|
|
402
|
+
info(`Switched to ${projectName} directory`);
|
|
403
|
+
|
|
404
|
+
log('Next steps:', 'yellow');
|
|
405
|
+
if (!config.installDeps) {
|
|
406
|
+
log(` npm install`);
|
|
407
|
+
}
|
|
408
|
+
log(` npm run dev`);
|
|
409
|
+
log(`\n${colors.cyan}Your app is configured with:${colors.reset}`);
|
|
410
|
+
log(` đĸ Company: ${config.companyName}`);
|
|
411
|
+
log(` đą App: ${config.appName}`);
|
|
412
|
+
log(` đŦ Tagline: ${config.tagline}`);
|
|
413
|
+
log(` đ¨ Color: ${config.appColor}`);
|
|
414
|
+
log(` đ¯ Icon: ${config.appIcon}`);
|
|
415
|
+
log(` đ Pages: ${config.pages.map(p => p.title).join(', ')}`);
|
|
416
|
+
log(` đ Backend: ${config.backendURL}`);
|
|
417
|
+
log(`\n${colors.magenta}You're now in the ${projectName} directory!${colors.reset}`);
|
|
418
|
+
log(`${colors.yellow}Run 'npm run dev' to begin development đš${colors.reset}\n`);
|
|
419
|
+
|
|
420
|
+
} catch (err) {
|
|
421
|
+
error(`Failed to create project: ${err.message}`);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-skateboard-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a new Skateboard app with React, TailwindCSS, and more",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-skateboard-app": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"react",
|
|
15
|
+
"boilerplate",
|
|
16
|
+
"template",
|
|
17
|
+
"tailwindcss",
|
|
18
|
+
"vite",
|
|
19
|
+
"skateboard",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"author": "stevederico",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/stevederico/create-skateboard-app.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/stevederico/create-skateboard-app/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/stevederico/create-skateboard-app#readme"
|
|
36
|
+
}
|