@@ -13,12 +13,44 @@ const THIS_DIR = path.dirname(fileURLToPath(import.meta.url));
1313// init.ts compiles to cli/dist/commands/everywhere/init.js, so the SDK's root
1414// package.json is four levels up. Both dev and published layouts match.
1515const SDK_PKG_PATH = path . resolve ( THIS_DIR , '../../../../package.json' ) ;
16+ const TYPE_DEFINITIONS_BY_DEP : Record < string , string > = {
17+ react : '@types/react' ,
18+ 'react-dom' : '@types/react-dom' ,
19+ } ;
20+ const DEFAULT_DEV_DEPENDENCIES : Record < string , string > = {
21+ typescript : '^5' ,
22+ } ;
23+
24+ type InitPackageJson = {
25+ name ?: string ;
26+ version ?: string ;
27+ title ?: string ;
28+ dependencies ?: Record < string , string > ;
29+ devDependencies ?: Record < string , string > ;
30+ } ;
31+
32+ type InitPackageJsonWithDependencies = InitPackageJson & {
33+ dependencies : Record < string , string > ;
34+ } ;
1635
1736function getSdkVersion ( ) : string {
1837 const pkg = JSON . parse ( fs . readFileSync ( SDK_PKG_PATH , 'utf-8' ) ) as { version : string } ;
1938 return pkg . version ;
2039}
2140
41+ export const resolveTypeDevDependencies = (
42+ desiredDeps : Record < string , string >
43+ ) : Record < string , string > => {
44+ const desiredTypeDeps : Record < string , string > = { ...DEFAULT_DEV_DEPENDENCIES } ;
45+ for ( const depName of Object . keys ( desiredDeps ) ) {
46+ const typePackageName = TYPE_DEFINITIONS_BY_DEP [ depName ] ;
47+ if ( typePackageName ) {
48+ desiredTypeDeps [ typePackageName ] = desiredDeps [ depName ] ;
49+ }
50+ }
51+ return desiredTypeDeps ;
52+ } ;
53+
2254// On Windows, npm is a `.cmd` shim that requires the explicit extension when
2355// spawned without a shell. Avoid `shell: true` because Node deprecates passing
2456// args alongside it (DEP0190).
@@ -119,12 +151,7 @@ export default class InitCommand extends EverywhereBaseCommand {
119151 }
120152
121153 // Pre-check 2: package.json parses and has a name
122- const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf-8' ) ) as {
123- name ?: string ;
124- version ?: string ;
125- title ?: string ;
126- dependencies ?: Record < string , string > ;
127- } ;
154+ const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf-8' ) ) as InitPackageJson ;
128155 if ( ! pkg . name || typeof pkg . name !== 'string' ) {
129156 this . error ( 'package.json must have a "name" field.' ) ;
130157 }
@@ -153,13 +180,14 @@ export default class InitCommand extends EverywhereBaseCommand {
153180 react : '^19' ,
154181 'react-dom' : '^19' ,
155182 } ;
183+ const desiredDevDeps = resolveTypeDevDependencies ( desiredDeps ) ;
156184 const existingDeps : Record < string , string > = pkg . dependencies ?? { } ;
185+ const existingDevDeps : Record < string , string > = pkg . devDependencies ?? { } ;
157186 const added : Array < { name : string ; version : string } > = [ ] ;
158- const skipped : Array < { name : string ; existingVersion : string } > = [ ] ;
187+ const addedDevDeps : Array < { name : string ; version : string } > = [ ] ;
159188
160189 for ( const [ name , version ] of Object . entries ( desiredDeps ) ) {
161190 if ( name in existingDeps ) {
162- skipped . push ( { name, existingVersion : existingDeps [ name ] } ) ;
163191 if ( verbose ) {
164192 this . log (
165193 `Dependency already present: ${ name } (keeping ${ chalk . dim ( existingDeps [ name ] ) } )`
@@ -173,12 +201,38 @@ export default class InitCommand extends EverywhereBaseCommand {
173201 }
174202 }
175203
204+ for ( const [ name , version ] of Object . entries ( desiredDevDeps ) ) {
205+ if ( name in existingDevDeps || name in existingDeps ) {
206+ if ( verbose ) {
207+ const source = name in existingDevDeps ? 'devDependencies' : 'dependencies' ;
208+ const existingVersion = existingDevDeps [ name ] ?? existingDeps [ name ] ;
209+ this . log (
210+ `Type dependency already present in ${ source } : ${ name } (keeping ${ chalk . dim ( existingVersion ) } )`
211+ ) ;
212+ }
213+ } else {
214+ addedDevDeps . push ( { name, version } ) ;
215+ if ( verbose ) {
216+ this . log ( `Adding dev dependency: ${ name } ${ chalk . dim ( version ) } ` ) ;
217+ }
218+ }
219+ }
220+
176221 // Mutation 1: write package.json if anything was added
177- if ( added . length > 0 || title ) {
178- const newPkg = { ...pkg , dependencies : { ...existingDeps } } ;
222+ if ( added . length > 0 || addedDevDeps . length > 0 || title ) {
223+ const newPkg : InitPackageJsonWithDependencies = {
224+ ...pkg ,
225+ dependencies : { ...existingDeps } ,
226+ } ;
179227 for ( const { name, version } of added ) {
180228 newPkg . dependencies [ name ] = version ;
181229 }
230+ if ( addedDevDeps . length > 0 || pkg . devDependencies ) {
231+ newPkg . devDependencies = { ...existingDevDeps } ;
232+ for ( const { name, version } of addedDevDeps ) {
233+ newPkg . devDependencies [ name ] = version ;
234+ }
235+ }
182236 if ( title ) {
183237 newPkg . title = title ;
184238 }
0 commit comments