@martel/calyx 0.1.0 → 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/.github/workflows/release.yml +6 -2
- package/CHANGELOG.md +13 -0
- package/README.md +130 -0
- package/benchmarks/di-benchmark.ts +15 -15
- package/benchmarks/run-calyx-lifecycle.ts +2 -2
- package/benchmarks/run-calyx.ts +2 -2
- package/bun.lock +1261 -0
- package/docs/controllers.md +2 -2
- package/docs/dependency-injection.md +1 -1
- package/docs/lifecycle.md +2 -2
- package/docs/migration.md +5 -5
- package/package.json +9 -5
- package/src/cli/index.ts +323 -0
- package/src/core/container.ts +252 -27
- package/src/core/decorators.ts +64 -10
- package/src/core/index.ts +1 -0
- package/src/core/metadata.ts +13 -0
- package/src/core/module-ref.ts +3 -1
- package/src/core/reflector.ts +32 -0
- package/src/http/application.ts +323 -154
- package/src/http/decorators.ts +29 -8
- package/src/http/factory.ts +4 -4
- package/src/http/router.ts +12 -0
- package/src/lifecycle/context.ts +2 -2
- package/src/lifecycle/interfaces.ts +20 -0
- package/tests/cli.test.ts +93 -0
- package/tests/di.test.ts +11 -11
- package/tests/dynamic-module.test.ts +2 -2
- package/tests/lifecycle.test.ts +4 -4
- package/tests/phase1.test.ts +143 -0
- package/tests/phase2.test.ts +107 -0
- package/tests/phase3.test.ts +203 -0
- package/tests/phase5.test.ts +73 -0
- package/tests/routing.test.ts +4 -4
package/docs/controllers.md
CHANGED
|
@@ -47,7 +47,7 @@ Method arguments can extract parts of the request payload using parameter decora
|
|
|
47
47
|
| Decorator | NestJS Match | Description |
|
|
48
48
|
| :--- | :--- | :--- |
|
|
49
49
|
| `@Req()` / `@Request()` | `req` | Exposes the raw Bun `Request` object. |
|
|
50
|
-
| `@Res()` / `@Response()` | `res` | Exposes the `
|
|
50
|
+
| `@Res()` / `@Response()` | `res` | Exposes the `CalyxResponse` wrapper for manual responses. |
|
|
51
51
|
| `@Param(key?: string)` | `params[key]` | Extracts path parameters (e.g. `@Param('id')`). |
|
|
52
52
|
| `@Query(key?: string)` | `query[key]` | Extracts query parameters (e.g. `@Query('limit')`). |
|
|
53
53
|
| `@Body(key?: string)` | `body[key]` | Extracts request body payload (parsed JSON/urlencoded). |
|
|
@@ -104,7 +104,7 @@ legacy() {}
|
|
|
104
104
|
If you need full control over the response, inject `@Res()`:
|
|
105
105
|
```typescript
|
|
106
106
|
@Get('manual')
|
|
107
|
-
manualResponse(@Res() res:
|
|
107
|
+
manualResponse(@Res() res: CalyxResponse) {
|
|
108
108
|
res.status(202).set('X-Manual', 'true').json({ success: true });
|
|
109
109
|
}
|
|
110
110
|
```
|
|
@@ -7,7 +7,7 @@ calyx features a native, compile-safe, high-performance Dependency Injection (DI
|
|
|
7
7
|
The DI system is composed of:
|
|
8
8
|
1. **Modules (`@Module`)**: Encapsulated scopes organizing providers and controllers.
|
|
9
9
|
2. **Providers (`@Injectable`)**: Classes, factories, or static values that can be injected into constructor functions.
|
|
10
|
-
3. **Container (`
|
|
10
|
+
3. **Container (`CalyxContainer`)**: The core registry that parses metadata, detects circular dependencies, and instantiates services.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
package/docs/lifecycle.md
CHANGED
|
@@ -135,14 +135,14 @@ square(@Query('num', ParseIntPipe) num: number) {
|
|
|
135
135
|
Filters catch unhandled exceptions thrown during request execution. Use `@Catch(...Exceptions)` to declare which exceptions the filter catches.
|
|
136
136
|
|
|
137
137
|
```typescript
|
|
138
|
-
import { ExceptionFilter, Catch, ArgumentsHost, UseFilters,
|
|
138
|
+
import { ExceptionFilter, Catch, ArgumentsHost, UseFilters, CalyxResponse } from '@martel/calyx';
|
|
139
139
|
|
|
140
140
|
class CustomError extends Error {}
|
|
141
141
|
|
|
142
142
|
@Catch(CustomError)
|
|
143
143
|
class TeapotFilter implements ExceptionFilter {
|
|
144
144
|
catch(exception: CustomError, host: ArgumentsHost) {
|
|
145
|
-
const res = host.switchToHttp().getResponse<
|
|
145
|
+
const res = host.switchToHttp().getResponse<CalyxResponse>();
|
|
146
146
|
res.status(418).json({
|
|
147
147
|
error: 'Teapot',
|
|
148
148
|
message: exception.message,
|
package/docs/migration.md
CHANGED
|
@@ -47,14 +47,14 @@ import { Module, Injectable, Controller, Get, Param } from '@nestjs/common';
|
|
|
47
47
|
import { NestFactory } from '@nestjs/core';
|
|
48
48
|
|
|
49
49
|
// AFTER (calyx)
|
|
50
|
-
import { Module, Injectable, Controller, Get, Param,
|
|
50
|
+
import { Module, Injectable, Controller, Get, Param, CalyxFactory } from '@martel/calyx';
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
55
|
## 3. Bootstrap Changes
|
|
56
56
|
|
|
57
|
-
In NestJS, applications are bootstrapped using `NestFactory.create`. In calyx, use `
|
|
57
|
+
In NestJS, applications are bootstrapped using `NestFactory.create`. In calyx, use `CalyxFactory.create` which returns a `CalyxApplication` instance backed by Bun's native HTTP server:
|
|
58
58
|
|
|
59
59
|
```typescript
|
|
60
60
|
// BEFORE (NestJS)
|
|
@@ -68,11 +68,11 @@ async function bootstrap() {
|
|
|
68
68
|
bootstrap();
|
|
69
69
|
|
|
70
70
|
// AFTER (calyx)
|
|
71
|
-
import {
|
|
71
|
+
import { CalyxFactory } from '@martel/calyx';
|
|
72
72
|
import { AppModule } from './app.module';
|
|
73
73
|
|
|
74
74
|
async function bootstrap() {
|
|
75
|
-
const app = await
|
|
75
|
+
const app = await CalyxFactory.create(AppModule);
|
|
76
76
|
await app.listen(3000);
|
|
77
77
|
}
|
|
78
78
|
bootstrap();
|
|
@@ -89,7 +89,7 @@ calyx implements identical decorators and interfaces for lifecycle elements:
|
|
|
89
89
|
* *Note: In NestJS, `next.handle()` returns an RxJS `Observable`. In calyx, it returns a standard native `Promise` or an `Observable` based on developer preference. If migrating RxJS interceptors, you can map the result or rewrite to simple async-await.*
|
|
90
90
|
* **Pipes**: Change `import { PipeTransform } from '@nestjs/common'` to `@martel/calyx`.
|
|
91
91
|
* **Exception Filters**: Change `import { ExceptionFilter, Catch } from '@nestjs/common'` to `@martel/calyx`.
|
|
92
|
-
* *Note: Expose `host.switchToHttp().getResponse()` which returns calyx's Express-compatible `
|
|
92
|
+
* *Note: Expose `host.switchToHttp().getResponse()` which returns calyx's Express-compatible `CalyxResponse` wrapper to make calls like `res.status(code).json(body)` compatible without modifications.*
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@martel/calyx",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "High-performance Bun-native NestJS-compatible framework",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
|
+
"bin": {
|
|
7
|
+
"calyx": "./src/cli/index.ts"
|
|
8
|
+
},
|
|
6
9
|
"type": "module",
|
|
7
10
|
"scripts": {
|
|
8
11
|
"test": "bun test",
|
|
@@ -19,13 +22,14 @@
|
|
|
19
22
|
"@types/bun": "latest",
|
|
20
23
|
"autocannon": "^8.0.0",
|
|
21
24
|
"rxjs": "^7.8.2",
|
|
22
|
-
"semantic-release": "^
|
|
25
|
+
"semantic-release": "^25.0.0",
|
|
23
26
|
"@semantic-release/changelog": "^6.0.3",
|
|
24
27
|
"@semantic-release/git": "^10.0.1",
|
|
25
|
-
"@semantic-release/github": "^
|
|
26
|
-
"@semantic-release/npm": "^
|
|
28
|
+
"@semantic-release/github": "^12.0.0",
|
|
29
|
+
"@semantic-release/npm": "^13.0.0"
|
|
27
30
|
},
|
|
28
31
|
"publishConfig": {
|
|
29
|
-
"access": "public"
|
|
32
|
+
"access": "public",
|
|
33
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
34
|
}
|
|
31
35
|
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
|
|
6
|
+
const args = Bun.argv.slice(2);
|
|
7
|
+
const command = args[0];
|
|
8
|
+
|
|
9
|
+
if (!command) {
|
|
10
|
+
printHelp();
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
switch (command) {
|
|
15
|
+
case 'info':
|
|
16
|
+
runInfo();
|
|
17
|
+
break;
|
|
18
|
+
case 'start':
|
|
19
|
+
runStart(args.slice(1));
|
|
20
|
+
break;
|
|
21
|
+
case 'build':
|
|
22
|
+
runBuild(args.slice(1));
|
|
23
|
+
break;
|
|
24
|
+
case 'new':
|
|
25
|
+
runNew(args[1]);
|
|
26
|
+
break;
|
|
27
|
+
case 'generate':
|
|
28
|
+
case 'g':
|
|
29
|
+
runGenerate(args[1], args[2]);
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
console.log(`Unknown command: ${command}`);
|
|
33
|
+
printHelp();
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`
|
|
39
|
+
Calyx CLI - Bun-native NestJS-compatible Framework CLI
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
calyx <command> [options]
|
|
43
|
+
|
|
44
|
+
Commands:
|
|
45
|
+
new <name> Scaffold a new Calyx application
|
|
46
|
+
generate, g <type> <name> Generate a new schematic element (module, controller, service)
|
|
47
|
+
start Start the application
|
|
48
|
+
-w, --watch Enable hot reload / watch mode
|
|
49
|
+
build Bundle the application using Bun compiler
|
|
50
|
+
info Display system and environment details
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runInfo() {
|
|
55
|
+
console.log(`
|
|
56
|
+
Calyx CLI Information:
|
|
57
|
+
Calyx Version: 0.1.0
|
|
58
|
+
Bun Version: ${Bun.version}
|
|
59
|
+
OS Platform: ${process.platform}
|
|
60
|
+
Node Compatibility: Yes
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function runStart(cmdArgs: string[]) {
|
|
65
|
+
const isWatch = cmdArgs.includes('--watch') || cmdArgs.includes('-w');
|
|
66
|
+
const mainPath = 'src/main.ts';
|
|
67
|
+
|
|
68
|
+
if (!existsSync(mainPath)) {
|
|
69
|
+
console.error(`Error: Cannot find entrypoint "${mainPath}". Make sure you are in a Calyx project root.`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const spawnArgs = isWatch ? ['--watch', mainPath] : [mainPath];
|
|
74
|
+
console.log(`Starting Calyx application... (${isWatch ? 'Watch Mode' : 'Standard Mode'})`);
|
|
75
|
+
|
|
76
|
+
const proc = spawnSync('bun', spawnArgs, { stdio: 'inherit' });
|
|
77
|
+
process.exit(proc.status ?? 0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function runBuild(cmdArgs: string[]) {
|
|
81
|
+
const mainPath = 'src/main.ts';
|
|
82
|
+
if (!existsSync(mainPath)) {
|
|
83
|
+
console.error(`Error: Cannot find entrypoint "${mainPath}". Make sure you are in a Calyx project root.`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('Building Calyx application using bun build...');
|
|
88
|
+
const proc = spawnSync('bun', ['build', mainPath, '--outdir', './dist', '--target', 'bun'], { stdio: 'inherit' });
|
|
89
|
+
if (proc.status === 0) {
|
|
90
|
+
console.log('Build completed successfully. Output at ./dist/main.js');
|
|
91
|
+
}
|
|
92
|
+
process.exit(proc.status ?? 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function runNew(name: string) {
|
|
96
|
+
if (!name) {
|
|
97
|
+
console.error('Error: Please specify the project name. Example: calyx new my-app');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (existsSync(name)) {
|
|
102
|
+
console.error(`Error: Directory "${name}" already exists.`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(`Scaffolding new Calyx application in "${name}"...`);
|
|
107
|
+
|
|
108
|
+
mkdirSync(name, { recursive: true });
|
|
109
|
+
mkdirSync(join(name, 'src'), { recursive: true });
|
|
110
|
+
|
|
111
|
+
// Write package.json
|
|
112
|
+
const packageJson = {
|
|
113
|
+
name,
|
|
114
|
+
version: '0.0.1',
|
|
115
|
+
description: 'Calyx application',
|
|
116
|
+
type: 'module',
|
|
117
|
+
scripts: {
|
|
118
|
+
"start": "calyx start",
|
|
119
|
+
"start:dev": "calyx start --watch",
|
|
120
|
+
"build": "calyx build"
|
|
121
|
+
},
|
|
122
|
+
dependencies: {
|
|
123
|
+
"@martel/calyx": "link:../..", // link back to Calyx package parent root for local tests
|
|
124
|
+
"reflect-metadata": "^0.2.2"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
writeFileSync(join(name, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
128
|
+
|
|
129
|
+
// Write tsconfig.json
|
|
130
|
+
const tsconfigJson = {
|
|
131
|
+
compilerOptions: {
|
|
132
|
+
module: "ESNext",
|
|
133
|
+
target: "ESNext",
|
|
134
|
+
moduleResolution: "bundler",
|
|
135
|
+
esModuleInterop: true,
|
|
136
|
+
experimentalDecorators: true,
|
|
137
|
+
emitDecoratorMetadata: true,
|
|
138
|
+
strict: true,
|
|
139
|
+
skipLibCheck: true
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
writeFileSync(join(name, 'tsconfig.json'), JSON.stringify(tsconfigJson, null, 2));
|
|
143
|
+
|
|
144
|
+
// Write src/app.service.ts
|
|
145
|
+
const appService = `import { Injectable } from '@martel/calyx';
|
|
146
|
+
|
|
147
|
+
@Injectable()
|
|
148
|
+
export class AppService {
|
|
149
|
+
getHello(): string {
|
|
150
|
+
return 'Hello World!';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
writeFileSync(join(name, 'src/app.service.ts'), appService);
|
|
155
|
+
|
|
156
|
+
// Write src/app.controller.ts
|
|
157
|
+
const appController = `import { Controller, Get } from '@martel/calyx';
|
|
158
|
+
import { AppService } from './app.service';
|
|
159
|
+
|
|
160
|
+
@Controller()
|
|
161
|
+
export class AppController {
|
|
162
|
+
constructor(private readonly appService: AppService) {}
|
|
163
|
+
|
|
164
|
+
@Get()
|
|
165
|
+
getHello(): string {
|
|
166
|
+
return this.appService.getHello();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
writeFileSync(join(name, 'src/app.controller.ts'), appController);
|
|
171
|
+
|
|
172
|
+
// Write src/app.module.ts
|
|
173
|
+
const appModule = `import { Module } from '@martel/calyx';
|
|
174
|
+
import { AppController } from './app.controller';
|
|
175
|
+
import { AppService } from './app.service';
|
|
176
|
+
|
|
177
|
+
@Module({
|
|
178
|
+
controllers: [AppController],
|
|
179
|
+
providers: [AppService],
|
|
180
|
+
})
|
|
181
|
+
export class AppModule {}
|
|
182
|
+
`;
|
|
183
|
+
writeFileSync(join(name, 'src/app.module.ts'), appModule);
|
|
184
|
+
|
|
185
|
+
// Write src/main.ts
|
|
186
|
+
const mainTs = `import 'reflect-metadata';
|
|
187
|
+
import { CalyxFactory } from '@martel/calyx';
|
|
188
|
+
import { AppModule } from './app.module';
|
|
189
|
+
|
|
190
|
+
async function bootstrap() {
|
|
191
|
+
const app = await CalyxFactory.create(AppModule);
|
|
192
|
+
await app.listen(3000);
|
|
193
|
+
console.log('Application is running on http://localhost:3000');
|
|
194
|
+
}
|
|
195
|
+
bootstrap();
|
|
196
|
+
`;
|
|
197
|
+
writeFileSync(join(name, 'src/main.ts'), mainTs);
|
|
198
|
+
|
|
199
|
+
console.log('Installing dependencies...');
|
|
200
|
+
spawnSync('bun', ['install'], { cwd: name, stdio: 'inherit' });
|
|
201
|
+
|
|
202
|
+
console.log(`
|
|
203
|
+
Calyx application successfully created!
|
|
204
|
+
|
|
205
|
+
To start running your app:
|
|
206
|
+
cd ${name}
|
|
207
|
+
bun run start:dev
|
|
208
|
+
`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function runGenerate(schematic: string, rawName: string) {
|
|
212
|
+
if (!schematic || !rawName) {
|
|
213
|
+
console.error('Error: Please specify schematic type and name. Example: calyx g controller users');
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const name = rawName.toLowerCase();
|
|
218
|
+
const pascalName = rawName.charAt(0).toUpperCase() + rawName.slice(1);
|
|
219
|
+
const type = schematic.toLowerCase();
|
|
220
|
+
|
|
221
|
+
const srcDir = existsSync('src') ? 'src' : '.';
|
|
222
|
+
const targetDir = join(srcDir, name);
|
|
223
|
+
|
|
224
|
+
if (!existsSync(targetDir)) {
|
|
225
|
+
mkdirSync(targetDir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
switch (type) {
|
|
229
|
+
case 'module': {
|
|
230
|
+
const filePath = join(targetDir, `${name}.module.ts`);
|
|
231
|
+
const content = `import { Module } from '@martel/calyx';
|
|
232
|
+
|
|
233
|
+
@Module({})
|
|
234
|
+
export class ${pascalName}Module {}
|
|
235
|
+
`;
|
|
236
|
+
writeFileSync(filePath, content);
|
|
237
|
+
console.log(`CREATE ${filePath}`);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case 'controller': {
|
|
241
|
+
const filePath = join(targetDir, `${name}.controller.ts`);
|
|
242
|
+
const content = `import { Controller, Get } from '@martel/calyx';
|
|
243
|
+
|
|
244
|
+
@Controller('${name}')
|
|
245
|
+
export class ${pascalName}Controller {
|
|
246
|
+
@Get()
|
|
247
|
+
findAll() {
|
|
248
|
+
return 'This action returns all ${name}';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
writeFileSync(filePath, content);
|
|
253
|
+
console.log(`CREATE ${filePath}`);
|
|
254
|
+
autoRegisterInModule(name, pascalName, 'controller');
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case 'service': {
|
|
258
|
+
const filePath = join(targetDir, `${name}.service.ts`);
|
|
259
|
+
const content = `import { Injectable } from '@martel/calyx';
|
|
260
|
+
|
|
261
|
+
@Injectable()
|
|
262
|
+
export class ${pascalName}Service {}
|
|
263
|
+
`;
|
|
264
|
+
writeFileSync(filePath, content);
|
|
265
|
+
console.log(`CREATE ${filePath}`);
|
|
266
|
+
autoRegisterInModule(name, pascalName, 'service');
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
default:
|
|
270
|
+
console.error(`Error: Unknown schematic type "${type}". Supported: module, controller, service`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function autoRegisterInModule(name: string, pascalName: string, type: 'controller' | 'service') {
|
|
276
|
+
const srcDir = existsSync('src') ? 'src' : '.';
|
|
277
|
+
const modulePath = join(srcDir, name, `${name}.module.ts`);
|
|
278
|
+
const rootModulePath = join(srcDir, `app.module.ts`);
|
|
279
|
+
|
|
280
|
+
const pathToCheck = existsSync(modulePath) ? modulePath : (existsSync(rootModulePath) ? rootModulePath : null);
|
|
281
|
+
if (!pathToCheck) return;
|
|
282
|
+
|
|
283
|
+
let content = readFileSync(pathToCheck, 'utf-8');
|
|
284
|
+
|
|
285
|
+
// Insert import statement
|
|
286
|
+
const importName = `${pascalName}${type === 'controller' ? 'Controller' : 'Service'}`;
|
|
287
|
+
const importRelativePath = pathToCheck === rootModulePath ? `./${name}/${name}.${type}` : `./${name}.${type}`;
|
|
288
|
+
const importStatement = `import { ${importName} } from '${importRelativePath}';\n`;
|
|
289
|
+
|
|
290
|
+
// Find last import line and insert after it
|
|
291
|
+
const lines = content.split('\n');
|
|
292
|
+
let lastImportIdx = -1;
|
|
293
|
+
for (let i = 0; i < lines.length; i++) {
|
|
294
|
+
if (lines[i].trim().startsWith('import ')) {
|
|
295
|
+
lastImportIdx = i;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
lines.splice(lastImportIdx + 1, 0, importStatement.trim());
|
|
299
|
+
content = lines.join('\n');
|
|
300
|
+
|
|
301
|
+
// Add item into metadata decorator
|
|
302
|
+
const arrayName = type === 'controller' ? 'controllers' : 'providers';
|
|
303
|
+
const arrayRegex = new RegExp(`(${arrayName}\\s*:\\s*\\[)([^\\]]*)(\\])`);
|
|
304
|
+
|
|
305
|
+
if (arrayRegex.test(content)) {
|
|
306
|
+
content = content.replace(arrayRegex, (match, prefix, list, suffix) => {
|
|
307
|
+
const trimmedList = list.trim();
|
|
308
|
+
const newList = trimmedList ? `${trimmedList}, ${importName}` : importName;
|
|
309
|
+
return `${prefix}${newList}${suffix}`;
|
|
310
|
+
});
|
|
311
|
+
} else {
|
|
312
|
+
// Add the metadata property if it doesn't exist
|
|
313
|
+
const decoratorRegex = /(@Module\(\s*\{)([\s\S]*?)(\}\s*\))/;
|
|
314
|
+
content = content.replace(decoratorRegex, (match, prefix, body, suffix) => {
|
|
315
|
+
const trimmedBody = body.trim();
|
|
316
|
+
const newBody = trimmedBody ? `${trimmedBody},\n ${arrayName}: [${importName}]` : ` ${arrayName}: [${importName}]`;
|
|
317
|
+
return `${prefix}\n${newBody}\n${suffix}`;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
writeFileSync(pathToCheck, content);
|
|
322
|
+
console.log(`UPDATE ${pathToCheck} (Registered ${importName})`);
|
|
323
|
+
}
|