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/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