@@ -158,6 +158,54 @@ export class CodeTransformer {
158
158
}
159
159
}
160
160
161
+ /**
162
+ * Add the given import declarations to the source file
163
+ * and merge named imports with the existing import
164
+ */
165
+ #addImportDeclarations(
166
+ file : SourceFile ,
167
+ importDeclarations : { isNamed : boolean ; module : string ; identifier : string } [ ]
168
+ ) {
169
+ const existingImports = file . getImportDeclarations ( )
170
+
171
+ importDeclarations . forEach ( ( importDeclaration ) => {
172
+ const existingImport = existingImports . find (
173
+ ( mod ) => mod . getModuleSpecifierValue ( ) === importDeclaration . module
174
+ )
175
+
176
+ /**
177
+ * Add a new named import to existing import for the
178
+ * same module
179
+ */
180
+ if ( existingImport && importDeclaration . isNamed ) {
181
+ if (
182
+ ! existingImport
183
+ . getNamedImports ( )
184
+ . find ( ( namedImport ) => namedImport . getName ( ) === importDeclaration . identifier )
185
+ ) {
186
+ existingImport . addNamedImport ( importDeclaration . identifier )
187
+ }
188
+ return
189
+ }
190
+
191
+ /**
192
+ * Ignore default import when the same module is already imported.
193
+ * The chances are the existing default import and the importDeclaration
194
+ * identifiers are not the same. But we should not modify existing source
195
+ */
196
+ if ( existingImport ) {
197
+ return
198
+ }
199
+
200
+ file . addImportDeclaration ( {
201
+ ...( importDeclaration . isNamed
202
+ ? { namedImports : [ importDeclaration . identifier ] }
203
+ : { defaultImport : importDeclaration . identifier } ) ,
204
+ moduleSpecifier : importDeclaration . module ,
205
+ } )
206
+ } )
207
+ }
208
+
161
209
/**
162
210
* Write a leading comment
163
211
*/
@@ -297,46 +345,9 @@ export class CodeTransformer {
297
345
const file = this . #project. getSourceFileOrThrow ( testBootstrapUrl )
298
346
299
347
/**
300
- * Add the import declaration
348
+ * Add the import declarations
301
349
*/
302
- const existingImports = file . getImportDeclarations ( )
303
-
304
- importDeclarations . forEach ( ( importDeclaration ) => {
305
- const existingImport = existingImports . find (
306
- ( mod ) => mod . getModuleSpecifierValue ( ) === importDeclaration . module
307
- )
308
-
309
- /**
310
- * Add a new named import to existing import for the
311
- * same module
312
- */
313
- if ( existingImport && importDeclaration . isNamed ) {
314
- if (
315
- ! existingImport
316
- . getNamedImports ( )
317
- . find ( ( namedImport ) => namedImport . getName ( ) === importDeclaration . identifier )
318
- ) {
319
- existingImport . addNamedImport ( importDeclaration . identifier )
320
- }
321
- return
322
- }
323
-
324
- /**
325
- * Ignore default import when the same module is already imported.
326
- * The chances are the existing default import and the importDeclaration
327
- * identifiers are not the same. But we should not modify existing source
328
- */
329
- if ( existingImport ) {
330
- return
331
- }
332
-
333
- file . addImportDeclaration ( {
334
- ...( importDeclaration . isNamed
335
- ? { namedImports : [ importDeclaration . identifier ] }
336
- : { defaultImport : importDeclaration . identifier } ) ,
337
- moduleSpecifier : importDeclaration . module ,
338
- } )
339
- } )
350
+ this . #addImportDeclarations( file , importDeclarations )
340
351
341
352
/**
342
353
* Insert the plugin call in the `plugins` array
@@ -358,6 +369,57 @@ export class CodeTransformer {
358
369
await file . save ( )
359
370
}
360
371
372
+ /**
373
+ * Add a new Vite plugin
374
+ */
375
+ async addVitePlugin (
376
+ pluginCall : string ,
377
+ importDeclarations : { isNamed : boolean ; module : string ; identifier : string } [ ]
378
+ ) {
379
+ /**
380
+ * Get the `vite.config.ts` source file
381
+ */
382
+ const viteConfigTsUrl = fileURLToPath ( new URL ( './vite.config.ts' , this . #cwd) )
383
+
384
+ const file = this . #project. getSourceFile ( viteConfigTsUrl )
385
+ if ( ! file ) {
386
+ throw new Error (
387
+ 'Cannot find vite.config.ts file. Make sure to rename vite.config.js to vite.config.ts'
388
+ )
389
+ }
390
+
391
+ /**
392
+ * Add the import declarations
393
+ */
394
+ this . #addImportDeclarations( file , importDeclarations )
395
+
396
+ /**
397
+ * Get the default export options
398
+ */
399
+ const defaultExport = file . getDefaultExportSymbol ( )
400
+ if ( ! defaultExport ) {
401
+ throw new Error ( 'Cannot find the default export in vite.config.ts' )
402
+ }
403
+
404
+ const options = defaultExport
405
+ . getDeclarations ( ) [ 0 ]
406
+ . getChildrenOfKind ( SyntaxKind . ObjectLiteralExpression ) [ 0 ]
407
+
408
+ const pluginsArray = options
409
+ . getPropertyOrThrow ( 'plugins' )
410
+ . getFirstChildByKindOrThrow ( SyntaxKind . ArrayLiteralExpression )
411
+
412
+ /**
413
+ * Add plugin call to the plugins array
414
+ */
415
+ if ( ! pluginsArray . getElements ( ) . find ( ( element ) => element . getText ( ) === pluginCall ) ) {
416
+ pluginsArray . addElement ( pluginCall )
417
+ }
418
+
419
+ file . formatText ( this . #editorSettings)
420
+ await file . save ( )
421
+ }
422
+
361
423
/**
362
424
* Adds a policy to the list of `policies` object configured
363
425
* inside the `app/policies/main.ts` file.
0 commit comments