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.
Files changed (72) hide show
  1. package/API.md +248 -38
  2. package/DOCS.md +327 -63
  3. package/NewCLIGENERATECOMMANDS.txt +5 -0
  4. package/README.md +168 -3
  5. package/USAGE.md +395 -2
  6. package/dist/cli/commands/build.command.d.ts.map +1 -1
  7. package/dist/cli/commands/build.command.js +20 -8
  8. package/dist/cli/commands/build.command.js.map +1 -1
  9. package/dist/cli/commands/diagnostics.command.d.ts +1 -2
  10. package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
  11. package/dist/cli/commands/diagnostics.command.js +37 -23
  12. package/dist/cli/commands/diagnostics.command.js.map +1 -1
  13. package/dist/cli/commands/generate.command.d.ts +5 -1
  14. package/dist/cli/commands/generate.command.d.ts.map +1 -1
  15. package/dist/cli/commands/generate.command.js +171 -39
  16. package/dist/cli/commands/generate.command.js.map +1 -1
  17. package/dist/cli/commands/init.command.d.ts.map +1 -1
  18. package/dist/cli/commands/init.command.js +98 -28
  19. package/dist/cli/commands/init.command.js.map +1 -1
  20. package/dist/cli/commands/migrate.command.d.ts +10 -17
  21. package/dist/cli/commands/migrate.command.d.ts.map +1 -1
  22. package/dist/cli/commands/migrate.command.js +133 -170
  23. package/dist/cli/commands/migrate.command.js.map +1 -1
  24. package/dist/cli/commands/serve.command.d.ts.map +1 -1
  25. package/dist/cli/commands/serve.command.js +9 -4
  26. package/dist/cli/commands/serve.command.js.map +1 -1
  27. package/dist/cli/commands/test.command.d.ts.map +1 -1
  28. package/dist/cli/commands/test.command.js +24 -6
  29. package/dist/cli/commands/test.command.js.map +1 -1
  30. package/dist/core/scanner/component-scanner.d.ts +12 -0
  31. package/dist/core/scanner/component-scanner.d.ts.map +1 -1
  32. package/dist/core/scanner/component-scanner.js +72 -14
  33. package/dist/core/scanner/component-scanner.js.map +1 -1
  34. package/dist/shared/config.utils.d.ts +58 -0
  35. package/dist/shared/config.utils.d.ts.map +1 -0
  36. package/dist/shared/config.utils.js +137 -0
  37. package/dist/shared/config.utils.js.map +1 -0
  38. package/dist/shared/env.utils.d.ts +27 -0
  39. package/dist/shared/env.utils.d.ts.map +1 -0
  40. package/dist/shared/env.utils.js +68 -0
  41. package/dist/shared/env.utils.js.map +1 -0
  42. package/dist/shared/tsconfig.utils.d.ts +122 -0
  43. package/dist/shared/tsconfig.utils.d.ts.map +1 -0
  44. package/dist/shared/tsconfig.utils.js +305 -0
  45. package/dist/shared/tsconfig.utils.js.map +1 -0
  46. package/dist/testing/runner.d.ts +9 -1
  47. package/dist/testing/runner.d.ts.map +1 -1
  48. package/dist/testing/runner.js +50 -10
  49. package/dist/testing/runner.js.map +1 -1
  50. package/dist/typeorm/typeorm-module.d.ts +1 -0
  51. package/dist/typeorm/typeorm-module.d.ts.map +1 -1
  52. package/dist/typeorm/typeorm-module.js +193 -85
  53. package/dist/typeorm/typeorm-module.js.map +1 -1
  54. package/dist/web/application.d.ts +0 -1
  55. package/dist/web/application.d.ts.map +1 -1
  56. package/dist/web/application.js +4 -26
  57. package/dist/web/application.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/cli/commands/build.command.ts +24 -9
  60. package/src/cli/commands/diagnostics.command.ts +42 -30
  61. package/src/cli/commands/generate.command.ts +212 -52
  62. package/src/cli/commands/init.command.ts +100 -29
  63. package/src/cli/commands/migrate.command.ts +145 -198
  64. package/src/cli/commands/serve.command.ts +181 -170
  65. package/src/cli/commands/test.command.ts +25 -11
  66. package/src/core/scanner/component-scanner.ts +100 -18
  67. package/src/shared/config.utils.ts +148 -0
  68. package/src/shared/env.utils.ts +72 -0
  69. package/src/shared/tsconfig.utils.ts +360 -0
  70. package/src/testing/runner.ts +62 -14
  71. package/src/typeorm/typeorm-module.ts +209 -86
  72. 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 chalk from "chalk";
