1
1
import fs from 'fs' ;
2
+ import { spawnSync } from 'child_process' ;
2
3
import { exec , execSync } from 'child_process' ;
3
4
import Generator from 'yeoman-generator' ;
4
5
import path from 'path' ;
@@ -8,6 +9,8 @@ import { fileURLToPath } from 'url';
8
9
const __filename = fileURLToPath ( import . meta. url ) ;
9
10
const __dirname = path . dirname ( __filename ) ;
10
11
12
+ const DOT = '.' ;
13
+
11
14
function isKebabCase ( str ) {
12
15
// Check if the string is empty
13
16
if ( ! str || str . trim ( ) . length === 0 ) {
@@ -39,6 +42,12 @@ function toKebabCase(str) {
39
42
. join ( '-' ) ;
40
43
}
41
44
45
+ function deleteFileIfExists ( filePath ) {
46
+ if ( fs . existsSync ( filePath ) ) {
47
+ fs . unlinkSync ( filePath ) ;
48
+ }
49
+ }
50
+
42
51
export default class extends Generator {
43
52
constructor ( args , opts ) {
44
53
super ( args , opts ) ;
@@ -149,7 +158,16 @@ export default class extends Generator {
149
158
. filter ( version => ! version . includes ( '-' ) ) ; // Exclude pre-releases like -alpha or -beta
150
159
151
160
// Sort descending and get the latest
152
- const latest90 = stableVersions . sort ( ( a , b ) => ( a > b ? - 1 : 1 ) ) [ 0 ] ;
161
+ const latest90 = stableVersions
162
+ . sort ( ( a , b ) => {
163
+ // Split version strings like '9.0.9' into [9, 0, 9]
164
+ const aParts = a . split ( DOT ) . map ( Number ) ;
165
+ const bParts = b . split ( DOT ) . map ( Number ) ;
166
+ // Compare major, then minor, then patch
167
+ if ( aParts [ 0 ] !== bParts [ 0 ] ) return bParts [ 0 ] - aParts [ 0 ] ;
168
+ if ( aParts [ 1 ] !== bParts [ 1 ] ) return bParts [ 1 ] - aParts [ 1 ] ;
169
+ return bParts [ 2 ] - aParts [ 2 ] ;
170
+ } ) [ 0 ] ;
153
171
154
172
if ( ! latest90 ) {
155
173
throw new Error ( 'No stable 9.0.x versions found.' ) ;
@@ -158,16 +176,22 @@ export default class extends Generator {
158
176
// Log the chosen version (optional)
159
177
this . log ( `Latest stable 9.0 version: ${ latest90 } ` ) ;
160
178
161
- // Use `this.spawnCommandSync` with the selected version
162
- this . spawnCommandSync ( 'npx' , [
179
+ spawnSync ( 'npx' , [
163
180
'-y' ,
164
181
`storybook@${ latest90 } ` ,
165
182
'init' ,
166
183
'--no-dev' ,
167
184
'--yes' , // Skip all prompts
168
185
'--type' , 'nextjs' , // Specify Next.js as the framework
169
- ] ) ;
186
+ ] , { stdio : 'inherit' , cwd : this . destinationRoot ( ) } ) ;
170
187
this . log ( 'Storybook installed!' ) ;
188
+ this . log ( 'Installing @storybook/react-vite for Vite builder support...' ) ;
189
+ spawnSync ( 'npm' , [
190
+ 'install' ,
191
+ '--save-dev' ,
192
+ '@storybook/react-vite'
193
+ ] , { stdio : 'inherit' , cwd : this . destinationRoot ( ) } ) ;
194
+ this . log ( '@storybook/react-vite installed!' ) ;
171
195
// if (this.options.tailwind && this.options.storybook) {
172
196
// Tailwind CSS specific setup for older versions of Storybook
173
197
// this.spawnCommandSync('npx', ['storybook@latest', 'add', '@storybook/addon-styling-webpack']);
@@ -179,16 +203,16 @@ export default class extends Generator {
179
203
// Conditionally add Cypress
180
204
if ( this . options . cypress ) {
181
205
this . log ( 'Installing Cypress...' ) ;
182
- this . spawnCommandSync ( 'npm' , [ 'install' , '--save-dev' , 'cypress' ] ) ;
206
+ spawnSync ( 'npm' , [ 'install' , '--save-dev' , 'cypress' ] , { stdio : 'inherit' , cwd : this . destinationRoot ( ) } ) ;
183
207
this . log ( 'Cypress installed!' ) ;
184
208
if ( this . options . bitloops ) {
185
- this . spawnCommandSync ( 'npm' , [
209
+ spawnSync ( 'npm' , [
186
210
'install' ,
187
211
'--save-dev' ,
188
212
'mochawesome' ,
189
213
'mochawesome-merge' ,
190
214
'mochawesome-report-generator' ,
191
- ] ) ;
215
+ ] , { stdio : 'inherit' , cwd : this . destinationRoot ( ) } ) ;
192
216
}
193
217
}
194
218
} ;
@@ -198,7 +222,7 @@ export default class extends Generator {
198
222
if ( this . options . storybook ) {
199
223
this . log ( 'Making Storybook changes...' ) ;
200
224
if ( this . options . tailwind ) {
201
- fs . unlinkSync ( this . destinationPath ( '.storybook/preview.ts' ) ) ;
225
+ deleteFileIfExists ( this . destinationPath ( '.storybook/preview.ts' ) ) ;
202
226
this . log ( 'Setting up Tailwind CSS with Storybook...' ) ;
203
227
this . fs . copyTpl (
204
228
this . templatePath ( 'storybook.preview.ts' ) ,
@@ -225,21 +249,21 @@ export default class extends Generator {
225
249
) ;
226
250
}
227
251
228
- fs . unlinkSync ( this . destinationPath ( 'src/app/page.tsx' ) ) ;
252
+ deleteFileIfExists ( this . destinationPath ( 'src/app/page.tsx' ) ) ;
229
253
this . fs . copyTpl (
230
254
this . templatePath ( 'next.app.page.tsx' ) ,
231
255
this . destinationPath ( 'src/app/page.tsx' )
232
256
) ;
233
257
234
- fs . unlinkSync ( this . destinationPath ( 'src/app/layout.tsx' ) ) ;
258
+ deleteFileIfExists ( this . destinationPath ( 'src/app/layout.tsx' ) ) ;
235
259
this . fs . copyTpl (
236
260
this . templatePath ( 'next.app.layout.tsx' ) ,
237
261
this . destinationPath ( 'src/app/layout.tsx' ) ,
238
262
{ projectName : this . options . project }
239
263
) ;
240
264
241
265
this . log ( 'Adding Meyer reset in global.css...' ) ;
242
- fs . unlinkSync ( this . destinationPath ( 'src/app/globals.css' ) ) ;
266
+ deleteFileIfExists ( this . destinationPath ( 'src/app/globals.css' ) ) ;
243
267
this . fs . copyTpl (
244
268
this . templatePath ( 'globals.css' ) ,
245
269
this . destinationPath ( 'src/app/globals.css' )
@@ -276,11 +300,11 @@ export default class extends Generator {
276
300
const path = 'cypress/helpers/index.ts' ;
277
301
this . fs . copyTpl ( this . templatePath ( path ) , this . destinationPath ( path ) ) ;
278
302
}
279
- this . spawnCommandSync ( 'npm' , [
303
+ spawnSync ( 'npm' , [
280
304
'install' ,
281
305
'--save-dev' ,
282
306
'react-aria-components' ,
283
- ] ) ;
307
+ ] , { stdio : 'inherit' , cwd : this . destinationRoot ( ) } ) ;
284
308
}
285
309
} ;
286
310
0 commit comments