gotodev 2.0.2 → 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 +1 -1
- package/template-fetcher.js +877 -262
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",
|