@adonisjs/assembler 8.0.0-next.4 → 8.0.0-next.6
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/chunk-F4RAGKQN.js +387 -0
- package/build/chunk-MC3FJR62.js +411 -0
- package/build/chunk-TIKQQRMX.js +116 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +606 -410
- package/build/src/bundler.d.ts +40 -3
- package/build/src/code_scanners/routes_scanner/main.d.ts +49 -9
- package/build/src/code_scanners/routes_scanner/main.js +445 -0
- package/build/src/code_scanners/routes_scanner/validator_extractor.d.ts +12 -4
- package/build/src/code_transformer/main.d.ts +44 -43
- package/build/src/code_transformer/main.js +123 -101
- package/build/src/code_transformer/rc_file_transformer.d.ts +56 -4
- package/build/src/debug.d.ts +12 -0
- package/build/src/dev_server.d.ts +40 -30
- package/build/src/file_buffer.d.ts +67 -0
- package/build/src/file_system.d.ts +45 -7
- package/build/src/helpers.d.ts +79 -4
- package/build/src/helpers.js +16 -0
- package/build/src/index_generator/main.d.ts +73 -0
- package/build/src/index_generator/main.js +7 -0
- package/build/src/index_generator/source.d.ts +66 -0
- package/build/src/paths_resolver.d.ts +27 -2
- package/build/src/shortcuts_manager.d.ts +42 -4
- package/build/src/test_runner.d.ts +58 -31
- package/build/src/types/code_scanners.d.ts +138 -24
- package/build/src/types/code_transformer.d.ts +61 -19
- package/build/src/types/common.d.ts +199 -55
- package/build/src/types/hooks.d.ts +173 -17
- package/build/src/types/main.d.ts +13 -0
- package/build/src/utils.d.ts +88 -10
- package/build/src/virtual_file_system.d.ts +112 -0
- package/package.json +9 -3
- package/build/chunk-RR4HCA4M.js +0 -7
- package/build/src/ast_file_system.d.ts +0 -17
package/build/src/bundler.d.ts
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import type tsStatic from 'typescript';
|
|
2
2
|
import type { BundlerOptions } from './types/common.ts';
|
|
3
3
|
import { type SupportedPackageManager } from './types/code_transformer.ts';
|
|
4
|
+
/**
|
|
5
|
+
* List of package managers we support in order to
|
|
6
|
+
* copy lockfiles
|
|
7
|
+
*/
|
|
8
|
+
export declare const SUPPORTED_PACKAGE_MANAGERS: {
|
|
9
|
+
[K in SupportedPackageManager]: {
|
|
10
|
+
packageManagerFiles: string[];
|
|
11
|
+
installCommand: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
4
14
|
/**
|
|
5
15
|
* The bundler class exposes the API to build an AdonisJS project.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const bundler = new Bundler(new URL('./'), ts, { hooks: [] })
|
|
19
|
+
* const success = await bundler.bundle()
|
|
6
20
|
*/
|
|
7
21
|
export declare class Bundler {
|
|
8
22
|
#private;
|
|
9
|
-
|
|
10
|
-
|
|
23
|
+
/**
|
|
24
|
+
* CLI UI instance for displaying colorful messages and progress information
|
|
25
|
+
*/
|
|
11
26
|
ui: {
|
|
12
27
|
colors: import("@poppinss/colors/types").Colors;
|
|
13
28
|
logger: import("@poppinss/cliui").Logger;
|
|
@@ -30,12 +45,34 @@ export declare class Bundler {
|
|
|
30
45
|
useColors(colorsToUse: import("@poppinss/colors/types").Colors): void;
|
|
31
46
|
};
|
|
32
47
|
/**
|
|
33
|
-
* Package manager
|
|
48
|
+
* Package manager detected from the project environment
|
|
34
49
|
*/
|
|
35
50
|
packageManager: SupportedPackageManager;
|
|
51
|
+
/**
|
|
52
|
+
* The current working directory URL
|
|
53
|
+
*/
|
|
54
|
+
cwd: URL;
|
|
55
|
+
/**
|
|
56
|
+
* Bundler configuration options including hooks and meta files
|
|
57
|
+
*/
|
|
58
|
+
options: BundlerOptions;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new bundler instance
|
|
61
|
+
*
|
|
62
|
+
* @param cwd - The current working directory URL
|
|
63
|
+
* @param ts - TypeScript module reference
|
|
64
|
+
* @param options - Bundler configuration options
|
|
65
|
+
*/
|
|
36
66
|
constructor(cwd: URL, ts: typeof tsStatic, options: BundlerOptions);
|
|
37
67
|
/**
|
|
38
68
|
* Bundles the application to be run in production
|
|
69
|
+
*
|
|
70
|
+
* @param stopOnError - Whether to stop the build process on TypeScript errors
|
|
71
|
+
* @param client - Override the detected package manager
|
|
72
|
+
* @returns Promise that resolves to true if build succeeded, false otherwise
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const success = await bundler.bundle(true, 'npm')
|
|
39
76
|
*/
|
|
40
77
|
bundle(stopOnError?: boolean, client?: SupportedPackageManager): Promise<boolean>;
|
|
41
78
|
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { type AsyncOrSync } from '@poppinss/utils/types';
|
|
2
2
|
import { PathsResolver } from '../../paths_resolver.ts';
|
|
3
|
-
import { AstFileSystem } from '../../ast_file_system.ts';
|
|
4
3
|
import { type ScannedRoute, type RoutesListItem, type ScannedController, type RoutesScannerRules } from '../../types/code_scanners.ts';
|
|
4
|
+
/**
|
|
5
|
+
* RoutesScanner is responsible for scanning application routes,
|
|
6
|
+
* extracting their controllers, validators, request and response types.
|
|
7
|
+
*
|
|
8
|
+
* The RoutesScanner analyzes route definitions to extract TypeScript type information
|
|
9
|
+
* for request validation, response types, and controller methods. It supports custom
|
|
10
|
+
* rules for overriding default behavior and provides hooks for extending functionality.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const scanner = new RoutesScanner(appRoot, [rules])
|
|
14
|
+
* scanner.defineResponse((route, controller) => {
|
|
15
|
+
* return { type: 'CustomResponseType', imports: [] }
|
|
16
|
+
* })
|
|
17
|
+
* await scanner.scan(routes)
|
|
18
|
+
* const scannedRoutes = scanner.getScannedRoutes()
|
|
19
|
+
*/
|
|
5
20
|
export declare class RoutesScanner {
|
|
6
21
|
#private;
|
|
7
22
|
/**
|
|
8
|
-
* CLI UI to log colorful messages
|
|
23
|
+
* CLI UI instance to log colorful messages and progress information
|
|
9
24
|
*/
|
|
10
25
|
ui: {
|
|
11
26
|
colors: import("@poppinss/colors/types").Colors;
|
|
@@ -33,21 +48,25 @@ export declare class RoutesScanner {
|
|
|
33
48
|
* imports to absolute paths
|
|
34
49
|
*/
|
|
35
50
|
pathsResolver: PathsResolver;
|
|
36
|
-
/**
|
|
37
|
-
* The AstFileSystem is used to convert files to AST-grep nodes.
|
|
38
|
-
* The AST is cached to speed up the performance.
|
|
39
|
-
*/
|
|
40
|
-
astFileSystem: AstFileSystem;
|
|
41
51
|
/**
|
|
42
52
|
* The rules to apply when scanning routes
|
|
43
53
|
*/
|
|
44
54
|
rules: RoutesScannerRules;
|
|
55
|
+
/**
|
|
56
|
+
* Create a new RoutesScanner instance
|
|
57
|
+
*
|
|
58
|
+
* @param appRoot - The root directory of the application
|
|
59
|
+
* @param rulesCollection - Collection of rules to apply during scanning
|
|
60
|
+
*/
|
|
45
61
|
constructor(appRoot: string, rulesCollection: RoutesScannerRules[]);
|
|
46
62
|
/**
|
|
47
63
|
* Register a callback to self compute the response types for
|
|
48
64
|
* a given route. The callback will be executed for all
|
|
49
65
|
* the routes and you must return undefined to fallback
|
|
50
66
|
* to the default behavior of detecting types
|
|
67
|
+
*
|
|
68
|
+
* @param callback - Function to compute response types for routes
|
|
69
|
+
* @returns This RoutesScanner instance for method chaining
|
|
51
70
|
*/
|
|
52
71
|
defineResponse(callback: (route: ScannedRoute, controller: ScannedController, scanner: RoutesScanner) => AsyncOrSync<ScannedRoute['response']>): this;
|
|
53
72
|
/**
|
|
@@ -55,25 +74,46 @@ export declare class RoutesScanner {
|
|
|
55
74
|
* a given route. The callback will be executed for all
|
|
56
75
|
* the routes and you must return undefined to fallback
|
|
57
76
|
* to the default behavior of detecting types
|
|
77
|
+
*
|
|
78
|
+
* @param callback - Function to compute request types for routes
|
|
79
|
+
* @returns This RoutesScanner instance for method chaining
|
|
58
80
|
*/
|
|
59
81
|
defineRequest(callback: (route: ScannedRoute, controller: ScannedController, scanner: RoutesScanner) => AsyncOrSync<ScannedRoute['request']>): this;
|
|
60
82
|
/**
|
|
61
|
-
* Register a callback to extract validators from the route
|
|
62
|
-
*
|
|
83
|
+
* Register a callback to extract validators from the route controller
|
|
84
|
+
*
|
|
85
|
+
* @param callback - Function to extract validators from controllers
|
|
86
|
+
* @returns This RoutesScanner instance for method chaining
|
|
63
87
|
*/
|
|
64
88
|
extractValidators(callback: (route: ScannedRoute, controller: ScannedController, scanner: RoutesScanner) => AsyncOrSync<ScannedRoute['validators']>): this;
|
|
65
89
|
/**
|
|
66
90
|
* Returns the scanned routes
|
|
91
|
+
*
|
|
92
|
+
* @returns Array of scanned routes with their metadata
|
|
67
93
|
*/
|
|
68
94
|
getScannedRoutes(): ScannedRoute[];
|
|
95
|
+
/**
|
|
96
|
+
* Returns an array of controllers bound to the provided
|
|
97
|
+
* routes
|
|
98
|
+
*
|
|
99
|
+
* @returns Array of controller's absolute paths
|
|
100
|
+
*/
|
|
101
|
+
getControllers(): string[];
|
|
69
102
|
/**
|
|
70
103
|
* Invalidating a controller will trigger computing the validators,
|
|
71
104
|
* request types and the response types.
|
|
105
|
+
*
|
|
106
|
+
* @param controllerPath - Path to the controller file to invalidate
|
|
72
107
|
*/
|
|
73
108
|
invalidate(controllerPath: string): Promise<void>;
|
|
74
109
|
/**
|
|
75
110
|
* Scans an array of Route list items and fetches their validators,
|
|
76
111
|
* controllers, and request/response types.
|
|
112
|
+
*
|
|
113
|
+
* This is the main method that processes all routes and extracts
|
|
114
|
+
* their type information for code generation purposes.
|
|
115
|
+
*
|
|
116
|
+
* @param routes - Array of route list items to scan
|
|
77
117
|
*/
|
|
78
118
|
scan(routes: RoutesListItem[]): Promise<void>;
|
|
79
119
|
}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findImport,
|
|
3
|
+
inspectClass,
|
|
4
|
+
inspectClassMethods,
|
|
5
|
+
inspectMethodArguments,
|
|
6
|
+
nodeToPlainText,
|
|
7
|
+
searchValidatorDirectUsage
|
|
8
|
+
} from "../../../chunk-TIKQQRMX.js";
|
|
9
|
+
import {
|
|
10
|
+
VirtualFileSystem,
|
|
11
|
+
debug_default,
|
|
12
|
+
isRelative
|
|
13
|
+
} from "../../../chunk-F4RAGKQN.js";
|
|
14
|
+
|
|
15
|
+
// src/code_scanners/routes_scanner/main.ts
|
|
16
|
+
import { cliui } from "@poppinss/cliui";
|
|
17
|
+
import string2 from "@poppinss/utils/string";
|
|
18
|
+
import { parseImports } from "parse-imports";
|
|
19
|
+
import StringBuilder from "@poppinss/utils/string_builder";
|
|
20
|
+
|
|
21
|
+
// src/paths_resolver.ts
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
23
|
+
var PathsResolver = class {
|
|
24
|
+
/**
|
|
25
|
+
* Cache of resolved paths to avoid resolving the same specifier multiple times
|
|
26
|
+
*/
|
|
27
|
+
#resolvedPaths = {};
|
|
28
|
+
/**
|
|
29
|
+
* The resolver function used to resolve import specifiers
|
|
30
|
+
*/
|
|
31
|
+
#resolver = (specifier) => import.meta.resolve(specifier);
|
|
32
|
+
/**
|
|
33
|
+
* Define a custom resolver that resolves a path
|
|
34
|
+
*
|
|
35
|
+
* The custom resolver function will be used instead of the default
|
|
36
|
+
* import.meta.resolve() for resolving import specifiers.
|
|
37
|
+
*
|
|
38
|
+
* @param resolver - Function that takes a specifier and returns resolved path
|
|
39
|
+
*/
|
|
40
|
+
use(resolver) {
|
|
41
|
+
this.#resolver = resolver;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve import specifier to an absolute file path
|
|
45
|
+
*
|
|
46
|
+
* This method caches resolved paths to improve performance on repeated
|
|
47
|
+
* resolutions. Relative paths are not supported and will throw an error.
|
|
48
|
+
*
|
|
49
|
+
* @param specifier - The import specifier to resolve (must not be relative)
|
|
50
|
+
* @returns The resolved absolute file path
|
|
51
|
+
* @throws Error when attempting to resolve relative paths
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const path = resolver.resolve('#app/models/user')
|
|
55
|
+
* const path2 = resolver.resolve('@/utils/helper')
|
|
56
|
+
*/
|
|
57
|
+
resolve(specifier) {
|
|
58
|
+
if (isRelative(specifier)) {
|
|
59
|
+
throw new Error("Cannot resolve relative paths using PathsResolver");
|
|
60
|
+
}
|
|
61
|
+
const cacheKey = specifier;
|
|
62
|
+
const cached = this.#resolvedPaths[cacheKey];
|
|
63
|
+
if (cached) {
|
|
64
|
+
return cached;
|
|
65
|
+
}
|
|
66
|
+
this.#resolvedPaths[cacheKey] = fileURLToPath(this.#resolver(specifier));
|
|
67
|
+
return this.#resolvedPaths[cacheKey];
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/code_scanners/routes_scanner/validator_extractor.ts
|
|
72
|
+
import { relative } from "path";
|
|
73
|
+
import string from "@poppinss/utils/string";
|
|
74
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
75
|
+
async function extractValidators(appRoot, vfs, controller) {
|
|
76
|
+
const root = await vfs.get(controller.path);
|
|
77
|
+
const fileContents = root.text();
|
|
78
|
+
const controllerClass = inspectClass(root);
|
|
79
|
+
if (!controllerClass) {
|
|
80
|
+
debug_default(`No class defined within the "%s"`, controller.import.specifier);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const method = inspectClassMethods(controllerClass).find((e) => {
|
|
84
|
+
return e.find({
|
|
85
|
+
rule: { kind: "property_identifier", regex: `\\b${controller.method}\\b` }
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
if (!method) {
|
|
89
|
+
debug_default(`Unable to find "%s" method in "%s"`, controller.method, controller.import.specifier);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const validationCalls = inspectMethodArguments(method, ["request.validateUsing", "vine.validate"]).map((node) => {
|
|
93
|
+
const firstArg = node.find({
|
|
94
|
+
rule: { any: [{ kind: "identifier" }, { kind: "member_expression" }] }
|
|
95
|
+
});
|
|
96
|
+
if (!firstArg) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
return nodeToPlainText(firstArg);
|
|
100
|
+
}).filter((node) => node !== void 0).concat(searchValidatorDirectUsage(method).map((node) => nodeToPlainText(node)));
|
|
101
|
+
if (!validationCalls.length) {
|
|
102
|
+
debug_default(
|
|
103
|
+
`Unable to detect any validation calls in "%s.%s" method`,
|
|
104
|
+
controller.import.specifier,
|
|
105
|
+
controller.method
|
|
106
|
+
);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const controllerName = controllerClass.find({ rule: { kind: "type_identifier" } }).text();
|
|
110
|
+
const controllerURL = pathToFileURL(controller.path);
|
|
111
|
+
const imports = await Promise.all(
|
|
112
|
+
validationCalls.map(async (validationCall) => {
|
|
113
|
+
const validatorNamespace = validationCall.split(".")[0];
|
|
114
|
+
if (validatorNamespace === controllerName) {
|
|
115
|
+
return {
|
|
116
|
+
name: validationCall,
|
|
117
|
+
import: {
|
|
118
|
+
specifier: controller.import.specifier,
|
|
119
|
+
type: "default",
|
|
120
|
+
value: controllerName
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const importCall = await findImport(fileContents, validationCall);
|
|
125
|
+
if (!importCall) {
|
|
126
|
+
debug_default(
|
|
127
|
+
'Unable to find import for "%s" used by "%s.%s" method',
|
|
128
|
+
validationCall,
|
|
129
|
+
controller.import.specifier,
|
|
130
|
+
controller.method
|
|
131
|
+
);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
name: validationCall,
|
|
136
|
+
import: {
|
|
137
|
+
specifier: isRelative(importCall.specifier) ? string.toUnixSlash(
|
|
138
|
+
relative(appRoot, fileURLToPath2(new URL(importCall.specifier, controllerURL)))
|
|
139
|
+
) : importCall.specifier,
|
|
140
|
+
type: importCall.clause.type,
|
|
141
|
+
value: importCall.clause.value
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
return imports.filter((value) => !!value);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/code_scanners/routes_scanner/main.ts
|
|
150
|
+
var RoutesScanner = class {
|
|
151
|
+
/**
|
|
152
|
+
* The root of the application from where we will resolve
|
|
153
|
+
* paths.
|
|
154
|
+
*/
|
|
155
|
+
#appRoot;
|
|
156
|
+
/**
|
|
157
|
+
* Collection of routes by their controller file path. This helps
|
|
158
|
+
* us re-compute request types when the controller is changed.
|
|
159
|
+
*/
|
|
160
|
+
#controllerRoutes = {};
|
|
161
|
+
/**
|
|
162
|
+
* Collection of scanned routes
|
|
163
|
+
*/
|
|
164
|
+
#scannedRoutes = [];
|
|
165
|
+
/**
|
|
166
|
+
* A custom method to self compute the response type for a route. Return
|
|
167
|
+
* undefined to fallback to the default behavior
|
|
168
|
+
*/
|
|
169
|
+
#computeResponseTypes;
|
|
170
|
+
/**
|
|
171
|
+
* A custom method to self compute the request type for a route. Return
|
|
172
|
+
* undefined to fallback to the default behavior
|
|
173
|
+
*/
|
|
174
|
+
#computeRequestTypes;
|
|
175
|
+
/**
|
|
176
|
+
* A custom method to self extract the validators from the route controller.
|
|
177
|
+
* Return undefined to fallback to the default behavior.
|
|
178
|
+
*/
|
|
179
|
+
#extractValidators;
|
|
180
|
+
/**
|
|
181
|
+
* CLI UI instance to log colorful messages and progress information
|
|
182
|
+
*/
|
|
183
|
+
ui = cliui();
|
|
184
|
+
/**
|
|
185
|
+
* The paths resolver is used to convert subpath and package
|
|
186
|
+
* imports to absolute paths
|
|
187
|
+
*/
|
|
188
|
+
pathsResolver = new PathsResolver();
|
|
189
|
+
/**
|
|
190
|
+
* The rules to apply when scanning routes
|
|
191
|
+
*/
|
|
192
|
+
rules = {
|
|
193
|
+
skip: [],
|
|
194
|
+
request: {},
|
|
195
|
+
response: {}
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Create a new RoutesScanner instance
|
|
199
|
+
*
|
|
200
|
+
* @param appRoot - The root directory of the application
|
|
201
|
+
* @param rulesCollection - Collection of rules to apply during scanning
|
|
202
|
+
*/
|
|
203
|
+
constructor(appRoot, rulesCollection) {
|
|
204
|
+
this.#appRoot = appRoot;
|
|
205
|
+
rulesCollection.forEach((rules) => {
|
|
206
|
+
this.rules.skip = this.rules.skip.concat(rules.skip);
|
|
207
|
+
Object.assign(this.rules.request, rules.request);
|
|
208
|
+
Object.assign(this.rules.response, rules.response);
|
|
209
|
+
});
|
|
210
|
+
this.rules.skip = Array.from(/* @__PURE__ */ new Set([...this.rules.skip]));
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Assumes the validators are from VineJS and computes the request types from them
|
|
214
|
+
*
|
|
215
|
+
* This method generates TypeScript type definitions for request validation
|
|
216
|
+
* by analyzing VineJS validators and creating Infer types.
|
|
217
|
+
*
|
|
218
|
+
* @param route - The scanned route with validators
|
|
219
|
+
* @returns Request type definition or undefined
|
|
220
|
+
*/
|
|
221
|
+
#prepareRequestTypes(route) {
|
|
222
|
+
if (!route.validators) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const schemas = route.validators.reduce((result, validator) => {
|
|
226
|
+
const validatorExport = validator.import.type === "default" ? ".default" : validator.import.type === "named" ? `.${validator.import.value}` : "";
|
|
227
|
+
const [, ...segments] = validator.name.split(".");
|
|
228
|
+
const namespace = segments.map((segment) => `['${segment}']`).join("");
|
|
229
|
+
result.push(
|
|
230
|
+
`Infer<(typeof import('${validator.import.specifier}')${validatorExport})${namespace}>`
|
|
231
|
+
);
|
|
232
|
+
return result;
|
|
233
|
+
}, []);
|
|
234
|
+
return {
|
|
235
|
+
type: schemas.join("|"),
|
|
236
|
+
imports: [`import { Infer } from '@vinejs/vine/types'`]
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Inspects the controller reference and fetches its import specifier
|
|
241
|
+
* and the controller name from it.
|
|
242
|
+
*/
|
|
243
|
+
async #inspectControllerSpecifier(importExpression, method) {
|
|
244
|
+
const imports = [...await parseImports(importExpression)];
|
|
245
|
+
const importedModule = imports.find(
|
|
246
|
+
($import) => $import.isDynamicImport && $import.moduleSpecifier.value
|
|
247
|
+
);
|
|
248
|
+
if (!importedModule || importedModule.moduleSpecifier.type !== "package" || !importedModule.moduleSpecifier.value) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const specifier = importedModule.moduleSpecifier.value;
|
|
252
|
+
const name = new StringBuilder(specifier.split("/").pop()).removeSuffix("Controller").pascalCase().suffix("Controller").toString();
|
|
253
|
+
return {
|
|
254
|
+
name,
|
|
255
|
+
method: method ?? "handle",
|
|
256
|
+
path: string2.toUnixSlash(this.pathsResolver.resolve(specifier)),
|
|
257
|
+
import: {
|
|
258
|
+
specifier,
|
|
259
|
+
type: "default",
|
|
260
|
+
value: name
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Defines the response type for the route
|
|
266
|
+
*/
|
|
267
|
+
async #setResponse(route, controller) {
|
|
268
|
+
const responseType = await this.#computeResponseTypes?.(route, controller, this);
|
|
269
|
+
route.response = responseType ?? {
|
|
270
|
+
type: `ReturnType<import('${controller.import.specifier}').default['${controller.method}']>`,
|
|
271
|
+
imports: []
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Defines the request type for the route
|
|
276
|
+
*/
|
|
277
|
+
async #setRequest(route, controller, vfs) {
|
|
278
|
+
route.validators = await this.#extractValidators?.(route, controller, this) ?? await extractValidators(this.#appRoot, vfs, controller);
|
|
279
|
+
route.request = await this.#computeRequestTypes?.(route, controller, this) ?? this.#prepareRequestTypes(route);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Scans a route that is not using a controller
|
|
283
|
+
*/
|
|
284
|
+
#processRouteWithoutController(route) {
|
|
285
|
+
if (!route.name) {
|
|
286
|
+
debug_default(`skipping route "%s" scanning. Missing a controller reference`, route.name);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const scannedRoute = {
|
|
290
|
+
name: route.name,
|
|
291
|
+
domain: route.domain,
|
|
292
|
+
methods: route.methods,
|
|
293
|
+
pattern: route.pattern,
|
|
294
|
+
tokens: route.tokens,
|
|
295
|
+
request: this.rules.request[route.name],
|
|
296
|
+
response: this.rules.response[route.name]
|
|
297
|
+
};
|
|
298
|
+
this.#scannedRoutes.push(scannedRoute);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Scans a route that is using a controller reference
|
|
302
|
+
*/
|
|
303
|
+
async #processRouteWithController(route, vfs) {
|
|
304
|
+
const controller = await this.#inspectControllerSpecifier(
|
|
305
|
+
route.controllerReference.importExpression,
|
|
306
|
+
route.controllerReference.method
|
|
307
|
+
);
|
|
308
|
+
if (!controller) {
|
|
309
|
+
return this.#processRouteWithoutController(route);
|
|
310
|
+
}
|
|
311
|
+
const routeName = route.name ?? new StringBuilder(controller.name).removeSuffix("Controller").snakeCase().suffix(`.${string2.snakeCase(controller.method)}`).toString();
|
|
312
|
+
if (this.rules.skip.includes(routeName)) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const scannedRoute = {
|
|
316
|
+
name: routeName,
|
|
317
|
+
domain: route.domain,
|
|
318
|
+
methods: route.methods,
|
|
319
|
+
pattern: route.pattern,
|
|
320
|
+
tokens: route.tokens,
|
|
321
|
+
request: this.rules.request[routeName],
|
|
322
|
+
response: this.rules.response[routeName],
|
|
323
|
+
controller
|
|
324
|
+
};
|
|
325
|
+
this.#scannedRoutes.push(scannedRoute);
|
|
326
|
+
if (!scannedRoute.request || !scannedRoute.response) {
|
|
327
|
+
this.#controllerRoutes[controller.path] ??= [];
|
|
328
|
+
this.#controllerRoutes[controller.path].push(scannedRoute);
|
|
329
|
+
if (!scannedRoute.response) {
|
|
330
|
+
await this.#setResponse(scannedRoute, controller);
|
|
331
|
+
}
|
|
332
|
+
if (!scannedRoute.request) {
|
|
333
|
+
await this.#setRequest(scannedRoute, controller, vfs);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Processing a given route list item and further scan it to
|
|
339
|
+
* fetch the controller, request and response types.
|
|
340
|
+
*/
|
|
341
|
+
async #processRoute(route, vfs) {
|
|
342
|
+
if (route.name && this.rules.skip.includes(route.name)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!route.controllerReference) {
|
|
346
|
+
this.#processRouteWithoutController(route);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
await this.#processRouteWithController(
|
|
350
|
+
route,
|
|
351
|
+
vfs
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Register a callback to self compute the response types for
|
|
356
|
+
* a given route. The callback will be executed for all
|
|
357
|
+
* the routes and you must return undefined to fallback
|
|
358
|
+
* to the default behavior of detecting types
|
|
359
|
+
*
|
|
360
|
+
* @param callback - Function to compute response types for routes
|
|
361
|
+
* @returns This RoutesScanner instance for method chaining
|
|
362
|
+
*/
|
|
363
|
+
defineResponse(callback) {
|
|
364
|
+
this.#computeResponseTypes = callback;
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Register a callback to self compute the request types for
|
|
369
|
+
* a given route. The callback will be executed for all
|
|
370
|
+
* the routes and you must return undefined to fallback
|
|
371
|
+
* to the default behavior of detecting types
|
|
372
|
+
*
|
|
373
|
+
* @param callback - Function to compute request types for routes
|
|
374
|
+
* @returns This RoutesScanner instance for method chaining
|
|
375
|
+
*/
|
|
376
|
+
defineRequest(callback) {
|
|
377
|
+
this.#computeRequestTypes = callback;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Register a callback to extract validators from the route controller
|
|
382
|
+
*
|
|
383
|
+
* @param callback - Function to extract validators from controllers
|
|
384
|
+
* @returns This RoutesScanner instance for method chaining
|
|
385
|
+
*/
|
|
386
|
+
extractValidators(callback) {
|
|
387
|
+
this.#extractValidators = callback;
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Returns the scanned routes
|
|
392
|
+
*
|
|
393
|
+
* @returns Array of scanned routes with their metadata
|
|
394
|
+
*/
|
|
395
|
+
getScannedRoutes() {
|
|
396
|
+
return this.#scannedRoutes;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Returns an array of controllers bound to the provided
|
|
400
|
+
* routes
|
|
401
|
+
*
|
|
402
|
+
* @returns Array of controller's absolute paths
|
|
403
|
+
*/
|
|
404
|
+
getControllers() {
|
|
405
|
+
return Object.keys(this.#controllerRoutes);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Invalidating a controller will trigger computing the validators,
|
|
409
|
+
* request types and the response types.
|
|
410
|
+
*
|
|
411
|
+
* @param controllerPath - Path to the controller file to invalidate
|
|
412
|
+
*/
|
|
413
|
+
async invalidate(controllerPath) {
|
|
414
|
+
const controllerRoutes = this.#controllerRoutes[controllerPath];
|
|
415
|
+
if (!controllerRoutes) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
for (let scannedRoute of controllerRoutes) {
|
|
419
|
+
if (scannedRoute.controller) {
|
|
420
|
+
const vfs = new VirtualFileSystem(this.#appRoot);
|
|
421
|
+
await this.#setResponse(scannedRoute, scannedRoute.controller);
|
|
422
|
+
await this.#setRequest(scannedRoute, scannedRoute.controller, vfs);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Scans an array of Route list items and fetches their validators,
|
|
428
|
+
* controllers, and request/response types.
|
|
429
|
+
*
|
|
430
|
+
* This is the main method that processes all routes and extracts
|
|
431
|
+
* their type information for code generation purposes.
|
|
432
|
+
*
|
|
433
|
+
* @param routes - Array of route list items to scan
|
|
434
|
+
*/
|
|
435
|
+
async scan(routes) {
|
|
436
|
+
const vfs = new VirtualFileSystem(this.#appRoot);
|
|
437
|
+
for (let route of routes) {
|
|
438
|
+
await this.#processRoute(route, vfs);
|
|
439
|
+
}
|
|
440
|
+
vfs.invalidate();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
export {
|
|
444
|
+
RoutesScanner
|
|
445
|
+
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type VirtualFileSystem } from '../../virtual_file_system.ts';
|
|
2
2
|
import { type ScannedController, type ScannedRoute } from '../../types/code_scanners.ts';
|
|
3
3
|
/**
|
|
4
|
-
* Extracts the VineJS validator usage from within a controller
|
|
5
|
-
*
|
|
4
|
+
* Extracts the VineJS validator usage from within a controller method.
|
|
5
|
+
*
|
|
6
|
+
* This function analyzes controller method code to detect validator usage patterns
|
|
7
|
+
* and extracts the validator references along with their import information.
|
|
8
|
+
* The following syntaxes are supported:
|
|
6
9
|
*
|
|
7
10
|
* - `request.validateUsing(validatorReference)`
|
|
8
11
|
* - `vine.validate(validatorReference)`
|
|
@@ -14,5 +17,10 @@ import { type ScannedController, type ScannedRoute } from '../../types/code_scan
|
|
|
14
17
|
*
|
|
15
18
|
* The app root is needed to create relative validator imports in case
|
|
16
19
|
* a relative import was used within the controller file.
|
|
20
|
+
*
|
|
21
|
+
* @param appRoot - The root directory of the application
|
|
22
|
+
* @param vfs - Virtual file system instance for code analysis
|
|
23
|
+
* @param controller - The controller to analyze for validator usage
|
|
24
|
+
* @returns Promise resolving to array of validator information or undefined
|
|
17
25
|
*/
|
|
18
|
-
export declare function extractValidators(appRoot: string,
|
|
26
|
+
export declare function extractValidators(appRoot: string, vfs: VirtualFileSystem, controller: ScannedController): Promise<ScannedRoute['validators']>;
|