@adonisjs/assembler 8.0.0-next.9 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +260 -0
  2. package/build/chunk-DF48asd8.js +9 -0
  3. package/build/codemod_exception-BMNJZ0i1.js +280 -0
  4. package/build/index.d.ts +1 -1
  5. package/build/index.js +1854 -1724
  6. package/build/main-Cpfvmdw6.js +562 -0
  7. package/build/main-INOi9swJ.js +471 -0
  8. package/build/src/bundler.d.ts +2 -0
  9. package/build/src/code_scanners/routes_scanner/main.d.ts +16 -2
  10. package/build/src/code_scanners/routes_scanner/main.js +4 -445
  11. package/build/src/code_transformer/main.d.ts +14 -1
  12. package/build/src/code_transformer/main.js +981 -622
  13. package/build/src/code_transformer/rc_file_transformer.d.ts +28 -2
  14. package/build/src/debug.d.ts +1 -1
  15. package/build/src/dev_server.d.ts +60 -12
  16. package/build/src/exceptions/codemod_exception.d.ts +178 -0
  17. package/build/src/file_buffer.d.ts +19 -0
  18. package/build/src/file_system.d.ts +3 -3
  19. package/build/src/helpers.js +205 -16
  20. package/build/src/index_generator/main.js +4 -7
  21. package/build/src/paths_resolver.d.ts +2 -1
  22. package/build/src/test_runner.d.ts +3 -2
  23. package/build/src/types/code_scanners.d.ts +29 -13
  24. package/build/src/types/code_transformer.d.ts +127 -0
  25. package/build/src/types/common.d.ts +98 -2
  26. package/build/src/types/hooks.d.ts +4 -1
  27. package/build/src/types/main.js +2 -0
  28. package/build/src/utils.d.ts +7 -3
  29. package/build/src/virtual_file_system.d.ts +1 -1
  30. package/build/virtual_file_system-dzfXNwEp.js +572 -0
  31. package/package.json +41 -39
  32. package/build/chunk-7XU453QB.js +0 -418
  33. package/build/chunk-PORDZS62.js +0 -391
  34. package/build/chunk-TIKQQRMX.js +0 -116
  35. package/build/src/hooks.d.ts +0 -224
package/README.md CHANGED
@@ -420,6 +420,266 @@ export const policies = {
420
420
  }
