@adonisjs/assembler 8.0.0-next.3 → 8.0.0-next.30
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.
- package/README.md +87 -59
- package/build/codemod_exception-vyN1VXuX.js +137 -0
- package/build/helpers-DDurYRsZ.js +72 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +925 -1319
- package/build/main-BeV45LeF.js +246 -0
- package/build/main-CknPN3rJ.js +188 -0
- package/build/src/bundler.d.ts +44 -3
- package/build/src/code_scanners/routes_scanner/main.d.ts +63 -11
- package/build/src/code_scanners/routes_scanner/main.js +4 -0
- package/build/src/code_scanners/routes_scanner/validator_extractor.d.ts +12 -4
- package/build/src/code_transformer/main.d.ts +53 -43
- package/build/src/code_transformer/main.js +354 -599
- package/build/src/code_transformer/rc_file_transformer.d.ts +83 -5
- package/build/src/debug.d.ts +13 -1
- package/build/src/dev_server.d.ts +92 -17
- package/build/src/exceptions/codemod_exception.d.ts +178 -0
- package/build/src/file_buffer.d.ts +87 -0
- package/build/src/file_system.d.ts +46 -8
- package/build/src/helpers.d.ts +79 -4
- package/build/src/helpers.js +2 -0
- package/build/src/index_generator/main.d.ts +68 -0
- package/build/src/index_generator/main.js +3 -0
- package/build/src/index_generator/source.d.ts +60 -0
- package/build/src/paths_resolver.d.ts +29 -3
- package/build/src/shortcuts_manager.d.ts +42 -4
- package/build/src/test_runner.d.ts +57 -12
- package/build/src/types/code_scanners.d.ts +160 -30
- package/build/src/types/code_transformer.d.ts +69 -19
- package/build/src/types/common.d.ts +233 -55
- package/build/src/types/hooks.d.ts +238 -22
- package/build/src/types/main.d.ts +15 -1
- package/build/src/types/main.js +1 -0
- package/build/src/utils.d.ts +96 -15
- package/build/src/virtual_file_system.d.ts +112 -0
- package/build/virtual_file_system-bGeoWsK-.js +285 -0
- package/package.json +46 -36
- package/build/chunk-RR4HCA4M.js +0 -7
- package/build/src/ast_file_system.d.ts +0 -17
package/README.md
CHANGED
|
@@ -420,83 +420,111 @@ export const policies = {
|
|
|
420
420
|
}
|
|
421
421
|
```
|
|
422
422
|
|
|
423
|
-
|
|
424
|
-
|
|
423
|
+
## Index generator
|
|
424
|
+
|
|
425
|
+
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.
|
|
426
|
+
|
|
427
|
+
For example, the core of the framework uses the following config to generate controllers, events, and listeners barrel file.
|
|
425
428
|
|
|
426
429
|
```ts
|
|
427
|
-
|
|
430
|
+
import hooks from '@adonisjs/assembler/hooks'
|
|
431
|
+
|
|
432
|
+
export default hooks.init((type, parent, indexGenerator) => {
|
|
433
|
+
indexGenerator.add('controllers', {
|
|
434
|
+
source: './app/controllers',
|
|
435
|
+
importAlias: '#controllers',
|
|
436
|
+
as: 'barrelFile',
|
|
437
|
+
exportName: 'controllers',
|
|
438
|
+
removeSuffix: 'controllers',
|
|
439
|
+
output: './.adonisjs/server/controllers.ts',
|
|
440
|
+
})
|
|
428
441
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
})
|
|
442
|
+
indexGenerator.add('events', {
|
|
443
|
+
source: './app/events',
|
|
444
|
+
importAlias: '#events',
|
|
445
|
+
as: 'barrelFile',
|
|
446
|
+
exportName: 'events',
|
|
447
|
+
output: './.adonisjs/server/events.ts',
|
|
448
|
+
})
|
|
436
449
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
450
|
+
indexGenerator.add('listeners', {
|
|
451
|
+
source: './app/listeners',
|
|
452
|
+
importAlias: '#listeners',
|
|
453
|
+
as: 'barrelFile',
|
|
454
|
+
exportName: 'listeners',
|
|
455
|
+
removeSuffix: 'listener',
|
|
456
|
+
output: './.adonisjs/server/listeners.ts',
|
|
457
|
+
})
|
|
458
|
+
})
|
|
445
459
|
```
|
|
446
460
|
|
|
447
|
-
|
|
461
|
+
Once the configurations have been registered with the `IndexGenerator`, it will scan the needed directories and generate the output files. Additionally, the file watchers will re-trigger the index generation when a file is added or removed from the source directory.
|
|
448
462
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
463
|
+
### Barrel file generation
|
|
464
|
+
|
|
465
|
+
Barrel files provide a single entry point by exporting a collection of lazily imported entities, recursively gathered from a source directory. The `IndexGenerator` automates this process by scanning nested directories and generating import mappings that mirror the file structure.
|
|
466
|
+
|
|
467
|
+
For example, given the following `controllers` directory structure:
|
|
468
|
+
|
|
469
|
+
```sh
|
|
470
|
+
app/controllers/
|
|
471
|
+
├── auth/
|
|
472
|
+
│ ├── login_controller.ts
|
|
473
|
+
│ └── register_controller.ts
|
|
474
|
+
├── blog/
|
|
475
|
+
│ ├── posts_controller.ts
|
|
476
|
+
│ └── post_comments_controller.ts
|
|
477
|
+
└── users_controller.ts
|
|
458
478
|
```
|
|
459
479
|
|
|
460
|
-
|
|
480
|
+
When processed with the controllers configuration, the `IndexGenerator` produces a barrel file that reflects the directory hierarchy as nested objects, using capitalized file names as property keys.
|
|
461
481
|
|
|
462
482
|
```ts
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
importAlias: '#controllers'
|
|
468
|
-
}, {
|
|
469
|
-
destination: '.adonisjs/backend/controllers',
|
|
470
|
-
exportName: 'controllers',
|
|
471
|
-
computeBaseName(filePath, sourcePath) {
|
|
472
|
-
const baseName = relative(sourcePath, filePath)
|
|
473
|
-
return new StringBuilder(baseName).toUnixSlash().removeExtension().removeSuffix('Controller').toString()
|
|
483
|
+
export const controllers = {
|
|
484
|
+
auth: {
|
|
485
|
+
Login: () => import('#controllers/auth/login_controller'),
|
|
486
|
+
Register: () => import('#controllers/auth/register_controller'),
|
|
474
487
|
},
|
|
475
|
-
|
|
488
|
+
blog: {
|
|
489
|
+
Posts: () => import('#controllers/blog/posts_controller'),
|
|
490
|
+
PostComments: () => import('#controllers/blog/post_comments_controller'),
|
|
491
|
+
},
|
|
492
|
+
Users: () => import('#controllers/users_controller'),
|
|
493
|
+
}
|
|
476
494
|
```
|
|
477
495
|
|
|
478
|
-
|
|
479
|
-
|
|
496
|
+
### Types generation
|
|
497
|
+
|
|
498
|
+
To generate a types file, register a custom callback that takes an instance of the `VirtualFileSystem` and updates the output string via the `buffer` object.
|
|
499
|
+
|
|
500
|
+
The collection is represented as key–value pairs:
|
|
501
|
+
|
|
502
|
+
- **Key** — the relative path (without extension) from the root of the source directory.
|
|
503
|
+
- **Value** — an object containing the file's `importPath`, `relativePath`, and `absolutePath`.
|
|
480
504
|
|
|
481
505
|
```ts
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
506
|
+
import hooks from '@adonisjs/assembler/hooks'
|
|
507
|
+
|
|
508
|
+
export default hooks.init((type, parent, indexGenerator) => {
|
|
509
|
+
indexGenerator.add('inertiaPages', {
|
|
510
|
+
source: './inertia/pages',
|
|
511
|
+
as: (vfs, buffer) => {
|
|
512
|
+
buffer.write(`declare module '@adonisjs/inertia' {`).indent()
|
|
513
|
+
buffer.write(`export interface Pages {`).indent()
|
|
514
|
+
|
|
515
|
+
const files = vfs.asList()
|
|
516
|
+
Object.keys(files).forEach((filePath) => {
|
|
517
|
+
buffer.write(
|
|
518
|
+
`'${filePath}': InferPageProps<typeof import('${file.importPath}').default>`
|
|
494
519
|
)
|
|
495
|
-
|
|
496
|
-
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
buffer.dedent().write('}')
|
|
523
|
+
buffer.dedent().write('}')
|
|
497
524
|
},
|
|
498
|
-
|
|
499
|
-
)
|
|
525
|
+
output: './.adonisjs/server/inertia_pages.d.ts',
|
|
526
|
+
})
|
|
527
|
+
})
|
|
500
528
|
```
|
|
501
529
|
|
|
502
530
|
## Contributing
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
var CodemodException = class CodemodException extends Error {
|
|
2
|
+
instructions;
|
|
3
|
+
filePath;
|
|
4
|
+
constructor(message, options) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "CodemodException";
|
|
7
|
+
this.instructions = options?.instructions;
|
|
8
|
+
this.filePath = options?.filePath;
|
|
9
|
+
}
|
|
10
|
+
static #formatEnvValidations(definition) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
if (definition.leadingComment) {
|
|
13
|
+
lines.push(`/*`);
|
|
14
|
+
lines.push(`|----------------------------------------------------------`);
|
|
15
|
+
lines.push(`| ${definition.leadingComment}`);
|
|
16
|
+
lines.push(`|----------------------------------------------------------`);
|
|
17
|
+
lines.push(`*/`);
|
|
18
|
+
}
|
|
19
|
+
for (const [variable, validation] of Object.entries(definition.variables)) lines.push(`${variable}: ${validation},`);
|
|
20
|
+
return lines.join("\n");
|
|
21
|
+
}
|
|
22
|
+
static #formatMiddleware(stack, middleware) {
|
|
23
|
+
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})`;
|
|
24
|
+
return `${stack}.use([\n ${middleware.map((m) => `() => import('${m.path}')`).join(",\n ")}\n])`;
|
|
25
|
+
}
|
|
26
|
+
static #formatPolicies(policies) {
|
|
27
|
+
return `export const policies = {\n ${policies.map((p) => `${p.name}: () => import('${p.path}')`).join(",\n ")}\n}`;
|
|
28
|
+
}
|
|
29
|
+
static #formatVitePlugin(pluginCall, importDeclarations) {
|
|
30
|
+
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})`;
|
|
31
|
+
}
|
|
32
|
+
static #formatJapaPlugin(pluginCall, importDeclarations) {
|
|
33
|
+
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]`;
|
|
34
|
+
}
|
|
35
|
+
static missingEnvFile(filePath, definition) {
|
|
36
|
+
const code = this.#formatEnvValidations(definition);
|
|
37
|
+
return new CodemodException(`Could not find source file at path: "${filePath}"`, {
|
|
38
|
+
filePath,
|
|
39
|
+
instructions: `Add the following code to "${filePath}":\n\n${code}`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
static missingEnvCreate(filePath, definition) {
|
|
43
|
+
return new CodemodException(`Cannot find Env.create statement in the file.`, {
|
|
44
|
+
filePath,
|
|
45
|
+
instructions: `Add the following code inside Env.create() in "${filePath}":\n\n${this.#formatEnvValidations(definition)}`
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
static invalidEnvCreate(filePath, definition) {
|
|
49
|
+
return new CodemodException(`The second argument of Env.create is not an object literal.`, {
|
|
50
|
+
filePath,
|
|
51
|
+
instructions: `Add the following code inside Env.create() in "${filePath}":\n\n${this.#formatEnvValidations(definition)}`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
static missingKernelFile(filePath, stack, middleware) {
|
|
55
|
+
const code = this.#formatMiddleware(stack, middleware);
|
|
56
|
+
return new CodemodException(`Could not find source file at path: "${filePath}"`, {
|
|
57
|
+
filePath,
|
|
58
|
+
instructions: `Add the following code to "${filePath}":\n\n${code}`
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
static missingMiddlewareStack(filePath, stack, middleware) {
|
|
62
|
+
const code = this.#formatMiddleware(stack, middleware);
|
|
63
|
+
return new CodemodException(`Cannot find ${stack === "named" ? "middleware variable" : `${stack}.use`} statement in the file.`, {
|
|
64
|
+
filePath,
|
|
65
|
+
instructions: `Add the following code to "${filePath}":\n\n${code}`
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
static invalidMiddlewareStack(filePath, stack, middleware, reason) {
|
|
69
|
+
return new CodemodException(reason, {
|
|
70
|
+
filePath,
|
|
71
|
+
instructions: `Add the following code to "${filePath}":\n\n${this.#formatMiddleware(stack, middleware)}`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
static missingPoliciesFile(filePath, policies) {
|
|
75
|
+
const code = this.#formatPolicies(policies);
|
|
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
|
+
static invalidPoliciesFile(filePath, policies, reason) {
|
|
82
|
+
return new CodemodException(reason, {
|
|
83
|
+
filePath,
|
|
84
|
+
instructions: `Add the following code to "${filePath}":\n\n${this.#formatPolicies(policies)}`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
static missingViteConfig(filePath, pluginCall, importDeclarations) {
|
|
88
|
+
return new CodemodException(`Cannot find vite.config.ts file. Make sure to rename vite.config.js to vite.config.ts`, {
|
|
89
|
+
filePath,
|
|
90
|
+
instructions: `Add the following code to "${filePath}":\n\n${this.#formatVitePlugin(pluginCall, importDeclarations)}`
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
static invalidViteConfig(filePath, pluginCall, importDeclarations, reason) {
|
|
94
|
+
return new CodemodException(reason, {
|
|
95
|
+
filePath,
|
|
96
|
+
instructions: `Add the following code to "${filePath}":\n\n${this.#formatVitePlugin(pluginCall, importDeclarations)}`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
static missingJapaBootstrap(filePath, pluginCall, importDeclarations) {
|
|
100
|
+
const code = this.#formatJapaPlugin(pluginCall, importDeclarations);
|
|
101
|
+
return new CodemodException(`Could not find source file at path: "${filePath}"`, {
|
|
102
|
+
filePath,
|
|
103
|
+
instructions: `Add the following code to "${filePath}":\n\n${code}`
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
static invalidJapaBootstrap(filePath, pluginCall, importDeclarations, reason) {
|
|
107
|
+
return new CodemodException(reason, {
|
|
108
|
+
filePath,
|
|
109
|
+
instructions: `Add the following code to "${filePath}":\n\n${this.#formatJapaPlugin(pluginCall, importDeclarations)}`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
static missingRcFile(filePath, codeToAdd) {
|
|
113
|
+
return new CodemodException(`Could not find source file at path: "${filePath}"`, {
|
|
114
|
+
filePath,
|
|
115
|
+
instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
static invalidRcFile(filePath, codeToAdd, reason) {
|
|
119
|
+
return new CodemodException(reason, {
|
|
120
|
+
filePath,
|
|
121
|
+
instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
static E_CODEMOD_FILE_NOT_FOUND(filePath, codeToAdd) {
|
|
125
|
+
return new CodemodException(`Could not find source file at path: "${filePath}"`, {
|
|
126
|
+
filePath,
|
|
127
|
+
instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
static E_CODEMOD_INVALID_STRUCTURE(message, filePath, codeToAdd) {
|
|
131
|
+
return new CodemodException(message, {
|
|
132
|
+
filePath,
|
|
133
|
+
instructions: `Add the following code to "${filePath}":\n\n${codeToAdd}`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
export { CodemodException as t };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { parseImports } from "parse-imports";
|
|
2
|
+
async function findImport(code, importReference) {
|
|
3
|
+
const importIdentifier = importReference.split(".")[0];
|
|
4
|
+
for (const $import of await parseImports(code, {})) {
|
|
5
|
+
if (!$import.importClause) continue;
|
|
6
|
+
if (!$import.moduleSpecifier.value) continue;
|
|
7
|
+
if ($import.importClause.default === importIdentifier) return {
|
|
8
|
+
specifier: $import.moduleSpecifier.value,
|
|
9
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
10
|
+
clause: {
|
|
11
|
+
type: "default",
|
|
12
|
+
value: importIdentifier
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
if ($import.importClause.namespace === importIdentifier) return {
|
|
16
|
+
specifier: $import.moduleSpecifier.value,
|
|
17
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
18
|
+
clause: {
|
|
19
|
+
type: "namespace",
|
|
20
|
+
value: importIdentifier
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const namedImport = $import.importClause.named.find(({ binding }) => {
|
|
24
|
+
return binding === importIdentifier;
|
|
25
|
+
});
|
|
26
|
+
if (namedImport) return {
|
|
27
|
+
specifier: $import.moduleSpecifier.value,
|
|
28
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
29
|
+
clause: {
|
|
30
|
+
type: "named",
|
|
31
|
+
value: namedImport.specifier,
|
|
32
|
+
...namedImport.binding !== namedImport.specifier ? { alias: namedImport.binding } : {}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function inspectClass(node) {
|
|
39
|
+
return node.find({ rule: { kind: "class_declaration" } });
|
|
40
|
+
}
|
|
41
|
+
function inspectClassMethods(node) {
|
|
42
|
+
return node.findAll({ rule: { kind: "method_definition" } });
|
|
43
|
+
}
|
|
44
|
+
function nodeToPlainText(node) {
|
|
45
|
+
let out = [];
|
|
46
|
+
function toText(one) {
|
|
47
|
+
const children = one.children();
|
|
48
|
+
if (!children.length) out.push(one.text());
|
|
49
|
+
else children.forEach((child) => toText(child));
|
|
50
|
+
}
|
|
51
|
+
toText(node);
|
|
52
|
+
return out.join("");
|
|
53
|
+
}
|
|
54
|
+
function inspectMethodArguments(node, methodCalls) {
|
|
55
|
+
return node.findAll({ rule: { any: methodCalls.map((methodCall) => {
|
|
56
|
+
return { pattern: {
|
|
57
|
+
context: `${methodCall}($$$ARGUMENTS)`,
|
|
58
|
+
selector: "call_expression"
|
|
59
|
+
} };
|
|
60
|
+
}) } }).flatMap((matchingExpression) => {
|
|
61
|
+
return matchingExpression.findAll({ rule: { kind: "arguments" } });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function searchValidatorDirectUsage(node) {
|
|
65
|
+
return node.findAll({ rule: { pattern: {
|
|
66
|
+
context: "$$$VALIDATOR.validate($$$)",
|
|
67
|
+
selector: "call_expression"
|
|
68
|
+
} } }).flatMap((expression) => {
|
|
69
|
+
return expression.getMultipleMatches("VALIDATOR");
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export { nodeToPlainText as a, inspectMethodArguments as i, inspectClass as n, searchValidatorDirectUsage as o, inspectClassMethods as r, findImport as t };
|
package/build/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { Bundler } from './src/bundler.ts';
|
|
2
2
|
export { DevServer } from './src/dev_server.ts';
|
|
3
3
|
export { TestRunner } from './src/test_runner.ts';
|
|
4
|
+
export { FileBuffer } from './src/file_buffer.ts';
|
|
5
|
+
export { VirtualFileSystem } from './src/virtual_file_system.ts';
|
|
6
|
+
export { CodemodException } from './src/exceptions/codemod_exception.ts';
|
|
7
|
+
export { SUPPORTED_PACKAGE_MANAGERS } from './src/bundler.ts';
|