gotodev 2.0.1 → 2.0.2

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 (2) hide show
  1. package/package.json +2 -1
  2. package/template-fetcher.js +373 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gotodev",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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,373 @@
1
+ /**
2
+ * Template Fetcher - Fetches official Vite templates from GitHub
3
+ */
4
+
5
+ const axios = require('axios');
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+
9
+ /**
10
+ * Fetch file from GitHub raw content
11
+ */
12
+ async function fetchFile(url) {
13
+ try {
14
+ const response = await axios.get(url, { timeout: 15000 });
15
+ return response.data;
16
+ } catch (error) {
17
+ if (error.response?.status === 404) return null;
18
+ throw new Error(`Failed to fetch ${url}: ${error.message}`);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Recursively fetch directory from GitHub
24
+ */
25
+ async function fetchDirectory(repo, branch, dirPath, targetDir, data) {
26
+ const apiUrl = `https://api.github.com/repos/${repo}/contents/${dirPath}?ref=${branch}`;
27
+
28
+ try {
29
+ const response = await axios.get(apiUrl, {
30
+ headers: { 'Accept': 'application/vnd.github.v3+json' },
31
+ timeout: 15000
32
+ });
33
+
34
+ const contents = response.data;
35
+
36
+ for (const item of contents) {
37
+ const targetPath = path.join(targetDir, item.name);
38
+
39
+ if (item.type === 'file') {
40
+ // Fetch file content
41
+ const content = await fetchFile(item.download_url);
42
+
43
+ if (content) {
44
+ // Simple EJS-like replacement for projectName
45
+ let processed = content;
46
+ if (data.projectName) {
47
+ processed = processed.replace(/\{\{projectName\}\}/g, data.projectName);
48
+ }
49
+
50
+ fs.ensureDirSync(path.dirname(targetPath));
51
+ fs.writeFileSync(targetPath, processed);
52
+ }
53
+ } else if (item.type === 'dir') {
54
+ // Recursively fetch subdirectory
55
+ await fetchDirectory(repo, branch, item.path, targetPath, data);
56
+ }
57
+ }
58
+ } catch (error) {
59
+ console.warn(`Warning: Could not fetch ${dirPath}: ${error.message}`);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Get Vite template for a framework
65
+ */
66
+ async function getViteTemplate(framework, features = {}, projectName = 'my-app') {
67
+ const data = { projectName, ...features };
68
+
69
+ // For now, use built-in templates (we'll add GitHub fetching later)
70
+ // This ensures reliability while we develop the feature
71
+
72
+ if (framework === 'vue') {
73
+ // Vue template
74
+ const vueFiles = {
75
+ 'src/main.js': `import { createApp } from 'vue'
76
+ import App from './App.vue'
77
+
78
+ createApp(App).mount('#app')`,
79
+ 'src/App.vue': `<template>
80
+ <div id="app">
81
+ <h1>⚡ Vue + Vite</h1>
82
+ <p>Powered by gotodev</p>
83
+ </div>
84
+ </template>
85
+
86
+ <style>
87
+ #app { font-family: sans-serif; padding: 2rem; text-align: center; }
88
+ </style>`,
89
+ 'index.html': `<!DOCTYPE html>
90
+ <html lang="en">
91
+ <head>
92
+ <meta charset="UTF-8" />
93
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
94
+ <title>${projectName}</title>
95
+ </head>
96
+ <body>
97
+ <div id="app"></div>
98
+ <script type="module" src="/src/main.js"></script>
99
+ </body>
100
+ </html>`,
101
+ 'vite.config.js': `import { defineConfig } from 'vite'
102
+ import vue from '@vitejs/plugin-vue'
103
+
104
+ export default defineConfig({
105
+ plugins: [vue()],
106
+ })`
107
+ };
108
+
109
+ // Add TypeScript
110
+ if (features.typescript) {
111
+ vueFiles['src/main.ts'] = vueFiles['src/main.js'].replace('.js', '.ts');
112
+ vueFiles['src/App.vue'] = vueFiles['src/App.vue'].replace('<script setup>', '<script setup lang="ts">');
113
+ vueFiles['tsconfig.json'] = `{
114
+ "compilerOptions": {
115
+ "target": "ES2020",
116
+ "module": "ESNext",
117
+ "lib": ["ES2020", "DOM"],
118
+ "skipLibCheck": true,
119
+ "moduleResolution": "bundler",
120
+ "strict": true
121
+ }
122
+ }`;
123
+ delete vueFiles['src/main.js'];
124
+ }
125
+
126
+ // Add Router and Pinia (handle both together)
127
+ if (features.router || features.pinia) {
128
+ let imports = [];
129
+ let uses = [];
130
+
131
+ if (features.router) {
132
+ vueFiles['src/router/index.js'] = `import { createRouter, createWebHistory } from 'vue-router'
133
+ import HomeView from '../views/HomeView.vue'
134
+
135
+ const routes = [{ path: '/', name: 'home', component: HomeView }]
136
+ const router = createRouter({ history: createWebHistory(), routes })
137
+ export default router`;
138
+
139
+ vueFiles['src/views/HomeView.vue'] = `<template><div><h2>Home</h2></div></template>`;
140
+ imports.push("import router from './router'");
141
+ uses.push(".use(router)");
142
+ }
143
+
144
+ if (features.pinia) {
145
+ vueFiles['src/store.js'] = `import { defineStore } from 'pinia'
146
+ export const useMainStore = defineStore('main', {
147
+ state: () => ({ count: 0 }),
148
+ actions: { increment() { this.count++ } }
149
+ })`;
150
+ imports.push("import { createPinia } from 'pinia'");
151
+ uses.push(".use(createPinia())");
152
+ }
153
+
154
+ const mainKey = features.typescript ? 'src/main.ts' : 'src/main.js';
155
+ const current = vueFiles[mainKey];
156
+
157
+ // Build the new main file
158
+ let newMain = current;
159
+
160
+ // Add imports after the first line
161
+ if (imports.length > 0) {
162
+ const lines = current.split('\n');
163
+ const importLineIndex = lines.findIndex(line => line.includes("import { createApp } from 'vue'"));
164
+ if (importLineIndex !== -1) {
165
+ lines.splice(importLineIndex + 1, 0, ...imports);
166
+ newMain = lines.join('\n');
167
+ }
168
+ }
169
+
170
+ // Replace mount call
171
+ const mountCall = "createApp(App).mount('#app')";
172
+ const newMountCall = `createApp(App)${uses.join('')}.mount('#app')`;
173
+ console.log('DEBUG: Looking for:', mountCall);
174
+ console.log('DEBUG: In content:', newMain);
175
+ console.log('DEBUG: Replacing with:', newMountCall);
176
+ newMain = newMain.replace(mountCall, newMountCall);
177
+ console.log('DEBUG: Result:', newMain);
178
+
179
+ vueFiles[mainKey] = newMain;
180
+ }
181
+
182
+ // Write files
183
+ Object.entries(vueFiles).forEach(([file, content]) => {
184
+ const fullPath = path.join(process.cwd(), file);
185
+ fs.ensureDirSync(path.dirname(fullPath));
186
+ if (file.includes('main')) {
187
+ console.log('DEBUG WRITING:', file, 'with content:', content);
188
+ }
189
+ fs.writeFileSync(fullPath, content);
190
+ });
191
+
192
+ } else if (framework === 'react') {
193
+ // React template
194
+ const reactFiles = {
195
+ 'src/main.jsx': `import React from 'react'
196
+ import { createRoot } from 'react-dom/client'
197
+ import App from './App.jsx'
198
+
199
+ createRoot(document.getElementById('root')).render(<App />)`,
200
+ 'src/App.jsx': `export default function App() {
201
+ return <div style={{ padding: '2rem', textAlign: 'center' }}>
202
+ <h1>🚀 React + Vite</h1>
203
+ </div>
204
+ }`,
205
+ 'index.html': `<!DOCTYPE html>
206
+ <html lang="en">
207
+ <head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
208
+ <title>${projectName}</title></head>
209
+ <body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body>
210
+ </html>`,
211
+ 'vite.config.js': `import { defineConfig } from 'vite'
212
+ import react from '@vitejs/plugin-react'
213
+ export default defineConfig({ plugins: [react()] })`
214
+ };
215
+
216
+ if (features.typescript) {
217
+ reactFiles['src/main.tsx'] = reactFiles['src/main.jsx'];
218
+ reactFiles['src/App.tsx'] = reactFiles['src/App.jsx'];
219
+ reactFiles['tsconfig.json'] = `{
220
+ "compilerOptions": {
221
+ "target": "ES2020",
222
+ "module": "ESNext",
223
+ "lib": ["ES2020", "DOM"],
224
+ "skipLibCheck": true,
225
+ "moduleResolution": "bundler",
226
+ "jsx": "react-jsx",
227
+ "strict": true
228
+ }
229
+ }`;
230
+ delete reactFiles['src/main.jsx'];
231
+ delete reactFiles['src/App.jsx'];
232
+ }
233
+
234
+ Object.entries(reactFiles).forEach(([file, content]) => {
235
+ const fullPath = path.join(process.cwd(), file);
236
+ fs.ensureDirSync(path.dirname(fullPath));
237
+ fs.writeFileSync(fullPath, content);
238
+ });
239
+
240
+ } else if (framework === 'svelte') {
241
+ // Svelte template
242
+ const svelteFiles = {
243
+ 'src/main.js': `import App from './App.svelte'
244
+ new App({ target: document.body })`,
245
+ 'src/App.svelte': `<script>let name = 'world';</script>
246
+ <main><h1>🚀 Svelte + Vite</h1><p>Hello {name}!</p></main>
247
+ <style>main{font-family:sans-serif;padding:2rem;text-align:center}</style>`,
248
+ 'index.html': `<!DOCTYPE html>
249
+ <html lang="en">
250
+ <head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
251
+ <title>${projectName}</title></head>
252
+ <body><script type="module" src="/src/main.js"></script></body>
253
+ </html>`,
254
+ 'vite.config.js': `import { defineConfig } from 'vite'
255
+ import { svelte } from '@vitejs/vite-plugin-svelte'
256
+ export default defineConfig({ plugins: [svelte()] })`
257
+ };
258
+
259
+ Object.entries(svelteFiles).forEach(([file, content]) => {
260
+ const fullPath = path.join(process.cwd(), file);
261
+ fs.ensureDirSync(path.dirname(fullPath));
262
+ fs.writeFileSync(fullPath, content);
263
+ });
264
+
265
+ } else if (framework === 'vanilla') {
266
+ // Vanilla template
267
+ const vanillaFiles = {
268
+ 'src/main.js': `console.log('🚀 Vanilla + Vite')`,
269
+ 'index.html': `<!DOCTYPE html>
270
+ <html lang="en">
271
+ <head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
272
+ <title>${projectName}</title></head>
273
+ <body><h1>🚀 Vanilla + Vite</h1><script type="module" src="/src/main.js"></script></body>
274
+ </html>`,
275
+ 'vite.config.js': `import { defineConfig } from 'vite'
276
+ export default defineConfig({ plugins: [] })`
277
+ };
278
+
279
+ Object.entries(vanillaFiles).forEach(([file, content]) => {
280
+ const fullPath = path.join(process.cwd(), file);
281
+ fs.ensureDirSync(path.dirname(fullPath));
282
+ fs.writeFileSync(fullPath, content);
283
+ });
284
+ }
285
+
286
+ // Generate package.json
287
+ const pkg = {
288
+ name: projectName,
289
+ version: '0.0.0',
290
+ type: 'module',
291
+ scripts: {
292
+ dev: 'vite',
293
+ build: 'vite build',
294
+ preview: 'vite preview'
295
+ },
296
+ dependencies: {},
297
+ devDependencies: { 'vite': '^6.0.0' }
298
+ };
299
+
300
+ if (framework === 'vue') {
301
+ pkg.dependencies['vue'] = '^3.5.0';
302
+ pkg.devDependencies['@vitejs/plugin-vue'] = '^5.2.0';
303
+ if (features.router) pkg.dependencies['vue-router'] = '^4.3.0';
304
+ if (features.pinia) pkg.dependencies['pinia'] = '^2.2.0';
305
+ } else if (framework === 'react') {
306
+ pkg.dependencies['react'] = '^18.3.0';
307
+ pkg.dependencies['react-dom'] = '^18.3.0';
308
+ pkg.devDependencies['@vitejs/plugin-react'] = '^4.3.0';
309
+ if (features.typescript) {
310
+ pkg.devDependencies['@types/react'] = '^18.3.0';
311
+ pkg.devDependencies['@types/react-dom'] = '^18.3.0';
312
+ }
313
+ } else if (framework === 'svelte') {
314
+ pkg.dependencies['svelte'] = '^5.0.0';
315
+ pkg.devDependencies['@sveltejs/vite-plugin-svelte'] = '^4.0.0';
316
+ }
317
+
318
+ if (features.typescript) {
319
+ pkg.devDependencies['typescript'] = '^5.6.0';
320
+ }
321
+
322
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
323
+
324
+ // Generate README
325
+ const readme = `# ${projectName}
326
+
327
+ Created with [gotodev](https://npmjs.com/package/gotodev) ⚡
328
+
329
+ ## Framework: ${framework.toUpperCase()}
330
+
331
+ ### Features${features.typescript ? '\n- TypeScript' : ''}${features.router ? '\n- Vue Router' : ''}${features.pinia ? '\n- Pinia' : ''}${features.jsx ? '\n- JSX Support' : ''}
332
+
333
+ ### Getting Started
334
+
335
+ \`\`\`bash
336
+ npm install
337
+ npm run dev
338
+ \`\`\`
339
+
340
+ ## Why gotodev?
341
+
342
+ - ⚡ **Lightning-fast**: Powered by Rust + Oxc
343
+ - 🎨 **Modern**: Latest Vite + framework versions
344
+ - 🔥 **Hot HMR**: Instant updates
345
+
346
+ ## Built with gotodev v${require('./package.json').version}`;
347
+
348
+ fs.writeFileSync('README.md', readme);
349
+
350
+ // Generate .gitignore
351
+ fs.writeFileSync('.gitignore', `node_modules/
352
+ dist/
353
+ build/
354
+ *.local
355
+ .env
356
+ .env.local
357
+ .vscode/
358
+ .idea/
359
+ *.log
360
+ .DS_Store
361
+ Thumbs.db
362
+ *.tsbuildinfo
363
+ .pnpm-debug.log*
364
+ .yarn-integrity
365
+ .cache
366
+ .next
367
+ .nuxt
368
+ .storybook-out
369
+ tmp/
370
+ temp/`);
371
+ }
372
+
373
+ module.exports = { getViteTemplate };