vite-on-github 1.0.0
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/LICENSE +21 -0
- package/README.md +169 -0
- package/assets/vite-on-github-banner.png +0 -0
- package/bin/vite-on-github.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +795 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +698 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
// src/commands/init.ts
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import pc2 from "picocolors";
|
|
4
|
+
import { join as join4 } from "path";
|
|
5
|
+
|
|
6
|
+
// src/utils/detect.ts
|
|
7
|
+
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
8
|
+
import { join as join2 } from "path";
|
|
9
|
+
|
|
10
|
+
// src/utils/fs.ts
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
function readText(filePath) {
|
|
14
|
+
if (!existsSync(filePath)) return null;
|
|
15
|
+
return readFileSync(filePath, "utf-8");
|
|
16
|
+
}
|
|
17
|
+
function writeText(filePath, content) {
|
|
18
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
19
|
+
writeFileSync(filePath, content, "utf-8");
|
|
20
|
+
}
|
|
21
|
+
function fileExists(filePath) {
|
|
22
|
+
return existsSync(filePath);
|
|
23
|
+
}
|
|
24
|
+
function findFirstExisting(cwd, candidates) {
|
|
25
|
+
for (const candidate of candidates) {
|
|
26
|
+
const full = join(cwd, candidate);
|
|
27
|
+
if (existsSync(full)) return full;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/utils/detect.ts
|
|
33
|
+
var VITE_CONFIG_CANDIDATES = [
|
|
34
|
+
"vite.config.ts",
|
|
35
|
+
"vite.config.js",
|
|
36
|
+
"vite.config.mts",
|
|
37
|
+
"vite.config.mjs",
|
|
38
|
+
"vite.config.cjs"
|
|
39
|
+
];
|
|
40
|
+
function detectPackageManager(cwd) {
|
|
41
|
+
if (existsSync2(join2(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
42
|
+
if (existsSync2(join2(cwd, "bun.lockb")) || existsSync2(join2(cwd, "bun.lock"))) return "bun";
|
|
43
|
+
if (existsSync2(join2(cwd, "yarn.lock"))) return "yarn";
|
|
44
|
+
return "npm";
|
|
45
|
+
}
|
|
46
|
+
function readPackageJson(cwd) {
|
|
47
|
+
const path = join2(cwd, "package.json");
|
|
48
|
+
const text = readText(path);
|
|
49
|
+
if (!text) return null;
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(text);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function getDeps(pkg) {
|
|
57
|
+
const deps = {
|
|
58
|
+
...pkg.dependencies,
|
|
59
|
+
...pkg.devDependencies
|
|
60
|
+
};
|
|
61
|
+
return deps;
|
|
62
|
+
}
|
|
63
|
+
function detectFramework(deps) {
|
|
64
|
+
if (deps["react"] || deps["@vitejs/plugin-react"] || deps["@vitejs/plugin-react-swc"]) {
|
|
65
|
+
return "react";
|
|
66
|
+
}
|
|
67
|
+
if (deps["vue"] || deps["@vitejs/plugin-vue"]) return "vue";
|
|
68
|
+
if (deps["svelte"] || deps["@sveltejs/vite-plugin-svelte"]) return "svelte";
|
|
69
|
+
if (deps["solid-js"] || deps["vite-plugin-solid"]) return "solid";
|
|
70
|
+
return "unknown";
|
|
71
|
+
}
|
|
72
|
+
function detectProject(cwd) {
|
|
73
|
+
const packageJsonPath = join2(cwd, "package.json");
|
|
74
|
+
if (!existsSync2(packageJsonPath)) return null;
|
|
75
|
+
const pkg = readPackageJson(cwd);
|
|
76
|
+
if (!pkg) return null;
|
|
77
|
+
const deps = getDeps(pkg);
|
|
78
|
+
const hasVite = Boolean(deps.vite) || existsSync2(join2(cwd, "node_modules", "vite"));
|
|
79
|
+
if (!hasVite) return null;
|
|
80
|
+
const viteConfigPath = findFirstExisting(cwd, VITE_CONFIG_CANDIDATES);
|
|
81
|
+
const framework = detectFramework(deps);
|
|
82
|
+
return {
|
|
83
|
+
cwd,
|
|
84
|
+
packageManager: detectPackageManager(cwd),
|
|
85
|
+
viteConfigPath,
|
|
86
|
+
framework,
|
|
87
|
+
hasReactRouter: Boolean(deps["react-router-dom"] || deps["react-router"]),
|
|
88
|
+
hasVueRouter: Boolean(deps["vue-router"]),
|
|
89
|
+
packageJsonPath
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function findRouterEntryFiles(cwd, framework) {
|
|
93
|
+
const found = [];
|
|
94
|
+
if (framework === "react") {
|
|
95
|
+
const candidates = [
|
|
96
|
+
"src/main.tsx",
|
|
97
|
+
"src/main.ts",
|
|
98
|
+
"src/main.jsx",
|
|
99
|
+
"src/main.js",
|
|
100
|
+
"src/index.tsx",
|
|
101
|
+
"src/index.jsx",
|
|
102
|
+
"src/App.tsx",
|
|
103
|
+
"src/App.jsx"
|
|
104
|
+
];
|
|
105
|
+
for (const c of candidates) {
|
|
106
|
+
const full = join2(cwd, c);
|
|
107
|
+
if (!existsSync2(full)) continue;
|
|
108
|
+
const text = readText(full);
|
|
109
|
+
if (text && /<BrowserRouter|createBrowserRouter/.test(text)) {
|
|
110
|
+
found.push(full);
|
|
111
|
+
} else if (/main\.(tsx|ts|jsx|js)$/.test(c)) {
|
|
112
|
+
found.push(full);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (framework === "vue") {
|
|
117
|
+
const routerDirs = ["src/router", "src/routers"];
|
|
118
|
+
for (const dir of routerDirs) {
|
|
119
|
+
const fullDir = join2(cwd, dir);
|
|
120
|
+
if (!existsSync2(fullDir)) continue;
|
|
121
|
+
for (const file of readdirSync(fullDir)) {
|
|
122
|
+
if (/\.(ts|js|mts|mjs)$/.test(file)) {
|
|
123
|
+
found.push(join2(fullDir, file));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const rootCandidates = ["src/router.ts", "src/router.js", "src/router/index.ts", "src/router/index.js"];
|
|
128
|
+
for (const c of rootCandidates) {
|
|
129
|
+
const full = join2(cwd, c);
|
|
130
|
+
if (existsSync2(full)) found.push(full);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return found;
|
|
134
|
+
}
|
|
135
|
+
function installCommand(pm, packages, dev = true) {
|
|
136
|
+
const flag = dev ? "--save-dev" : "--save";
|
|
137
|
+
switch (pm) {
|
|
138
|
+
case "pnpm":
|
|
139
|
+
return `pnpm add ${flag} ${packages.join(" ")}`;
|
|
140
|
+
case "yarn":
|
|
141
|
+
return `yarn add ${dev ? "-D" : ""} ${packages.join(" ")}`.replace(/\s+/g, " ").trim();
|
|
142
|
+
case "bun":
|
|
143
|
+
return `bun add ${dev ? "-d" : ""} ${packages.join(" ")}`;
|
|
144
|
+
default:
|
|
145
|
+
return `npm install ${flag} ${packages.join(" ")}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function buildCommand(pm) {
|
|
149
|
+
switch (pm) {
|
|
150
|
+
case "pnpm":
|
|
151
|
+
return "pnpm run build";
|
|
152
|
+
case "yarn":
|
|
153
|
+
return "yarn build";
|
|
154
|
+
case "bun":
|
|
155
|
+
return "bun run build";
|
|
156
|
+
default:
|
|
157
|
+
return "npm run build";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/utils/git.ts
|
|
162
|
+
import { execSync } from "child_process";
|
|
163
|
+
function detectGitRemote(cwd) {
|
|
164
|
+
try {
|
|
165
|
+
const url = execSync("git remote get-url origin", {
|
|
166
|
+
cwd,
|
|
167
|
+
encoding: "utf-8",
|
|
168
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
169
|
+
}).trim();
|
|
170
|
+
const match = url.match(/github\.com[:/]([^/]+)\/(.+?)(?:\.git)?$/i) ?? url.match(/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
171
|
+
if (!match) return null;
|
|
172
|
+
const owner = match[1];
|
|
173
|
+
const repo = match[2].replace(/\.git$/, "");
|
|
174
|
+
const isUserSite = repo === `${owner}.github.io`;
|
|
175
|
+
return { owner, repo, isUserSite };
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/utils/package-json.ts
|
|
182
|
+
function readPackageJson2(path) {
|
|
183
|
+
const text = readText(path);
|
|
184
|
+
if (!text) return null;
|
|
185
|
+
try {
|
|
186
|
+
return JSON.parse(text);
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function writePackageJson(path, pkg) {
|
|
192
|
+
writeText(path, `${JSON.stringify(pkg, null, 2)}
|
|
193
|
+
`);
|
|
194
|
+
}
|
|
195
|
+
function ensureBuildScript(pkg) {
|
|
196
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
197
|
+
if (pkg.scripts.build) return false;
|
|
198
|
+
pkg.scripts.build = "vite build";
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
function hasDevDependency(pkg, name) {
|
|
202
|
+
return Boolean(pkg.devDependencies?.[name] || pkg.dependencies?.[name]);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/utils/vite-config.ts
|
|
206
|
+
function hasViteBasepathImport(content) {
|
|
207
|
+
return /from\s+['"]vite-basepath['"]/.test(content) || /require\s*\(\s*['"]vite-basepath['"]\s*\)/.test(content);
|
|
208
|
+
}
|
|
209
|
+
function hasViteBasepathPlugin(content) {
|
|
210
|
+
return /viteBasepath\s*\(/.test(content);
|
|
211
|
+
}
|
|
212
|
+
function isCommonJsConfig(path) {
|
|
213
|
+
return /\.cjs$/.test(path);
|
|
214
|
+
}
|
|
215
|
+
function addImport(content, configPath) {
|
|
216
|
+
if (hasViteBasepathImport(content)) return content;
|
|
217
|
+
const importLine = isCommonJsConfig(configPath) ? "const viteBasepath = require('vite-basepath');\n" : "import viteBasepath from 'vite-basepath';\n";
|
|
218
|
+
const importMatch = content.match(/^import .+?;[\r\n]/m) ?? content.match(/^const .+ = require\(.+?\);[\r\n]/m);
|
|
219
|
+
if (importMatch && importMatch.index !== void 0) {
|
|
220
|
+
const lastImportEnd = findLastImportEnd(content);
|
|
221
|
+
return content.slice(0, lastImportEnd) + importLine + content.slice(lastImportEnd);
|
|
222
|
+
}
|
|
223
|
+
return importLine + content;
|
|
224
|
+
}
|
|
225
|
+
function findLastImportEnd(content) {
|
|
226
|
+
const importBlock = /^(?:import\s[\s\S]*?from\s+['"][^'"]+['"];|const\s+\w+\s*=\s*require\(['"][^'"]+['"]\);)/gm;
|
|
227
|
+
let lastEnd = 0;
|
|
228
|
+
let match;
|
|
229
|
+
while ((match = importBlock.exec(content)) !== null) {
|
|
230
|
+
lastEnd = match.index + match[0].length;
|
|
231
|
+
}
|
|
232
|
+
if (lastEnd > 0) {
|
|
233
|
+
const after = content.slice(lastEnd).match(/^[\r\n]+/);
|
|
234
|
+
return lastEnd + (after ? after[0].length : 0);
|
|
235
|
+
}
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
function addPluginToArray(content) {
|
|
239
|
+
const pluginsArrayMatch = content.match(/plugins\s*:\s*\[/);
|
|
240
|
+
if (pluginsArrayMatch && pluginsArrayMatch.index !== void 0) {
|
|
241
|
+
const bracketIndex = content.indexOf("[", pluginsArrayMatch.index);
|
|
242
|
+
const insertAt = bracketIndex + 1;
|
|
243
|
+
const afterBracket = content.slice(insertAt).match(/^\s*\n?/);
|
|
244
|
+
const gap = afterBracket ? afterBracket[0] : "\n ";
|
|
245
|
+
return content.slice(0, insertAt) + `${gap}viteBasepath(), ` + content.slice(insertAt);
|
|
246
|
+
}
|
|
247
|
+
const defineConfigMatch = content.match(/defineConfig\s*\(\s*\{/);
|
|
248
|
+
if (defineConfigMatch && defineConfigMatch.index !== void 0) {
|
|
249
|
+
const braceIndex = content.indexOf("{", defineConfigMatch.index);
|
|
250
|
+
const insertAt = braceIndex + 1;
|
|
251
|
+
return content.slice(0, insertAt) + "\n plugins: [viteBasepath()]," + content.slice(insertAt);
|
|
252
|
+
}
|
|
253
|
+
const exportDefaultMatch = content.match(/export\s+default\s*\{/);
|
|
254
|
+
if (exportDefaultMatch && exportDefaultMatch.index !== void 0) {
|
|
255
|
+
const braceIndex = content.indexOf("{", exportDefaultMatch.index);
|
|
256
|
+
const insertAt = braceIndex + 1;
|
|
257
|
+
return content.slice(0, insertAt) + "\n plugins: [viteBasepath()]," + content.slice(insertAt);
|
|
258
|
+
}
|
|
259
|
+
return content;
|
|
260
|
+
}
|
|
261
|
+
function patchViteConfig(configPath) {
|
|
262
|
+
const content = readText(configPath);
|
|
263
|
+
if (!content) {
|
|
264
|
+
return { changed: false, message: `Could not read ${configPath}` };
|
|
265
|
+
}
|
|
266
|
+
if (hasViteBasepathPlugin(content)) {
|
|
267
|
+
return { changed: false, message: "vite-basepath already configured in vite config" };
|
|
268
|
+
}
|
|
269
|
+
let updated = addImport(content, configPath);
|
|
270
|
+
updated = addPluginToArray(updated);
|
|
271
|
+
if (updated === content) {
|
|
272
|
+
return {
|
|
273
|
+
changed: false,
|
|
274
|
+
message: "Could not auto-patch vite config \u2014 add viteBasepath() to plugins manually"
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
writeText(configPath, updated);
|
|
278
|
+
return { changed: true, message: `Updated ${configPath}` };
|
|
279
|
+
}
|
|
280
|
+
function frameworkPluginImport(framework) {
|
|
281
|
+
switch (framework) {
|
|
282
|
+
case "react":
|
|
283
|
+
return {
|
|
284
|
+
importLine: "import react from '@vitejs/plugin-react';\n",
|
|
285
|
+
pluginCall: "react()"
|
|
286
|
+
};
|
|
287
|
+
case "vue":
|
|
288
|
+
return {
|
|
289
|
+
importLine: "import vue from '@vitejs/plugin-vue';\n",
|
|
290
|
+
pluginCall: "vue()"
|
|
291
|
+
};
|
|
292
|
+
case "svelte":
|
|
293
|
+
return {
|
|
294
|
+
importLine: "import { svelte } from '@sveltejs/vite-plugin-svelte';\n",
|
|
295
|
+
pluginCall: "svelte()"
|
|
296
|
+
};
|
|
297
|
+
case "solid":
|
|
298
|
+
return {
|
|
299
|
+
importLine: "import solid from 'vite-plugin-solid';\n",
|
|
300
|
+
pluginCall: "solid()"
|
|
301
|
+
};
|
|
302
|
+
default:
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function createDefaultViteConfig(cwd, isTs, framework = "unknown") {
|
|
307
|
+
const filename = isTs ? "vite.config.ts" : "vite.config.js";
|
|
308
|
+
const path = `${cwd}/${filename}`.replace(/\\/g, "/");
|
|
309
|
+
const fw = frameworkPluginImport(framework);
|
|
310
|
+
const plugins = fw ? `viteBasepath(), ${fw.pluginCall}` : "viteBasepath()";
|
|
311
|
+
const content = `import { defineConfig } from 'vite';
|
|
312
|
+
import viteBasepath from 'vite-basepath';
|
|
313
|
+
${fw?.importLine ?? ""}
|
|
314
|
+
export default defineConfig({
|
|
315
|
+
plugins: [${plugins}],
|
|
316
|
+
});
|
|
317
|
+
`;
|
|
318
|
+
writeText(path, content);
|
|
319
|
+
return path;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/utils/router.ts
|
|
323
|
+
function hasRuntimeImport(content) {
|
|
324
|
+
return /from\s+['"]vite-basepath\/runtime['"]/.test(content);
|
|
325
|
+
}
|
|
326
|
+
function patchReactRouter(content) {
|
|
327
|
+
if (content.includes("getBase()") || hasRuntimeImport(content)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
if (!/<BrowserRouter[\s>/]/.test(content) && !/BrowserRouter\s*\(/.test(content)) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
let updated = content;
|
|
334
|
+
if (!hasRuntimeImport(updated)) {
|
|
335
|
+
const runtimeImport = "import { getBase } from 'vite-basepath/runtime';\n";
|
|
336
|
+
const lastImportEnd = findLastImportEnd2(updated);
|
|
337
|
+
updated = updated.slice(0, lastImportEnd) + runtimeImport + updated.slice(lastImportEnd);
|
|
338
|
+
}
|
|
339
|
+
updated = updated.replace(
|
|
340
|
+
/<BrowserRouter([^>]*)\s+basename=\{[^}]+\}/g,
|
|
341
|
+
"<BrowserRouter$1 basename={getBase()}"
|
|
342
|
+
);
|
|
343
|
+
updated = updated.replace(
|
|
344
|
+
/<BrowserRouter([^>]*)\s+basename=["'][^"']*["']/g,
|
|
345
|
+
"<BrowserRouter$1 basename={getBase()}"
|
|
346
|
+
);
|
|
347
|
+
updated = updated.replace(
|
|
348
|
+
/<BrowserRouter(?![^>]*\bbasename=)([\s/>])/g,
|
|
349
|
+
"<BrowserRouter basename={getBase()}$1"
|
|
350
|
+
);
|
|
351
|
+
updated = updated.replace(
|
|
352
|
+
/BrowserRouter\s*\(\s*\{/g,
|
|
353
|
+
"BrowserRouter({ basename: getBase(), "
|
|
354
|
+
);
|
|
355
|
+
return updated !== content ? updated : null;
|
|
356
|
+
}
|
|
357
|
+
function patchVueRouter(content) {
|
|
358
|
+
if (content.includes("getBase()") || hasRuntimeImport(content)) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
if (!/createWebHistory\s*\(/.test(content)) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
let updated = content;
|
|
365
|
+
if (!hasRuntimeImport(updated)) {
|
|
366
|
+
const runtimeImport = "import { getBase } from 'vite-basepath/runtime';\n";
|
|
367
|
+
const lastImportEnd = findLastImportEnd2(updated);
|
|
368
|
+
updated = updated.slice(0, lastImportEnd) + runtimeImport + updated.slice(lastImportEnd);
|
|
369
|
+
}
|
|
370
|
+
updated = updated.replace(
|
|
371
|
+
/createWebHistory\s*\(\s*[^)]*\)/g,
|
|
372
|
+
"createWebHistory(getBase())"
|
|
373
|
+
);
|
|
374
|
+
return updated !== content ? updated : null;
|
|
375
|
+
}
|
|
376
|
+
function findLastImportEnd2(content) {
|
|
377
|
+
const importBlock = /^(?:import\s[\s\S]*?from\s+['"][^'"]+['"];|const\s+\w+\s*=\s*require\(['"][^'"]+['"]\);)/gm;
|
|
378
|
+
let lastEnd = 0;
|
|
379
|
+
let match;
|
|
380
|
+
while ((match = importBlock.exec(content)) !== null) {
|
|
381
|
+
lastEnd = match.index + match[0].length;
|
|
382
|
+
}
|
|
383
|
+
if (lastEnd > 0) {
|
|
384
|
+
const after = content.slice(lastEnd).match(/^[\r\n]+/);
|
|
385
|
+
return lastEnd + (after ? after[0].length : 0);
|
|
386
|
+
}
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
function patchRouterFiles(files, framework) {
|
|
390
|
+
const results = [];
|
|
391
|
+
for (const file of files) {
|
|
392
|
+
const content = readText(file);
|
|
393
|
+
if (!content) continue;
|
|
394
|
+
let patched = null;
|
|
395
|
+
if (framework === "react") {
|
|
396
|
+
patched = patchReactRouter(content);
|
|
397
|
+
} else if (framework === "vue") {
|
|
398
|
+
patched = patchVueRouter(content);
|
|
399
|
+
}
|
|
400
|
+
if (patched) {
|
|
401
|
+
writeText(file, patched);
|
|
402
|
+
results.push({ file, changed: true, message: `Updated router in ${file}` });
|
|
403
|
+
} else if (framework === "react" && /<BrowserRouter/.test(content) || framework === "vue" && /createWebHistory/.test(content)) {
|
|
404
|
+
results.push({
|
|
405
|
+
file,
|
|
406
|
+
changed: false,
|
|
407
|
+
message: `Router in ${file} already configured or needs manual update`
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return results;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/utils/ci.ts
|
|
415
|
+
import { existsSync as existsSync3 } from "fs";
|
|
416
|
+
import { join as join3 } from "path";
|
|
417
|
+
var DEFAULT_NODE_VERSION = "22";
|
|
418
|
+
function getSetupAction(pm, cwd) {
|
|
419
|
+
switch (pm) {
|
|
420
|
+
case "pnpm":
|
|
421
|
+
return { install: "pnpm install --frozen-lockfile", cache: "pnpm" };
|
|
422
|
+
case "yarn":
|
|
423
|
+
return { install: "yarn install --frozen-lockfile", cache: "yarn" };
|
|
424
|
+
case "bun":
|
|
425
|
+
return { install: "bun install --frozen-lockfile" };
|
|
426
|
+
default:
|
|
427
|
+
return {
|
|
428
|
+
install: existsSync3(join3(cwd, "package-lock.json")) ? "npm ci" : "npm install"
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function getSetupNodeCache(pm, cwd) {
|
|
433
|
+
const { cache } = getSetupAction(pm, cwd);
|
|
434
|
+
if (!cache) return "";
|
|
435
|
+
return `
|
|
436
|
+
cache: '${cache}'`;
|
|
437
|
+
}
|
|
438
|
+
function getPnpmSetupStep(pm) {
|
|
439
|
+
if (pm !== "pnpm") return "";
|
|
440
|
+
return `
|
|
441
|
+
- name: Setup pnpm
|
|
442
|
+
uses: pnpm/action-setup@v4
|
|
443
|
+
with:
|
|
444
|
+
version: 9
|
|
445
|
+
`;
|
|
446
|
+
}
|
|
447
|
+
function generateWorkflow(options) {
|
|
448
|
+
const nodeVersion = options.nodeVersion ?? DEFAULT_NODE_VERSION;
|
|
449
|
+
const outDir = options.buildOutputDir ?? "dist";
|
|
450
|
+
const { install } = getSetupAction(options.packageManager, options.cwd);
|
|
451
|
+
const cacheLine = getSetupNodeCache(options.packageManager, options.cwd);
|
|
452
|
+
const build = buildCommand(options.packageManager);
|
|
453
|
+
return `# Generated by vite-on-github \u2014 deploys Vite app to GitHub Pages
|
|
454
|
+
name: Deploy to GitHub Pages
|
|
455
|
+
|
|
456
|
+
on:
|
|
457
|
+
push:
|
|
458
|
+
branches: [main, master]
|
|
459
|
+
workflow_dispatch:
|
|
460
|
+
|
|
461
|
+
permissions:
|
|
462
|
+
contents: read
|
|
463
|
+
pages: write
|
|
464
|
+
id-token: write
|
|
465
|
+
|
|
466
|
+
concurrency:
|
|
467
|
+
group: pages
|
|
468
|
+
cancel-in-progress: true
|
|
469
|
+
|
|
470
|
+
jobs:
|
|
471
|
+
build:
|
|
472
|
+
runs-on: ubuntu-latest
|
|
473
|
+
steps:
|
|
474
|
+
- name: Checkout
|
|
475
|
+
uses: actions/checkout@v4
|
|
476
|
+
${getPnpmSetupStep(options.packageManager)}
|
|
477
|
+
- name: Setup Node.js
|
|
478
|
+
uses: actions/setup-node@v4
|
|
479
|
+
with:
|
|
480
|
+
node-version: '${nodeVersion}'${cacheLine}
|
|
481
|
+
|
|
482
|
+
- name: Install dependencies
|
|
483
|
+
run: ${install}
|
|
484
|
+
|
|
485
|
+
- name: Build
|
|
486
|
+
run: ${build}
|
|
487
|
+
|
|
488
|
+
- name: SPA fallback for client-side routing
|
|
489
|
+
run: cp ${outDir}/index.html ${outDir}/404.html
|
|
490
|
+
|
|
491
|
+
- name: Setup Pages
|
|
492
|
+
uses: actions/configure-pages@v5
|
|
493
|
+
|
|
494
|
+
- name: Upload artifact
|
|
495
|
+
uses: actions/upload-pages-artifact@v3
|
|
496
|
+
with:
|
|
497
|
+
path: ${outDir}
|
|
498
|
+
|
|
499
|
+
deploy:
|
|
500
|
+
needs: build
|
|
501
|
+
runs-on: ubuntu-latest
|
|
502
|
+
environment:
|
|
503
|
+
name: github-pages
|
|
504
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
505
|
+
steps:
|
|
506
|
+
- name: Deploy to GitHub Pages
|
|
507
|
+
id: deployment
|
|
508
|
+
uses: actions/deploy-pages@v4
|
|
509
|
+
`;
|
|
510
|
+
}
|
|
511
|
+
function writeWorkflow(options) {
|
|
512
|
+
const workflowPath = join3(options.cwd, ".github", "workflows", "deploy.yml");
|
|
513
|
+
const exists = fileExists(workflowPath);
|
|
514
|
+
writeText(workflowPath, generateWorkflow(options));
|
|
515
|
+
return { path: workflowPath, created: !exists };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// src/utils/exec.ts
|
|
519
|
+
import { execSync as execSync2 } from "child_process";
|
|
520
|
+
|
|
521
|
+
// src/utils/logger.ts
|
|
522
|
+
import pc from "picocolors";
|
|
523
|
+
var log = {
|
|
524
|
+
info: (msg) => console.log(pc.cyan("\u2139"), msg),
|
|
525
|
+
success: (msg) => console.log(pc.green("\u2714"), msg),
|
|
526
|
+
warn: (msg) => console.log(pc.yellow("\u26A0"), msg),
|
|
527
|
+
error: (msg) => console.error(pc.red("\u2716"), msg),
|
|
528
|
+
step: (msg) => console.log(pc.bold(pc.blue("\u2192")), msg),
|
|
529
|
+
dim: (msg) => console.log(pc.dim(msg))
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// src/utils/exec.ts
|
|
533
|
+
function runCommand(command, cwd, silent = false) {
|
|
534
|
+
log.dim(` $ ${command}`);
|
|
535
|
+
execSync2(command, {
|
|
536
|
+
cwd,
|
|
537
|
+
stdio: silent ? "pipe" : "inherit",
|
|
538
|
+
encoding: "utf-8"
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/commands/init.ts
|
|
543
|
+
function getPagesUrl(owner, repo, isUserSite) {
|
|
544
|
+
if (isUserSite) return `https://${owner}.github.io/`;
|
|
545
|
+
return `https://${owner}.github.io/${repo}/`;
|
|
546
|
+
}
|
|
547
|
+
async function init(options = {}) {
|
|
548
|
+
const cwd = options.cwd ?? process.cwd();
|
|
549
|
+
const steps = [];
|
|
550
|
+
const warnings = [];
|
|
551
|
+
const project = detectProject(cwd);
|
|
552
|
+
if (!project) {
|
|
553
|
+
return {
|
|
554
|
+
success: false,
|
|
555
|
+
steps,
|
|
556
|
+
warnings: ["Not a Vite project \u2014 run this inside a folder with vite in package.json"]
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
const gitRemote = detectGitRemote(cwd);
|
|
560
|
+
let pagesUrl;
|
|
561
|
+
if (!options.yes) {
|
|
562
|
+
p.intro(pc2.bgCyan(pc2.black(" vite-on-github ")));
|
|
563
|
+
const confirm2 = await p.confirm({
|
|
564
|
+
message: `Prepare ${pc2.bold(project.cwd)} for GitHub Pages?`,
|
|
565
|
+
initialValue: true
|
|
566
|
+
});
|
|
567
|
+
if (p.isCancel(confirm2) || !confirm2) {
|
|
568
|
+
p.cancel("Setup cancelled.");
|
|
569
|
+
process.exit(0);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const spinner2 = p.spinner();
|
|
573
|
+
let installOk = options.skipInstall;
|
|
574
|
+
if (!options.skipInstall) {
|
|
575
|
+
spinner2.start("Installing vite-basepath\u2026");
|
|
576
|
+
const pkg2 = readPackageJson2(project.packageJsonPath);
|
|
577
|
+
const alreadyInstalled = pkg2 ? hasDevDependency(pkg2, "vite-basepath") : false;
|
|
578
|
+
if (alreadyInstalled) {
|
|
579
|
+
spinner2.stop("vite-basepath already installed");
|
|
580
|
+
steps.push("vite-basepath already present");
|
|
581
|
+
installOk = true;
|
|
582
|
+
} else {
|
|
583
|
+
try {
|
|
584
|
+
const cmd = installCommand(project.packageManager, ["vite-basepath"]);
|
|
585
|
+
runCommand(cmd, cwd, true);
|
|
586
|
+
spinner2.stop("Installed vite-basepath");
|
|
587
|
+
steps.push("Installed vite-basepath");
|
|
588
|
+
installOk = true;
|
|
589
|
+
} catch {
|
|
590
|
+
spinner2.stop("Failed to install vite-basepath");
|
|
591
|
+
warnings.push(
|
|
592
|
+
"Could not install vite-basepath \u2014 install it manually, then run init again"
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
spinner2.start("Checking package.json\u2026");
|
|
598
|
+
const pkg = readPackageJson2(project.packageJsonPath);
|
|
599
|
+
if (pkg) {
|
|
600
|
+
const addedBuild = ensureBuildScript(pkg);
|
|
601
|
+
if (addedBuild) {
|
|
602
|
+
writePackageJson(project.packageJsonPath, pkg);
|
|
603
|
+
steps.push("Added build script to package.json");
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
spinner2.stop("package.json ready");
|
|
607
|
+
if (!options.skipViteConfig && installOk) {
|
|
608
|
+
spinner2.start("Configuring Vite\u2026");
|
|
609
|
+
if (project.viteConfigPath) {
|
|
610
|
+
const result = patchViteConfig(project.viteConfigPath);
|
|
611
|
+
if (result.changed) {
|
|
612
|
+
steps.push(result.message);
|
|
613
|
+
} else if (!result.message.includes("already")) {
|
|
614
|
+
warnings.push(result.message);
|
|
615
|
+
} else {
|
|
616
|
+
steps.push(result.message);
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
const useTs = fileExists(join4(cwd, "tsconfig.json"));
|
|
620
|
+
const created = createDefaultViteConfig(cwd, useTs, project.framework);
|
|
621
|
+
steps.push(`Created ${created}`);
|
|
622
|
+
}
|
|
623
|
+
spinner2.stop("Vite configured with vite-basepath");
|
|
624
|
+
} else if (!options.skipViteConfig && !installOk) {
|
|
625
|
+
warnings.push("Skipped vite config \u2014 install vite-basepath first");
|
|
626
|
+
}
|
|
627
|
+
if (!options.skipRouter) {
|
|
628
|
+
const needsRouter = project.framework === "react" && project.hasReactRouter || project.framework === "vue" && project.hasVueRouter;
|
|
629
|
+
if (needsRouter) {
|
|
630
|
+
spinner2.start("Configuring client-side router\u2026");
|
|
631
|
+
const routerFiles = findRouterEntryFiles(cwd, project.framework);
|
|
632
|
+
const results = patchRouterFiles(routerFiles, project.framework);
|
|
633
|
+
for (const r of results) {
|
|
634
|
+
if (r.changed) steps.push(r.message);
|
|
635
|
+
else warnings.push(r.message);
|
|
636
|
+
}
|
|
637
|
+
if (results.length === 0) {
|
|
638
|
+
warnings.push(
|
|
639
|
+
`Found ${project.framework} router dependency but could not locate entry file \u2014 add basename={getBase()} manually`
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
spinner2.stop("Router setup complete");
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (!options.skipCi) {
|
|
646
|
+
spinner2.start("Creating GitHub Actions workflow\u2026");
|
|
647
|
+
const { path, created } = writeWorkflow({
|
|
648
|
+
cwd,
|
|
649
|
+
packageManager: project.packageManager,
|
|
650
|
+
nodeVersion: options.nodeVersion,
|
|
651
|
+
buildOutputDir: options.outDir
|
|
652
|
+
});
|
|
653
|
+
steps.push(created ? `Created ${path}` : `Updated ${path}`);
|
|
654
|
+
spinner2.stop("CI workflow ready");
|
|
655
|
+
}
|
|
656
|
+
if (gitRemote) {
|
|
657
|
+
pagesUrl = getPagesUrl(gitRemote.owner, gitRemote.repo, gitRemote.isUserSite);
|
|
658
|
+
}
|
|
659
|
+
return { success: true, steps, warnings, pagesUrl };
|
|
660
|
+
}
|
|
661
|
+
function printSummary(result, packageManager) {
|
|
662
|
+
if (!result.success) {
|
|
663
|
+
p.log.error(result.warnings[0] ?? "Setup failed");
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
p.note(
|
|
667
|
+
[
|
|
668
|
+
"Push your code to GitHub",
|
|
669
|
+
"Go to repo \u2192 Settings \u2192 Pages",
|
|
670
|
+
'Set Source to "GitHub Actions"',
|
|
671
|
+
`Run ${pc2.cyan(buildCommand(packageManager))} locally to verify`
|
|
672
|
+
].join("\n"),
|
|
673
|
+
"Next steps"
|
|
674
|
+
);
|
|
675
|
+
if (result.pagesUrl) {
|
|
676
|
+
p.log.success(`Your site will be live at: ${pc2.cyan(pc2.underline(result.pagesUrl))}`);
|
|
677
|
+
}
|
|
678
|
+
if (result.warnings.length > 0) {
|
|
679
|
+
p.log.warn("Notes:");
|
|
680
|
+
for (const w of result.warnings) {
|
|
681
|
+
p.log.message(` \u2022 ${w}`);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
p.outro(pc2.green("Done! Your Vite app is ready for GitHub Pages."));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// src/cli.ts
|
|
688
|
+
var HELP = `
|
|
689
|
+
${"\x1B[1m"}vite-on-github\x1B[0m \u2014 Prepare any Vite project for GitHub Pages
|
|
690
|
+
|
|
691
|
+
\x1B[1mUsage:\x1B[0m
|
|
692
|
+
npx vite-on-github [command] [options]
|
|
693
|
+
|
|
694
|
+
\x1B[1mCommands:\x1B[0m
|
|
695
|
+
init Set up vite config, base paths, router, and CI (default)
|
|
696
|
+
help Show this help
|
|
697
|
+
|
|
698
|
+
\x1B[1mOptions:\x1B[0m
|
|
699
|
+
-y, --yes Skip prompts, use defaults
|
|
700
|
+
--skip-install Do not install vite-basepath
|
|
701
|
+
--skip-vite-config Do not patch vite.config
|
|
702
|
+
--skip-router Do not patch React/Vue router
|
|
703
|
+
--skip-ci Do not create GitHub Actions workflow
|
|
704
|
+
--node <version> Node.js version for CI (default: 22)
|
|
705
|
+
--out-dir <dir> Build output directory (default: dist)
|
|
706
|
+
|
|
707
|
+
\x1B[1mExamples:\x1B[0m
|
|
708
|
+
npx vite-on-github
|
|
709
|
+
npx vite-on-github init
|
|
710
|
+
npx vite-on-github init -y
|
|
711
|
+
npx vite-on-github init --node 20 --out-dir dist
|
|
712
|
+
`;
|
|
713
|
+
function parseArgs(argv) {
|
|
714
|
+
const args = argv.slice(2);
|
|
715
|
+
let command = "init";
|
|
716
|
+
const options = {
|
|
717
|
+
yes: false,
|
|
718
|
+
skipInstall: false,
|
|
719
|
+
skipViteConfig: false,
|
|
720
|
+
skipRouter: false,
|
|
721
|
+
skipCi: false,
|
|
722
|
+
nodeVersion: void 0,
|
|
723
|
+
outDir: void 0
|
|
724
|
+
};
|
|
725
|
+
let i = 0;
|
|
726
|
+
if (args[0] && !args[0].startsWith("-")) {
|
|
727
|
+
command = args[0];
|
|
728
|
+
i = 1;
|
|
729
|
+
}
|
|
730
|
+
for (; i < args.length; i++) {
|
|
731
|
+
const arg = args[i];
|
|
732
|
+
switch (arg) {
|
|
733
|
+
case "-y":
|
|
734
|
+
case "--yes":
|
|
735
|
+
options.yes = true;
|
|
736
|
+
break;
|
|
737
|
+
case "--skip-install":
|
|
738
|
+
options.skipInstall = true;
|
|
739
|
+
break;
|
|
740
|
+
case "--skip-vite-config":
|
|
741
|
+
options.skipViteConfig = true;
|
|
742
|
+
break;
|
|
743
|
+
case "--skip-router":
|
|
744
|
+
options.skipRouter = true;
|
|
745
|
+
break;
|
|
746
|
+
case "--skip-ci":
|
|
747
|
+
options.skipCi = true;
|
|
748
|
+
break;
|
|
749
|
+
case "--node":
|
|
750
|
+
options.nodeVersion = args[++i];
|
|
751
|
+
break;
|
|
752
|
+
case "--out-dir":
|
|
753
|
+
options.outDir = args[++i];
|
|
754
|
+
break;
|
|
755
|
+
default:
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return { command, options };
|
|
760
|
+
}
|
|
761
|
+
async function main() {
|
|
762
|
+
const { command, options } = parseArgs(process.argv);
|
|
763
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
764
|
+
console.log(HELP);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (command !== "init") {
|
|
768
|
+
console.error(`Unknown command: ${command}
|
|
769
|
+
Run "vite-on-github help" for usage.`);
|
|
770
|
+
process.exit(1);
|
|
771
|
+
}
|
|
772
|
+
const cwd = process.cwd();
|
|
773
|
+
const project = detectProject(cwd);
|
|
774
|
+
const pm = project?.packageManager ?? detectPackageManager(cwd);
|
|
775
|
+
const result = await init({
|
|
776
|
+
cwd,
|
|
777
|
+
yes: options.yes,
|
|
778
|
+
skipInstall: options.skipInstall,
|
|
779
|
+
skipViteConfig: options.skipViteConfig,
|
|
780
|
+
skipRouter: options.skipRouter,
|
|
781
|
+
skipCi: options.skipCi,
|
|
782
|
+
nodeVersion: options.nodeVersion,
|
|
783
|
+
outDir: options.outDir
|
|
784
|
+
});
|
|
785
|
+
if (!result.success) {
|
|
786
|
+
console.error(result.warnings[0] ?? "Setup failed");
|
|
787
|
+
process.exit(1);
|
|
788
|
+
}
|
|
789
|
+
printSummary(result, pm);
|
|
790
|
+
}
|
|
791
|
+
main().catch((err) => {
|
|
792
|
+
console.error(err instanceof Error ? err.message : err);
|
|
793
|
+
process.exit(1);
|
|
794
|
+
});
|
|
795
|
+
//# sourceMappingURL=cli.js.map
|