11/* eslint-disable no-console */
22
3+ import type { PackageManager } from './helpers/install.js' ;
4+
35import path from 'path' ;
4- import { execSync , spawn , SpawnOptions } from 'child_process ' ;
6+ import { fileURLToPath } from 'url ' ;
57
68import { Command } from 'commander' ;
79import chalk from 'chalk' ;
810import fetch from 'node-fetch' ;
911import prompts from 'prompts' ;
12+ import { build } from 'vite' ;
1013
1114import packageJson from '../package.json' ;
15+ import { runStyles } from '../styles.js' ;
16+ import { runStates } from '../states.js' ;
17+ import { runServer } from '../server.js' ;
18+ import { runPrerender } from '../prerender.js' ;
19+ import { runIntegration } from '../integration.js' ;
1220
13- import { createApp } from './create-app.js' ;
1421import { validateNpmName } from './helpers/validate-pkg.js' ;
22+ import { createApp } from './create-app.js' ;
1523
1624const { green, yellow, bold, cyan, red } = chalk ;
1725const packageName = 'buner' ;
1826
19- const run = ( cmd : string , args : string [ ] = [ ] , options : SpawnOptions = { } ) => {
20- return new Promise < void > ( ( resolve , reject ) => {
21- const child = spawn ( cmd , args , {
22- stdio : 'inherit' ,
23- shell : true ,
24- cwd : process . cwd ( ) ,
25- ...options ,
26- } ) ;
27+ // Package's own directory — after compilation, buner.js lives in dist/
28+ // so __dirname equivalent is the dist/ folder itself
29+ const packageDir = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
2730
28- child . on ( 'close' , ( code ) => {
29- if ( code !== 0 ) {
30- reject ( new Error ( `Command "${ cmd } ${ args . join ( ' ' ) } " exited with code ${ code } ` ) ) ;
31- } else {
32- resolve ( ) ;
33- }
34- } ) ;
31+ /** Resolve the vite config path */
32+ const viteConfigPath = ( ) => path . resolve ( packageDir , '..' , 'vite.config.ts' ) ;
3533
36- child . on ( 'error' , reject ) ;
37- } ) ;
38- } ;
34+ /** Run vite build with the given options */
35+ const viteBuild = async ( opts : { outDir : string ; ssr ?: string ; mode ?: string ; watch ?: boolean } ) => {
36+ // Set BUNER_MODE so xpack/paths.ts picks up the correct mode
37+ // (it can't read --mode from process.argv when using the build() API)
38+ const prevMode = process . env . BUNER_MODE ;
39+
40+ if ( opts . mode ) {
41+ process . env . BUNER_MODE = opts . mode ;
42+ }
3943
40- const runSync = ( cmd : string ) => {
41- execSync ( cmd , { stdio : 'inherit' , cwd : process . cwd ( ) } ) ;
44+ const config : Record < string , unknown > = {
45+ configFile : viteConfigPath ( ) ,
46+ build : {
47+ outDir : opts . outDir ,
48+ ...( opts . ssr ? { ssr : opts . ssr } : { } ) ,
49+ ...( opts . watch ? { watch : { } } : { } ) ,
50+ } ,
51+ } ;
52+
53+ if ( opts . mode ) {
54+ config . mode = opts . mode ;
55+ }
56+
57+ try {
58+ await build ( config ) ;
59+ } finally {
60+ if ( prevMode === undefined ) {
61+ delete process . env . BUNER_MODE ;
62+ } else {
63+ process . env . BUNER_MODE = prevMode ;
64+ }
65+ }
4266} ;
4367
4468const onPromptState = ( state : { value ?: string ; aborted ?: boolean } ) => {
@@ -83,6 +107,8 @@ program
83107 . argument ( '[project-directory]' , 'the project name' , '' )
84108 . description ( 'Scaffold a new frontend project' )
85109 . action ( async ( projectPath : string ) => {
110+ let packageManager : PackageManager = 'npm' ;
111+
86112 if ( ! projectPath ) {
87113 const validation = validateNpmName ( 'my-app' ) ;
88114
@@ -108,6 +134,24 @@ program
108134 }
109135 }
110136
137+ const packageManagerPrompt = await prompts ( {
138+ onState : onPromptState ,
139+ type : 'select' ,
140+ name : 'packageManager' ,
141+ message : 'Which package manager do you want to use?' ,
142+ initial : 0 ,
143+ choices : [
144+ { title : 'npm' , value : 'npm' } ,
145+ { title : 'pnpm' , value : 'pnpm' } ,
146+ { title : 'yarn' , value : 'yarn' } ,
147+ { title : 'bun' , value : 'bun' } ,
148+ ] ,
149+ } ) ;
150+
151+ if ( packageManagerPrompt . packageManager ) {
152+ packageManager = packageManagerPrompt . packageManager as PackageManager ;
153+ }
154+
111155 if ( ! projectPath ) {
112156 console . log (
113157 '\nPlease specify the project directory:\n' +
@@ -120,7 +164,7 @@ program
120164
121165 const resolvedProjectPath = path . resolve ( projectPath ) ;
122166
123- await createApp ( { appPath : resolvedProjectPath } ) ;
167+ await createApp ( { appPath : resolvedProjectPath , packageManager } ) ;
124168 await notifyUpdate ( ) ;
125169 } ) ;
126170
@@ -129,14 +173,11 @@ program
129173 . command ( 'dev' )
130174 . description ( 'Start development mode with all watchers' )
131175 . action ( async ( ) => {
132- await run ( 'npx' , [
133- 'concurrently' ,
134- '--kill-others' ,
135- '"bun styles.ts --watch"' ,
136- '"bun states.ts --watch"' ,
137- '"cross-env scriptOnly=true npx vite build --mode development --watch"' ,
138- '"bun server.ts --mode development"' ,
139- ] ) ;
176+ runStyles ( { watch : true } ) ;
177+ runStates ( { watch : true } ) ;
178+ process . env . scriptOnly = 'true' ;
179+ viteBuild ( { outDir : 'dist/static' , mode : 'development' , watch : true } ) ;
180+ runServer ( { mode : 'development' } ) ;
140181 } ) ;
141182
142183// buner serve
@@ -145,16 +186,16 @@ program
145186 . description ( 'Start the SSR dev server' )
146187 . option ( '--mode <mode>' , 'server mode' , 'development' )
147188 . action ( async ( opts ) => {
148- await run ( 'bun' , [ 'server.ts' , '-- mode' , opts . mode ] ) ;
189+ runServer ( { mode : opts . mode } ) ;
149190 } ) ;
150191
151192// buner build
152193program
153194 . command ( 'build' )
154195 . description ( 'Build the project (static + SSR)' )
155196 . action ( async ( ) => {
156- runSync ( 'npx vite build -- outDir dist/static') ;
157- runSync ( 'npx vite build -- ssr src/entry-server.tsx --outDir dist/server' ) ;
197+ await viteBuild ( { outDir : ' dist/static' } ) ;
198+ await viteBuild ( { outDir : 'dist/server' , ssr : ' src/entry-server.tsx' } ) ;
158199 } ) ;
159200
160201// buner generate
@@ -163,40 +204,35 @@ program
163204 . description ( 'Full static site generation (states + styles + build + prerender)' )
164205 . option ( '--mode <mode>' , 'build mode' , 'production' )
165206 . action ( async ( opts ) => {
166- runSync ( 'bun states.ts' ) ;
167- runSync ( 'bun styles.ts' ) ;
168- if ( opts . mode === 'production' ) {
169- runSync ( 'npx vite build --outDir dist/static' ) ;
170- runSync ( 'npx vite build --ssr src/entry-server.tsx --outDir dist/server' ) ;
171- } else {
172- runSync ( `npx vite build --outDir dist/static --mode ${ opts . mode } ` ) ;
173- runSync ( `npx vite build --ssr src/entry-server.tsx --outDir dist/server --mode ${ opts . mode } ` ) ;
174- }
175- runSync ( `bun prerender.ts --add-hash --mode ${ opts . mode } ` ) ;
207+ runStates ( ) ;
208+ runStyles ( ) ;
209+ await viteBuild ( { outDir : 'dist/static' , mode : opts . mode !== 'production' ? opts . mode : undefined } ) ;
210+ await viteBuild ( { outDir : 'dist/server' , ssr : 'src/entry-server.tsx' , mode : opts . mode !== 'production' ? opts . mode : undefined } ) ;
211+ await runPrerender ( { addHash : true , mode : opts . mode } ) ;
176212 } ) ;
177213
178214// buner eshn
179215program
180216 . command ( 'eshn' )
181217 . description ( 'Generate with --mode eshn' )
182218 . action ( async ( ) => {
183- runSync ( 'bun states.ts' ) ;
184- runSync ( 'bun styles.ts' ) ;
185- runSync ( 'npx vite build -- outDir dist/static -- mode eshn') ;
186- runSync ( 'npx vite build -- ssr src/entry-server.tsx --outDir dist/server -- mode eshn') ;
187- runSync ( 'bun prerender.ts --add-hash -- mode eshn') ;
219+ runStates ( ) ;
220+ runStyles ( ) ;
221+ await viteBuild ( { outDir : ' dist/static' , mode : ' eshn' } ) ;
222+ await viteBuild ( { outDir : 'dist/server' , ssr : ' src/entry-server.tsx' , mode : ' eshn' } ) ;
223+ await runPrerender ( { addHash : true , mode : ' eshn' } ) ;
188224 } ) ;
189225
190226// buner inte
191227program
192228 . command ( 'inte' )
193229 . description ( 'Build and integrate with backend (styles + build + prerender + integration)' )
194230 . action ( async ( ) => {
195- runSync ( 'bun styles.ts' ) ;
196- runSync ( 'npx vite build -- outDir dist/static') ;
197- runSync ( 'npx vite build -- ssr src/entry-server.tsx --outDir dist/server' ) ;
198- runSync ( 'bun prerender.ts' ) ;
199- runSync ( 'bun integration.ts' ) ;
231+ runStyles ( ) ;
232+ await viteBuild ( { outDir : ' dist/static' } ) ;
233+ await viteBuild ( { outDir : 'dist/server' , ssr : ' src/entry-server.tsx' } ) ;
234+ await runPrerender ( ) ;
235+ runIntegration ( ) ;
200236 } ) ;
201237
202238// buner styles
@@ -205,10 +241,7 @@ program
205241 . description ( 'Compile SCSS' )
206242 . option ( '--watch' , 'Watch for changes' )
207243 . action ( async ( opts ) => {
208- const args = [ 'styles.ts' ] ;
209-
210- if ( opts . watch ) args . push ( '--watch' ) ;
211- await run ( 'bun' , args ) ;
244+ runStyles ( { watch : opts . watch } ) ;
212245 } ) ;
213246
214247// buner prerender
@@ -218,11 +251,7 @@ program
218251 . option ( '--add-hash' , 'Add content hashes to asset URLs' )
219252 . option ( '--mode <mode>' , 'build mode' , 'production' )
220253 . action ( async ( opts ) => {
221- const args = [ 'prerender.ts' ] ;
222-
223- if ( opts . addHash ) args . push ( '--add-hash' ) ;
224- args . push ( '--mode' , opts . mode ) ;
225- await run ( 'bun' , args ) ;
254+ await runPrerender ( { addHash : opts . addHash , mode : opts . mode } ) ;
226255 } ) ;
227256
228257program . parseAsync ( process . argv ) . catch ( async ( error ) => {
0 commit comments