@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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { i as isRelative, m as debug_default, t as VirtualFileSystem } from "./virtual_file_system-bGeoWsK-.js";
|
|
2
|
+
import { a as nodeToPlainText, i as inspectMethodArguments, n as inspectClass, o as searchValidatorDirectUsage, r as inspectClassMethods, t as findImport } from "./helpers-DDurYRsZ.js";
|
|
3
|
+
import { cliui } from "@poppinss/cliui";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import string from "@poppinss/utils/string";
|
|
6
|
+
import { join, relative } from "node:path";
|
|
7
|
+
import StringBuilder from "@poppinss/utils/string_builder";
|
|
8
|
+
import { parseImports } from "parse-imports";
|
|
9
|
+
import { resolve } from "import-meta-resolve";
|
|
10
|
+
var PathsResolver = class {
|
|
11
|
+
#appRoot;
|
|
12
|
+
#resolvedPaths = {};
|
|
13
|
+
#resolver = (specifier, parentPath) => resolve(specifier, parentPath);
|
|
14
|
+
constructor(appRoot) {
|
|
15
|
+
this.#appRoot = pathToFileURL(join(appRoot, "index.js")).href;
|
|
16
|
+
}
|
|
17
|
+
use(resolver) {
|
|
18
|
+
this.#resolver = resolver;
|
|
19
|
+
}
|
|
20
|
+
resolve(specifier, rewriteAliasImportExtension = false) {
|
|
21
|
+
if (isRelative(specifier)) throw new Error("Cannot resolve relative paths using PathsResolver");
|
|
22
|
+
const cacheKey = specifier;
|
|
23
|
+
const cached = this.#resolvedPaths[cacheKey];
|
|
24
|
+
if (cached) return cached;
|
|
25
|
+
let resolvedPath = fileURLToPath(this.#resolver(specifier, this.#appRoot));
|
|
26
|
+
if (rewriteAliasImportExtension && specifier.startsWith("#") && resolvedPath.endsWith(".js")) resolvedPath = resolvedPath.replace(/\.js$/, ".ts");
|
|
27
|
+
this.#resolvedPaths[cacheKey] = resolvedPath;
|
|
28
|
+
return this.#resolvedPaths[cacheKey];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
async function extractValidators(appRoot, vfs, controller) {
|
|
32
|
+
const root = await vfs.get(controller.path);
|
|
33
|
+
const fileContents = root.text();
|
|
34
|
+
const controllerClass = inspectClass(root);
|
|
35
|
+
if (!controllerClass) {
|
|
36
|
+
debug_default(`No class defined within the "%s"`, controller.import.specifier);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const method = inspectClassMethods(controllerClass).find((methodNode) => {
|
|
40
|
+
return methodNode.find({ rule: { kind: "property_identifier" } })?.text() === controller.method;
|
|
41
|
+
});
|
|
42
|
+
if (!method) {
|
|
43
|
+
debug_default(`Unable to find "%s" method in "%s"`, controller.method, controller.import.specifier);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const validationCalls = inspectMethodArguments(method, ["request.validateUsing", "vine.validate"]).map((node) => {
|
|
47
|
+
const firstArg = node.find({ rule: { any: [{ kind: "identifier" }, { kind: "member_expression" }] } });
|
|
48
|
+
if (!firstArg) return;
|
|
49
|
+
return nodeToPlainText(firstArg);
|
|
50
|
+
}).filter((node) => node !== void 0).concat(searchValidatorDirectUsage(method).map((node) => nodeToPlainText(node)));
|
|
51
|
+
if (!validationCalls.length) {
|
|
52
|
+
debug_default(`Unable to detect any validation calls in "%s.%s" method`, controller.import.specifier, controller.method);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const controllerName = controllerClass.find({ rule: { kind: "type_identifier" } }).text();
|
|
56
|
+
const controllerURL = pathToFileURL(controller.path);
|
|
57
|
+
return (await Promise.all(validationCalls.map(async (validationCall) => {
|
|
58
|
+
if (validationCall.split(".")[0] === controllerName) return {
|
|
59
|
+
name: validationCall,
|
|
60
|
+
import: {
|
|
61
|
+
specifier: controller.import.specifier,
|
|
62
|
+
type: "default",
|
|
63
|
+
value: controllerName
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const importCall = await findImport(fileContents, validationCall);
|
|
67
|
+
if (!importCall) {
|
|
68
|
+
debug_default("Unable to find import for \"%s\" used by \"%s.%s\" method", validationCall, controller.import.specifier, controller.method);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
name: validationCall,
|
|
73
|
+
import: {
|
|
74
|
+
specifier: isRelative(importCall.specifier) ? string.toUnixSlash(relative(appRoot, fileURLToPath(new URL(importCall.specifier, controllerURL)))) : importCall.specifier,
|
|
75
|
+
type: importCall.clause.type,
|
|
76
|
+
value: importCall.clause.value
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}))).filter((value) => !!value);
|
|
80
|
+
}
|
|
81
|
+
var RoutesScanner = class {
|
|
82
|
+
#filter;
|
|
83
|
+
#appRoot;
|
|
84
|
+
#controllerRoutes = {};
|
|
85
|
+
#scannedRoutes = [];
|
|
86
|
+
#computeResponseTypes;
|
|
87
|
+
#computeRequestTypes;
|
|
88
|
+
#extractValidators;
|
|
89
|
+
ui = cliui();
|
|
90
|
+
pathsResolver;
|
|
91
|
+
rules = {
|
|
92
|
+
request: {},
|
|
93
|
+
response: {}
|
|
94
|
+
};
|
|
95
|
+
constructor(appRoot, rulesCollection) {
|
|
96
|
+
this.#appRoot = appRoot;
|
|
97
|
+
this.pathsResolver = new PathsResolver(appRoot);
|
|
98
|
+
rulesCollection.forEach((rules) => {
|
|
99
|
+
Object.assign(this.rules.request, rules.request);
|
|
100
|
+
Object.assign(this.rules.response, rules.response);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
#shouldSkipRoute(route) {
|
|
104
|
+
if (this.#filter) return !this.#filter(route);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
#prepareRequestTypes(route) {
|
|
108
|
+
if (!route.validators) return;
|
|
109
|
+
return {
|
|
110
|
+
type: route.validators.reduce((result, validator) => {
|
|
111
|
+
const validatorExport = validator.import.type === "default" ? ".default" : validator.import.type === "named" ? `.${validator.import.value}` : "";
|
|
112
|
+
const [, ...segments] = validator.name.split(".");
|
|
113
|
+
const namespace = segments.map((segment) => `['${segment}']`).join("");
|
|
114
|
+
result.push(`InferInput<(typeof import('${validator.import.specifier}')${validatorExport})${namespace}>`);
|
|
115
|
+
return result;
|
|
116
|
+
}, []).join("|"),
|
|
117
|
+
imports: [`import { InferInput } from '@vinejs/vine/types'`]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async #inspectControllerSpecifier(importExpression, method) {
|
|
121
|
+
const importedModule = [...await parseImports(importExpression)].find(($import) => $import.isDynamicImport && $import.moduleSpecifier.value);
|
|
122
|
+
if (!importedModule || importedModule.moduleSpecifier.type !== "package" || !importedModule.moduleSpecifier.value) return null;
|
|
123
|
+
const specifier = importedModule.moduleSpecifier.value;
|
|
124
|
+
const name = new StringBuilder(specifier.split("/").pop()).removeSuffix("Controller").pascalCase().suffix("Controller").toString();
|
|
125
|
+
return {
|
|
126
|
+
name,
|
|
127
|
+
method: method ?? "handle",
|
|
128
|
+
path: string.toUnixSlash(this.pathsResolver.resolve(specifier, true)),
|
|
129
|
+
import: {
|
|
130
|
+
specifier,
|
|
131
|
+
type: "default",
|
|
132
|
+
value: name
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async #setResponse(route, controller) {
|
|
137
|
+
route.response = await this.#computeResponseTypes?.(route, controller, this) ?? {
|
|
138
|
+
type: `ReturnType<import('${controller.import.specifier}').default['${controller.method}']>`,
|
|
139
|
+
imports: []
|
|
140
|
+
};
|
|
141
|
+
debug_default("computed route \"%s\" response %O", route.name, route.response);
|
|
142
|
+
}
|
|
143
|
+
async #setRequest(route, controller, vfs) {
|
|
144
|
+
route.validators = await this.#extractValidators?.(route, controller, this) ?? await extractValidators(this.#appRoot, vfs, controller);
|
|
145
|
+
debug_default("computed route \"%s\" validators %O", route.name, route.validators);
|
|
146
|
+
route.request = await this.#computeRequestTypes?.(route, controller, this) ?? this.#prepareRequestTypes(route);
|
|
147
|
+
debug_default("computed route \"%s\" request input %O", route.name, route.request);
|
|
148
|
+
}
|
|
149
|
+
#processRouteWithoutController(route) {
|
|
150
|
+
if (!route.name) {
|
|
151
|
+
debug_default(`skipping route "%s" as it does not have a name`, route.pattern);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const scannedRoute = {
|
|
155
|
+
name: route.name,
|
|
156
|
+
domain: route.domain,
|
|
157
|
+
methods: route.methods,
|
|
158
|
+
pattern: route.pattern,
|
|
159
|
+
tokens: route.tokens,
|
|
160
|
+
request: this.rules.request[route.name],
|
|
161
|
+
response: this.rules.response[route.name]
|
|
162
|
+
};
|
|
163
|
+
debug_default("scanned route without controller %O", scannedRoute);
|
|
164
|
+
this.#scannedRoutes.push(scannedRoute);
|
|
165
|
+
}
|
|
166
|
+
async #processRouteWithController(route, vfs) {
|
|
167
|
+
if (!route.handler || !route.handler.importExpression) return this.#processRouteWithoutController(route);
|
|
168
|
+
const controller = await this.#inspectControllerSpecifier(route.handler.importExpression, route.handler.method);
|
|
169
|
+
if (!controller) return this.#processRouteWithoutController(route);
|
|
170
|
+
debug_default("processing route \"%s\" with inspected controller %O", route.name, controller);
|
|
171
|
+
route.name = route.name ?? new StringBuilder(controller.name).removeSuffix("Controller").snakeCase().suffix(`.${string.snakeCase(controller.method)}`).toString();
|
|
172
|
+
if (this.#shouldSkipRoute(route)) return;
|
|
173
|
+
const scannedRoute = {
|
|
174
|
+
name: route.name,
|
|
175
|
+
domain: route.domain,
|
|
176
|
+
methods: route.methods,
|
|
177
|
+
pattern: route.pattern,
|
|
178
|
+
tokens: route.tokens,
|
|
179
|
+
request: this.rules.request[route.name],
|
|
180
|
+
response: this.rules.response[route.name],
|
|
181
|
+
controller
|
|
182
|
+
};
|
|
183
|
+
debug_default("scanned route %O", scannedRoute);
|
|
184
|
+
this.#scannedRoutes.push(scannedRoute);
|
|
185
|
+
if (!scannedRoute.request || !scannedRoute.response) {
|
|
186
|
+
debug_default("tracking controller for rescanning %O", scannedRoute);
|
|
187
|
+
this.#controllerRoutes[controller.path] ??= [];
|
|
188
|
+
this.#controllerRoutes[controller.path].push(scannedRoute);
|
|
189
|
+
if (!scannedRoute.response) await this.#setResponse(scannedRoute, controller);
|
|
190
|
+
if (!scannedRoute.request) await this.#setRequest(scannedRoute, controller, vfs);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async #processRoute(route, vfs) {
|
|
194
|
+
if (route.name && this.#shouldSkipRoute(route)) {
|
|
195
|
+
debug_default("route skipped route: %O, rules: %O", route, this.rules);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (typeof route.handler === "function") {
|
|
199
|
+
this.#processRouteWithoutController(route);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
await this.#processRouteWithController(route, vfs);
|
|
203
|
+
}
|
|
204
|
+
defineResponse(callback) {
|
|
205
|
+
this.#computeResponseTypes = callback;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
defineRequest(callback) {
|
|
209
|
+
this.#computeRequestTypes = callback;
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
extractValidators(callback) {
|
|
213
|
+
this.#extractValidators = callback;
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
getScannedRoutes() {
|
|
217
|
+
return this.#scannedRoutes;
|
|
218
|
+
}
|
|
219
|
+
getControllers() {
|
|
220
|
+
return Object.keys(this.#controllerRoutes);
|
|
221
|
+
}
|
|
222
|
+
async invalidate(controllerPath) {
|
|
223
|
+
const controllerRoutes = this.#controllerRoutes[controllerPath];
|
|
224
|
+
if (!controllerRoutes || !controllerRoutes.length) {
|
|
225
|
+
debug_default("\"%s\" controllers is not part of scanned controllers %O", controllerPath, this.#controllerRoutes);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
for (let scannedRoute of controllerRoutes) if (scannedRoute.controller) {
|
|
229
|
+
debug_default("invalidating route %O", scannedRoute);
|
|
230
|
+
const vfs = new VirtualFileSystem(this.#appRoot);
|
|
231
|
+
await this.#setResponse(scannedRoute, scannedRoute.controller);
|
|
232
|
+
await this.#setRequest(scannedRoute, scannedRoute.controller, vfs);
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
filter(filterFn) {
|
|
237
|
+
this.#filter = filterFn;
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
async scan(routes) {
|
|
241
|
+
const vfs = new VirtualFileSystem(this.#appRoot);
|
|
242
|
+
for (const route of routes) await this.#processRoute(route, vfs);
|
|
243
|
+
vfs.invalidate();
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
export { RoutesScanner as t };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { f as throttle, l as removeExtension, m as debug_default, t as VirtualFileSystem } from "./virtual_file_system-bGeoWsK-.js";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import string from "@poppinss/utils/string";
|
|
4
|
+
import { dirname, join, relative } from "node:path/posix";
|
|
5
|
+
import StringBuilder from "@poppinss/utils/string_builder";
|
|
6
|
+
var FileBuffer = class FileBuffer {
|
|
7
|
+
#eol = false;
|
|
8
|
+
#buffer = [];
|
|
9
|
+
#identationSize = 0;
|
|
10
|
+
#compiledOutput;
|
|
11
|
+
create() {
|
|
12
|
+
return new FileBuffer();
|
|
13
|
+
}
|
|
14
|
+
get size() {
|
|
15
|
+
return this.#buffer.length;
|
|
16
|
+
}
|
|
17
|
+
eol(enabled) {
|
|
18
|
+
this.#eol = enabled;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
writeLine(text) {
|
|
22
|
+
if (typeof text === "string") this.#buffer.push(`${" ".repeat(this.#identationSize)}${text}\n`);
|
|
23
|
+
else {
|
|
24
|
+
this.#buffer.push(text);
|
|
25
|
+
this.#buffer.push("");
|
|
26
|
+
}
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
write(text) {
|
|
30
|
+
if (typeof text === "string") this.#buffer.push(`${" ".repeat(this.#identationSize)}${text}`);
|
|
31
|
+
else this.#buffer.push(text);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
indent() {
|
|
35
|
+
this.#identationSize += 2;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
dedent() {
|
|
39
|
+
this.#identationSize -= 2;
|
|
40
|
+
if (this.#identationSize < 0) this.#identationSize = 0;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
toString() {
|
|
44
|
+
return this.flush();
|
|
45
|
+
}
|
|
46
|
+
flush() {
|
|
47
|
+
if (this.#compiledOutput !== void 0) return this.#compiledOutput;
|
|
48
|
+
this.#compiledOutput = this.#buffer.join("\n");
|
|
49
|
+
return this.#eol ? `${this.#compiledOutput}\n` : this.#compiledOutput;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var IndexGeneratorSource = class {
|
|
53
|
+
#appRoot;
|
|
54
|
+
#output;
|
|
55
|
+
#source;
|
|
56
|
+
#outputDirname;
|
|
57
|
+
#vfs;
|
|
58
|
+
#config;
|
|
59
|
+
#cliLogger;
|
|
60
|
+
#generateOutput = throttle(async () => {
|
|
61
|
+
const buffer = new FileBuffer().eol(true);
|
|
62
|
+
if (this.#config.as === "barrelFile") this.#asBarrelFile(this.#vfs, buffer, this.#config.exportName, this.#config.disableLazyImports);
|
|
63
|
+
else this.#config.as(this.#vfs, buffer, this.#config, { toImportPath: this.#createBarrelFileImportGenerator(this.#source, this.#outputDirname, this.#config) });
|
|
64
|
+
await mkdir(dirname(this.#output), { recursive: true });
|
|
65
|
+
await writeFile(this.#output, buffer.flush());
|
|
66
|
+
});
|
|
67
|
+
name;
|
|
68
|
+
constructor(name, appRoot, cliLogger, config) {
|
|
69
|
+
this.name = name;
|
|
70
|
+
this.#config = config;
|
|
71
|
+
this.#appRoot = appRoot;
|
|
72
|
+
this.#cliLogger = cliLogger;
|
|
73
|
+
this.#source = join(this.#appRoot, this.#config.source);
|
|
74
|
+
this.#output = join(this.#appRoot, this.#config.output);
|
|
75
|
+
this.#outputDirname = dirname(this.#output);
|
|
76
|
+
this.#vfs = new VirtualFileSystem(this.#source, {
|
|
77
|
+
glob: this.#config.glob,
|
|
78
|
+
filter: this.#config.filter
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
#treeToString(input, buffer) {
|
|
82
|
+
Object.keys(input).forEach((key) => {
|
|
83
|
+
const value = input[key];
|
|
84
|
+
if (typeof value === "string") buffer.write(`${key}: ${value},`);
|
|
85
|
+
else {
|
|
86
|
+
buffer.write(`${key}: {`).indent();
|
|
87
|
+
this.#treeToString(value, buffer);
|
|
88
|
+
buffer.dedent().write(`},`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
#createBarrelFileKeyGenerator(config) {
|
|
93
|
+
return function(key) {
|
|
94
|
+
let paths = key.split("/");
|
|
95
|
+
let baseName = new StringBuilder(paths.pop());
|
|
96
|
+
if (config.skipSegments?.length) paths = paths.filter((p) => !config.skipSegments.includes(p));
|
|
97
|
+
if (config.computeBaseName) baseName = config.computeBaseName(baseName);
|
|
98
|
+
else baseName = baseName.removeSuffix(config.removeSuffix ?? "").pascalCase();
|
|
99
|
+
return [...paths.map((p) => string.camelCase(p)), baseName.toString()].join("/");
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
#createBarrelFileImportGenerator(source, outputDirname, config) {
|
|
103
|
+
return function(filePath) {
|
|
104
|
+
if (config.importAlias) {
|
|
105
|
+
debug_default("converting \"%s\" to import alias, source \"%s\"", filePath, source);
|
|
106
|
+
return removeExtension(filePath.replace(source, config.importAlias));
|
|
107
|
+
}
|
|
108
|
+
debug_default("converting \"%s\" to relative import, source \"%s\"", filePath, outputDirname);
|
|
109
|
+
return relative(outputDirname, filePath);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
#asBarrelFile(vfs, buffer, exportName, disableLazyImports) {
|
|
113
|
+
const useEagerImports = disableLazyImports === true ? true : false;
|
|
114
|
+
const keyGenerator = this.#createBarrelFileKeyGenerator(this.#config);
|
|
115
|
+
const importsBuffer = buffer.create();
|
|
116
|
+
const importGenerator = this.#createBarrelFileImportGenerator(this.#source, this.#outputDirname, this.#config);
|
|
117
|
+
const tree = vfs.asTree({
|
|
118
|
+
transformKey: keyGenerator,
|
|
119
|
+
transformValue: (filePath, key) => {
|
|
120
|
+
if (useEagerImports) {
|
|
121
|
+
const importKey = new StringBuilder(key).pascalCase().toString();
|
|
122
|
+
importsBuffer.write(`import ${importKey} from '${importGenerator(filePath)}'`);
|
|
123
|
+
return importKey;
|
|
124
|
+
}
|
|
125
|
+
return `() => import('${importGenerator(filePath)}')`;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
if (!Object.keys(tree).length) {
|
|
129
|
+
buffer.write(`export const ${exportName} = {}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (useEagerImports) buffer.writeLine(importsBuffer);
|
|
133
|
+
buffer.write(`export const ${exportName} = {`).indent();
|
|
134
|
+
this.#treeToString(tree, buffer);
|
|
135
|
+
buffer.dedent().write(`}`);
|
|
136
|
+
}
|
|
137
|
+
#logCreation(startTime) {
|
|
138
|
+
this.#cliLogger.info(`created ${this.#config.output}`, { startTime });
|
|
139
|
+
}
|
|
140
|
+
async addFile(filePath) {
|
|
141
|
+
if (this.#vfs.add(filePath)) {
|
|
142
|
+
debug_default("file added, re-generating \"%s\" index", this.name);
|
|
143
|
+
const startTime = process.hrtime();
|
|
144
|
+
await this.#generateOutput();
|
|
145
|
+
this.#logCreation(startTime);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async removeFile(filePath) {
|
|
149
|
+
if (this.#vfs.remove(filePath)) {
|
|
150
|
+
debug_default("file removed, re-generating \"%s\" index", this.name);
|
|
151
|
+
const startTime = process.hrtime();
|
|
152
|
+
await this.#generateOutput();
|
|
153
|
+
this.#logCreation(startTime);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async generate() {
|
|
157
|
+
debug_default("generating \"%s\" index", this.name);
|
|
158
|
+
await this.#vfs.scan();
|
|
159
|
+
await this.#generateOutput();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var IndexGenerator = class {
|
|
163
|
+
appRoot;
|
|
164
|
+
#sources = {};
|
|
165
|
+
#cliLogger;
|
|
166
|
+
constructor(appRoot, cliLogger) {
|
|
167
|
+
this.appRoot = appRoot;
|
|
168
|
+
this.#cliLogger = cliLogger;
|
|
169
|
+
}
|
|
170
|
+
add(name, config) {
|
|
171
|
+
this.#sources[name] = new IndexGeneratorSource(name, this.appRoot, this.#cliLogger, config);
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
async addFile(filePath) {
|
|
175
|
+
const sources = Object.values(this.#sources);
|
|
176
|
+
for (let source of sources) await source.addFile(filePath);
|
|
177
|
+
}
|
|
178
|
+
async removeFile(filePath) {
|
|
179
|
+
const sources = Object.values(this.#sources);
|
|
180
|
+
for (let source of sources) await source.removeFile(filePath);
|
|
181
|
+
}
|
|
182
|
+
async generate() {
|
|
183
|
+
const sources = Object.values(this.#sources);
|
|
184
|
+
for (let source of sources) await source.generate();
|
|
185
|
+
if (sources.length) this.#cliLogger.info(`codegen: created ${sources.length} file(s)`);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
export { FileBuffer as n, IndexGenerator as t };
|
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,38 @@ 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
|
+
* The current working project directory path as string
|
|
57
|
+
*/
|
|
58
|
+
cwdPath: string;
|
|
59
|
+
/**
|
|
60
|
+
* Bundler configuration options including hooks and meta files
|
|
61
|
+
*/
|
|
62
|
+
options: BundlerOptions;
|
|
63
|
+
/**
|
|
64
|
+
* Create a new bundler instance
|
|
65
|
+
*
|
|
66
|
+
* @param cwd - The current working directory URL
|
|
67
|
+
* @param ts - TypeScript module reference
|
|
68
|
+
* @param options - Bundler configuration options
|
|
69
|
+
*/
|
|
36
70
|
constructor(cwd: URL, ts: typeof tsStatic, options: BundlerOptions);
|
|
37
71
|
/**
|
|
38
72
|
* Bundles the application to be run in production
|
|
73
|
+
*
|
|
74
|
+
* @param stopOnError - Whether to stop the build process on TypeScript errors
|
|
75
|
+
* @param client - Override the detected package manager
|
|
76
|
+
* @returns Promise that resolves to true if build succeeded, false otherwise
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const success = await bundler.bundle(true, 'npm')
|
|
39
80
|
*/
|
|
40
81
|
bundle(stopOnError?: boolean, client?: SupportedPackageManager): Promise<boolean>;
|
|
41
82
|
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { type AsyncOrSync } from '@poppinss/utils/types';
|
|
2
2
|
import { PathsResolver } from '../../paths_resolver.ts';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { type ScannedRoute, type RoutesListItem, type ScannedController, type RoutesScannerRules, type RoutesScannerFilterFn } 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,58 @@ 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
|
|
107
|
+
*/
|
|
108
|
+
invalidate(controllerPath: string): Promise<boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* Sets a filter function to selectively include routes during scanning.
|
|
111
|
+
*
|
|
112
|
+
* @param filterFn - Function that returns true for routes to include
|
|
113
|
+
* @returns This RoutesScanner instance for method chaining
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* scanner.filter((route) => {
|
|
117
|
+
* return route.pattern.startsWith('/api')
|
|
118
|
+
* })
|
|
72
119
|
*/
|
|
73
|
-
|
|
120
|
+
filter(filterFn: RoutesScannerFilterFn): this;
|
|
74
121
|
/**
|
|
75
122
|
* Scans an array of Route list items and fetches their validators,
|
|
76
123
|
* controllers, and request/response types.
|
|
124
|
+
*
|
|
125
|
+
* This is the main method that processes all routes and extracts
|
|
126
|
+
* their type information for code generation purposes.
|
|
127
|
+
*
|
|
128
|
+
* @param routes - Array of route list items to scan
|
|
77
129
|
*/
|
|
78
130
|
scan(routes: RoutesListItem[]): Promise<void>;
|
|
79
131
|
}
|
|
@@ -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']>;
|