gotodev 2.0.1 → 2.0.3
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/cli.js +90 -127
- package/package.json +2 -1
- package/template-fetcher.js +988 -0
package/cli.js
CHANGED
|
@@ -15,91 +15,12 @@ const { getViteTemplate } = require('./template-fetcher');
|
|
|
15
15
|
|
|
16
16
|
const program = new Command();
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
// OFFICIAL TEMPLATE REGISTRY
|
|
20
|
-
// -------------------------------------------------------------------------
|
|
21
|
-
// Mapping from user-friendly name → GitHub repo (owner/repo). The repo must
|
|
22
|
-
// contain a `create-<framework>` directory with the scaffold files we need.
|
|
23
|
-
// Example: "next" → "vercel/next.js" (the repo that contains the `create-vite`
|
|
24
|
-
// template used by `create-next-app`).
|
|
25
|
-
// -------------------------------------------------------------------------
|
|
26
|
-
const TEMPLATE_REGISTRY = {
|
|
27
|
-
next: 'vercel/next.js',
|
|
28
|
-
nextjs: 'vercel/next.js',
|
|
29
|
-
shadcn: 'shadcn/ui',
|
|
30
|
-
tailwind: 'shadcn/ui',
|
|
31
|
-
'react-router': 'tanstack/react-router',
|
|
32
|
-
'tanstack-query': 'tanstack/query',
|
|
33
|
-
'tanstack-query-core': 'tanstack/query',
|
|
34
|
-
// add more as you like …
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Simple downloader that fetches from GitHub raw URLs
|
|
38
|
-
async function downloadTemplate(name) {
|
|
39
|
-
if (!TEMPLATE_REGISTRY[name]) {
|
|
40
|
-
console.error(`Unknown template: ${name}`);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
const repo = TEMPLATE_REGISTRY[name];
|
|
44
|
-
|
|
45
|
-
// Use axios to fetch the repository's raw files
|
|
46
|
-
const axios = require('axios');
|
|
47
|
-
|
|
48
|
-
// For now, let's use a simple approach: fetch the create-vite template
|
|
49
|
-
// This is a placeholder - in a real implementation, you'd fetch the actual template files
|
|
50
|
-
console.log(`Downloading template: ${name} from ${repo}`);
|
|
51
|
-
|
|
52
|
-
// Create a temporary directory
|
|
53
|
-
const tmpDir = path.join(process.cwd(), `tmp/_gotodev-${Date.now()}`);
|
|
54
|
-
await fs.ensureDir(tmpDir);
|
|
55
|
-
|
|
56
|
-
// For demonstration, create a simple package.json
|
|
57
|
-
const packageJson = {
|
|
58
|
-
name: 'template-app',
|
|
59
|
-
version: '1.0.0',
|
|
60
|
-
type: 'module',
|
|
61
|
-
scripts: {
|
|
62
|
-
dev: 'vite',
|
|
63
|
-
build: 'vite build',
|
|
64
|
-
preview: 'vite preview'
|
|
65
|
-
},
|
|
66
|
-
dependencies: {
|
|
67
|
-
'react': '^18.2.0',
|
|
68
|
-
'react-dom': '^18.2.0'
|
|
69
|
-
},
|
|
70
|
-
devDependencies: {
|
|
71
|
-
'vite': '^5.0.0',
|
|
72
|
-
'@vitejs/plugin-react': '^4.2.1'
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
await fs.writeFile(path.join(tmpDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
77
|
-
await fs.writeFile(path.join(tmpDir, 'index.html'), '<!DOCTYPE html><html><head><title>Template</title></head><body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body></html>');
|
|
78
|
-
await fs.ensureDir(path.join(tmpDir, 'src'));
|
|
79
|
-
await fs.writeFile(path.join(tmpDir, 'src/main.jsx'), `import React from 'react';
|
|
80
|
-
import ReactDOM from 'react-dom/client';
|
|
81
|
-
import App from './App.jsx';
|
|
82
|
-
|
|
83
|
-
ReactDOM.createRoot(document.getElementById('root')).render(<App />);`);
|
|
84
|
-
await fs.writeFile(path.join(tmpDir, 'src/App.jsx'), `export default function App() {
|
|
85
|
-
return <h1>Hello from ${name} template!</h1>;
|
|
86
|
-
}`);
|
|
87
|
-
await fs.writeFile(path.join(tmpDir, 'vite.config.js'), `import { defineConfig } from 'vite';
|
|
88
|
-
import react from '@vitejs/plugin-react';
|
|
89
|
-
|
|
90
|
-
export default defineConfig({
|
|
91
|
-
plugins: [react()],
|
|
92
|
-
});`);
|
|
93
|
-
|
|
94
|
-
return tmpDir;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Feature options
|
|
18
|
+
// Feature options - UPDATED with Tailwind
|
|
98
19
|
const FEATURE_OPTIONS = [
|
|
99
20
|
{ value: 'typescript', label: 'TypeScript' },
|
|
100
|
-
{ value: '
|
|
101
|
-
{ value: '
|
|
102
|
-
{ value: '
|
|
21
|
+
{ value: 'tailwind', label: 'Tailwind CSS' },
|
|
22
|
+
{ value: 'router', label: 'Vue Router (Vue only)' },
|
|
23
|
+
{ value: 'pinia', label: 'Pinia (Vue only)' },
|
|
103
24
|
];
|
|
104
25
|
|
|
105
26
|
// Framework options
|
|
@@ -169,7 +90,7 @@ async function init() {
|
|
|
169
90
|
const args = process.argv.slice(2);
|
|
170
91
|
|
|
171
92
|
// Parse CLI arguments
|
|
172
|
-
const flags = ['typescript', 'ts', '
|
|
93
|
+
const flags = ['framework', 'typescript', 'ts', 'tailwind', 'router', 'pinia', 'force', 'bare', 'help', 'version'];
|
|
173
94
|
const options = {};
|
|
174
95
|
let targetDir = null;
|
|
175
96
|
|
|
@@ -192,24 +113,24 @@ async function init() {
|
|
|
192
113
|
console.log(`\
|
|
193
114
|
Usage: gotodev create [DIRECTORY] [OPTIONS]
|
|
194
115
|
|
|
195
|
-
Create a new Vite-powered project
|
|
116
|
+
Create a new Vite-powered project with latest dependencies.
|
|
196
117
|
|
|
197
118
|
Options:
|
|
119
|
+
--framework <name> Framework: react, vue, svelte, vanilla
|
|
198
120
|
--typescript, --ts Add TypeScript support
|
|
121
|
+
--tailwind Add Tailwind CSS styling
|
|
199
122
|
--router Add Vue Router (Vue only)
|
|
200
123
|
--pinia Add Pinia (Vue only)
|
|
201
|
-
--jsx Add JSX support
|
|
202
124
|
--force Force overwrite existing directory
|
|
203
|
-
--bare Minimal template without example code
|
|
204
125
|
--help Display this help message
|
|
205
126
|
--version Display version number
|
|
206
127
|
|
|
207
128
|
Feature flags can be used to skip interactive prompts.
|
|
208
129
|
|
|
209
130
|
Examples:
|
|
210
|
-
gotodev create my-app
|
|
211
|
-
gotodev create my-app --
|
|
212
|
-
gotodev create my-app --
|
|
131
|
+
gotodev create my-app --framework react --typescript --tailwind
|
|
132
|
+
gotodev create my-app --framework vue --ts --tailwind --force
|
|
133
|
+
gotodev create my-app --framework svelte --typescript`);
|
|
213
134
|
process.exit(0);
|
|
214
135
|
}
|
|
215
136
|
|
|
@@ -277,9 +198,9 @@ Examples:
|
|
|
277
198
|
packageName = _packageName;
|
|
278
199
|
}
|
|
279
200
|
|
|
280
|
-
// Check if any feature flags were used
|
|
201
|
+
// Check if any feature flags or framework were used
|
|
281
202
|
const isFeatureFlagsUsed = Object.keys(options).some(key =>
|
|
282
|
-
['typescript', 'ts', 'router', 'pinia', '
|
|
203
|
+
['typescript', 'ts', 'tailwind', 'router', 'pinia', 'framework'].includes(key)
|
|
283
204
|
);
|
|
284
205
|
|
|
285
206
|
let selectedFramework = 'vue';
|
|
@@ -299,9 +220,18 @@ Examples:
|
|
|
299
220
|
}
|
|
300
221
|
|
|
301
222
|
// Interactive feature selection
|
|
223
|
+
// Filter features based on framework
|
|
224
|
+
const availableFeatures = FEATURE_OPTIONS.filter(option => {
|
|
225
|
+
if (selectedFramework === 'vue') return true;
|
|
226
|
+
if (selectedFramework === 'react') return ['typescript', 'tailwind'].includes(option.value);
|
|
227
|
+
if (selectedFramework === 'svelte') return ['typescript', 'tailwind'].includes(option.value);
|
|
228
|
+
if (selectedFramework === 'vanilla') return ['tailwind'].includes(option.value);
|
|
229
|
+
return false;
|
|
230
|
+
});
|
|
231
|
+
|
|
302
232
|
selectedFeatures = await multiselect({
|
|
303
233
|
message: 'Select features (use space to toggle):',
|
|
304
|
-
options:
|
|
234
|
+
options: availableFeatures,
|
|
305
235
|
required: false
|
|
306
236
|
});
|
|
307
237
|
|
|
@@ -311,20 +241,31 @@ Examples:
|
|
|
311
241
|
}
|
|
312
242
|
} else {
|
|
313
243
|
// Use CLI flags
|
|
244
|
+
if (options.framework) {
|
|
245
|
+
selectedFramework = options.framework;
|
|
246
|
+
if (!['react', 'vue', 'svelte', 'vanilla'].includes(selectedFramework)) {
|
|
247
|
+
console.error(`${red('✖')} Invalid framework: ${selectedFramework}. Must be one of: react, vue, svelte, vanilla`);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Handle both --typescript and --ts
|
|
314
253
|
if (options.typescript || options.ts) selectedFeatures.push('typescript');
|
|
254
|
+
if (options.tailwind) selectedFeatures.push('tailwind');
|
|
315
255
|
if (options.router) selectedFeatures.push('router');
|
|
316
256
|
if (options.pinia) selectedFeatures.push('pinia');
|
|
317
|
-
if (options.jsx) selectedFeatures.push('jsx');
|
|
318
257
|
|
|
319
258
|
// Default to vue if no framework specified
|
|
320
|
-
|
|
259
|
+
if (!options.framework) {
|
|
260
|
+
selectedFramework = 'vue';
|
|
261
|
+
}
|
|
321
262
|
}
|
|
322
263
|
|
|
323
264
|
// Determine features
|
|
324
265
|
const needsTypeScript = selectedFeatures.includes('typescript');
|
|
266
|
+
const needsTailwind = selectedFeatures.includes('tailwind');
|
|
325
267
|
const needsRouter = selectedFeatures.includes('router');
|
|
326
268
|
const needsPinia = selectedFeatures.includes('pinia');
|
|
327
|
-
const needsJsx = selectedFeatures.includes('jsx');
|
|
328
269
|
|
|
329
270
|
// Create project directory
|
|
330
271
|
const root = path.join(cwd, targetDir);
|
|
@@ -339,20 +280,25 @@ Examples:
|
|
|
339
280
|
|
|
340
281
|
// Scaffolding
|
|
341
282
|
const s = spinner();
|
|
342
|
-
s.start('
|
|
283
|
+
s.start('Generating template...');
|
|
343
284
|
|
|
344
285
|
try {
|
|
345
|
-
// Use template fetcher to
|
|
346
|
-
await getViteTemplate(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
286
|
+
// Use template fetcher to generate templates
|
|
287
|
+
await getViteTemplate(
|
|
288
|
+
packageName,
|
|
289
|
+
selectedFramework,
|
|
290
|
+
{
|
|
291
|
+
typescript: needsTypeScript,
|
|
292
|
+
tailwind: needsTailwind,
|
|
293
|
+
router: needsRouter,
|
|
294
|
+
pinia: needsPinia
|
|
295
|
+
},
|
|
296
|
+
root
|
|
297
|
+
);
|
|
352
298
|
|
|
353
|
-
s.stop('Template
|
|
299
|
+
s.stop('Template generated!');
|
|
354
300
|
} catch (error) {
|
|
355
|
-
s.stop('Failed to
|
|
301
|
+
s.stop('Failed to generate template');
|
|
356
302
|
console.error(red('Error:'), error.message);
|
|
357
303
|
process.exit(1);
|
|
358
304
|
}
|
|
@@ -382,7 +328,7 @@ Examples:
|
|
|
382
328
|
}
|
|
383
329
|
|
|
384
330
|
// Success message
|
|
385
|
-
outro(`${green('✔')} ${bold('Success!')} Created ${selectedFramework.toUpperCase()} project in ${targetDir}
|
|
331
|
+
outro(`${green('✔')} ${bold('Success!')} Created ${selectedFramework.toUpperCase()} project in ${targetDir}${needsTypeScript ? ' + TypeScript' : ''}${needsTailwind ? ' + Tailwind' : ''}
|
|
386
332
|
|
|
387
333
|
${dim('Next steps:')}
|
|
388
334
|
${cyan('cd')} ${targetDir}
|
|
@@ -400,10 +346,11 @@ program
|
|
|
400
346
|
program
|
|
401
347
|
.command('create [directory]')
|
|
402
348
|
.description('Create a new project')
|
|
349
|
+
.option('--framework <framework>', 'Framework to use (react, vue, svelte, vanilla)')
|
|
403
350
|
.option('--typescript, --ts', 'Add TypeScript support')
|
|
351
|
+
.option('--tailwind', 'Add Tailwind CSS styling')
|
|
404
352
|
.option('--router', 'Add Vue Router (Vue only)')
|
|
405
353
|
.option('--pinia', 'Add Pinia (Vue only)')
|
|
406
|
-
.option('--jsx', 'Add JSX support')
|
|
407
354
|
.option('--force', 'Force overwrite existing directory')
|
|
408
355
|
.option('--template <name>', 'Download an official starter template (e.g. next, shadcn, tailwind)')
|
|
409
356
|
.action(async (directory, options) => {
|
|
@@ -424,25 +371,40 @@ program
|
|
|
424
371
|
fs.mkdirSync(root, { recursive: true });
|
|
425
372
|
}
|
|
426
373
|
|
|
427
|
-
//
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
} else {
|
|
440
|
-
await fs.copyFile(srcPath, destPath);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
374
|
+
// For official templates, use the template fetcher
|
|
375
|
+
const templateMap = {
|
|
376
|
+
'nextjs': { framework: 'react', features: { typescript: true, tailwind: true } },
|
|
377
|
+
'next': { framework: 'react', features: { typescript: true, tailwind: true } },
|
|
378
|
+
'shadcn': { framework: 'react', features: { typescript: true, tailwind: true } },
|
|
379
|
+
'tailwind': { framework: 'vanilla', features: { tailwind: true } },
|
|
380
|
+
'react-ts': { framework: 'react', features: { typescript: true } },
|
|
381
|
+
'react': { framework: 'react', features: { typescript: false } },
|
|
382
|
+
'vue-ts': { framework: 'vue', features: { typescript: true } },
|
|
383
|
+
'vue': { framework: 'vue', features: { typescript: false } },
|
|
384
|
+
'svelte-ts': { framework: 'svelte', features: { typescript: true } },
|
|
385
|
+
'svelte': { framework: 'svelte', features: { typescript: false } }
|
|
443
386
|
};
|
|
444
387
|
|
|
445
|
-
|
|
388
|
+
if (templateMap[options.template]) {
|
|
389
|
+
const { framework, features } = templateMap[options.template];
|
|
390
|
+
const packageName = toValidPackageName(targetDir);
|
|
391
|
+
|
|
392
|
+
const s = spinner();
|
|
393
|
+
s.start(`Downloading template: ${options.template} from ${framework}...`);
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
await getViteTemplate(packageName, framework, features, root);
|
|
397
|
+
s.stop(`Template ${options.template} downloaded!`);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
s.stop('Failed to download template');
|
|
400
|
+
console.error(red('Error:'), error.message);
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
console.error(`${red('✖')} Unknown template: ${options.template}`);
|
|
405
|
+
console.log(`Available templates: ${Object.keys(templateMap).join(', ')}`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
446
408
|
|
|
447
409
|
// Install dependencies
|
|
448
410
|
const pm = getPackageManager();
|
|
@@ -475,10 +437,11 @@ ${dim('Happy coding! 🚀')}`);
|
|
|
475
437
|
// Otherwise, use the original init flow
|
|
476
438
|
const args = [];
|
|
477
439
|
if (directory) args.push(directory);
|
|
478
|
-
if (options.
|
|
440
|
+
if (options.framework) args.push(`--framework=${options.framework}`);
|
|
441
|
+
if (options.typescript || options.ts) args.push('--typescript');
|
|
442
|
+
if (options.tailwind) args.push('--tailwind');
|
|
479
443
|
if (options.router) args.push('--router');
|
|
480
444
|
if (options.pinia) args.push('--pinia');
|
|
481
|
-
if (options.jsx) args.push('--jsx');
|
|
482
445
|
if (options.force) args.push('--force');
|
|
483
446
|
|
|
484
447
|
// Set process.argv for init to parse
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gotodev",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "⚡ Lightning-fast app creator for React, Vue, Svelte, and all modern frameworks. Built with Rust 1.92 + Oxc 0.106 for instant compilation - 10-100x faster than Vite/Vitest.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"index.js",
|
|
84
84
|
"index.d.ts",
|
|
85
85
|
"cli.js",
|
|
86
|
+
"template-fetcher.js",
|
|
86
87
|
"templates/",
|
|
87
88
|
"scripts/",
|
|
88
89
|
"gotodev-*.node",
|
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Fetcher - Generates official Vite templates with latest dependencies
|
|
3
|
+
* Updated with latest versions and security fixes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get latest package versions from npm
|
|
12
|
+
*/
|
|
13
|
+
async function getLatestVersion(pkg) {
|
|
14
|
+
try {
|
|
15
|
+
const result = execSync(`npm view ${pkg} version --json`, { encoding: 'utf8' });
|
|
16
|
+
return result.trim().replace(/['"]/g, '');
|
|
17
|
+
} catch (error) {
|
|
18
|
+
// Fallback to known latest versions
|
|
19
|
+
const fallbacks = {
|
|
20
|
+
'vite': '6.0.7',
|
|
21
|
+
'react': '19.0.0',
|
|
22
|
+
'react-dom': '19.0.0',
|
|
23
|
+
'vue': '3.5.13',
|
|
24
|
+
'svelte': '5.17.3',
|
|
25
|
+
'@vitejs/plugin-react': '4.3.4',
|
|
26
|
+
'@vitejs/plugin-vue': '5.2.1',
|
|
27
|
+
'@sveltejs/vite-plugin-svelte': '5.0.3',
|
|
28
|
+
'typescript': '5.7.2',
|
|
29
|
+
'tailwindcss': '3.4.17',
|
|
30
|
+
'postcss': '8.4.49',
|
|
31
|
+
'autoprefixer': '10.4.20',
|
|
32
|
+
'vue-router': '4.3.2',
|
|
33
|
+
'pinia': '2.3.0',
|
|
34
|
+
'@types/react': '19.0.1',
|
|
35
|
+
'@types/react-dom': '19.0.1',
|
|
36
|
+
'vue-tsc': '2.2.0',
|
|
37
|
+
'svelte-check': '4.1.4',
|
|
38
|
+
'svelte-preprocess': '6.0.3'
|
|
39
|
+
};
|
|
40
|
+
return fallbacks[pkg] || 'latest';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate React template files
|
|
46
|
+
*/
|
|
47
|
+
async function generateReactTemplate(projectName, features, targetDir) {
|
|
48
|
+
const files = {};
|
|
49
|
+
|
|
50
|
+
// Get latest versions
|
|
51
|
+
const versions = {
|
|
52
|
+
react: await getLatestVersion('react'),
|
|
53
|
+
reactDom: await getLatestVersion('react-dom'),
|
|
54
|
+
vite: await getLatestVersion('vite'),
|
|
55
|
+
pluginReact: await getLatestVersion('@vitejs/plugin-react'),
|
|
56
|
+
typescript: await getLatestVersion('typescript'),
|
|
57
|
+
typesReact: await getLatestVersion('@types/react'),
|
|
58
|
+
typesReactDom: await getLatestVersion('@types/react-dom'),
|
|
59
|
+
tailwind: await getLatestVersion('tailwindcss'),
|
|
60
|
+
postcss: await getLatestVersion('postcss'),
|
|
61
|
+
autoprefixer: await getLatestVersion('autoprefixer')
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Main entry point
|
|
65
|
+
if (features.typescript) {
|
|
66
|
+
files['src/main.tsx'] = `import React from 'react';
|
|
67
|
+
import ReactDOM from 'react-dom/client';
|
|
68
|
+
import App from './App.tsx';
|
|
69
|
+
import './index.css';
|
|
70
|
+
|
|
71
|
+
const root = ReactDOM.createRoot(
|
|
72
|
+
document.getElementById('root') as HTMLElement
|
|
73
|
+
);
|
|
74
|
+
root.render(
|
|
75
|
+
<React.StrictMode>
|
|
76
|
+
<App />
|
|
77
|
+
</React.StrictMode>
|
|
78
|
+
);`;
|
|
79
|
+
|
|
80
|
+
files['src/App.tsx'] = `import './index.css';
|
|
81
|
+
|
|
82
|
+
export default function App() {
|
|
83
|
+
return (
|
|
84
|
+
<div className="min-h-screen bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
|
|
85
|
+
<div className="text-center text-white p-8 bg-black/20 backdrop-blur-lg rounded-2xl shadow-2xl">
|
|
86
|
+
<h1 className="text-5xl font-bold mb-4">🚀 ${projectName}</h1>
|
|
87
|
+
<p className="text-xl mb-6">Built with React 19 + TypeScript + Tailwind</p>
|
|
88
|
+
<div className="flex gap-4 justify-center">
|
|
89
|
+
<a href="https://vitejs.dev" target="_blank" className="px-4 py-2 bg-white/20 rounded-lg hover:bg-white/30 transition">
|
|
90
|
+
Vite Docs
|
|
91
|
+
</a>
|
|
92
|
+
<a href="https://react.dev" target="_blank" className="px-4 py-2 bg-white/20 rounded-lg hover:bg-white/30 transition">
|
|
93
|
+
React Docs
|
|
94
|
+
</a>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}`;
|
|
100
|
+
} else {
|
|
101
|
+
files['src/main.jsx'] = `import React from 'react';
|
|
102
|
+
import ReactDOM from 'react-dom/client';
|
|
103
|
+
import App from './App.jsx';
|
|
104
|
+
import './index.css';
|
|
105
|
+
|
|
106
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
107
|
+
root.render(<App />);`;
|
|
108
|
+
|
|
109
|
+
files['src/App.jsx'] = `import './index.css';
|
|
110
|
+
|
|
111
|
+
export default function App() {
|
|
112
|
+
return (
|
|
113
|
+
<div className="min-h-screen bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
|
|
114
|
+
<div className="text-center text-white p-8 bg-black/20 backdrop-blur-lg rounded-2xl shadow-2xl">
|
|
115
|
+
<h1 className="text-5xl font-bold mb-4">🚀 ${projectName}</h1>
|
|
116
|
+
<p className="text-xl mb-6">Built with React 19 + Vite</p>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// HTML
|
|
124
|
+
files['index.html'] = `<!DOCTYPE html>
|
|
125
|
+
<html lang="en">
|
|
126
|
+
<head>
|
|
127
|
+
<meta charset="UTF-8" />
|
|
128
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
129
|
+
<title>${projectName}</title>
|
|
130
|
+
</head>
|
|
131
|
+
<body>
|
|
132
|
+
<div id="root"></div>
|
|
133
|
+
<script type="module" src="/src/main.${features.typescript ? 'tsx' : 'jsx'}"></script>
|
|
134
|
+
</body>
|
|
135
|
+
</html>`;
|
|
136
|
+
|
|
137
|
+
// Vite config
|
|
138
|
+
files['vite.config.js'] = `import { defineConfig } from 'vite';
|
|
139
|
+
import react from '@vitejs/plugin-react';
|
|
140
|
+
|
|
141
|
+
export default defineConfig({
|
|
142
|
+
plugins: [react()],
|
|
143
|
+
});`;
|
|
144
|
+
|
|
145
|
+
// CSS (with Tailwind if requested)
|
|
146
|
+
if (features.tailwind) {
|
|
147
|
+
files['src/index.css'] = `@tailwind base;
|
|
148
|
+
@tailwind components;
|
|
149
|
+
@tailwind utilities;
|
|
150
|
+
|
|
151
|
+
body {
|
|
152
|
+
margin: 0;
|
|
153
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
154
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
155
|
+
sans-serif;
|
|
156
|
+
-webkit-font-smoothing: antialiased;
|
|
157
|
+
-moz-osx-font-smoothing: grayscale;
|
|
158
|
+
}`;
|
|
159
|
+
|
|
160
|
+
files['tailwind.config.js'] = `/** @type {import('tailwindcss').Config} */
|
|
161
|
+
export default {
|
|
162
|
+
content: [
|
|
163
|
+
"./index.html",
|
|
164
|
+
"./src/**/*.{js,ts,jsx,tsx}"
|
|
165
|
+
],
|
|
166
|
+
theme: {
|
|
167
|
+
extend: {},
|
|
168
|
+
},
|
|
169
|
+
plugins: [],
|
|
170
|
+
}`;
|
|
171
|
+
|
|
172
|
+
files['postcss.config.js'] = `export default {
|
|
173
|
+
plugins: {
|
|
174
|
+
tailwindcss: {},
|
|
175
|
+
autoprefixer: {},
|
|
176
|
+
},
|
|
177
|
+
}`;
|
|
178
|
+
} else {
|
|
179
|
+
files['src/index.css'] = `body {
|
|
180
|
+
margin: 0;
|
|
181
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
182
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
183
|
+
sans-serif;
|
|
184
|
+
-webkit-font-smoothing: antialiased;
|
|
185
|
+
-moz-osx-font-smoothing: grayscale;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
code {
|
|
189
|
+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
190
|
+
monospace;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#root {
|
|
194
|
+
min-height: 100vh;
|
|
195
|
+
display: flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
199
|
+
}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Package.json
|
|
203
|
+
const pkg = {
|
|
204
|
+
name: projectName,
|
|
205
|
+
version: '0.0.0',
|
|
206
|
+
type: 'module',
|
|
207
|
+
scripts: {
|
|
208
|
+
dev: 'vite',
|
|
209
|
+
build: 'vite build',
|
|
210
|
+
lint: 'eslint .',
|
|
211
|
+
preview: 'vite preview'
|
|
212
|
+
},
|
|
213
|
+
dependencies: {
|
|
214
|
+
react: `^${versions.react}`,
|
|
215
|
+
'react-dom': `^${versions.reactDom}`
|
|
216
|
+
},
|
|
217
|
+
devDependencies: {
|
|
218
|
+
'@vitejs/plugin-react': `^${versions.pluginReact}`,
|
|
219
|
+
vite: `^${versions.vite}`
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
if (features.typescript) {
|
|
224
|
+
pkg.devDependencies['@types/react'] = `^${versions.typesReact}`;
|
|
225
|
+
pkg.devDependencies['@types/react-dom'] = `^${versions.typesReactDom}`;
|
|
226
|
+
pkg.devDependencies['typescript'] = `^${versions.typescript}`;
|
|
227
|
+
pkg.scripts.lint = 'eslint . --ext .ts,.tsx';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (features.tailwind) {
|
|
231
|
+
pkg.devDependencies['tailwindcss'] = `^${versions.tailwind}`;
|
|
232
|
+
pkg.devDependencies['postcss'] = `^${versions.postcss}`;
|
|
233
|
+
pkg.devDependencies['autoprefixer'] = `^${versions.autoprefixer}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
files['package.json'] = JSON.stringify(pkg, null, 2);
|
|
237
|
+
|
|
238
|
+
// Write all files
|
|
239
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
240
|
+
const fullPath = path.join(targetDir, filePath);
|
|
241
|
+
fs.ensureDirSync(path.dirname(fullPath));
|
|
242
|
+
fs.writeFileSync(fullPath, content);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Generate Vue template files
|
|
248
|
+
*/
|
|
249
|
+
async function generateVueTemplate(projectName, features, targetDir) {
|
|
250
|
+
const files = {};
|
|
251
|
+
|
|
252
|
+
// Get latest versions
|
|
253
|
+
const versions = {
|
|
254
|
+
vue: await getLatestVersion('vue'),
|
|
255
|
+
vite: await getLatestVersion('vite'),
|
|
256
|
+
pluginVue: await getLatestVersion('@vitejs/plugin-vue'),
|
|
257
|
+
typescript: await getLatestVersion('typescript'),
|
|
258
|
+
vueTsc: await getLatestVersion('vue-tsc'),
|
|
259
|
+
tailwind: await getLatestVersion('tailwindcss'),
|
|
260
|
+
postcss: await getLatestVersion('postcss'),
|
|
261
|
+
autoprefixer: await getLatestVersion('autoprefixer'),
|
|
262
|
+
vueRouter: await getLatestVersion('vue-router'),
|
|
263
|
+
pinia: await getLatestVersion('pinia')
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Main entry
|
|
267
|
+
if (features.typescript) {
|
|
268
|
+
files['src/main.ts'] = `import { createApp } from 'vue';
|
|
269
|
+
import App from './App.vue';
|
|
270
|
+
import './index.css';
|
|
271
|
+
|
|
272
|
+
createApp(App).mount('#app');`;
|
|
273
|
+
} else {
|
|
274
|
+
files['src/main.js'] = `import { createApp } from 'vue';
|
|
275
|
+
import App from './App.vue';
|
|
276
|
+
import './index.css';
|
|
277
|
+
|
|
278
|
+
createApp(App).mount('#app');`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// App component
|
|
282
|
+
files['src/App.vue'] = `<template>
|
|
283
|
+
<div class="min-h-screen bg-gradient-to-br from-green-400 to-blue-500 flex items-center justify-center">
|
|
284
|
+
<div class="text-center text-white p-8 bg-black/20 backdrop-blur-lg rounded-2xl shadow-2xl">
|
|
285
|
+
<h1 class="text-5xl font-bold mb-4">🚀 ${projectName}</h1>
|
|
286
|
+
<p class="text-xl mb-6">Built with Vue 3 + Vite${features.typescript ? ' + TypeScript' : ''}${features.tailwind ? ' + Tailwind' : ''}</p>
|
|
287
|
+
<div class="flex gap-4 justify-center">
|
|
288
|
+
<a href="https://vitejs.dev" target="_blank" class="px-4 py-2 bg-white/20 rounded-lg hover:bg-white/30 transition">Vite Docs</a>
|
|
289
|
+
<a href="https://vuejs.org" target="_blank" class="px-4 py-2 bg-white/20 rounded-lg hover:bg-white/30 transition">Vue Docs</a>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
</template>
|
|
294
|
+
|
|
295
|
+
<script setup>
|
|
296
|
+
// Vue 3 Composition API
|
|
297
|
+
</script>
|
|
298
|
+
|
|
299
|
+
<style scoped>
|
|
300
|
+
/* Component-specific styles */
|
|
301
|
+
</style>`;
|
|
302
|
+
|
|
303
|
+
// HTML
|
|
304
|
+
files['index.html'] = `<!DOCTYPE html>
|
|
305
|
+
<html lang="en">
|
|
306
|
+
<head>
|
|
307
|
+
<meta charset="UTF-8" />
|
|
308
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
309
|
+
<title>${projectName}</title>
|
|
310
|
+
</head>
|
|
311
|
+
<body>
|
|
312
|
+
<div id="app"></div>
|
|
313
|
+
<script type="module" src="/src/main.${features.typescript ? 'ts' : 'js'}"></script>
|
|
314
|
+
</body>
|
|
315
|
+
</html>`;
|
|
316
|
+
|
|
317
|
+
// Vite config
|
|
318
|
+
files['vite.config.js'] = `import { defineConfig } from 'vite';
|
|
319
|
+
import vue from '@vitejs/plugin-vue';
|
|
320
|
+
|
|
321
|
+
export default defineConfig({
|
|
322
|
+
plugins: [vue()],
|
|
323
|
+
});`;
|
|
324
|
+
|
|
325
|
+
// CSS
|
|
326
|
+
if (features.tailwind) {
|
|
327
|
+
files['src/index.css'] = `@tailwind base;
|
|
328
|
+
@tailwind components;
|
|
329
|
+
@tailwind utilities;
|
|
330
|
+
|
|
331
|
+
body {
|
|
332
|
+
margin: 0;
|
|
333
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
334
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
335
|
+
sans-serif;
|
|
336
|
+
-webkit-font-smoothing: antialiased;
|
|
337
|
+
-moz-osx-font-smoothing: grayscale;
|
|
338
|
+
}`;
|
|
339
|
+
|
|
340
|
+
files['tailwind.config.js'] = `/** @type {import('tailwindcss').Config} */
|
|
341
|
+
export default {
|
|
342
|
+
content: [
|
|
343
|
+
"./index.html",
|
|
344
|
+
"./src/**/*.{vue,js,ts,jsx,tsx}"
|
|
345
|
+
],
|
|
346
|
+
theme: {
|
|
347
|
+
extend: {},
|
|
348
|
+
},
|
|
349
|
+
plugins: [],
|
|
350
|
+
}`;
|
|
351
|
+
|
|
352
|
+
files['postcss.config.js'] = `export default {
|
|
353
|
+
plugins: {
|
|
354
|
+
tailwindcss: {},
|
|
355
|
+
autoprefixer: {},
|
|
356
|
+
},
|
|
357
|
+
}`;
|
|
358
|
+
} else {
|
|
359
|
+
files['src/index.css'] = `body {
|
|
360
|
+
margin: 0;
|
|
361
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
362
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
363
|
+
sans-serif;
|
|
364
|
+
-webkit-font-smoothing: antialiased;
|
|
365
|
+
-moz-osx-font-smoothing: grayscale;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#root {
|
|
369
|
+
min-height: 100vh;
|
|
370
|
+
display: flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
justify-content: center;
|
|
373
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
374
|
+
}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Package.json
|
|
378
|
+
const pkg = {
|
|
379
|
+
name: projectName,
|
|
380
|
+
version: '0.0.0',
|
|
381
|
+
type: 'module',
|
|
382
|
+
scripts: {
|
|
383
|
+
dev: 'vite',
|
|
384
|
+
build: 'vite build',
|
|
385
|
+
preview: 'vite preview'
|
|
386
|
+
},
|
|
387
|
+
dependencies: {
|
|
388
|
+
vue: `^${versions.vue}`
|
|
389
|
+
},
|
|
390
|
+
devDependencies: {
|
|
391
|
+
'@vitejs/plugin-vue': `^${versions.pluginVue}`,
|
|
392
|
+
vite: `^${versions.vite}`
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
if (features.typescript) {
|
|
397
|
+
pkg.devDependencies['typescript'] = `^${versions.typescript}`;
|
|
398
|
+
pkg.devDependencies['vue-tsc'] = `^${versions.vueTsc}`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (features.router) {
|
|
402
|
+
pkg.dependencies['vue-router'] = `^${versions.vueRouter}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (features.pinia) {
|
|
406
|
+
pkg.dependencies['pinia'] = `^${versions.pinia}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (features.tailwind) {
|
|
410
|
+
pkg.devDependencies['tailwindcss'] = `^${versions.tailwind}`;
|
|
411
|
+
pkg.devDependencies['postcss'] = `^${versions.postcss}`;
|
|
412
|
+
pkg.devDependencies['autoprefixer'] = `^${versions.autoprefixer}`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
files['package.json'] = JSON.stringify(pkg, null, 2);
|
|
416
|
+
|
|
417
|
+
// Write all files
|
|
418
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
419
|
+
const fullPath = path.join(targetDir, filePath);
|
|
420
|
+
fs.ensureDirSync(path.dirname(fullPath));
|
|
421
|
+
fs.writeFileSync(fullPath, content);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Generate Svelte template files
|
|
427
|
+
*/
|
|
428
|
+
async function generateSvelteTemplate(projectName, features, targetDir) {
|
|
429
|
+
const files = {};
|
|
430
|
+
|
|
431
|
+
// Get latest versions
|
|
432
|
+
const versions = {
|
|
433
|
+
svelte: await getLatestVersion('svelte'),
|
|
434
|
+
vite: await getLatestVersion('vite'),
|
|
435
|
+
pluginSvelte: await getLatestVersion('@sveltejs/vite-plugin-svelte'),
|
|
436
|
+
typescript: await getLatestVersion('typescript'),
|
|
437
|
+
svelteCheck: await getLatestVersion('svelte-check'),
|
|
438
|
+
sveltePreprocess: await getLatestVersion('svelte-preprocess'),
|
|
439
|
+
tailwind: await getLatestVersion('tailwindcss'),
|
|
440
|
+
postcss: await getLatestVersion('postcss'),
|
|
441
|
+
autoprefixer: await getLatestVersion('autoprefixer')
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Main entry
|
|
445
|
+
if (features.typescript) {
|
|
446
|
+
files['src/main.ts'] = `import App from './App.svelte';
|
|
447
|
+
|
|
448
|
+
const app = new App({
|
|
449
|
+
target: document.body,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
export default app;`;
|
|
453
|
+
|
|
454
|
+
files['src/App.svelte'] = `<script lang="ts">
|
|
455
|
+
let name: string = '${projectName}';
|
|
456
|
+
</script>
|
|
457
|
+
|
|
458
|
+
<main>
|
|
459
|
+
<div class="container">
|
|
460
|
+
<h1>🚀 {name}</h1>
|
|
461
|
+
<p>Built with Svelte 5 + TypeScript + Tailwind</p>
|
|
462
|
+
<div class="links">
|
|
463
|
+
<a href="https://vitejs.dev" target="_blank">Vite Docs</a>
|
|
464
|
+
<a href="https://svelte.dev" target="_blank">Svelte Docs</a>
|
|
465
|
+
</div>
|
|
466
|
+
</div>
|
|
467
|
+
</main>
|
|
468
|
+
|
|
469
|
+
<style>
|
|
470
|
+
main {
|
|
471
|
+
min-height: 100vh;
|
|
472
|
+
display: flex;
|
|
473
|
+
align-items: center;
|
|
474
|
+
justify-content: center;
|
|
475
|
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
476
|
+
}
|
|
477
|
+
.container {
|
|
478
|
+
text-align: center;
|
|
479
|
+
padding: 2rem;
|
|
480
|
+
background: rgba(255, 255, 255, 0.1);
|
|
481
|
+
backdrop-filter: blur(10px);
|
|
482
|
+
border-radius: 1rem;
|
|
483
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
484
|
+
}
|
|
485
|
+
h1 {
|
|
486
|
+
font-size: 3rem;
|
|
487
|
+
margin-bottom: 1rem;
|
|
488
|
+
color: white;
|
|
489
|
+
}
|
|
490
|
+
p {
|
|
491
|
+
font-size: 1.2rem;
|
|
492
|
+
color: white;
|
|
493
|
+
margin-bottom: 2rem;
|
|
494
|
+
}
|
|
495
|
+
.links {
|
|
496
|
+
display: flex;
|
|
497
|
+
gap: 1rem;
|
|
498
|
+
justify-content: center;
|
|
499
|
+
}
|
|
500
|
+
a {
|
|
501
|
+
padding: 0.5rem 1rem;
|
|
502
|
+
background: rgba(255, 255, 255, 0.2);
|
|
503
|
+
color: white;
|
|
504
|
+
text-decoration: none;
|
|
505
|
+
border-radius: 0.5rem;
|
|
506
|
+
transition: background 0.3s;
|
|
507
|
+
}
|
|
508
|
+
a:hover {
|
|
509
|
+
background: rgba(255, 255, 255, 0.3);
|
|
510
|
+
}
|
|
511
|
+
</style>`;
|
|
512
|
+
} else {
|
|
513
|
+
files['src/main.js'] = `import App from './App.svelte';
|
|
514
|
+
|
|
515
|
+
const app = new App({
|
|
516
|
+
target: document.body,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
export default app;`;
|
|
520
|
+
|
|
521
|
+
files['src/App.svelte'] = `<script>
|
|
522
|
+
let name = '${projectName}';
|
|
523
|
+
</script>
|
|
524
|
+
|
|
525
|
+
<main>
|
|
526
|
+
<div class="container">
|
|
527
|
+
<h1>🚀 {name}</h1>
|
|
528
|
+
<p>Built with Svelte 5 + Vite</p>
|
|
529
|
+
</div>
|
|
530
|
+
</main>
|
|
531
|
+
|
|
532
|
+
<style>
|
|
533
|
+
main {
|
|
534
|
+
min-height: 100vh;
|
|
535
|
+
display: flex;
|
|
536
|
+
align-items: center;
|
|
537
|
+
justify-content: center;
|
|
538
|
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
539
|
+
}
|
|
540
|
+
.container {
|
|
541
|
+
text-align: center;
|
|
542
|
+
padding: 2rem;
|
|
543
|
+
background: rgba(255, 255, 255, 0.1);
|
|
544
|
+
backdrop-filter: blur(10px);
|
|
545
|
+
border-radius: 1rem;
|
|
546
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
547
|
+
}
|
|
548
|
+
h1 {
|
|
549
|
+
font-size: 3rem;
|
|
550
|
+
margin-bottom: 1rem;
|
|
551
|
+
color: white;
|
|
552
|
+
}
|
|
553
|
+
p {
|
|
554
|
+
font-size: 1.2rem;
|
|
555
|
+
color: white;
|
|
556
|
+
}
|
|
557
|
+
</style>`;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// HTML
|
|
561
|
+
files['index.html'] = `<!DOCTYPE html>
|
|
562
|
+
<html lang="en">
|
|
563
|
+
<head>
|
|
564
|
+
<meta charset="UTF-8" />
|
|
565
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
566
|
+
<title>${projectName}</title>
|
|
567
|
+
</head>
|
|
568
|
+
<body>
|
|
569
|
+
<script type="module" src="/src/main.${features.typescript ? 'ts' : 'js'}"></script>
|
|
570
|
+
</body>
|
|
571
|
+
</html>`;
|
|
572
|
+
|
|
573
|
+
// Vite config
|
|
574
|
+
files['vite.config.js'] = `import { defineConfig } from 'vite';
|
|
575
|
+
import { svelte } from '@vitejs/vite-plugin-svelte';
|
|
576
|
+
|
|
577
|
+
export default defineConfig({
|
|
578
|
+
plugins: [svelte()],
|
|
579
|
+
});`;
|
|
580
|
+
|
|
581
|
+
// Svelte config (for TypeScript)
|
|
582
|
+
if (features.typescript) {
|
|
583
|
+
files['svelte.config.js'] = `import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
584
|
+
|
|
585
|
+
export default {
|
|
586
|
+
preprocess: vitePreprocess(),
|
|
587
|
+
};`;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// TypeScript config
|
|
591
|
+
if (features.typescript) {
|
|
592
|
+
files['tsconfig.json'] = `{
|
|
593
|
+
"extends": "@sveltejs/tsconfig/tsconfig.json",
|
|
594
|
+
"compilerOptions": {
|
|
595
|
+
"strict": true
|
|
596
|
+
},
|
|
597
|
+
"include": ["src/**/*"],
|
|
598
|
+
"exclude": ["node_modules/*", "dist/*"]
|
|
599
|
+
}`;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// CSS (with Tailwind if requested)
|
|
603
|
+
if (features.tailwind) {
|
|
604
|
+
files['src/app.css'] = `@tailwind base;
|
|
605
|
+
@tailwind components;
|
|
606
|
+
@tailwind utilities;
|
|
607
|
+
|
|
608
|
+
body {
|
|
609
|
+
margin: 0;
|
|
610
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
611
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
612
|
+
sans-serif;
|
|
613
|
+
-webkit-font-smoothing: antialiased;
|
|
614
|
+
-moz-osx-font-smoothing: grayscale;
|
|
615
|
+
}`;
|
|
616
|
+
|
|
617
|
+
files['tailwind.config.js'] = `/** @type {import('tailwindcss').Config} */
|
|
618
|
+
export default {
|
|
619
|
+
content: [
|
|
620
|
+
"./index.html",
|
|
621
|
+
"./src/**/*.{svelte,js,ts}"
|
|
622
|
+
],
|
|
623
|
+
theme: {
|
|
624
|
+
extend: {},
|
|
625
|
+
},
|
|
626
|
+
plugins: [],
|
|
627
|
+
}`;
|
|
628
|
+
|
|
629
|
+
files['postcss.config.js'] = `export default {
|
|
630
|
+
plugins: {
|
|
631
|
+
tailwindcss: {},
|
|
632
|
+
autoprefixer: {},
|
|
633
|
+
},
|
|
634
|
+
}`;
|
|
635
|
+
} else {
|
|
636
|
+
files['src/app.css'] = `body {
|
|
637
|
+
margin: 0;
|
|
638
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
639
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
640
|
+
sans-serif;
|
|
641
|
+
-webkit-font-smoothing: antialiased;
|
|
642
|
+
-moz-osx-font-smoothing: grayscale;
|
|
643
|
+
}`;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Package.json
|
|
647
|
+
const pkg = {
|
|
648
|
+
name: projectName,
|
|
649
|
+
version: '0.0.0',
|
|
650
|
+
type: 'module',
|
|
651
|
+
scripts: {
|
|
652
|
+
dev: 'vite',
|
|
653
|
+
build: 'vite build',
|
|
654
|
+
preview: 'vite preview'
|
|
655
|
+
},
|
|
656
|
+
dependencies: {
|
|
657
|
+
svelte: `^${versions.svelte}`
|
|
658
|
+
},
|
|
659
|
+
devDependencies: {
|
|
660
|
+
'@sveltejs/vite-plugin-svelte': `^${versions.pluginSvelte}`,
|
|
661
|
+
vite: `^${versions.vite}`
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
if (features.typescript) {
|
|
666
|
+
pkg.devDependencies['typescript'] = `^${versions.typescript}`;
|
|
667
|
+
pkg.devDependencies['svelte-check'] = `^${versions.svelteCheck}`;
|
|
668
|
+
pkg.devDependencies['svelte-preprocess'] = `^${versions.sveltePreprocess}`;
|
|
669
|
+
pkg.scripts.check = 'svelte-check';
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (features.tailwind) {
|
|
673
|
+
pkg.devDependencies['tailwindcss'] = `^${versions.tailwind}`;
|
|
674
|
+
pkg.devDependencies['postcss'] = `^${versions.postcss}`;
|
|
675
|
+
pkg.devDependencies['autoprefixer'] = `^${versions.autoprefixer}`;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
files['package.json'] = JSON.stringify(pkg, null, 2);
|
|
679
|
+
|
|
680
|
+
// Write all files
|
|
681
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
682
|
+
const fullPath = path.join(targetDir, filePath);
|
|
683
|
+
fs.ensureDirSync(path.dirname(fullPath));
|
|
684
|
+
fs.writeFileSync(fullPath, content);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Generate Vanilla template files
|
|
690
|
+
*/
|
|
691
|
+
async function generateVanillaTemplate(projectName, features, targetDir) {
|
|
692
|
+
const files = {};
|
|
693
|
+
|
|
694
|
+
// Get latest versions
|
|
695
|
+
const versions = {
|
|
696
|
+
vite: await getLatestVersion('vite'),
|
|
697
|
+
tailwind: await getLatestVersion('tailwindcss'),
|
|
698
|
+
postcss: await getLatestVersion('postcss'),
|
|
699
|
+
autoprefixer: await getLatestVersion('autoprefixer')
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// Main entry
|
|
703
|
+
files['src/main.js'] = `import './style.css';
|
|
704
|
+
|
|
705
|
+
document.querySelector('#app').innerHTML = \`
|
|
706
|
+
<div class="container">
|
|
707
|
+
<h1>🚀 ${projectName}</h1>
|
|
708
|
+
<p>Built with Vanilla JavaScript + Vite${features.tailwind ? ' + Tailwind' : ''}</p>
|
|
709
|
+
<div class="links">
|
|
710
|
+
<a href="https://vitejs.dev" target="_blank">Vite Docs</a>
|
|
711
|
+
<a href="https://developer.mozilla.org" target="_blank">MDN Docs</a>
|
|
712
|
+
</div>
|
|
713
|
+
</div>
|
|
714
|
+
\`;`;
|
|
715
|
+
|
|
716
|
+
// HTML
|
|
717
|
+
files['index.html'] = `<!DOCTYPE html>
|
|
718
|
+
<html lang="en">
|
|
719
|
+
<head>
|
|
720
|
+
<meta charset="UTF-8" />
|
|
721
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
722
|
+
<title>${projectName}</title>
|
|
723
|
+
</head>
|
|
724
|
+
<body>
|
|
725
|
+
<div id="app"></div>
|
|
726
|
+
<script type="module" src="/src/main.js"></script>
|
|
727
|
+
</body>
|
|
728
|
+
</html>`;
|
|
729
|
+
|
|
730
|
+
// Vite config
|
|
731
|
+
files['vite.config.js'] = `import { defineConfig } from 'vite';
|
|
732
|
+
|
|
733
|
+
export default defineConfig({
|
|
734
|
+
plugins: [],
|
|
735
|
+
});`;
|
|
736
|
+
|
|
737
|
+
// CSS
|
|
738
|
+
if (features.tailwind) {
|
|
739
|
+
files['style.css'] = `@tailwind base;
|
|
740
|
+
@tailwind components;
|
|
741
|
+
@tailwind utilities;
|
|
742
|
+
|
|
743
|
+
body {
|
|
744
|
+
margin: 0;
|
|
745
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
746
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
747
|
+
sans-serif;
|
|
748
|
+
-webkit-font-smoothing: antialiased;
|
|
749
|
+
-moz-osx-font-smoothing: grayscale;
|
|
750
|
+
min-height: 100vh;
|
|
751
|
+
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
|
752
|
+
display: flex;
|
|
753
|
+
align-items: center;
|
|
754
|
+
justify-content: center;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.container {
|
|
758
|
+
text-align: center;
|
|
759
|
+
padding: 2rem;
|
|
760
|
+
background: rgba(255, 255, 255, 0.1);
|
|
761
|
+
backdrop-filter: blur(10px);
|
|
762
|
+
border-radius: 1rem;
|
|
763
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
764
|
+
color: white;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
h1 {
|
|
768
|
+
font-size: 3rem;
|
|
769
|
+
margin-bottom: 1rem;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
p {
|
|
773
|
+
font-size: 1.2rem;
|
|
774
|
+
margin-bottom: 2rem;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.links {
|
|
778
|
+
display: flex;
|
|
779
|
+
gap: 1rem;
|
|
780
|
+
justify-content: center;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
a {
|
|
784
|
+
padding: 0.5rem 1rem;
|
|
785
|
+
background: rgba(255, 255, 255, 0.2);
|
|
786
|
+
color: white;
|
|
787
|
+
text-decoration: none;
|
|
788
|
+
border-radius: 0.5rem;
|
|
789
|
+
transition: background 0.3s;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
a:hover {
|
|
793
|
+
background: rgba(255, 255, 255, 0.3);
|
|
794
|
+
}`;
|
|
795
|
+
|
|
796
|
+
files['tailwind.config.js'] = `/** @type {import('tailwindcss').Config} */
|
|
797
|
+
export default {
|
|
798
|
+
content: [
|
|
799
|
+
"./index.html",
|
|
800
|
+
"./src/**/*.{js,ts}"
|
|
801
|
+
],
|
|
802
|
+
theme: {
|
|
803
|
+
extend: {},
|
|
804
|
+
},
|
|
805
|
+
plugins: [],
|
|
806
|
+
}`;
|
|
807
|
+
|
|
808
|
+
files['postcss.config.js'] = `export default {
|
|
809
|
+
plugins: {
|
|
810
|
+
tailwindcss: {},
|
|
811
|
+
autoprefixer: {},
|
|
812
|
+
},
|
|
813
|
+
}`;
|
|
814
|
+
} else {
|
|
815
|
+
files['style.css'] = `body {
|
|
816
|
+
margin: 0;
|
|
817
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
818
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
819
|
+
sans-serif;
|
|
820
|
+
-webkit-font-smoothing: antialiased;
|
|
821
|
+
-moz-osx-font-smoothing: grayscale;
|
|
822
|
+
min-height: 100vh;
|
|
823
|
+
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
|
824
|
+
display: flex;
|
|
825
|
+
align-items: center;
|
|
826
|
+
justify-content: center;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
#app {
|
|
830
|
+
text-align: center;
|
|
831
|
+
padding: 2rem;
|
|
832
|
+
background: rgba(255, 255, 255, 0.1);
|
|
833
|
+
backdrop-filter: blur(10px);
|
|
834
|
+
border-radius: 1rem;
|
|
835
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
836
|
+
color: white;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
h1 {
|
|
840
|
+
font-size: 3rem;
|
|
841
|
+
margin-bottom: 1rem;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
p {
|
|
845
|
+
font-size: 1.2rem;
|
|
846
|
+
margin-bottom: 2rem;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.links {
|
|
850
|
+
display: flex;
|
|
851
|
+
gap: 1rem;
|
|
852
|
+
justify-content: center;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
a {
|
|
856
|
+
padding: 0.5rem 1rem;
|
|
857
|
+
background: rgba(255, 255, 255, 0.2);
|
|
858
|
+
color: white;
|
|
859
|
+
text-decoration: none;
|
|
860
|
+
border-radius: 0.5rem;
|
|
861
|
+
transition: background 0.3s;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
a:hover {
|
|
865
|
+
background: rgba(255, 255, 255, 0.3);
|
|
866
|
+
}`;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Package.json
|
|
870
|
+
const pkg = {
|
|
871
|
+
name: projectName,
|
|
872
|
+
version: '0.0.0',
|
|
873
|
+
type: 'module',
|
|
874
|
+
scripts: {
|
|
875
|
+
dev: 'vite',
|
|
876
|
+
build: 'vite build',
|
|
877
|
+
preview: 'vite preview'
|
|
878
|
+
},
|
|
879
|
+
devDependencies: {
|
|
880
|
+
vite: `^${versions.vite}`
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
if (features.tailwind) {
|
|
885
|
+
pkg.devDependencies['tailwindcss'] = `^${versions.tailwind}`;
|
|
886
|
+
pkg.devDependencies['postcss'] = `^${versions.postcss}`;
|
|
887
|
+
pkg.devDependencies['autoprefixer'] = `^${versions.autoprefixer}`;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
files['package.json'] = JSON.stringify(pkg, null, 2);
|
|
891
|
+
|
|
892
|
+
// Write all files
|
|
893
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
894
|
+
const fullPath = path.join(targetDir, filePath);
|
|
895
|
+
fs.ensureDirSync(path.dirname(fullPath));
|
|
896
|
+
fs.writeFileSync(fullPath, content);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Main function to get Vite template
|
|
902
|
+
*/
|
|
903
|
+
async function getViteTemplate(projectName, framework, features, targetDir) {
|
|
904
|
+
const ora = require('ora').default;
|
|
905
|
+
const spinner = ora(`Generating ${framework} template with latest dependencies...`).start();
|
|
906
|
+
|
|
907
|
+
try {
|
|
908
|
+
// Ensure target directory exists
|
|
909
|
+
fs.ensureDirSync(targetDir);
|
|
910
|
+
|
|
911
|
+
// Generate template based on framework
|
|
912
|
+
if (framework === 'react') {
|
|
913
|
+
await generateReactTemplate(projectName, features, targetDir);
|
|
914
|
+
} else if (framework === 'vue') {
|
|
915
|
+
await generateVueTemplate(projectName, features, targetDir);
|
|
916
|
+
} else if (framework === 'svelte') {
|
|
917
|
+
await generateSvelteTemplate(projectName, features, targetDir);
|
|
918
|
+
} else if (framework === 'vanilla') {
|
|
919
|
+
await generateVanillaTemplate(projectName, features, targetDir);
|
|
920
|
+
} else {
|
|
921
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Generate README
|
|
925
|
+
const readme = `# ${projectName}
|
|
926
|
+
|
|
927
|
+
Created with [gotodev](https://npmjs.com/package/gotodev) ⚡
|
|
928
|
+
|
|
929
|
+
## Framework: ${framework.toUpperCase()}${features.typescript ? ' + TypeScript' : ''}${features.tailwind ? ' + Tailwind CSS' : ''}
|
|
930
|
+
|
|
931
|
+
### Latest Dependencies
|
|
932
|
+
- Vite: ${await getLatestVersion('vite')}
|
|
933
|
+
- Framework: ${framework === 'react' ? 'React 19' : framework === 'vue' ? 'Vue 3' : framework === 'svelte' ? 'Svelte 5' : 'Vanilla JS'}
|
|
934
|
+
- Build tool: Rust + Oxc (10-100x faster)
|
|
935
|
+
|
|
936
|
+
### Getting Started
|
|
937
|
+
|
|
938
|
+
\`\`\`bash
|
|
939
|
+
npm install
|
|
940
|
+
npm run dev
|
|
941
|
+
\`\`\`
|
|
942
|
+
|
|
943
|
+
### Features
|
|
944
|
+
${features.typescript ? '- TypeScript support\n' : ''}${features.tailwind ? '- Tailwind CSS styling\n' : ''}${features.router ? '- Vue Router\n' : ''}${features.pinia ? '- Pinia state management\n' : ''}
|
|
945
|
+
|
|
946
|
+
### Why gotodev?
|
|
947
|
+
- ⚡ **Lightning-fast**: Powered by Rust + Oxc
|
|
948
|
+
- 🎨 **Modern**: Latest Vite + framework versions
|
|
949
|
+
- 🔥 **Hot HMR**: Instant updates
|
|
950
|
+
- 🛡️ **Secure**: SSRF and path traversal protection
|
|
951
|
+
|
|
952
|
+
Built with gotodev v${require('./package.json').version}`;
|
|
953
|
+
|
|
954
|
+
fs.writeFileSync(path.join(targetDir, 'README.md'), readme);
|
|
955
|
+
|
|
956
|
+
// Generate .gitignore
|
|
957
|
+
const gitignore = `node_modules/
|
|
958
|
+
dist/
|
|
959
|
+
build/
|
|
960
|
+
*.local
|
|
961
|
+
.env
|
|
962
|
+
.env.local
|
|
963
|
+
.vscode/
|
|
964
|
+
.idea/
|
|
965
|
+
*.log
|
|
966
|
+
.DS_Store
|
|
967
|
+
Thumbs.db
|
|
968
|
+
*.tsbuildinfo
|
|
969
|
+
.pnpm-debug.log*
|
|
970
|
+
.yarn-integrity
|
|
971
|
+
.cache
|
|
972
|
+
.next
|
|
973
|
+
.nuxt
|
|
974
|
+
.storybook-out
|
|
975
|
+
tmp/
|
|
976
|
+
temp/`;
|
|
977
|
+
|
|
978
|
+
fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
|
|
979
|
+
|
|
980
|
+
spinner.succeed(`✅ Created ${framework} project with latest dependencies!`);
|
|
981
|
+
|
|
982
|
+
} catch (error) {
|
|
983
|
+
spinner.fail(`❌ Failed to create template: ${error.message}`);
|
|
984
|
+
throw error;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
module.exports = { getViteTemplate };
|