421
421
  ```
422
422
 
423
+ ### addValidator
424
+ Create a new validator file or add a validator to an existing file. If the file does not exist, it will be created with the provided contents. If it exists and the export name is not already defined, the validator will be appended to the file.
425
+
426
+ > [!IMPORTANT]
427
+ > This codemod respects the `validators` directory configured in `adonisrc.ts` and defaults to `app/validators`.
428
+
429
+ ```ts
430
+ const transformer = new CodeTransformer(appRoot)
431
+
432
+ try {
433
+ await transformer.addValidator({
434
+ validatorFileName: 'user.ts',
435
+ exportName: 'loginValidator',
436
+ contents: `export const loginValidator = vine.compile(
437
+ vine.object({
438
+ email: vine.string().email(),
439
+ password: vine.string().minLength(8)
440
+ })
441
+ )`
442
+ })
443
+ } catch (error) {
444
+ console.error('Unable to add validator')
445
+ console.error(error)
446
+ }
447
+ ```
448
+
449
+ Output (app/validators/user.ts)
450
+
451
+ ```ts
452
+ export const loginValidator = vine.compile(
453
+ vine.object({
454
+ email: vine.string().email(),
455
+ password: vine.string().minLength(8)
456
+ })
457
+ )
458
+ ```
459
+
460
+ ### addLimiter
461
+ Create a new rate limiter file or add a limiter to an existing file. If the file does not exist, it will be created with the provided contents. If it exists and the export name is not already defined, the limiter will be appended to the file.
462
+
463
+ > [!IMPORTANT]
464
+ > Limiters are created in the `start` directory configured in `adonisrc.ts` and defaults to `start`.
465
+
466
+ ```ts
467
+ const transformer = new CodeTransformer(appRoot)
468
+
469
+ try {
470
+ await transformer.addLimiter({
471
+ limiterFileName: 'limiters.ts',
472
+ exportName: 'apiThrottle',
473
+ contents: `export const apiThrottle = limiter.define('api', () => {
474
+ return limiter.allowRequests(10).every('1 minute')
475
+ })`
476
+ })
477
+ } catch (error) {
478
+ console.error('Unable to add limiter')
479
+ console.error(error)
480
+ }
481
+ ```
482
+
483
+ Output (start/limiters.ts)
484
+
485
+ ```ts
486
+ export const apiThrottle = limiter.define('api', () => {
487
+ return limiter.allowRequests(10).every('1 minute')
488
+ })
489
+ ```
490
+
491
+ ### addModelMixins
492
+ Apply one or more mixins to a model class. This wraps the model's extends clause with the `compose` helper and applies the specified mixins.
493
+
494
+ > [!IMPORTANT]
495
+ > This codemod expects the model file to exist with a default exported class that extends a base class.
496
+
497
+ ```ts
498
+ const transformer = new CodeTransformer(appRoot)
499
+
500
+ try {
501
+ await transformer.addModelMixins('user.ts', [
502
+ {
503
+ name: 'SoftDeletes',
504
+ importPath: '@adonisjs/lucid/orm/mixins/soft_deletes',
505
+ importType: 'named'
506
+ },
507
+ {
508
+ name: 'Sluggable',
509
+ importPath: '#mixins/sluggable',
510
+ importType: 'default',
511
+ args: ['title', '{ strategy: "dbIncrement" }']
512
+ }
513
+ ])
514
+ } catch (error) {
515
+ console.error('Unable to add mixins to model')
516
+ console.error(error)
517
+ }
518
+ ```
519
+
520
+ Input (app/models/user.ts)
521
+
522
+ ```ts
523
+ import { BaseModel } from '@adonisjs/lucid/orm'
524
+
525
+ export default class User extends BaseModel {
526
+ // ...
527
+ }
528
+ ```
529
+
530
+ Output (app/models/user.ts)
531
+
532
+ ```ts
533
+ import { BaseModel } from '@adonisjs/lucid/orm'
534
+ import { compose } from '@adonisjs/core/helpers'
535
+ import { SoftDeletes } from '@adonisjs/lucid/orm/mixins/soft_deletes'
536
+ import Sluggable from '#mixins/sluggable'
537
+
538
+ export default class User extends compose(BaseModel, SoftDeletes(), Sluggable(title, { strategy: "dbIncrement" })) {
539
+ // ...
540
+ }
541
+ ```
542
+
543
+ ### addControllerMethod
544
+ Create a new controller file or add a method to an existing controller class. If the controller file does not exist, it will be created with the class and method. If it exists and the method is not already defined, the method will be added to the class.
545
+
546
+ > [!IMPORTANT]
547
+ > This codemod respects the `controllers` directory configured in `adonisrc.ts` and defaults to `app/controllers`.
548
+
549
+ ```ts
550
+ const transformer = new CodeTransformer(appRoot)
551
+
552
+ try {
553
+ await transformer.addControllerMethod({
554
+ controllerFileName: 'users_controller.ts',
555
+ className: 'UsersController',
556
+ name: 'destroy',
557
+ contents: `async destroy({ params, response }: HttpContext) {
558
+ const user = await User.findOrFail(params.id)
559
+ await user.delete()
560
+ return response.noContent()
561
+ }`,
562
+ imports: [
563
+ { isType: false, isNamed: true, name: 'HttpContext', path: '@adonisjs/core/http' },
564
+ { isType: false, isNamed: false, name: 'User', path: '#models/user' }
565
+ ]
566
+ })
567
+ } catch (error) {
568
+ console.error('Unable to add controller method')
569
+ console.error(error)
570
+ }
571
+ ```
572
+
573
+ Output (app/controllers/users_controller.ts)
574
+
575
+ ```ts
576
+ import type { HttpContext } from '@adonisjs/core/http'
577
+ import User from '#models/user'
578
+
579
+ export default class UsersController {
580
+ async destroy({ params, response }: HttpContext) {
581
+ const user = await User.findOrFail(params.id)
582
+ await user.delete()
583
+ return response.noContent()
584
+ }
585
+ }
586
+ ```
587
+
588
+ ### RcFileTransformer additional methods
589
+
590
+ The `RcFileTransformer` class (accessible via `updateRcFile` callback) now supports additional methods for managing imports and hooks.
591
+
592
+ #### addNamedImport
593
+ Add a named import to the `adonisrc.ts` file.
594
+
595
+ ```ts
596
+ const transformer = new CodeTransformer(appRoot)
597
+
598
+ try {
599
+ await transformer.updateRcFile((rcFile) => {
600
+ rcFile.addNamedImport('@adonisjs/core/types', ['Middleware', 'Provider'])
601
+ })
602
+ } catch (error) {
603
+ console.error('Unable to add named import')
604
+ console.error(error)
605
+ }
606
+ ```
607
+
608
+ Output
609
+
610
+ ```ts
611
+ import { defineConfig } from '@adonisjs/core/app'
612
+ import { Middleware, Provider } from '@adonisjs/core/types'
613
+
614
+ export default defineConfig({
615
+ // ...
616
+ })
617
+ ```
618
+
619
+ #### addDefaultImport
620
+ Add a default import to the `adonisrc.ts` file.
621
+
622
+ ```ts
623
+ const transformer = new CodeTransformer(appRoot)
624
+
625
+ try {
626
+ await transformer.updateRcFile((rcFile) => {
627
+ rcFile.addDefaultImport('#config/database', 'databaseConfig')
628
+ })
629
+ } catch (error) {
630
+ console.error('Unable to add default import')
631
+ console.error(error)
632
+ }
633
+ ```
634
+
635
+ Output
636
+
637
+ ```ts
638
+ import { defineConfig } from '@adonisjs/core/app'
639
+ import databaseConfig from '#config/database'
640
+
641
+ export default defineConfig({
642
+ // ...
643
+ })
644
+ ```
645
+
646
+ #### addAssemblerHook
647
+ Add assembler hooks to the `adonisrc.ts` file. Hooks can be added as thunk imports (lazy loaded) or as raw values for direct import references.
648
+
649
+ ```ts
650
+ const transformer = new CodeTransformer(appRoot)
651
+
652
+ try {
653
+ await transformer.updateRcFile((rcFile) => {
654
+ // Add a thunk-style hook (lazy import)
655
+ rcFile.addAssemblerHook('onBuildStarting', './commands/build_hook.js')
656
+
657
+ // Add a raw hook (direct import reference)
658
+ rcFile.addAssemblerHook('onBuildCompleted', 'buildCompletedHook', true)
659
+ })
660
+ } catch (error) {
661
+ console.error('Unable to add assembler hook')
662
+ console.error(error)
663
+ }
664
+ ```
665
+
666
+ Output
667
+
668
+ ```ts
669
+ import { defineConfig } from '@adonisjs/core/app'
670
+
671
+ export default defineConfig({
672
+ hooks: {
673
+ onBuildStarting: [
674
+ () => import('./commands/build_hook.js')
675
+ ],
676
+ onBuildCompleted: [
677
+ buildCompletedHook
678
+ ]
679
+ }
680
+ })
681
+ ```
682
+
423
683
  ## Index generator
424
684
 
425
685
  The `IndexGenerator` is a core concept in Assembler that is used to watch the filesystem and create barrel files or types from a source directory.
@@ -0,0 +1,9 @@
1
+ import "node:module";
2
+ Object.create;
3
+ Object.defineProperty;
4
+ Object.getOwnPropertyDescriptor;
5
+ Object.getOwnPropertyNames;
6
+ Object.getPrototypeOf;
7
+ Object.prototype.hasOwnProperty;
8
+ //#endregion
9
+ export {};
@@ -0,0 +1,280 @@
1
+ //#region src/exceptions/codemod_exception.ts
2
+ /**
3
+ * Custom exception for codemod errors that provides helpful instructions
4
+ * to the user when automatic code transformation fails.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * throw CodemodException.missingEnvFile('start/env.ts', {
9
+ * variables: { MY_VAR: 'Env.schema.string()' }
10
+ * })
11
+ * ```
12
+ */
13
+ var CodemodException = class CodemodException extends Error {
14
+ /**
15
+ * Instructions for the user to manually perform the codemod action
16
+ */
17
+ instructions;
18
+ /**
19
+ * The file path that was being modified
20
+ */
21
+ filePath;
22
+ constructor(message, options) {
23
+ super(message);
24
+ this.name = "CodemodException";
25
+ this.instructions = options?.instructions;
26
+ this.filePath = options?.filePath;
27
+ }
28
+ /**
29
+ * Format env validations as code string
30
+ */
31
+ static #formatEnvValidations(definition) {
32
+ const lines = [];
33
+ if (definition.leadingComment) {
34
+ lines.push(`/*`);
35
+ lines.push(`|----------------------------------------------------------`);
36
+ lines.push(`| ${definition.leadingComment}`);
37
+ lines.push(`|----------------------------------------------------------`);
38
+ lines.push(`*/`);
39
+ }
40
+ for (const [variable, validation] of Object.entries(definition.variables)) lines.push(`${variable}: ${validation},`);
41
+ return lines.join("\n");
42
+ }
43
+ /**
44
+ * Format middleware as code string
45
+ */
46
+ static #formatMiddleware(stack, middleware) {
47
+ if (stack === "named") return `export const middleware = router.named({\n ${middleware.filter((m) => m.name).map((m) => `${m.name}: () => import('${m.path}')`).join(",\n ")}\n})`;
48
+ return `${stack}.use([\n ${middleware.map((m) => `() => import('${m.path}')`).join(",\n ")}\n])`;
49
+ }
50
+ /**
51
+ * Format policies as code string
52
+ */
53
+ static #formatPolicies(policies) {
54
+ return `export const policies = {\n ${policies.map((p) => `${p.name}: () => import('${p.path}')`).join(",\n ")}\n}`;
55
+ }
56
+ /**
57
+ * Format Vite plugin as code string
58
+ */
59
+ static #formatVitePlugin(pluginCall, importDeclarations) {
60
+ return `${importDeclarations.map((decl) => decl.isNamed ? `import { ${decl.identifier} } from '${decl.module}'` : `import ${decl.identifier} from '${decl.module}'`).join("\n")}\n\nexport default defineConfig({\n plugins: [${pluginCall}]\n})`;
61
+ }
62
+ /**
63
+ * Format Japa plugin as code string
64
+ */
65
+ static #formatJapaPlugin(pluginCall, importDeclarations) {
66
+ return `${importDeclarations.map((decl) => decl.isNamed ? `import { ${decl.identifier} } from '${decl.module}'` : `import ${decl.identifier} from '${decl.module}'`).join("\n")}\n\nexport const plugins: Config['plugins'] = [\n ${pluginCall}\n]`;
67
+ }
68
+ /**
69
+ * Creates an exception when start/env.ts file is missing
70
+ *
71
+ * @param filePath - The path to the missing file
72
+ * @param definition - The environment validation definition that was being added
73
+ */
74
+ static missingEnvFile(filePath, definition) {
75
+ const code = this.#formatEnvValidations(definition);
76
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
77
+ filePath,
78
+ instructions: `Add the following code to "${filePath}":\n\n${code}`
79
+ });
80
+ }
81
+ /**
82
+ * Creates an exception when Env.create is not found in the file
83
+ *
84
+ * @param filePath - The path to the file being modified
85
+ * @param definition - The environment validation definition that was being added
86
+ */
87
+ static missingEnvCreate(filePath, definition) {
88
+ return new CodemodException(`Cannot find Env.create statement in the file.`, {
89
+ filePath,
90
+ instructions: `Add the following code inside Env.create() in "${filePath}":\n\n${this.#formatEnvValidations(definition)}`
91
+ });
92
+ }
93
+ /**
94
+ * Creates an exception when Env.create has invalid structure
95
+ *
96
+ * @param filePath - The path to the file being modified
97
+ * @param definition - The environment validation definition that was being added
98
+ */
99
+ static invalidEnvCreate(filePath, definition) {
100
+ return new CodemodException(`The second argument of Env.create is not an object literal.`, {
101
+ filePath,
102
+ instructions: `Add the following code inside Env.create() in "${filePath}":\n\n${this.#formatEnvValidations(definition)}`
103
+ });
104
+ }
105
+ /**
106
+ * Creates an exception when start/kernel.ts file is missing
107
+ *
108
+ * @param filePath - The path to the missing file
109
+ * @param stack - The middleware stack that was being modified
110
+ * @param middleware - The middleware entries that were being added
111
+ */
112
+ static missingKernelFile(filePath, stack, middleware) {
113
+ const code = this.#formatMiddleware(stack, middleware);
114
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
115
+ filePath,
116
+ instructions: `Add the following code to "${filePath}":\n\n${code}`
117
+ });
118
+ }
119
+ /**
120
+ * Creates an exception when server.use/router.use is not found
121
+ *
122
+ * @param filePath - The path to the file being modified
123
+ * @param stack - The middleware stack that was being modified
124
+ * @param middleware - The middleware entries that were being added
125
+ */
126
+ static missingMiddlewareStack(filePath, stack, middleware) {
127
+ const code = this.#formatMiddleware(stack, middleware);
128
+ return new CodemodException(`Cannot find ${stack === "named" ? "middleware variable" : `${stack}.use`} statement in the file.`, {
129
+ filePath,
130
+ instructions: `Add the following code to "${filePath}":\n\n${code}`
131
+ });
132
+ }
133
+ /**
134
+ * Creates an exception when middleware array structure is invalid
135
+ *
136
+ * @param filePath - The path to the file being modified
137
+ * @param stack - The middleware stack that was being modified
138
+ * @param middleware - The middleware entries that were being added
139
+ * @param reason - The reason why the structure is invalid
140
+ */
141
+ static invalidMiddlewareStack(filePath, stack, middleware, reason) {
142
+ return new CodemodException(reason, {
143
+ filePath,
144
+ instructions: `Add the following code to "${filePath}":\n\n${this.#formatMiddleware(stack, middleware)}`
145
+ });
146
+ }
147
+ /**
148
+ * Creates an exception when app/policies/main.ts file is missing
149
+ *
150
+ * @param filePath - The path to the missing file
151
+ * @param policies - The policies that were being added
152
+ */
153
+ static missingPoliciesFile(filePath, policies) {
154
+ const code = this.#formatPolicies(policies);
155
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
156
+ filePath,
157
+ instructions: `Add the following code to "${filePath}":\n\n${code}`
158
+ });
159
+ }
160
+ /**
161
+ * Creates an exception when policies structure is invalid
162
+ *
163
+ * @param filePath - The path to the file being modified
164
+ * @param policies - The policies that were being added
165
+ * @param reason - The reason why the structure is invalid
166
+ */
167
+ static invalidPoliciesFile(filePath, policies, reason) {
168
+ return new CodemodException(reason, {
169
+ filePath,
170
+ instructions: `Add the following code to "${filePath}":\n\n${this.#formatPolicies(policies)}`
171
+ });
172
+ }
173
+ /**
174
+ * Creates an exception when vite.config.ts file is missing
175
+ *
176
+ * @param filePath - The path to the missing file
177
+ * @param pluginCall - The plugin call that was being added
178
+ * @param importDeclarations - The import declarations needed for the plugin
179
+ */
180
+ static missingViteConfig(filePath, pluginCall, importDeclarations) {
181
+ return new CodemodException(`Cannot find vite.config.ts file. Make sure to rename vite.config.js to vite.config.ts`, {
182
+ filePath,
183
+ instructions: `Add the following code to "${filePath}":\n\n${this.#formatVitePlugin(pluginCall, importDeclarations)}`
184
+ });
185
+ }
186
+ /**
187
+ * Creates an exception when vite.config.ts structure is invalid
188
+ *
189
+ * @param filePath - The path to the file being modified
190
+ * @param pluginCall - The plugin call that was being added
191
+ * @param importDeclarations - The import declarations needed for the plugin
192
+ * @param reason - The reason why the structure is invalid
193
+ */
194
+ static invalidViteConfig(filePath, pluginCall, importDeclarations, reason) {
195
+ return new CodemodException(reason, {
196
+ filePath,
197
+ instructions: `Add the following code to "${filePath}":\n\n${this.#formatVitePlugin(pluginCall, importDeclarations)}`
198
+ });
199
+ }
200
+ /**
201
+ * Creates an exception when tests/bootstrap.ts file is missing
202
+ *
203
+ * @param filePath - The path to the missing file
204
+ * @param pluginCall - The plugin call that was being added
205
+ * @param importDeclarations - The import declarations needed for the plugin
206
+ */
207
+ static missingJapaBootstrap(filePath, pluginCall, importDeclarations) {
208
+ const code = this.#formatJapaPlugin(pluginCall, importDeclarations);
209
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
210
+ filePath,
211
+ instructions: `Add the following code to "${filePath}":\n\n${code}`
212
+ });
213
+ }
214
+ /**
215
+ * Creates an exception when tests/bootstrap.ts structure is invalid
216
+ *
217
+ * @param filePath - The path to the file being modified
218
+ * @param pluginCall - The plugin call that was being added
219
+ * @param importDeclarations - The import declarations needed for the plugin
220
+ * @param reason - The reason why the structure is invalid
221
+ */
222
+ static invalidJapaBootstrap(filePath, pluginCall, importDeclarations, reason) {
223
+ return new CodemodException(reason, {
224
+ filePath,
225
+ instructions: `Add the following code to "${filePath}":\n\n${this.#formatJapaPlugin(pluginCall, importDeclarations)}`
226
+ });
227
+ }
228
+ /**
229
+ * Creates an exception when adonisrc.ts file is missing
230
+ *
231
+ * @param filePath - The path to the missing file
232
+ * @param codeToAdd - The code that should be added manually
233
+ */
234
+ static missingRcFile(filePath, codeToAdd) {
235
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
236
+ filePath,
237
+ instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
238
+ });
239
+ }
240
+ /**
241
+ * Creates an exception when adonisrc.ts structure is invalid
242
+ *
243
+ * @param filePath - The path to the file being modified
244
+ * @param codeToAdd - The code that should be added manually
245
+ * @param reason - The reason why the structure is invalid
246
+ */
247
+ static invalidRcFile(filePath, codeToAdd, reason) {
248
+ return new CodemodException(reason, {
249
+ filePath,
250
+ instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
251
+ });
252
+ }
253
+ /**
254
+ * Creates an exception when a file required for transformation is not found.
255
+ *
256
+ * @param filePath - The path to the missing file
257
+ * @param codeToAdd - The code that should be added manually
258
+ */
259
+ static E_CODEMOD_FILE_NOT_FOUND(filePath, codeToAdd) {
260
+ return new CodemodException(`Could not find source file at path: "${filePath}"`, {
261
+ filePath,
262
+ instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
263
+ });
264
+ }
265
+ /**
266
+ * Creates an exception when the file structure doesn't match expected patterns.
267
+ *
268
+ * @param message - Description of what structure was expected
269
+ * @param filePath - The path to the file being modified
270
+ * @param codeToAdd - The code that should be added manually
271
+ */
272
+ static E_CODEMOD_INVALID_STRUCTURE(message, filePath, codeToAdd) {
273
+ return new CodemodException(message, {
274
+ filePath,
275
+ instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
276
+ });
277
+ }
278
+ };
279
+ //#endregion
280
+ export { CodemodException as t };
package/build/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { hooks } from './src/hooks.ts';
2
1
  export { Bundler } from './src/bundler.ts';
3
2
  export { DevServer } from './src/dev_server.ts';
4
3
  export { TestRunner } from './src/test_runner.ts';
5
4
  export { FileBuffer } from './src/file_buffer.ts';
6
5
  export { VirtualFileSystem } from './src/virtual_file_system.ts';
6
+ export { CodemodException } from './src/exceptions/codemod_exception.ts';
7
7
  export { SUPPORTED_PACKAGE_MANAGERS } from './src/bundler.ts';