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.
Files changed (3) hide show
  1. package/cli.js +90 -127
  2. package/package.json +1 -1
  3. 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: 'router', label: 'Vue Router' },
101
- { value: 'pinia', label: 'Pinia (State Management)' },
102
- { value: 'jsx', label: 'JSX Support' },
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', 'router', 'pinia', 'jsx', 'force', 'bare', 'help', 'version'];
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 using official templates.
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 --typescript --router
212
- gotodev create my-app --ts --pinia --force`);
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', 'jsx'].includes(key)
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: FEATURE_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
- selectedFramework = 'vue';
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('Fetching official Vite template...');
283
+ s.start('Generating template...');
343
284
 
344
285
  try {
345
- // Use template fetcher to get official Vite templates
346
- await getViteTemplate(selectedFramework, {
347
- typescript: needsTypeScript,
348
- router: needsRouter,
349
- pinia: needsPinia,
350
- jsx: needsJsx
351
- }, packageName);
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 fetched and scaffolded!');
299
+ s.stop('Template generated!');
354
300
  } catch (error) {
355
- s.stop('Failed to fetch template');
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
- // Download and copy template
428
- const tmpDir = await downloadTemplate(options.template);
429
- const copyDir = async (src, dest) => {
430
- await fs.ensureDir(dest);
431
- const entries = await fs.readdir(src, { withFileTypes: true });
432
-
433
- for (const entry of entries) {
434
- const srcPath = path.join(src, entry.name);
435
- const destPath = path.join(dest, entry.name);
436
-
437
- if (entry.isDirectory()) {
438
- await copyDir(srcPath, destPath);
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
- await copyDir(tmpDir, root);
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.typescript) args.push('--typescript');
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.2",
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",