fragment-ts 1.0.17 ā 1.0.18
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/SETUP.md +570 -0
- package/changes/1.md +420 -0
- package/dist/cli/commands/init.command.js +4 -4
- package/dist/cli/commands/init.command.js.map +1 -1
- package/dist/cli/commands/test.command.d.ts +6 -0
- package/dist/cli/commands/test.command.d.ts.map +1 -0
- package/dist/cli/commands/test.command.js +311 -0
- package/dist/cli/commands/test.command.js.map +1 -0
- package/dist/cli/index.js +6 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/core/container/di-container.d.ts +5 -0
- package/dist/core/container/di-container.d.ts.map +1 -1
- package/dist/core/container/di-container.js +121 -21
- package/dist/core/container/di-container.js.map +1 -1
- package/dist/core/decorators/controller.decorator.js +5 -5
- package/dist/core/decorators/injection.decorators.d.ts +44 -1
- package/dist/core/decorators/injection.decorators.d.ts.map +1 -1
- package/dist/core/decorators/injection.decorators.js +92 -1
- package/dist/core/decorators/injection.decorators.js.map +1 -1
- package/dist/core/metadata/metadata-keys.d.ts +29 -17
- package/dist/core/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/core/metadata/metadata-keys.js +35 -17
- package/dist/core/metadata/metadata-keys.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.command.ts +4 -4
- package/src/cli/commands/test.command.ts +289 -0
- package/src/cli/index.ts +16 -11
- package/src/core/container/di-container.ts +166 -31
- package/src/core/decorators/DECORATOR_USAGE.md +326 -0
- package/src/core/decorators/controller.decorator.ts +9 -9
- package/src/core/decorators/injection.decorators.ts +129 -5
- package/src/core/metadata/metadata-keys.ts +44 -18
- package/src/testing/TEST.md +321 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injection.decorators.d.ts","sourceRoot":"","sources":["../../../src/core/decorators/injection.decorators.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"injection.decorators.d.ts","sourceRoot":"","sources":["../../../src/core/decorators/injection.decorators.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,IAAI,iBAAiB,CAe7C;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,iBAAiB,CAIlE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,GAAG,iBAAiB,CASpE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAS3D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,iBAAiB,CAI5C;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,iBAAiB,CAwBxC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,eAAe,CAY/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAY5C"}
|
|
@@ -2,28 +2,119 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Autowired = Autowired;
|
|
4
4
|
exports.Inject = Inject;
|
|
5
|
+
exports.InjectRepository = InjectRepository;
|
|
5
6
|
exports.Qualifier = Qualifier;
|
|
6
7
|
exports.Value = Value;
|
|
8
|
+
exports.Optional = Optional;
|
|
9
|
+
exports.Lazy = Lazy;
|
|
10
|
+
exports.PostConstruct = PostConstruct;
|
|
11
|
+
exports.PreDestroy = PreDestroy;
|
|
7
12
|
const metadata_keys_1 = require("../metadata/metadata-keys");
|
|
13
|
+
/**
|
|
14
|
+
* @Autowired - Automatically inject dependencies by type
|
|
15
|
+
* Usage: @Autowired() private myService: MyService;
|
|
16
|
+
*/
|
|
8
17
|
function Autowired() {
|
|
9
18
|
return (target, propertyKey) => {
|
|
10
|
-
|
|
19
|
+
// Get the design type from TypeScript metadata
|
|
20
|
+
const type = Reflect.getMetadata("design:type", target, propertyKey);
|
|
21
|
+
if (!type || type === Object) {
|
|
22
|
+
throw new Error(`Cannot use @Autowired on property "${String(propertyKey)}" in ${target.constructor.name}. ` +
|
|
23
|
+
`Make sure TypeScript emitDecoratorMetadata is enabled and the type is explicitly declared.`);
|
|
24
|
+
}
|
|
25
|
+
// Store the type in metadata so DI container can resolve it
|
|
11
26
|
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.AUTOWIRED, type, target, propertyKey);
|
|
12
27
|
};
|
|
13
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* @Inject - Inject dependency by token (string or class)
|
|
31
|
+
* Usage: @Inject('MyService') private service: MyService;
|
|
32
|
+
* or: @Inject(MyService) private service: MyService;
|
|
33
|
+
*/
|
|
14
34
|
function Inject(token) {
|
|
15
35
|
return (target, propertyKey) => {
|
|
16
36
|
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.INJECT, token, target, propertyKey);
|
|
17
37
|
};
|
|
18
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* @InjectRepository - Inject TypeORM repository for an entity
|
|
41
|
+
* Usage: @InjectRepository(User) private userRepo: Repository<User>;
|
|
42
|
+
*/
|
|
43
|
+
function InjectRepository(entity) {
|
|
44
|
+
return (target, propertyKey) => {
|
|
45
|
+
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.INJECT_REPOSITORY, entity, target, propertyKey);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @Qualifier - Specify which bean to inject when multiple exist
|
|
50
|
+
* Usage: @Qualifier('primary') @Autowired() private service: MyService;
|
|
51
|
+
*/
|
|
19
52
|
function Qualifier(name) {
|
|
20
53
|
return (target, propertyKey) => {
|
|
21
54
|
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.QUALIFIER, name, target, propertyKey);
|
|
22
55
|
};
|
|
23
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* @Value - Inject configuration value from environment or config
|
|
59
|
+
* Usage: @Value('${PORT}') private port: number;
|
|
60
|
+
* or: @Value('${DB_HOST:localhost}') private host: string;
|
|
61
|
+
*/
|
|
24
62
|
function Value(expression) {
|
|
25
63
|
return (target, propertyKey) => {
|
|
26
64
|
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.VALUE, expression, target, propertyKey);
|
|
27
65
|
};
|
|
28
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* @Optional - Mark dependency as optional (won't throw if not found)
|
|
69
|
+
* Usage: @Optional() @Autowired() private service?: MyService;
|
|
70
|
+
*/
|
|
71
|
+
function Optional() {
|
|
72
|
+
return (target, propertyKey) => {
|
|
73
|
+
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.OPTIONAL, true, target, propertyKey);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @Lazy - Lazy load dependency (create on first access)
|
|
78
|
+
* Usage: @Lazy() @Autowired() private service: MyService;
|
|
79
|
+
*/
|
|
80
|
+
function Lazy() {
|
|
81
|
+
return (target, propertyKey) => {
|
|
82
|
+
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.LAZY, true, target, propertyKey);
|
|
83
|
+
const type = Reflect.getMetadata("design:type", target, propertyKey);
|
|
84
|
+
// Create a getter that resolves on first access
|
|
85
|
+
let cached = null;
|
|
86
|
+
let resolved = false;
|
|
87
|
+
Object.defineProperty(target, propertyKey, {
|
|
88
|
+
get() {
|
|
89
|
+
if (!resolved) {
|
|
90
|
+
const { DIContainer } = require("../container/di-container");
|
|
91
|
+
const container = DIContainer.getInstance();
|
|
92
|
+
cached = container.resolve(type);
|
|
93
|
+
resolved = true;
|
|
94
|
+
}
|
|
95
|
+
return cached;
|
|
96
|
+
},
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true,
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @PostConstruct - Method called after dependency injection is complete
|
|
104
|
+
* Usage: @PostConstruct() init() { ... }
|
|
105
|
+
*/
|
|
106
|
+
function PostConstruct() {
|
|
107
|
+
return (target, propertyKey, descriptor) => {
|
|
108
|
+
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.POST_CONSTRUCT, propertyKey, target.constructor);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @PreDestroy - Method called before bean is destroyed
|
|
113
|
+
* Usage: @PreDestroy() cleanup() { ... }
|
|
114
|
+
*/
|
|
115
|
+
function PreDestroy() {
|
|
116
|
+
return (target, propertyKey, descriptor) => {
|
|
117
|
+
Reflect.defineMetadata(metadata_keys_1.METADATA_KEYS.PRE_DESTROY, propertyKey, target.constructor);
|
|
118
|
+
};
|
|
119
|
+
}
|
|
29
120
|
//# sourceMappingURL=injection.decorators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injection.decorators.js","sourceRoot":"","sources":["../../../src/core/decorators/injection.decorators.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"injection.decorators.js","sourceRoot":"","sources":["../../../src/core/decorators/injection.decorators.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
export declare const METADATA_KEYS: {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
REPOSITORY:
|
|
7
|
-
AUTO_CONFIGURATION:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
readonly APPLICATION: "fragment:application";
|
|
3
|
+
readonly INJECTABLE: "fragment:injectable";
|
|
4
|
+
readonly CONTROLLER: "fragment:controller";
|
|
5
|
+
readonly SERVICE: "fragment:service";
|
|
6
|
+
readonly REPOSITORY: "fragment:repository";
|
|
7
|
+
readonly AUTO_CONFIGURATION: "fragment:auto-configuration";
|
|
8
|
+
readonly SCOPE: "fragment:scope";
|
|
9
|
+
readonly AUTOWIRED: "fragment:autowired";
|
|
10
|
+
readonly INJECT: "fragment:inject";
|
|
11
|
+
readonly INJECT_REPOSITORY: "fragment:inject-repository";
|
|
12
|
+
readonly QUALIFIER: "fragment:qualifier";
|
|
13
|
+
readonly VALUE: "fragment:value";
|
|
14
|
+
readonly OPTIONAL: "fragment:optional";
|
|
15
|
+
readonly LAZY: "fragment:lazy";
|
|
16
|
+
readonly HTTP_METHOD: "fragment:http-method";
|
|
17
|
+
readonly ROUTE_PATH: "fragment:route-path";
|
|
18
|
+
readonly POST_CONSTRUCT: "fragment:post-construct";
|
|
19
|
+
readonly PRE_DESTROY: "fragment:pre-destroy";
|
|
20
|
+
readonly PARAM_METADATA: "fragment:param-metadata";
|
|
21
|
+
readonly CONDITIONAL_ON_CLASS: "fragment:conditional-on-class";
|
|
22
|
+
readonly CONDITIONAL_ON_MISSING_BEAN: "fragment:conditional-on-missing-bean";
|
|
23
|
+
readonly CONDITIONAL_ON_PROPERTY: "fragment:conditional-on-property";
|
|
24
|
+
readonly CONDITIONAL_ON_BEAN: "fragment:conditional-on-bean";
|
|
25
|
+
readonly USE_GUARDS: "fragment:use-guards";
|
|
26
|
+
readonly USE_INTERCEPTORS: "fragment:use-interceptors";
|
|
27
|
+
readonly USE_FILTERS: "fragment:use-filters";
|
|
28
|
+
readonly VALIDATE: "fragment:validate";
|
|
29
|
+
readonly TRANSFORM: "fragment:transform";
|
|
19
30
|
};
|
|
31
|
+
export type MetadataKey = (typeof METADATA_KEYS)[keyof typeof METADATA_KEYS];
|
|
20
32
|
//# sourceMappingURL=metadata-keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata-keys.d.ts","sourceRoot":"","sources":["../../../src/core/metadata/metadata-keys.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"metadata-keys.d.ts","sourceRoot":"","sources":["../../../src/core/metadata/metadata-keys.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ChB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC"}
|
|
@@ -2,22 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.METADATA_KEYS = void 0;
|
|
4
4
|
exports.METADATA_KEYS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
CONTROLLER:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
AUTOWIRED:
|
|
15
|
-
INJECT:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
5
|
+
// Class decorators
|
|
6
|
+
APPLICATION: "fragment:application",
|
|
7
|
+
INJECTABLE: "fragment:injectable",
|
|
8
|
+
CONTROLLER: "fragment:controller",
|
|
9
|
+
SERVICE: "fragment:service",
|
|
10
|
+
REPOSITORY: "fragment:repository",
|
|
11
|
+
AUTO_CONFIGURATION: "fragment:auto-configuration",
|
|
12
|
+
SCOPE: "fragment:scope",
|
|
13
|
+
// Property decorators
|
|
14
|
+
AUTOWIRED: "fragment:autowired",
|
|
15
|
+
INJECT: "fragment:inject",
|
|
16
|
+
INJECT_REPOSITORY: "fragment:inject-repository",
|
|
17
|
+
QUALIFIER: "fragment:qualifier",
|
|
18
|
+
VALUE: "fragment:value",
|
|
19
|
+
OPTIONAL: "fragment:optional",
|
|
20
|
+
LAZY: "fragment:lazy",
|
|
21
|
+
// Method decorators
|
|
22
|
+
HTTP_METHOD: "fragment:http-method",
|
|
23
|
+
ROUTE_PATH: "fragment:route-path",
|
|
24
|
+
POST_CONSTRUCT: "fragment:post-construct",
|
|
25
|
+
PRE_DESTROY: "fragment:pre-destroy",
|
|
26
|
+
// Parameter decorators
|
|
27
|
+
PARAM_METADATA: "fragment:param-metadata",
|
|
28
|
+
// Conditional decorators
|
|
29
|
+
CONDITIONAL_ON_CLASS: "fragment:conditional-on-class",
|
|
30
|
+
CONDITIONAL_ON_MISSING_BEAN: "fragment:conditional-on-missing-bean",
|
|
31
|
+
CONDITIONAL_ON_PROPERTY: "fragment:conditional-on-property",
|
|
32
|
+
CONDITIONAL_ON_BEAN: "fragment:conditional-on-bean",
|
|
33
|
+
// Middleware & Guards
|
|
34
|
+
USE_GUARDS: "fragment:use-guards",
|
|
35
|
+
USE_INTERCEPTORS: "fragment:use-interceptors",
|
|
36
|
+
USE_FILTERS: "fragment:use-filters",
|
|
37
|
+
// Validation
|
|
38
|
+
VALIDATE: "fragment:validate",
|
|
39
|
+
TRANSFORM: "fragment:transform",
|
|
22
40
|
};
|
|
23
41
|
//# sourceMappingURL=metadata-keys.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata-keys.js","sourceRoot":"","sources":["../../../src/core/metadata/metadata-keys.ts"],"names":[],"mappings":";;;AAAa,QAAA,aAAa,GAAG;IAC3B,UAAU,EAAE,qBAAqB;IACjC,
|
|
1
|
+
{"version":3,"file":"metadata-keys.js","sourceRoot":"","sources":["../../../src/core/metadata/metadata-keys.ts"],"names":[],"mappings":";;;AAAa,QAAA,aAAa,GAAG;IAC3B,mBAAmB;IACnB,WAAW,EAAE,sBAAsB;IACnC,UAAU,EAAE,qBAAqB;IACjC,UAAU,EAAE,qBAAqB;IACjC,OAAO,EAAE,kBAAkB;IAC3B,UAAU,EAAE,qBAAqB;IACjC,kBAAkB,EAAE,6BAA6B;IACjD,KAAK,EAAE,gBAAgB;IAEvB,sBAAsB;IACtB,SAAS,EAAE,oBAAoB;IAC/B,MAAM,EAAE,iBAAiB;IACzB,iBAAiB,EAAE,4BAA4B;IAC/C,SAAS,EAAE,oBAAoB;IAC/B,KAAK,EAAE,gBAAgB;IACvB,QAAQ,EAAE,mBAAmB;IAC7B,IAAI,EAAE,eAAe;IAErB,oBAAoB;IACpB,WAAW,EAAE,sBAAsB;IACnC,UAAU,EAAE,qBAAqB;IACjC,cAAc,EAAE,yBAAyB;IACzC,WAAW,EAAE,sBAAsB;IAEnC,uBAAuB;IACvB,cAAc,EAAE,yBAAyB;IAEzC,yBAAyB;IACzB,oBAAoB,EAAE,+BAA+B;IACrD,2BAA2B,EAAE,sCAAsC;IACnE,uBAAuB,EAAE,kCAAkC;IAC3D,mBAAmB,EAAE,8BAA8B;IAEnD,sBAAsB;IACtB,UAAU,EAAE,qBAAqB;IACjC,gBAAgB,EAAE,2BAA2B;IAC7C,WAAW,EAAE,sBAAsB;IAEnC,aAAa;IACb,QAAQ,EAAE,mBAAmB;IAC7B,SAAS,EAAE,oBAAoB;CACvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -169,12 +169,14 @@ export class InitCommand {
|
|
|
169
169
|
"migrate:revert": "fragment migrate:revert",
|
|
170
170
|
},
|
|
171
171
|
dependencies: {
|
|
172
|
-
"fragment-ts": "^1.0.
|
|
172
|
+
"fragment-ts": "^1.0.18",
|
|
173
173
|
"reflect-metadata": "^0.1.13",
|
|
174
174
|
},
|
|
175
175
|
devDependencies: {
|
|
176
176
|
typescript: "^5.3.3",
|
|
177
177
|
"@types/node": "^20.10.5",
|
|
178
|
+
"ts-node": "^10.9.2",
|
|
179
|
+
"tsconfig-paths": "^4.2.0",
|
|
178
180
|
},
|
|
179
181
|
};
|
|
180
182
|
|
|
@@ -214,9 +216,7 @@ export class InitCommand {
|
|
|
214
216
|
database: process.env.DATABASE_FILE || "database.sqlite",
|
|
215
217
|
synchronize: true,
|
|
216
218
|
logging: false,
|
|
217
|
-
entities: isProd
|
|
218
|
-
? ["dist/**/*.entity.js"]
|
|
219
|
-
: ["src/**/*.entity.ts"],
|
|
219
|
+
entities: isProd ? ["dist/**/*.entity.js"] : ["src/**/*.entity.ts"],
|
|
220
220
|
migrations: isProd
|
|
221
221
|
? ["dist/migrations/**/*.js"]
|
|
222
222
|
: ["src/migrations/**/*.ts"],
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
|
|
7
|
+
export class TestCommand {
|
|
8
|
+
static register(program: Command): void {
|
|
9
|
+
program
|
|
10
|
+
.command("test")
|
|
11
|
+
.description("Run tests")
|
|
12
|
+
.option("--watch", "Run tests in watch mode")
|
|
13
|
+
.option(
|
|
14
|
+
"--env <environment>",
|
|
15
|
+
"Environment mode: dev (use src/) or prod (use dist/)",
|
|
16
|
+
"auto",
|
|
17
|
+
)
|
|
18
|
+
.option("--pattern <pattern>", "Test file pattern", "**/*.spec.ts")
|
|
19
|
+
.option("--coverage", "Generate coverage report")
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
await this.runTests(options);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private static async runTests(options: any): Promise<void> {
|
|
26
|
+
console.log(chalk.blue("\nš§Ŗ Running Fragment Tests...\n"));
|
|
27
|
+
|
|
28
|
+
const cwd = process.cwd();
|
|
29
|
+
const hasSource = fs.existsSync(path.join(cwd, "src"));
|
|
30
|
+
const hasDist = fs.existsSync(path.join(cwd, "dist"));
|
|
31
|
+
|
|
32
|
+
let useTypeScript: boolean;
|
|
33
|
+
let mode: string;
|
|
34
|
+
let basePath: string;
|
|
35
|
+
let pattern: string;
|
|
36
|
+
|
|
37
|
+
if (options.env === "dev") {
|
|
38
|
+
if (!hasSource) {
|
|
39
|
+
console.log(
|
|
40
|
+
chalk.red("Development mode requested but src/ directory not found"),
|
|
41
|
+
);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
useTypeScript = true;
|
|
45
|
+
mode = "development (src/)";
|
|
46
|
+
basePath = "src";
|
|
47
|
+
pattern = options.pattern || "**/*.spec.ts";
|
|
48
|
+
} else if (options.env === "prod") {
|
|
49
|
+
if (!hasDist) {
|
|
50
|
+
console.log(
|
|
51
|
+
chalk.red(
|
|
52
|
+
"Production mode requested but dist/ directory not found. Run: fragment build",
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
useTypeScript = false;
|
|
58
|
+
mode = "production (dist/)";
|
|
59
|
+
basePath = "dist";
|
|
60
|
+
pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
|
|
61
|
+
} else {
|
|
62
|
+
// Auto-detect
|
|
63
|
+
if (hasSource) {
|
|
64
|
+
useTypeScript = true;
|
|
65
|
+
mode = "auto-detected development (src/)";
|
|
66
|
+
basePath = "src";
|
|
67
|
+
pattern = options.pattern || "**/*.spec.ts";
|
|
68
|
+
} else if (hasDist) {
|
|
69
|
+
useTypeScript = false;
|
|
70
|
+
mode = "auto-detected production (dist/)";
|
|
71
|
+
basePath = "dist";
|
|
72
|
+
pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
|
|
73
|
+
} else {
|
|
74
|
+
console.log(
|
|
75
|
+
chalk.red("No src/ or dist/ directory found. Run: fragment init"),
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(chalk.gray(` Mode: ${mode}\n`));
|
|
82
|
+
|
|
83
|
+
const scriptContent = `
|
|
84
|
+
${useTypeScript ? "require('ts-node/register/transpile-only');" : ""}
|
|
85
|
+
require('reflect-metadata');
|
|
86
|
+
|
|
87
|
+
const { glob } = require('glob');
|
|
88
|
+
const path = require('path');
|
|
89
|
+
|
|
90
|
+
// Global test state
|
|
91
|
+
const testState = {
|
|
92
|
+
suites: [],
|
|
93
|
+
currentSuite: null,
|
|
94
|
+
passed: 0,
|
|
95
|
+
failed: 0,
|
|
96
|
+
errors: []
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Global test functions
|
|
100
|
+
global.describe = function(name, fn) {
|
|
101
|
+
const suite = { name, tests: [] };
|
|
102
|
+
testState.suites.push(suite);
|
|
103
|
+
testState.currentSuite = suite;
|
|
104
|
+
fn();
|
|
105
|
+
testState.currentSuite = null;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
global.it = function(name, fn) {
|
|
109
|
+
if (!testState.currentSuite) {
|
|
110
|
+
throw new Error('it() must be called inside describe()');
|
|
111
|
+
}
|
|
112
|
+
testState.currentSuite.tests.push({ name, fn });
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
global.expect = function(actual) {
|
|
116
|
+
return {
|
|
117
|
+
toBe(expected) {
|
|
118
|
+
if (actual !== expected) {
|
|
119
|
+
throw new Error(\`Expected \${actual} to be \${expected}\`);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
toEqual(expected) {
|
|
123
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
124
|
+
throw new Error(\`Expected \${JSON.stringify(actual)} to equal \${JSON.stringify(expected)}\`);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
toBeTruthy() {
|
|
128
|
+
if (!actual) {
|
|
129
|
+
throw new Error(\`Expected \${actual} to be truthy\`);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
toBeFalsy() {
|
|
133
|
+
if (actual) {
|
|
134
|
+
throw new Error(\`Expected \${actual} to be falsy\`);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
toThrow(expectedError) {
|
|
138
|
+
let threw = false;
|
|
139
|
+
let error = null;
|
|
140
|
+
try {
|
|
141
|
+
actual();
|
|
142
|
+
} catch (e) {
|
|
143
|
+
threw = true;
|
|
144
|
+
error = e;
|
|
145
|
+
}
|
|
146
|
+
if (!threw) {
|
|
147
|
+
throw new Error('Expected function to throw');
|
|
148
|
+
}
|
|
149
|
+
if (expectedError && error.message !== expectedError) {
|
|
150
|
+
throw new Error(\`Expected error message "\${expectedError}" but got "\${error.message}"\`);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
toBeInstanceOf(expected) {
|
|
154
|
+
if (!(actual instanceof expected)) {
|
|
155
|
+
throw new Error(\`Expected \${actual} to be instance of \${expected.name}\`);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
toContain(expected) {
|
|
159
|
+
if (!actual.includes(expected)) {
|
|
160
|
+
throw new Error(\`Expected \${actual} to contain \${expected}\`);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
toHaveProperty(property, value) {
|
|
164
|
+
if (!(property in actual)) {
|
|
165
|
+
throw new Error(\`Expected object to have property \${property}\`);
|
|
166
|
+
}
|
|
167
|
+
if (value !== undefined && actual[property] !== value) {
|
|
168
|
+
throw new Error(\`Expected property \${property} to be \${value} but got \${actual[property]}\`);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
toBeNull() {
|
|
172
|
+
if (actual !== null) {
|
|
173
|
+
throw new Error(\`Expected \${actual} to be null\`);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
toBeUndefined() {
|
|
177
|
+
if (actual !== undefined) {
|
|
178
|
+
throw new Error(\`Expected \${actual} to be undefined\`);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
toHaveLength(expected) {
|
|
182
|
+
if (actual.length !== expected) {
|
|
183
|
+
throw new Error(\`Expected length \${expected} but got \${actual.length}\`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
async function runTests() {
|
|
190
|
+
try {
|
|
191
|
+
// Find and load test files
|
|
192
|
+
const files = await glob('${basePath}/${pattern}', { cwd: process.cwd() });
|
|
193
|
+
|
|
194
|
+
if (files.length === 0) {
|
|
195
|
+
console.log('No test files found matching pattern: ${basePath}/${pattern}');
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(\`Found \${files.length} test file(s)\\n\`);
|
|
200
|
+
|
|
201
|
+
// Load all test files
|
|
202
|
+
for (const file of files) {
|
|
203
|
+
require(path.resolve(file));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Run all tests
|
|
207
|
+
for (const suite of testState.suites) {
|
|
208
|
+
console.log(\`\\nš¦ \${suite.name}\`);
|
|
209
|
+
|
|
210
|
+
for (const test of suite.tests) {
|
|
211
|
+
try {
|
|
212
|
+
await test.fn();
|
|
213
|
+
console.log(\` ā \${test.name}\`);
|
|
214
|
+
testState.passed++;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.log(\` ā \${test.name}\`);
|
|
217
|
+
console.error(\` \${error.message}\`);
|
|
218
|
+
testState.failed++;
|
|
219
|
+
testState.errors.push({
|
|
220
|
+
suite: suite.name,
|
|
221
|
+
test: test.name,
|
|
222
|
+
error: error.message,
|
|
223
|
+
stack: error.stack
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Print summary
|
|
230
|
+
console.log(\`\\n\\nš Results: \${testState.passed} passed, \${testState.failed} failed\\n\`);
|
|
231
|
+
|
|
232
|
+
if (testState.failed > 0) {
|
|
233
|
+
console.log('\\nā Failed Tests:\\n');
|
|
234
|
+
testState.errors.forEach(err => {
|
|
235
|
+
console.log(\` \${err.suite} > \${err.test}\`);
|
|
236
|
+
console.log(\` \${err.error}\\n\`);
|
|
237
|
+
});
|
|
238
|
+
process.exit(1);
|
|
239
|
+
} else {
|
|
240
|
+
console.log('ā
All tests passed!\\n');
|
|
241
|
+
process.exit(0);
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('Error running tests:', error);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
runTests();
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
const scriptPath = path.join(cwd, ".fragment-test-runner.js");
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
256
|
+
|
|
257
|
+
const nodeArgs = [scriptPath];
|
|
258
|
+
const env = {
|
|
259
|
+
...process.env,
|
|
260
|
+
TS_NODE_TRANSPILE_ONLY: "true",
|
|
261
|
+
NODE_ENV: "test",
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const proc = spawn("node", nodeArgs, {
|
|
265
|
+
cwd,
|
|
266
|
+
stdio: "inherit",
|
|
267
|
+
env,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
proc.on("close", (code) => {
|
|
271
|
+
try {
|
|
272
|
+
fs.unlinkSync(scriptPath);
|
|
273
|
+
} catch {}
|
|
274
|
+
process.exit(code || 0);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
proc.on("error", (error) => {
|
|
278
|
+
console.error(chalk.red("Failed to run tests:"), error);
|
|
279
|
+
try {
|
|
280
|
+
fs.unlinkSync(scriptPath);
|
|
281
|
+
} catch {}
|
|
282
|
+
process.exit(1);
|
|
283
|
+
});
|
|
284
|
+
} catch (error: any) {
|
|
285
|
+
console.error(chalk.red("Failed to create test runner:"), error.message);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { InitCommand } from
|
|
3
|
-
import { GenerateCommand } from
|
|
4
|
-
import { ServeCommand } from
|
|
5
|
-
import { BuildCommand } from
|
|
6
|
-
import { MigrateCommand } from
|
|
7
|
-
import { DiagnosticsCommand } from
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { InitCommand } from "./commands/init.command";
|
|
3
|
+
import { GenerateCommand } from "./commands/generate.command";
|
|
4
|
+
import { ServeCommand } from "./commands/serve.command";
|
|
5
|
+
import { BuildCommand } from "./commands/build.command";
|
|
6
|
+
import { MigrateCommand } from "./commands/migrate.command";
|
|
7
|
+
import { DiagnosticsCommand } from "./commands/diagnostics.command";
|
|
8
|
+
import { TestCommand } from "./commands/test.command";
|
|
8
9
|
|
|
9
10
|
const program = new Command();
|
|
10
11
|
|
|
11
12
|
program
|
|
12
|
-
.name(
|
|
13
|
-
.description(
|
|
14
|
-
|
|
13
|
+
.name("fragment")
|
|
14
|
+
.description(
|
|
15
|
+
"Fragment Framework CLI - A TypeScript framework inspired by Spring Boot",
|
|
16
|
+
)
|
|
17
|
+
.version("1.0.0");
|
|
15
18
|
|
|
19
|
+
// Register all commands
|
|
16
20
|
InitCommand.register(program);
|
|
17
21
|
GenerateCommand.register(program);
|
|
18
22
|
ServeCommand.register(program);
|
|
19
23
|
BuildCommand.register(program);
|
|
24
|
+
TestCommand.register(program);
|
|
20
25
|
MigrateCommand.register(program);
|
|
21
26
|
DiagnosticsCommand.register(program);
|
|
22
27
|
|
|
23
|
-
program.parse();
|
|
28
|
+
program.parse();
|