@vercube/nitro 1.0.0-beta.2 → 1.1.0
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/dist/index.mjs +13 -369
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { createNitroApp } from "./runtime/App.mjs";
|
|
2
2
|
import { destroyContainer } from "@vercube/di";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import { join,
|
|
4
|
+
import { join, resolve } from "pathe";
|
|
5
5
|
import { watch } from "chokidar";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
7
|
-
import {
|
|
8
|
-
import { parseSync } from "oxc-parser";
|
|
7
|
+
import { IMPORT_SOURCE, MIDDLEWARE_IMPORT_SOURCE, SERVICE_IMPORT_SOURCE, scanFiles, transformMiddleware, transformRoute, transformService } from "@vercube/scan";
|
|
9
8
|
//#region src/helpers/helpers.ts
|
|
10
9
|
function useVercubeApp() {
|
|
11
10
|
return globalThis.__vercubeApp__;
|
|
@@ -38,208 +37,14 @@ function validateTypescript(nitro) {
|
|
|
38
37
|
}
|
|
39
38
|
//#endregion
|
|
40
39
|
//#region src/_internal/scan.ts
|
|
41
|
-
const GLOB_SCAN_PATTERN = "**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}";
|
|
42
|
-
async function scanFiles(nitro, name) {
|
|
43
|
-
return await Promise.all(nitro.options.scanDirs.map((dir) => scanDir(nitro, dir, name))).then((r) => r.flat());
|
|
44
|
-
}
|
|
45
|
-
async function scanDir(nitro, dir, name) {
|
|
46
|
-
return (await glob(join(name, GLOB_SCAN_PATTERN), {
|
|
47
|
-
cwd: dir,
|
|
48
|
-
dot: true,
|
|
49
|
-
absolute: true
|
|
50
|
-
}).catch((error) => {
|
|
51
|
-
if (error?.code === "ENOTDIR") {
|
|
52
|
-
nitro.logger.warn(`Ignoring \`${join(dir, name)}\`. It must be a directory.`);
|
|
53
|
-
return [];
|
|
54
|
-
}
|
|
55
|
-
throw error;
|
|
56
|
-
})).map((fullPath) => {
|
|
57
|
-
return {
|
|
58
|
-
fullPath,
|
|
59
|
-
path: relative(join(dir, name), fullPath)
|
|
60
|
-
};
|
|
61
|
-
}).sort((a, b) => a.path.localeCompare(b.path));
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/build/Routes.ts
|
|
65
|
-
/**
|
|
66
|
-
* Mapping of decorator names to their corresponding uppercase HTTP method strings.
|
|
67
|
-
* Used for O(1) lookup and simultaneous method name resolution.
|
|
68
|
-
*/
|
|
69
|
-
const HTTP_METHODS = {
|
|
70
|
-
Get: "GET",
|
|
71
|
-
Post: "POST",
|
|
72
|
-
Put: "PUT",
|
|
73
|
-
Delete: "DELETE",
|
|
74
|
-
Patch: "PATCH",
|
|
75
|
-
Options: "OPTIONS",
|
|
76
|
-
Head: "HEAD"
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Pre-compiled regular expression for extracting named route parameters.
|
|
80
|
-
* Matches path segments prefixed with ':' (e.g. ':id', ':slug').
|
|
81
|
-
* Uses the global flag for iterative matching via `exec`.
|
|
82
|
-
*/
|
|
83
|
-
const PARAM_RE = /:([^/]+)/g;
|
|
84
|
-
/**
|
|
85
|
-
* Internal module path used for generated import statements.
|
|
86
|
-
*/
|
|
87
|
-
const IMPORT_SOURCE = "#internal/vercube-route-plugin";
|
|
88
|
-
/**
|
|
89
|
-
* Reads a controller file from disk and returns all routes defined in classes
|
|
90
|
-
* decorated with `Controller` and methods decorated with HTTP decorators (Get, Post, etc.).
|
|
91
|
-
* @param route - File info (path and fullPath) to analyze.
|
|
92
|
-
* @returns List of `RouteInfo` for every route in the file.
|
|
93
|
-
*/
|
|
94
|
-
async function transformRoute(route) {
|
|
95
|
-
return extractRoutes(readFileSync(route.fullPath, "utf8")).map((parsedRoute) => ({
|
|
96
|
-
...parsedRoute,
|
|
97
|
-
...route
|
|
98
|
-
}));
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Extracts all route definitions from the given TypeScript/JavaScript source code.
|
|
102
|
-
*
|
|
103
|
-
* Parses the source using `oxc-parser` and traverses the resulting AST to find
|
|
104
|
-
* classes decorated with `@Controller(path)`. For each such class, it inspects
|
|
105
|
-
* method definitions for HTTP method decorators (`@Get`, `@Post`, `@Put`, `@Delete`,
|
|
106
|
-
* `@Patch`, `@Options`, `@Head`) and constructs full route paths by concatenating
|
|
107
|
-
* the controller base path with the method-level path.
|
|
108
|
-
*
|
|
109
|
-
* Generates appropriate import statements based on the export style:
|
|
110
|
-
* - `export default class Foo` → `import Foo from '...'`
|
|
111
|
-
* - `export class Foo` → `import { Foo } from '...'`
|
|
112
|
-
*
|
|
113
|
-
* Supports classes declared via `export class`, `export default class`,
|
|
114
|
-
* and plain `class` declarations.
|
|
115
|
-
*
|
|
116
|
-
* @param code - The raw TypeScript or JavaScript source code string to analyze.
|
|
117
|
-
* @returns An array of {@link RouteInfo} objects representing all discovered routes.
|
|
118
|
-
*/
|
|
119
|
-
function extractRoutes(code) {
|
|
120
|
-
const ast = parseSync("file.ts", code).program;
|
|
121
|
-
const routes = [];
|
|
122
|
-
for (const node of ast.body) {
|
|
123
|
-
const classInfo = getClassNode$2(node);
|
|
124
|
-
if (!classInfo) continue;
|
|
125
|
-
const { classNode, isDefault } = classInfo;
|
|
126
|
-
const basePath = extractDecoratorArg(classNode.decorators, "Controller");
|
|
127
|
-
if (basePath === null) continue;
|
|
128
|
-
const className = classNode.id?.name;
|
|
129
|
-
if (!className) continue;
|
|
130
|
-
const importStatement = isDefault ? `import ${className} from '${IMPORT_SOURCE}';` : `import { ${className} } from '${IMPORT_SOURCE}';`;
|
|
131
|
-
for (const member of classNode.body?.body ?? []) {
|
|
132
|
-
if (member.type !== "MethodDefinition") continue;
|
|
133
|
-
for (const decorator of member.decorators ?? []) {
|
|
134
|
-
const expr = decorator.expression;
|
|
135
|
-
if (expr?.type !== "CallExpression") continue;
|
|
136
|
-
const method = HTTP_METHODS[expr.callee?.name];
|
|
137
|
-
if (!method) continue;
|
|
138
|
-
const arg = expr.arguments?.[0];
|
|
139
|
-
const fullRoute = normalizePath(basePath + (arg?.type === "Literal" && typeof arg.value === "string" ? arg.value : ""));
|
|
140
|
-
routes.push({
|
|
141
|
-
import: importStatement,
|
|
142
|
-
importClassName: className,
|
|
143
|
-
route: fullRoute,
|
|
144
|
-
method,
|
|
145
|
-
fullPath: "",
|
|
146
|
-
path: "",
|
|
147
|
-
params: extractParams(fullRoute)
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return routes;
|
|
153
|
-
}
|
|
154
40
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* - `ExportDefaultDeclaration` wrapping a `ClassDeclaration` → `isDefault: true`
|
|
160
|
-
* - `ExportNamedDeclaration` wrapping a `ClassDeclaration` → `isDefault: false`
|
|
161
|
-
* - Direct `ClassDeclaration` or `Class` nodes → `isDefault: false`
|
|
162
|
-
*
|
|
163
|
-
* @param node - An AST node from the program body to inspect.
|
|
164
|
-
* @returns An object containing the class AST node and whether it is a default export, or `null` if not a class.
|
|
41
|
+
* Scans every configured Nitro scan directory for files inside `name`.
|
|
42
|
+
* @param nitro - The nitro instance (provides `options.scanDirs` and `logger`).
|
|
43
|
+
* @param name - The subdirectory to scan within each scan dir.
|
|
44
|
+
* @returns The discovered files.
|
|
165
45
|
*/
|
|
166
|
-
function
|
|
167
|
-
|
|
168
|
-
const decl = node.declaration;
|
|
169
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
170
|
-
classNode: decl,
|
|
171
|
-
isDefault: true
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (node.type === "ExportNamedDeclaration") {
|
|
175
|
-
const decl = node.declaration;
|
|
176
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
177
|
-
classNode: decl,
|
|
178
|
-
isDefault: false
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
if (node?.type === "ClassDeclaration" || node?.type === "Class") return {
|
|
182
|
-
classNode: node,
|
|
183
|
-
isDefault: false
|
|
184
|
-
};
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Searches a list of decorators for a `CallExpression` matching the given name
|
|
189
|
-
* and extracts its first string argument.
|
|
190
|
-
*
|
|
191
|
-
* This is used to retrieve the path argument from decorators like `@Controller('/api/foo')`.
|
|
192
|
-
* If the decorator is found but has no string argument, an empty string is returned.
|
|
193
|
-
* If no matching decorator is found, `null` is returned.
|
|
194
|
-
*
|
|
195
|
-
* @param decorators - The array of decorator AST nodes to search, or `undefined` if none exist.
|
|
196
|
-
* @param name - The decorator function name to match against (e.g. 'Controller').
|
|
197
|
-
* @returns The string argument value, an empty string if no argument is provided, or `null` if the decorator is not found.
|
|
198
|
-
*/
|
|
199
|
-
function extractDecoratorArg(decorators, name) {
|
|
200
|
-
if (!decorators) return null;
|
|
201
|
-
for (const dec of decorators) {
|
|
202
|
-
const expr = dec.expression;
|
|
203
|
-
if (expr?.type === "CallExpression" && expr.callee?.name === name) {
|
|
204
|
-
const arg = expr.arguments?.[0];
|
|
205
|
-
if (arg?.type === "Literal" && typeof arg.value === "string") return arg.value;
|
|
206
|
-
return "";
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Normalizes a route path by removing duplicate slashes and ensuring
|
|
213
|
-
* the path starts with a single leading slash.
|
|
214
|
-
*
|
|
215
|
-
* Splits the path on '/' separators, filters out empty segments,
|
|
216
|
-
* and rejoins with single '/' separators.
|
|
217
|
-
*
|
|
218
|
-
* @param path - The raw concatenated path string to normalize.
|
|
219
|
-
* @returns A cleaned path string with a leading slash and no duplicate separators.
|
|
220
|
-
*/
|
|
221
|
-
function normalizePath(path) {
|
|
222
|
-
return "/" + path.split("/").filter(Boolean).join("/");
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Extracts named route parameters from a path string.
|
|
226
|
-
*
|
|
227
|
-
* Scans the path for segments prefixed with ':' using a pre-compiled
|
|
228
|
-
* regular expression and returns an array of parameter names with
|
|
229
|
-
* the ':' prefix stripped.
|
|
230
|
-
*
|
|
231
|
-
* Resets the regex `lastIndex` after each invocation to ensure
|
|
232
|
-
* consistent behavior across repeated calls.
|
|
233
|
-
*
|
|
234
|
-
* @param route - The normalized route path to scan for parameters.
|
|
235
|
-
* @returns An array of parameter name strings, empty if none are found.
|
|
236
|
-
*/
|
|
237
|
-
function extractParams(route) {
|
|
238
|
-
const params = [];
|
|
239
|
-
let match;
|
|
240
|
-
while (match = PARAM_RE.exec(route)) params.push(match[1]);
|
|
241
|
-
PARAM_RE.lastIndex = 0;
|
|
242
|
-
return params;
|
|
46
|
+
async function scanFiles$1(nitro, name) {
|
|
47
|
+
return scanFiles(nitro.options.scanDirs, name, nitro.logger);
|
|
243
48
|
}
|
|
244
49
|
//#endregion
|
|
245
50
|
//#region src/setup/Routes.ts
|
|
@@ -257,7 +62,7 @@ async function setupRoutes(nitro) {
|
|
|
257
62
|
handler: `@vercube/nitro/runtime/handler`,
|
|
258
63
|
lazy: true
|
|
259
64
|
})));
|
|
260
|
-
nitro.options.ignore = [
|
|
65
|
+
nitro.options.ignore = [.../* @__PURE__ */ new Set([...nitro.options.ignore ?? [], ...routes.map((route) => route.fullPath.replace(nitro.options.rootDir, "").replace(String(nitro.options?.serverDir), "").replace("src/", ""))])];
|
|
261
66
|
nitro.routing.sync();
|
|
262
67
|
}
|
|
263
68
|
/**
|
|
@@ -278,7 +83,7 @@ async function getTransformedRoutes(nitro) {
|
|
|
278
83
|
* @returns The file info of the routes
|
|
279
84
|
*/
|
|
280
85
|
async function scanRoutes(nitro) {
|
|
281
|
-
return Promise.all([scanFiles(nitro, nitro.options.apiDir || "api"), scanFiles(nitro, nitro.options.routesDir || "routes")]).then((r) => r.flat());
|
|
86
|
+
return Promise.all([scanFiles$1(nitro, nitro.options.apiDir || "api"), scanFiles$1(nitro, nitro.options.routesDir || "routes")]).then((r) => r.flat());
|
|
282
87
|
}
|
|
283
88
|
/**
|
|
284
89
|
* Clears the routes from the handlers
|
|
@@ -335,83 +140,6 @@ function setupHooks(nitro, options) {
|
|
|
335
140
|
});
|
|
336
141
|
}
|
|
337
142
|
//#endregion
|
|
338
|
-
//#region src/build/Middleware.ts
|
|
339
|
-
/**
|
|
340
|
-
* Internal module path used as a placeholder in generated import statements.
|
|
341
|
-
* Replaced with the actual file path during setup.
|
|
342
|
-
*/
|
|
343
|
-
const MIDDLEWARE_IMPORT_SOURCE = "#internal/vercube-middleware-source";
|
|
344
|
-
/**
|
|
345
|
-
* Reads a file from disk and returns all classes extending `BaseMiddleware` found within it.
|
|
346
|
-
*/
|
|
347
|
-
async function transformMiddleware(file) {
|
|
348
|
-
return extractMiddlewares(readFileSync(file.fullPath, "utf8")).map((m) => ({
|
|
349
|
-
...m,
|
|
350
|
-
...file
|
|
351
|
-
}));
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Extracts all class definitions that extend `BaseMiddleware` from the given source code.
|
|
355
|
-
*
|
|
356
|
-
* Parses the source using `oxc-parser` and traverses the AST to find classes
|
|
357
|
-
* whose `superClass` resolves to the identifier `BaseMiddleware`. Generates
|
|
358
|
-
* appropriate import statements based on whether the class is a default or named export.
|
|
359
|
-
*
|
|
360
|
-
* @param code - The raw TypeScript or JavaScript source code string to analyze.
|
|
361
|
-
* @returns An array of {@link MiddlewareInfo} objects for all discovered middleware classes.
|
|
362
|
-
*/
|
|
363
|
-
function extractMiddlewares(code) {
|
|
364
|
-
const ast = parseSync("file.ts", code).program;
|
|
365
|
-
const middlewares = [];
|
|
366
|
-
for (const node of ast.body) {
|
|
367
|
-
const classInfo = getClassNode$1(node);
|
|
368
|
-
if (!classInfo) continue;
|
|
369
|
-
const { classNode, isDefault } = classInfo;
|
|
370
|
-
if (!extendsBaseMiddleware(classNode)) continue;
|
|
371
|
-
const className = classNode.id?.name;
|
|
372
|
-
if (!className) continue;
|
|
373
|
-
const importStatement = isDefault ? `import ${className} from '${MIDDLEWARE_IMPORT_SOURCE}';` : `import { ${className} } from '${MIDDLEWARE_IMPORT_SOURCE}';`;
|
|
374
|
-
middlewares.push({
|
|
375
|
-
import: importStatement,
|
|
376
|
-
importClassName: className,
|
|
377
|
-
fullPath: "",
|
|
378
|
-
path: ""
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
return middlewares;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Returns true if the class node extends `BaseMiddleware`.
|
|
385
|
-
*/
|
|
386
|
-
function extendsBaseMiddleware(classNode) {
|
|
387
|
-
return classNode.superClass?.type === "Identifier" && classNode.superClass.name === "BaseMiddleware";
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Unwraps export declarations to retrieve the underlying class node,
|
|
391
|
-
* along with metadata about the export style.
|
|
392
|
-
*/
|
|
393
|
-
function getClassNode$1(node) {
|
|
394
|
-
if (node.type === "ExportDefaultDeclaration") {
|
|
395
|
-
const decl = node.declaration;
|
|
396
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
397
|
-
classNode: decl,
|
|
398
|
-
isDefault: true
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
if (node.type === "ExportNamedDeclaration") {
|
|
402
|
-
const decl = node.declaration;
|
|
403
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
404
|
-
classNode: decl,
|
|
405
|
-
isDefault: false
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
if (node?.type === "ClassDeclaration" || node?.type === "Class") return {
|
|
409
|
-
classNode: node,
|
|
410
|
-
isDefault: false
|
|
411
|
-
};
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
//#endregion
|
|
415
143
|
//#region src/setup/Middleware.ts
|
|
416
144
|
/**
|
|
417
145
|
* Gets the transformed middleware from the middleware directory.
|
|
@@ -428,95 +156,11 @@ async function getTransformedMiddlewares(nitro) {
|
|
|
428
156
|
* excludes those files from Nitro's native middleware handling.
|
|
429
157
|
*/
|
|
430
158
|
async function scanMiddlewares(nitro) {
|
|
431
|
-
const files = await scanFiles(nitro, "middleware");
|
|
432
|
-
nitro.options.ignore = [
|
|
159
|
+
const files = await scanFiles$1(nitro, "middleware");
|
|
160
|
+
nitro.options.ignore = [.../* @__PURE__ */ new Set([...nitro.options.ignore ?? [], ...files.map((file) => file.fullPath.replace(nitro.options.rootDir, "").replace(String(nitro.options?.serverDir), "").replace("src/", ""))])];
|
|
433
161
|
return files;
|
|
434
162
|
}
|
|
435
163
|
//#endregion
|
|
436
|
-
//#region src/build/Services.ts
|
|
437
|
-
/**
|
|
438
|
-
* Internal module path used as a placeholder in generated import statements.
|
|
439
|
-
* Replaced with the actual file path during setup.
|
|
440
|
-
*/
|
|
441
|
-
const SERVICE_IMPORT_SOURCE = "#internal/vercube-service-source";
|
|
442
|
-
/**
|
|
443
|
-
* Reads a file from disk and returns all `@Injectable`-decorated classes found within it.
|
|
444
|
-
*/
|
|
445
|
-
async function transformService(file) {
|
|
446
|
-
return extractServices(readFileSync(file.fullPath, "utf8")).map((s) => ({
|
|
447
|
-
...s,
|
|
448
|
-
...file
|
|
449
|
-
}));
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Extracts all `@Injectable`-decorated class definitions from the given source code.
|
|
453
|
-
*
|
|
454
|
-
* Parses the source using `oxc-parser` and traverses the AST to find classes
|
|
455
|
-
* annotated with `@Injectable()`. Generates appropriate import statements based
|
|
456
|
-
* on whether the class is a default or named export.
|
|
457
|
-
*
|
|
458
|
-
* @param code - The raw TypeScript or JavaScript source code string to analyze.
|
|
459
|
-
* @returns An array of {@link ServiceInfo} objects for all discovered injectable classes.
|
|
460
|
-
*/
|
|
461
|
-
function extractServices(code) {
|
|
462
|
-
const ast = parseSync("file.ts", code).program;
|
|
463
|
-
const services = [];
|
|
464
|
-
for (const node of ast.body) {
|
|
465
|
-
const classInfo = getClassNode(node);
|
|
466
|
-
if (!classInfo) continue;
|
|
467
|
-
const { classNode, isDefault } = classInfo;
|
|
468
|
-
if (!hasDecorator(classNode.decorators, "Injectable")) continue;
|
|
469
|
-
const className = classNode.id?.name;
|
|
470
|
-
if (!className) continue;
|
|
471
|
-
const importStatement = isDefault ? `import ${className} from '${SERVICE_IMPORT_SOURCE}';` : `import { ${className} } from '${SERVICE_IMPORT_SOURCE}';`;
|
|
472
|
-
services.push({
|
|
473
|
-
import: importStatement,
|
|
474
|
-
importClassName: className,
|
|
475
|
-
fullPath: "",
|
|
476
|
-
path: ""
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
return services;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Unwraps export declarations to retrieve the underlying class node,
|
|
483
|
-
* along with metadata about the export style.
|
|
484
|
-
*/
|
|
485
|
-
function getClassNode(node) {
|
|
486
|
-
if (node.type === "ExportDefaultDeclaration") {
|
|
487
|
-
const decl = node.declaration;
|
|
488
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
489
|
-
classNode: decl,
|
|
490
|
-
isDefault: true
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
if (node.type === "ExportNamedDeclaration") {
|
|
494
|
-
const decl = node.declaration;
|
|
495
|
-
if (decl?.type === "ClassDeclaration" || decl?.type === "Class") return {
|
|
496
|
-
classNode: decl,
|
|
497
|
-
isDefault: false
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
if (node?.type === "ClassDeclaration" || node?.type === "Class") return {
|
|
501
|
-
classNode: node,
|
|
502
|
-
isDefault: false
|
|
503
|
-
};
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Returns true if the given decorators array contains a decorator matching `name`.
|
|
508
|
-
* Supports both `@Name` identifier and `@Name()` call expression styles.
|
|
509
|
-
*/
|
|
510
|
-
function hasDecorator(decorators, name) {
|
|
511
|
-
if (!decorators) return false;
|
|
512
|
-
for (const dec of decorators) {
|
|
513
|
-
const expr = dec.expression;
|
|
514
|
-
if (expr?.type === "CallExpression" && expr.callee?.name === name) return true;
|
|
515
|
-
if (expr?.type === "Identifier" && expr.name === name) return true;
|
|
516
|
-
}
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
|
-
//#endregion
|
|
520
164
|
//#region src/setup/Services.ts
|
|
521
165
|
/**
|
|
522
166
|
* Scans the project source directory for `@Injectable`-decorated classes and returns
|
|
@@ -535,7 +179,7 @@ async function getTransformedServices(nitro, scanDirs) {
|
|
|
535
179
|
* from `.` to cover all subdirectories without adding an extra path segment.
|
|
536
180
|
*/
|
|
537
181
|
async function scanServices(nitro, scanDirs) {
|
|
538
|
-
return Promise.all(scanDirs?.map((dir) => scanFiles(nitro, dir)) ?? [scanFiles(nitro, ".")]).then((r) => r.flat());
|
|
182
|
+
return Promise.all(scanDirs?.map((dir) => scanFiles$1(nitro, dir)) ?? [scanFiles$1(nitro, ".")]).then((r) => r.flat());
|
|
539
183
|
}
|
|
540
184
|
//#endregion
|
|
541
185
|
//#region src/plugin/VercubePlugin.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercube/nitro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Nitro module for Vercube framework",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
"chokidar": "5.0.0",
|
|
32
32
|
"defu": "6.1.7",
|
|
33
33
|
"nitro": "3.0.260610-beta",
|
|
34
|
-
"oxc-parser": "0.
|
|
34
|
+
"oxc-parser": "0.138.0",
|
|
35
35
|
"pathe": "2.0.3",
|
|
36
36
|
"tinyglobby": "0.2.17",
|
|
37
|
-
"@vercube/core": "1.
|
|
38
|
-
"@vercube/
|
|
39
|
-
"@vercube/di": "1.
|
|
37
|
+
"@vercube/core": "1.1.0",
|
|
38
|
+
"@vercube/scan": "1.1.0",
|
|
39
|
+
"@vercube/di": "1.1.0",
|
|
40
|
+
"@vercube/storage": "1.1.0"
|
|
40
41
|
},
|
|
41
42
|
"publishConfig": {
|
|
42
43
|
"access": "public"
|