@@ -205,3 +205,282 @@ export async function updateResourceConfig(resourceId, columnName, fieldType, co
205
205
throw new Error ( `Failed to update resource file ${ path . basename ( filePath ) } : ${ error . message } ` ) ;
206
206
}
207
207
}
208
+
209
+
210
+ export async function injectLoginComponent ( indexFilePath , componentPath ) {
211
+ console . log ( chalk . dim ( `Reading file: ${ indexFilePath } ` ) ) ;
212
+ const content = await fs . readFile ( indexFilePath , 'utf-8' ) ;
213
+ const ast = recast . parse ( content , {
214
+ parser : typescriptParser ,
215
+ } ) ;
216
+
217
+ let updated = false ;
218
+
219
+ recast . visit ( ast , {
220
+ visitNewExpression ( path ) {
221
+ if (
222
+ n . Identifier . check ( path . node . callee ) &&
223
+ path . node . callee . name === 'AdminForth' &&
224
+ path . node . arguments . length > 0 &&
225
+ n . ObjectExpression . check ( path . node . arguments [ 0 ] )
226
+ ) {
227
+ const configObject = path . node . arguments [ 0 ] ;
228
+
229
+ let customizationProp = configObject . properties . find (
230
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === 'customization'
231
+ ) ;
232
+
233
+ if ( ! customizationProp ) {
234
+ const customizationObj = b . objectExpression ( [ ] ) ;
235
+ customizationProp = b . objectProperty ( b . identifier ( 'customization' ) , customizationObj ) ;
236
+ configObject . properties . push ( customizationProp ) ;
237
+ console . log ( chalk . dim ( `Added missing 'customization' property.` ) ) ;
238
+ }
239
+
240
+ const customizationValue = customizationProp . value ;
241
+ if ( ! n . ObjectExpression . check ( customizationValue ) ) return false ;
242
+
243
+ let loginPageInjections = customizationValue . properties . find (
244
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === 'loginPageInjections'
245
+ ) ;
246
+
247
+ if ( ! loginPageInjections ) {
248
+ const injectionsObj = b . objectExpression ( [ ] ) ;
249
+ loginPageInjections = b . objectProperty ( b . identifier ( 'loginPageInjections' ) , injectionsObj ) ;
250
+ customizationValue . properties . push ( loginPageInjections ) ;
251
+ console . log ( chalk . dim ( `Added missing 'loginPageInjections'.` ) ) ;
252
+ }
253
+
254
+ const injectionsValue = loginPageInjections . value ;
255
+ if ( ! n . ObjectExpression . check ( injectionsValue ) ) return false ;
256
+
257
+ let underInputsProp = injectionsValue . properties . find (
258
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === 'underInputs'
259
+ ) ;
260
+
261
+ if ( underInputsProp ) {
262
+ underInputsProp . value = b . stringLiteral ( componentPath ) ;
263
+ console . log ( chalk . dim ( `Updated 'underInputs' to ${ componentPath } ` ) ) ;
264
+ } else {
265
+ injectionsValue . properties . push (
266
+ b . objectProperty ( b . identifier ( 'underInputs' ) , b . stringLiteral ( componentPath ) )
267
+ ) ;
268
+ console . log ( chalk . dim ( `Added 'underInputs': ${ componentPath } ` ) ) ;
269
+ }
270
+
271
+ updated = true ;
272
+ this . abort ( ) ;
273
+ }
274
+ return false ;
275
+ }
276
+ } ) ;
277
+
278
+ if ( ! updated ) {
279
+ throw new Error ( `Could not find AdminForth configuration in file: ${ indexFilePath } ` ) ;
280
+ }
281
+
282
+ const outputCode = recast . print ( ast ) . code ;
283
+ await fs . writeFile ( indexFilePath , outputCode , 'utf-8' ) ;
284
+ console . log ( chalk . green ( `✅ Successfully updated login injection in: ${ indexFilePath } ` ) ) ;
285
+ }
286
+
287
+
288
+ export async function injectGlobalComponent ( indexFilePath , injectionType , componentPath ) {
289
+ console . log ( chalk . dim ( `Reading file: ${ indexFilePath } ` ) ) ;
290
+ const content = await fs . readFile ( indexFilePath , 'utf-8' ) ;
291
+ const ast = recast . parse ( content , {
292
+ parser : typescriptParser ,
293
+ } ) ;
294
+
295
+ let updated = false ;
296
+
297
+ console . log ( JSON . stringify ( injectionType ) ) ;
298
+ recast . visit ( ast , {
299
+ visitNewExpression ( path ) {
300
+ if (
301
+ n . Identifier . check ( path . node . callee ) &&
302
+ path . node . callee . name === 'AdminForth' &&
303
+ path . node . arguments . length > 0 &&
304
+ n . ObjectExpression . check ( path . node . arguments [ 0 ] )
305
+ ) {
306
+ const configObject = path . node . arguments [ 0 ] ;
307
+
308
+ let customizationProp = configObject . properties . find (
309
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === 'customization'
310
+ ) ;
311
+
312
+ if ( ! customizationProp ) {
313
+ const customizationObj = b . objectExpression ( [ ] ) ;
314
+ customizationProp = b . objectProperty ( b . identifier ( 'customization' ) , customizationObj ) ;
315
+ configObject . properties . push ( customizationProp ) ;
316
+ console . log ( chalk . dim ( `Added missing 'customization' property.` ) ) ;
317
+ }
318
+
319
+ const customizationValue = customizationProp . value ;
320
+ if ( ! n . ObjectExpression . check ( customizationValue ) ) return false ;
321
+
322
+ let globalInjections = customizationValue . properties . find (
323
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === 'globalInjections'
324
+ ) ;
325
+
326
+ if ( ! globalInjections ) {
327
+ const injectionsObj = b . objectExpression ( [ ] ) ;
328
+ globalInjections = b . objectProperty ( b . identifier ( 'globalInjections' ) , injectionsObj ) ;
329
+ customizationValue . properties . push ( globalInjections ) ;
330
+ console . log ( chalk . dim ( `Added missing 'globalInjections'.` ) ) ;
331
+ }
332
+
333
+ const injectionsValue = globalInjections . value ;
334
+ if ( ! n . ObjectExpression . check ( injectionsValue ) ) return false ;
335
+ console . log ( JSON . stringify ( injectionType ) ) ;
336
+ let injectionProp = injectionsValue . properties . find (
337
+ p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === injectionType
338
+ ) ;
339
+ if ( injectionProp ) {
340
+ const currentValue = injectionProp . value ;
341
+
342
+ if ( n . ArrayExpression . check ( currentValue ) ) {
343
+ currentValue . elements . push ( b . stringLiteral ( componentPath ) ) ;
344
+ console . log ( chalk . dim ( `Added '${ componentPath } ' to existing array in '${ injectionType } '` ) ) ;
345
+ } else if ( n . StringLiteral . check ( currentValue ) ) {
346
+ injectionProp . value = b . arrayExpression ( [
347
+ b . stringLiteral ( currentValue . value ) ,
348
+ b . stringLiteral ( componentPath )
349
+ ] ) ;
350
+ console . log ( chalk . dim ( `Converted '${ injectionType } ' from string to array and added '${ componentPath } '` ) ) ;
351
+ } else {
352
+ throw new Error ( `Unsupported value type for '${ injectionType } '. Must be string or array.` ) ;
353
+ }
354
+ } else {
355
+ injectionsValue . properties . push (
356
+ b . objectProperty (
357
+ b . identifier ( injectionType ) ,
358
+ b . arrayExpression ( [ b . stringLiteral ( componentPath ) ] )
359
+ )
360
+ ) ;
361
+ console . log ( chalk . dim ( `Added new array for '${ injectionType } ' with '${ componentPath } '` ) ) ;
362
+ }
363
+
364
+ updated = true ;
365
+ this . abort ( ) ;
366
+ }
367
+ return false ;
368
+ }
369
+ } ) ;
370
+
371
+ if ( ! updated ) {
372
+ throw new Error ( `Could not find AdminForth configuration in file: ${ indexFilePath } ` ) ;
373
+ }
374
+
375
+ const outputCode = recast . print ( ast ) . code ;
376
+ await fs . writeFile ( indexFilePath , outputCode , 'utf-8' ) ;
377
+ console . log ( chalk . green ( `✅ Successfully updated global injection '${ injectionType } ' in: ${ indexFilePath } ` ) ) ;
378
+ }
379
+
380
+ export async function updateCrudInjectionConfig ( resourceId , crudType , injectionPosition , componentPathForConfig , isThin ) {
381
+ const filePath = await findResourceFilePath ( resourceId ) ;
382
+ console . log ( chalk . dim ( `Attempting to update resource CRUD injection: ${ filePath } ` ) ) ;
383
+
384
+ let content ;
385
+ try {
386
+ content = await fs . readFile ( filePath , 'utf-8' ) ;
387
+ } catch ( error ) {
388
+ console . error ( chalk . red ( `❌ Error reading resource file: ${ filePath } ` ) ) ;
389
+ throw new Error ( `Could not read resource file ${ filePath } .` ) ;
390
+ }
391
+
392
+ try {
393
+ const ast = recast . parse ( content , {
394
+ parser : typescriptParser
395
+ } ) ;
396
+
397
+ let updateApplied = false ;
398
+
399
+ recast . visit ( ast , {
400
+ visitExportDefaultDeclaration ( path ) {
401
+ const declaration = path . node . declaration ;
402
+ let objectExpressionNode = null ;
403
+
404
+ if ( n . TSAsExpression . check ( declaration ) && n . ObjectExpression . check ( declaration . expression ) ) {
405
+ objectExpressionNode = declaration . expression ;
406
+ } else if ( n . ObjectExpression . check ( declaration ) ) {
407
+ objectExpressionNode = declaration ;
408
+ }
409
+
410
+ if ( ! objectExpressionNode ) {
411
+ console . warn ( chalk . yellow ( `Warning: Default export in ${ filePath } is not an ObjectExpression. Skipping update.` ) ) ;
412
+ return false ;
413
+ }
414
+
415
+ const getOrCreateObjectProp = ( obj , propName ) => {
416
+ let prop = obj . properties . find ( p => n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === propName ) ;
417
+ if ( ! prop ) {
418
+ const newObject = b . objectExpression ( [ ] ) ;
419
+ prop = b . objectProperty ( b . identifier ( propName ) , newObject ) ;
420
+ obj . properties . push ( prop ) ;
421
+ }
422
+ return prop . value ;
423
+ } ;
424
+
425
+ const options = getOrCreateObjectProp ( objectExpressionNode , 'options' ) ;
426
+ if ( ! n . ObjectExpression . check ( options ) ) return false ;
427
+
428
+ const pageInjections = getOrCreateObjectProp ( options , 'pageInjections' ) ;
429
+ if ( ! n . ObjectExpression . check ( pageInjections ) ) return false ;
430
+
431
+ let crudProp = pageInjections . properties . find ( p =>
432
+ n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === crudType
433
+ ) ;
434
+
435
+ if ( ! crudProp ) {
436
+ crudProp = b . objectProperty (
437
+ b . identifier ( crudType ) ,
438
+ b . objectExpression ( [ ] )
439
+ ) ;
440
+ pageInjections . properties . push ( crudProp ) ;
441
+ }
442
+
443
+ const crudValue = crudProp . value ;
444
+ if ( ! n . ObjectExpression . check ( crudValue ) ) return false ;
445
+
446
+ let injectionProp = crudValue . properties . find ( p =>
447
+ n . ObjectProperty . check ( p ) && n . Identifier . check ( p . key ) && p . key . name === injectionPosition
448
+ ) ;
449
+
450
+ const newInjectionObject = b . objectExpression ( [
451
+ b . objectProperty ( b . identifier ( 'file' ) , b . stringLiteral ( componentPathForConfig ) ) ,
452
+ b . objectProperty (
453
+ b . identifier ( 'meta' ) ,
454
+ b . objectExpression ( [
455
+ b . objectProperty ( b . identifier ( 'thinEnoughToShrinkTable' ) , b . booleanLiteral ( ! ! isThin ) ) ,
456
+ ] )
457
+ ) ,
458
+ ] ) ;
459
+
460
+ if ( injectionProp ) {
461
+ injectionProp . value = newInjectionObject ;
462
+ console . log ( chalk . dim ( `Updated '${ injectionPosition } ' injection for '${ crudType } '.` ) ) ;
463
+ } else {
464
+ crudValue . properties . push ( b . objectProperty ( b . identifier ( injectionPosition ) , newInjectionObject ) ) ;
465
+ console . log ( chalk . dim ( `Added '${ injectionPosition } ' injection for '${ crudType } '.` ) ) ;
466
+ }
467
+
468
+ updateApplied = true ;
469
+ this . abort ( ) ;
470
+ return false ;
471
+ }
472
+ } ) ;
473
+
474
+ if ( ! updateApplied ) {
475
+ throw new Error ( `Could not inject CRUD component in resource ${ resourceId } .` ) ;
476
+ }
477
+
478
+ const outputCode = recast . print ( ast ) . code ;
479
+ await fs . writeFile ( filePath , outputCode , 'utf-8' ) ;
480
+ console . log ( chalk . dim ( `✅ Successfully updated CRUD injection in resource file: ${ filePath } ` ) ) ;
481
+
482
+ } catch ( error ) {
483
+ console . error ( chalk . red ( `❌ Error processing resource file: ${ filePath } ` ) ) ;
484
+ throw new Error ( `Failed to inject CRUD component in ${ path . basename ( filePath ) } : ${ error . message } ` ) ;
485
+ }
486
+ }
0 commit comments