fragment-ts 1.0.34 → 1.0.35
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/API.md +248 -38
- package/DOCS.md +327 -63
- package/NewCLIGENERATECOMMANDS.txt +5 -0
- package/README.md +168 -3
- package/USAGE.md +395 -2
- package/dist/cli/commands/build.command.d.ts.map +1 -1
- package/dist/cli/commands/build.command.js +20 -8
- package/dist/cli/commands/build.command.js.map +1 -1
- package/dist/cli/commands/diagnostics.command.d.ts +1 -2
- package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
- package/dist/cli/commands/diagnostics.command.js +37 -23
- package/dist/cli/commands/diagnostics.command.js.map +1 -1
- package/dist/cli/commands/generate.command.d.ts +5 -1
- package/dist/cli/commands/generate.command.d.ts.map +1 -1
- package/dist/cli/commands/generate.command.js +171 -39
- package/dist/cli/commands/generate.command.js.map +1 -1
- package/dist/cli/commands/init.command.d.ts.map +1 -1
- package/dist/cli/commands/init.command.js +98 -28
- package/dist/cli/commands/init.command.js.map +1 -1
- package/dist/cli/commands/migrate.command.d.ts +10 -17
- package/dist/cli/commands/migrate.command.d.ts.map +1 -1
- package/dist/cli/commands/migrate.command.js +133 -170
- package/dist/cli/commands/migrate.command.js.map +1 -1
- package/dist/cli/commands/serve.command.d.ts.map +1 -1
- package/dist/cli/commands/serve.command.js +9 -4
- package/dist/cli/commands/serve.command.js.map +1 -1
- package/dist/cli/commands/test.command.d.ts.map +1 -1
- package/dist/cli/commands/test.command.js +24 -6
- package/dist/cli/commands/test.command.js.map +1 -1
- package/dist/core/scanner/component-scanner.d.ts +12 -0
- package/dist/core/scanner/component-scanner.d.ts.map +1 -1
- package/dist/core/scanner/component-scanner.js +72 -14
- package/dist/core/scanner/component-scanner.js.map +1 -1
- package/dist/shared/config.utils.d.ts +58 -0
- package/dist/shared/config.utils.d.ts.map +1 -0
- package/dist/shared/config.utils.js +137 -0
- package/dist/shared/config.utils.js.map +1 -0
- package/dist/shared/env.utils.d.ts +27 -0
- package/dist/shared/env.utils.d.ts.map +1 -0
- package/dist/shared/env.utils.js +68 -0
- package/dist/shared/env.utils.js.map +1 -0
- package/dist/shared/tsconfig.utils.d.ts +122 -0
- package/dist/shared/tsconfig.utils.d.ts.map +1 -0
- package/dist/shared/tsconfig.utils.js +305 -0
- package/dist/shared/tsconfig.utils.js.map +1 -0
- package/dist/testing/runner.d.ts +9 -1
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js +50 -10
- package/dist/testing/runner.js.map +1 -1
- package/dist/typeorm/typeorm-module.d.ts +1 -0
- package/dist/typeorm/typeorm-module.d.ts.map +1 -1
- package/dist/typeorm/typeorm-module.js +193 -85
- package/dist/typeorm/typeorm-module.js.map +1 -1
- package/dist/web/application.d.ts +0 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +4 -26
- package/dist/web/application.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/build.command.ts +24 -9
- package/src/cli/commands/diagnostics.command.ts +42 -30
- package/src/cli/commands/generate.command.ts +212 -52
- package/src/cli/commands/init.command.ts +100 -29
- package/src/cli/commands/migrate.command.ts +145 -198
- package/src/cli/commands/serve.command.ts +181 -170
- package/src/cli/commands/test.command.ts +25 -11
- package/src/core/scanner/component-scanner.ts +100 -18
- package/src/shared/config.utils.ts +148 -0
- package/src/shared/env.utils.ts +72 -0
- package/src/shared/tsconfig.utils.ts +360 -0
- package/src/testing/runner.ts +62 -14
- package/src/typeorm/typeorm-module.ts +209 -86
- package/src/web/application.ts +4 -33
|
@@ -1,189 +1,200 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { ChildProcess, spawn, execSync } from "child_process";
|
|
1
3
|
import { Command } from "commander";
|
|
2
|
-
import { spawn, ChildProcess, execSync } from "child_process";
|
|
3
4
|
import * as fs from "fs";
|
|
4
5
|
import * as path from "path";
|
|
5
|
-
import
|
|
6
|
+
import { TsConfigUtils } from "../../shared/tsconfig.utils";
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
export class ServeCommand {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static async execute(options: any): Promise<void> {
|
|
20
|
-
console.log(chalk.blue("🚀 Starting Fragment development server...\n"));
|
|
21
|
-
|
|
22
|
-
const mainFile = path.join(process.cwd(), "src", "main.ts");
|
|
23
|
-
|
|
24
|
-
if (!fs.existsSync(mainFile)) {
|
|
25
|
-
console.error(chalk.red("Main file not found at src/main.ts"));
|
|
26
|
-
process.exit(1);
|
|
10
|
+
static register(program: Command): void {
|
|
11
|
+
program
|
|
12
|
+
.command("serve")
|
|
13
|
+
.description("Start the development server")
|
|
14
|
+
.option("-w, --watch", "Watch for changes", true)
|
|
15
|
+
.option("-p, --port <port>", "Port to run on", "3000")
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
await ServeCommand.execute(options);
|
|
18
|
+
});
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!tsNode) {
|
|
32
|
-
console.error(chalk.red("ts-node not found. Install it:"));
|
|
33
|
-
console.log(chalk.cyan(" npm install --save-dev ts-node"));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
21
|
+
static async execute(options: any): Promise<void> {
|
|
22
|
+
console.log(chalk.blue("🚀 Starting Fragment development server...\n"));
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
// Use TsConfigUtils to get the correct main file path
|
|
25
|
+
const srcDir = TsConfigUtils.getRootDir();
|
|
26
|
+
const mainFile = path.join(srcDir, "main.ts");
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
if (!fs.existsSync(mainFile)) {
|
|
29
|
+
console.error(
|
|
30
|
+
chalk.red(
|
|
31
|
+
`Main file not found at ${path.relative(process.cwd(), mainFile)}`
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// Find ts-node
|
|
38
|
+
const tsNode = this.findTsNode();
|
|
39
|
+
if (!tsNode) {
|
|
40
|
+
console.error(chalk.red("ts-node not found. Install it:"));
|
|
41
|
+
console.log(chalk.cyan(" npm install --save-dev ts-node"));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
45
|
+
let appProcess: ChildProcess | null = null;
|
|
46
|
+
let isRestarting = false;
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
console.error(chalk.red(`Server exited with code ${code}`));
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const killServer = (): Promise<void> => {
|
|
61
|
-
return new Promise((resolve) => {
|
|
62
|
-
if (!appProcess || appProcess.killed) {
|
|
63
|
-
resolve();
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
48
|
+
const startServer = () => {
|
|
49
|
+
if (isRestarting) return;
|
|
66
50
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const restartServer = async (filePath?: string) => {
|
|
86
|
-
if (isRestarting) return;
|
|
87
|
-
isRestarting = true;
|
|
88
|
-
|
|
89
|
-
if (filePath) {
|
|
90
|
-
console.log(
|
|
91
|
-
chalk.yellow(
|
|
92
|
-
`\n🔄 File changed: ${path.relative(process.cwd(), filePath)}`,
|
|
93
|
-
),
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
console.log(chalk.blue("Restarting server...\n"));
|
|
97
|
-
|
|
98
|
-
await killServer();
|
|
99
|
-
|
|
100
|
-
// Wait a bit for port to be released
|
|
101
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
102
|
-
|
|
103
|
-
isRestarting = false;
|
|
104
|
-
startServer();
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// Start initial server
|
|
108
|
-
startServer();
|
|
109
|
-
|
|
110
|
-
// Watch for changes
|
|
111
|
-
if (options.watch) {
|
|
112
|
-
try {
|
|
113
|
-
const chokidar = await import("chokidar");
|
|
114
|
-
|
|
115
|
-
const watcher = chokidar.watch("src/**/*.ts", {
|
|
116
|
-
ignored: /(^|[/\\])\../,
|
|
117
|
-
persistent: true,
|
|
118
|
-
ignoreInitial: true,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
let debounceTimer: NodeJS.Timeout | null = null;
|
|
122
|
-
|
|
123
|
-
watcher.on("change", (filePath) => {
|
|
124
|
-
// Debounce rapid changes
|
|
125
|
-
if (debounceTimer) {
|
|
126
|
-
clearTimeout(debounceTimer);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
debounceTimer = setTimeout(() => {
|
|
130
|
-
restartServer(filePath);
|
|
131
|
-
}, 300);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
watcher.on("error", (error) => {
|
|
135
|
-
console.error(chalk.red(`Watcher error: ${error}`));
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Cleanup on exit
|
|
139
|
-
const cleanup = async () => {
|
|
140
|
-
console.log(chalk.yellow("\n\n🛑 Shutting down..."));
|
|
141
|
-
watcher.close();
|
|
142
|
-
await killServer();
|
|
143
|
-
process.exit(0);
|
|
51
|
+
// Use ts-node directly instead of node with -r flag
|
|
52
|
+
appProcess = spawn(tsNode, [mainFile], {
|
|
53
|
+
stdio: "inherit",
|
|
54
|
+
env: { ...process.env, PORT: options.port },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
appProcess.on("error", (error) => {
|
|
58
|
+
console.error(chalk.red(`Failed to start server: ${error.message}`));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
appProcess.on("exit", (code, signal) => {
|
|
62
|
+
if (!isRestarting && signal !== "SIGTERM" && signal !== "SIGINT") {
|
|
63
|
+
console.error(chalk.red(`Server exited with code ${code}`));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
144
66
|
};
|
|
145
67
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
68
|
+
const killServer = (): Promise<void> => {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
if (!appProcess || appProcess.killed) {
|
|
71
|
+
resolve();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const timeout = setTimeout(() => {
|
|
76
|
+
if (appProcess && !appProcess.killed) {
|
|
77
|
+
console.log(chalk.yellow("Force killing server..."));
|
|
78
|
+
appProcess.kill("SIGKILL");
|
|
79
|
+
}
|
|
80
|
+
resolve();
|
|
81
|
+
}, 3000);
|
|
82
|
+
|
|
83
|
+
appProcess.once("exit", () => {
|
|
84
|
+
clearTimeout(timeout);
|
|
85
|
+
appProcess = null;
|
|
86
|
+
resolve();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
appProcess.kill("SIGTERM");
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const restartServer = async (filePath?: string) => {
|
|
94
|
+
if (isRestarting) return;
|
|
95
|
+
isRestarting = true;
|
|
96
|
+
|
|
97
|
+
if (filePath) {
|
|
98
|
+
console.log(
|
|
99
|
+
chalk.yellow(
|
|
100
|
+
`\n🔄 File changed: ${path.relative(process.cwd(), filePath)}`
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
console.log(chalk.blue("Restarting server...\n"));
|
|
105
|
+
|
|
106
|
+
await killServer();
|
|
107
|
+
|
|
108
|
+
// Wait a bit for port to be released
|
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
110
|
+
|
|
111
|
+
isRestarting = false;
|
|
112
|
+
startServer();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Start initial server
|
|
116
|
+
startServer();
|
|
117
|
+
|
|
118
|
+
// Watch for changes
|
|
119
|
+
if (options.watch) {
|
|
120
|
+
try {
|
|
121
|
+
const chokidar = await import("chokidar");
|
|
122
|
+
|
|
123
|
+
// Use source directory from tsconfig
|
|
124
|
+
const watchPattern = path.join(srcDir, "**/*.ts");
|
|
125
|
+
|
|
126
|
+
const watcher = chokidar.watch(watchPattern, {
|
|
127
|
+
ignored: /(^|[/\\])\../,
|
|
128
|
+
persistent: true,
|
|
129
|
+
ignoreInitial: true,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
let debounceTimer: NodeJS.Timeout | null = null;
|
|
133
|
+
|
|
134
|
+
watcher.on("change", (filePath) => {
|
|
135
|
+
// Debounce rapid changes
|
|
136
|
+
if (debounceTimer) {
|
|
137
|
+
clearTimeout(debounceTimer);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
debounceTimer = setTimeout(() => {
|
|
141
|
+
restartServer(filePath);
|
|
142
|
+
}, 300);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
watcher.on("error", (error) => {
|
|
146
|
+
console.error(chalk.red(`Watcher error: ${error}`));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Cleanup on exit
|
|
150
|
+
const cleanup = async () => {
|
|
151
|
+
console.log(chalk.yellow("\n\n🛑 Shutting down..."));
|
|
152
|
+
watcher.close();
|
|
153
|
+
await killServer();
|
|
154
|
+
process.exit(0);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
process.on("SIGINT", cleanup);
|
|
158
|
+
process.on("SIGTERM", cleanup);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(
|
|
161
|
+
chalk.red(
|
|
162
|
+
"Failed to start file watcher. Install chokidar: npm install chokidar"
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
// No watch mode - just handle cleanup
|
|
169
|
+
const cleanup = async () => {
|
|
170
|
+
await killServer();
|
|
171
|
+
process.exit(0);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
process.on("SIGINT", cleanup);
|
|
175
|
+
process.on("SIGTERM", cleanup);
|
|
176
|
+
}
|
|
179
177
|
}
|
|
180
178
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
179
|
+
private static findTsNode(): string | null {
|
|
180
|
+
// Try local ts-node
|
|
181
|
+
const localTsNode = path.join(
|
|
182
|
+
process.cwd(),
|
|
183
|
+
"node_modules",
|
|
184
|
+
".bin",
|
|
185
|
+
process.platform === "win32" ? "ts-node.cmd" : "ts-node"
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (fs.existsSync(localTsNode)) {
|
|
189
|
+
return localTsNode;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Try global ts-node
|
|
193
|
+
try {
|
|
194
|
+
execSync("ts-node --version", { stdio: "ignore" });
|
|
195
|
+
return "ts-node";
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
187
199
|
}
|
|
188
|
-
}
|
|
189
200
|
}
|
|
@@ -4,6 +4,8 @@ import * as path from "path";
|
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
import * as fs from "fs";
|
|
6
6
|
import chokidar from "chokidar";
|
|
7
|
+
import { EnvUtils } from "../../shared/env.utils";
|
|
8
|
+
import { TsConfigUtils } from "../../shared/tsconfig.utils";
|
|
7
9
|
|
|
8
10
|
export class TestCommand {
|
|
9
11
|
static register(program: Command): void {
|
|
@@ -28,6 +30,9 @@ export class TestCommand {
|
|
|
28
30
|
console.log(chalk.blue("\n🧪 Running Fragment Tests...\n"));
|
|
29
31
|
|
|
30
32
|
const cwd = process.cwd();
|
|
33
|
+
|
|
34
|
+
// Use EnvUtils for consistent environment detection
|
|
35
|
+
const isDevMode = EnvUtils.isDevelopmentMode();
|
|
31
36
|
const hasSource = fs.existsSync(path.join(cwd, "src"));
|
|
32
37
|
const hasDist = fs.existsSync(path.join(cwd, "dist"));
|
|
33
38
|
|
|
@@ -61,13 +66,25 @@ export class TestCommand {
|
|
|
61
66
|
basePath = "dist";
|
|
62
67
|
pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
|
|
63
68
|
} else {
|
|
64
|
-
// Auto-detect
|
|
65
|
-
if (hasSource) {
|
|
69
|
+
// Auto-detect using same logic as EnvUtils
|
|
70
|
+
if (isDevMode && hasSource) {
|
|
71
|
+
useTypeScript = true;
|
|
72
|
+
mode = "auto-detected development (src/)";
|
|
73
|
+
basePath = "src";
|
|
74
|
+
pattern = options.pattern || "**/*.spec.ts";
|
|
75
|
+
} else if (!isDevMode && hasDist) {
|
|
76
|
+
useTypeScript = false;
|
|
77
|
+
mode = "auto-detected production (dist/)";
|
|
78
|
+
basePath = "dist";
|
|
79
|
+
pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
|
|
80
|
+
} else if (hasSource) {
|
|
81
|
+
// Fallback to source if available
|
|
66
82
|
useTypeScript = true;
|
|
67
83
|
mode = "auto-detected development (src/)";
|
|
68
84
|
basePath = "src";
|
|
69
85
|
pattern = options.pattern || "**/*.spec.ts";
|
|
70
86
|
} else if (hasDist) {
|
|
87
|
+
// Fallback to dist if available
|
|
71
88
|
useTypeScript = false;
|
|
72
89
|
mode = "auto-detected production (dist/)";
|
|
73
90
|
basePath = "dist";
|
|
@@ -84,8 +101,7 @@ export class TestCommand {
|
|
|
84
101
|
console.log(chalk.gray(` Pattern: ${basePath}/${pattern}`));
|
|
85
102
|
|
|
86
103
|
// Check if we need to use ts-node for TypeScript files
|
|
87
|
-
const
|
|
88
|
-
const hasTsConfig = fs.existsSync(tsConfigPath);
|
|
104
|
+
const hasTsConfig = TsConfigUtils.exists();
|
|
89
105
|
|
|
90
106
|
// Create a simple runner script that uses our test runner module
|
|
91
107
|
const scriptContent = this.generateRunnerScript({
|
|
@@ -93,7 +109,7 @@ export class TestCommand {
|
|
|
93
109
|
basePath,
|
|
94
110
|
pattern,
|
|
95
111
|
hasTsConfig,
|
|
96
|
-
tsConfigPath,
|
|
112
|
+
tsConfigPath: path.join(cwd, "tsconfig.json"),
|
|
97
113
|
watchMode: options.watch,
|
|
98
114
|
coverage: options.coverage,
|
|
99
115
|
noColor: options.color === false,
|
|
@@ -113,12 +129,10 @@ export class TestCommand {
|
|
|
113
129
|
chalk.green("\n👀 Watch mode enabled. Listening for changes...\n"),
|
|
114
130
|
);
|
|
115
131
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
);
|
|
132
|
+
const watchPattern = `${basePath}/**/*.spec.${useTypeScript ? "ts" : "js"}`;
|
|
133
|
+
const watcher = chokidar.watch(watchPattern, {
|
|
134
|
+
ignoreInitial: true,
|
|
135
|
+
});
|
|
122
136
|
|
|
123
137
|
const runTests = () => {
|
|
124
138
|
const proc = spawn("node", [scriptPath], { stdio: "inherit" });
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import { glob } from "glob";
|
|
4
|
+
import { ConfigUtils } from "../../shared/config.utils";
|
|
5
|
+
import { TsConfigUtils } from "../../shared/tsconfig.utils";
|
|
4
6
|
|
|
5
7
|
export class ComponentScanner {
|
|
6
8
|
/**
|
|
7
9
|
* Scans and imports compiled JS files (production mode)
|
|
8
10
|
* This ensures decorators are executed
|
|
9
11
|
*/
|
|
10
|
-
static async scan(
|
|
11
|
-
patterns: string[] = [
|
|
12
|
-
"dist/**/*.controller.js",
|
|
13
|
-
"dist/**/*.service.js",
|
|
14
|
-
"dist/**/*.repository.js",
|
|
15
|
-
"dist/**/*.entity.js",
|
|
16
|
-
],
|
|
17
|
-
): Promise<void> {
|
|
12
|
+
static async scan(patterns: string[] = []): Promise<void> {
|
|
18
13
|
const cwd = process.cwd();
|
|
14
|
+
|
|
15
|
+
// If no patterns provided, use defaults from fragment.json config
|
|
16
|
+
const scanPatterns =
|
|
17
|
+
patterns.length > 0 ? patterns : this.getDefaultPatterns(false);
|
|
18
|
+
|
|
19
19
|
const allFiles: string[] = [];
|
|
20
20
|
|
|
21
21
|
// Collect all matching files
|
|
22
|
-
for (const pattern of
|
|
22
|
+
for (const pattern of scanPatterns) {
|
|
23
23
|
try {
|
|
24
24
|
const files = await glob(pattern, { cwd, absolute: true });
|
|
25
25
|
allFiles.push(...files);
|
|
@@ -56,18 +56,16 @@ export class ComponentScanner {
|
|
|
56
56
|
/**
|
|
57
57
|
* Scans TypeScript source files (development mode with ts-node/tsx)
|
|
58
58
|
*/
|
|
59
|
-
static async scanSource(
|
|
60
|
-
patterns: string[] = [
|
|
61
|
-
"src/**/*.controller.ts",
|
|
62
|
-
"src/**/*.service.ts",
|
|
63
|
-
"src/**/*.repository.ts",
|
|
64
|
-
"src/**/*.entity.ts",
|
|
65
|
-
],
|
|
66
|
-
): Promise<void> {
|
|
59
|
+
static async scanSource(patterns: string[] = []): Promise<void> {
|
|
67
60
|
const cwd = process.cwd();
|
|
61
|
+
|
|
62
|
+
// If no patterns provided, use defaults from fragment.json config
|
|
63
|
+
const scanPatterns =
|
|
64
|
+
patterns.length > 0 ? patterns : this.getDefaultPatterns(true);
|
|
65
|
+
|
|
68
66
|
const allFiles: string[] = [];
|
|
69
67
|
|
|
70
|
-
for (const pattern of
|
|
68
|
+
for (const pattern of scanPatterns) {
|
|
71
69
|
try {
|
|
72
70
|
const files = await glob(pattern, { cwd, absolute: true });
|
|
73
71
|
allFiles.push(...files);
|
|
@@ -98,6 +96,55 @@ export class ComponentScanner {
|
|
|
98
96
|
);
|
|
99
97
|
}
|
|
100
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Get default scan patterns based on fragment.json configuration
|
|
101
|
+
*/
|
|
102
|
+
private static getDefaultPatterns(useTypeScript: boolean): string[] {
|
|
103
|
+
const dbConfig = ConfigUtils.getDatabaseConfig();
|
|
104
|
+
const patterns: string[] = [];
|
|
105
|
+
|
|
106
|
+
// Use configured entity paths if available
|
|
107
|
+
if (dbConfig.entities && dbConfig.entities.length > 0) {
|
|
108
|
+
// Convert entity patterns to component patterns
|
|
109
|
+
const entityPatterns = dbConfig.entities.map((p) =>
|
|
110
|
+
p.replace(/\.entity\.[tj]s$/, `.${useTypeScript ? "ts" : "js"}`),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Extract base directory and create component patterns
|
|
114
|
+
const baseDirs = new Set<string>();
|
|
115
|
+
entityPatterns.forEach((pattern) => {
|
|
116
|
+
const dir = path.dirname(pattern.replace("/**/*", ""));
|
|
117
|
+
baseDirs.add(dir);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Create patterns for all component types
|
|
121
|
+
baseDirs.forEach((baseDir) => {
|
|
122
|
+
patterns.push(
|
|
123
|
+
`${baseDir}/**/*.controller.${useTypeScript ? "ts" : "js"}`,
|
|
124
|
+
`${baseDir}/**/*.service.${useTypeScript ? "ts" : "js"}`,
|
|
125
|
+
`${baseDir}/**/*.repository.${useTypeScript ? "ts" : "js"}`,
|
|
126
|
+
`${baseDir}/**/*.entity.${useTypeScript ? "ts" : "js"}`,
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Fallback to default patterns if no config found
|
|
132
|
+
if (patterns.length === 0) {
|
|
133
|
+
const srcDir = useTypeScript
|
|
134
|
+
? TsConfigUtils.getRootDir()
|
|
135
|
+
: path.join(process.cwd(), "dist");
|
|
136
|
+
const ext = useTypeScript ? "ts" : "js";
|
|
137
|
+
patterns.push(
|
|
138
|
+
`${srcDir}/**/*.controller.${ext}`,
|
|
139
|
+
`${srcDir}/**/*.service.${ext}`,
|
|
140
|
+
`${srcDir}/**/*.repository.${ext}`,
|
|
141
|
+
`${srcDir}/**/*.entity.${ext}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return patterns;
|
|
146
|
+
}
|
|
147
|
+
|
|
101
148
|
/**
|
|
102
149
|
* Recursive directory scan (alternative approach)
|
|
103
150
|
*/
|
|
@@ -126,4 +173,39 @@ export class ComponentScanner {
|
|
|
126
173
|
}
|
|
127
174
|
}
|
|
128
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Auto-scan based on current environment mode
|
|
179
|
+
*/
|
|
180
|
+
static async autoScan(): Promise<void> {
|
|
181
|
+
// Detect environment using the same logic as other components
|
|
182
|
+
const isDevMode = this.isDevelopmentMode();
|
|
183
|
+
|
|
184
|
+
if (isDevMode) {
|
|
185
|
+
await this.scanSource();
|
|
186
|
+
} else {
|
|
187
|
+
await this.scan();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Environment detection logic (replicated from EnvUtils for independence)
|
|
193
|
+
*/
|
|
194
|
+
private static isDevelopmentMode(): boolean {
|
|
195
|
+
// Check explicit FRAGMENT_DEV_MODE
|
|
196
|
+
if (process.env.FRAGMENT_DEV_MODE !== undefined) {
|
|
197
|
+
return process.env.FRAGMENT_DEV_MODE === "true";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check NODE_ENV
|
|
201
|
+
if (process.env.NODE_ENV) {
|
|
202
|
+
return process.env.NODE_ENV === "development";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check filesystem (src/ vs dist/)
|
|
206
|
+
const cwd = process.cwd();
|
|
207
|
+
const hasSource = fs.existsSync(path.join(cwd, "src"));
|
|
208
|
+
const hasDist = fs.existsSync(path.join(cwd, "dist"));
|
|
209
|
+
return hasSource && (!hasDist || hasSource);
|
|
210
|
+
}
|
|
129
211
|
}
|