6
+ import { TsConfigUtils } from "../../shared/tsconfig.utils";
7
+
6
8
 
7
9
  export class ServeCommand {
8
- static register(program: Command): void {
9
- program
10
- .command("serve")
11
- .description("Start the development server")
12
- .option("-w, --watch", "Watch for changes", true)
13
- .option("-p, --port <port>", "Port to run on", "3000")
14
- .action(async (options) => {
15
- await ServeCommand.execute(options);
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
- // Find ts-node
30
- const tsNode = this.findTsNode();
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
- let appProcess: ChildProcess | null = null;
38
- let isRestarting = false;
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
- const startServer = () => {
41
- if (isRestarting) return;
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
- // Use ts-node directly instead of node with -r flag
44
- appProcess = spawn(tsNode, [mainFile], {
45
- stdio: "inherit",
46
- env: { ...process.env, PORT: options.port },
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
- appProcess.on("error", (error) => {
50
- console.error(chalk.red(`Failed to start server: ${error.message}`));
51
- });
45
+ let appProcess: ChildProcess | null = null;
46
+ let isRestarting = false;
52
47
 
53
- appProcess.on("exit", (code, signal) => {
54
- if (!isRestarting && signal !== "SIGTERM" && signal !== "SIGINT") {
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
- const timeout = setTimeout(() => {
68
- if (appProcess && !appProcess.killed) {
69
- console.log(chalk.yellow("Force killing server..."));
70
- appProcess.kill("SIGKILL");
71
- }
72
- resolve();
73
- }, 3000);
74
-
75
- appProcess.once("exit", () => {
76
- clearTimeout(timeout);
77
- appProcess = null;
78
- resolve();
79
- });
80
-
81
- appProcess.kill("SIGTERM");
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
- process.on("SIGINT", cleanup);
147
- process.on("SIGTERM", cleanup);
148
- } catch (error) {
149
- console.error(
150
- chalk.red(
151
- "Failed to start file watcher. Install chokidar: npm install chokidar",
152
- ),
153
- );
154
- process.exit(1);
155
- }
156
- } else {
157
- // No watch mode - just handle cleanup
158
- const cleanup = async () => {
159
- await killServer();
160
- process.exit(0);
161
- };
162
-
163
- process.on("SIGINT", cleanup);
164
- process.on("SIGTERM", cleanup);
165
- }
166
- }
167
-
168
- private static findTsNode(): string | null {
169
- // Try local ts-node
170
- const localTsNode = path.join(
171
- process.cwd(),
172
- "node_modules",
173
- ".bin",
174
- process.platform === "win32" ? "ts-node.cmd" : "ts-node",
175
- );
176
-
177
- if (fs.existsSync(localTsNode)) {
178
- return localTsNode;
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
- // Try global ts-node
182
- try {
183
- execSync("ts-node --version", { stdio: "ignore" });
184
- return "ts-node";
185
- } catch {
186
- return null;
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 tsConfigPath = path.join(cwd, "tsconfig.json");
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 watcher = chokidar.watch(
117
- `${basePath}/**/*.spec.${useTypeScript ? "ts" : "js"}`,
118
- {
119
- ignoreInitial: true,
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 patterns) {
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 patterns) {
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
  }