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.
- package/package.json +2 -1
- package/template-fetcher.js +373 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gotodev",
|
|
3
|
-
"version": "2.0.
|
|
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 };
|