fragment-ts 1.0.45 โ†’ 1.0.47

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 (65) hide show
  1. package/README.md +96 -91
  2. package/dist/cli/commands/build.command.d.ts.map +1 -1
  3. package/dist/cli/commands/build.command.js +12 -19
  4. package/dist/cli/commands/build.command.js.map +1 -1
  5. package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
  6. package/dist/cli/commands/diagnostics.command.js +68 -14
  7. package/dist/cli/commands/diagnostics.command.js.map +1 -1
  8. package/dist/cli/commands/env.config.d.ts +10 -0
  9. package/dist/cli/commands/env.config.d.ts.map +1 -0
  10. package/dist/cli/commands/env.config.js +202 -0
  11. package/dist/cli/commands/env.config.js.map +1 -0
  12. package/dist/cli/commands/generate.command.js +9 -9
  13. package/dist/cli/commands/init.command.d.ts.map +1 -1
  14. package/dist/cli/commands/init.command.js +16 -59
  15. package/dist/cli/commands/init.command.js.map +1 -1
  16. package/dist/cli/commands/migrate.command.d.ts +5 -4
  17. package/dist/cli/commands/migrate.command.d.ts.map +1 -1
  18. package/dist/cli/commands/migrate.command.js +34 -9
  19. package/dist/cli/commands/migrate.command.js.map +1 -1
  20. package/dist/cli/commands/serve.command.d.ts.map +1 -1
  21. package/dist/cli/commands/serve.command.js +74 -38
  22. package/dist/cli/commands/serve.command.js.map +1 -1
  23. package/dist/cli/commands/test.command.d.ts.map +1 -1
  24. package/dist/cli/commands/test.command.js +21 -9
  25. package/dist/cli/commands/test.command.js.map +1 -1
  26. package/dist/cli/index.js +3 -12
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/core/scanner/component-scanner.d.ts +4 -0
  29. package/dist/core/scanner/component-scanner.d.ts.map +1 -1
  30. package/dist/core/scanner/component-scanner.js +15 -0
  31. package/dist/core/scanner/component-scanner.js.map +1 -1
  32. package/dist/shared/config.utils.d.ts +2 -3
  33. package/dist/shared/config.utils.d.ts.map +1 -1
  34. package/dist/shared/config.utils.js +2 -3
  35. package/dist/shared/config.utils.js.map +1 -1
  36. package/dist/shared/env.utils.d.ts +6 -6
  37. package/dist/shared/env.utils.d.ts.map +1 -1
  38. package/dist/shared/env.utils.js +25 -84
  39. package/dist/shared/env.utils.js.map +1 -1
  40. package/dist/testing/runner.d.ts.map +1 -1
  41. package/dist/testing/runner.js +12 -2
  42. package/dist/testing/runner.js.map +1 -1
  43. package/dist/typeorm/typeorm-module.d.ts.map +1 -1
  44. package/dist/typeorm/typeorm-module.js +3 -6
  45. package/dist/typeorm/typeorm-module.js.map +1 -1
  46. package/dist/web/application.d.ts.map +1 -1
  47. package/dist/web/application.js +20 -1
  48. package/dist/web/application.js.map +1 -1
  49. package/examples/blog-api/package-lock.json +0 -38
  50. package/package.json +3 -3
  51. package/src/cli/commands/build.command.ts +13 -29
  52. package/src/cli/commands/diagnostics.command.ts +74 -13
  53. package/src/cli/commands/{env.command.ts โ†’ env.config.ts} +9 -32
  54. package/src/cli/commands/generate.command.ts +9 -9
  55. package/src/cli/commands/init.command.ts +17 -63
  56. package/src/cli/commands/migrate.command.ts +37 -9
  57. package/src/cli/commands/serve.command.ts +205 -170
  58. package/src/cli/commands/test.command.ts +30 -8
  59. package/src/cli/index.ts +3 -12
  60. package/src/core/scanner/component-scanner.ts +15 -0
  61. package/src/shared/config.utils.ts +5 -6
  62. package/src/shared/env.utils.ts +27 -55
  63. package/src/testing/runner.ts +11 -2
  64. package/src/typeorm/typeorm-module.ts +4 -18
  65. package/src/web/application.ts +21 -1
@@ -6,6 +6,7 @@ import ora from "ora";
6
6
  import { DataSource } from "typeorm";
7
7
  import { TypeORMModule } from "../../typeorm/typeorm-module";
8
8
  import { ConfigUtils } from "../../shared/config.utils";
9
+ import { EnvUtils } from "../../shared/env.utils";
9
10
  import { TsConfigUtils } from "../../shared/tsconfig.utils";
10
11
  import { globSync } from "glob";
11
12
 
@@ -100,12 +101,18 @@ export class MigrateCommand {
100
101
  }
101
102
 
102
103
  /**
103
- * Detect if we should use TypeScript or JavaScript
104
- * - Check if tsconfig.json exists AND has decorator support
105
- * - Check if TypeScript files exist in include patterns
106
- * - Check if ts-node is available
104
+ * Use TypeScript only if:
105
+ * - NODE_ENV !== 'production'
106
+ * - tsconfig.json exists with decorators enabled
107
+ * - ts-node is available
108
+ * - TypeScript files exist
107
109
  */
108
110
  private static shouldUseTypeScript(): boolean {
111
+ // Never use TS in production
112
+ if (EnvUtils.getEnvironmentMode() === "production") {
113
+ return false;
114
+ }
115
+
109
116
  if (!TsConfigUtils.exists()) {
110
117
  return false;
111
118
  }
@@ -123,7 +130,9 @@ export class MigrateCommand {
123
130
 
124
131
  for (const pattern of includePatterns) {
125
132
  try {
126
- const files = globSync(pattern, { cwd });
133
+ // Normalize pattern to forward slashes for Windows compatibility
134
+ const normalizedPattern = pattern.replace(/\\/g, "/");
135
+ const files = globSync(normalizedPattern, { cwd });
127
136
  if (
128
137
  files.some(
129
138
  (file: string) => file.endsWith(".ts") && !file.endsWith(".d.ts"),
@@ -176,12 +185,14 @@ export class MigrateCommand {
176
185
  } {
177
186
  const dbConfig = ConfigUtils.getDatabaseConfig();
178
187
 
188
+ // Helper to resolve extension based on mode
179
189
  const resolveExtension = (pattern: string): string => {
180
190
  return useTypeScript
181
191
  ? pattern.replace(/\.js$/, ".ts")
182
192
  : pattern.replace(/\.ts$/, ".js");
183
193
  };
184
194
 
195
+ // If fragment.json has explicit paths, use them
185
196
  if (dbConfig.entities || dbConfig.migrations) {
186
197
  const entities = dbConfig.entities
187
198
  ? dbConfig.entities.map(resolveExtension)
@@ -195,6 +206,7 @@ export class MigrateCommand {
195
206
  : "dist/migrations/**/*.js",
196
207
  ];
197
208
 
209
+ // Extract migration directory for file creation
198
210
  const migrationDir = this.extractMigrationDirectory(
199
211
  migrations[0],
200
212
  useTypeScript,
@@ -204,6 +216,7 @@ export class MigrateCommand {
204
216
  return { entities, migrations, seeds: seedsDir };
205
217
  }
206
218
 
219
+ // No fragment.json config - infer from tsconfig
207
220
  const rootDir = TsConfigUtils.getRootDir();
208
221
  const outDir = TsConfigUtils.getOutDir();
209
222
 
@@ -227,18 +240,23 @@ export class MigrateCommand {
227
240
  pattern: string,
228
241
  useTypeScript: boolean,
229
242
  ): string {
243
+ // Handle common patterns
230
244
  if (pattern.includes("/**/*")) {
245
+ // Pattern like "src/migrations/**/*.ts" โ†’ "src/migrations"
231
246
  return pattern.split("/**/*")[0];
232
247
  }
233
248
 
234
249
  if (pattern.includes("/*.")) {
250
+ // Pattern like "src/migrations/*.ts" โ†’ "src/migrations"
235
251
  return pattern.split("/*")[0];
236
252
  }
237
253
 
254
+ // Fallback: assume it's a directory path
238
255
  if (pattern.endsWith(".ts") || pattern.endsWith(".js")) {
239
256
  return path.dirname(pattern);
240
257
  }
241
258
 
259
+ // Default fallback
242
260
  return useTypeScript ? "src/migrations" : "dist/migrations";
243
261
  }
244
262
 
@@ -265,6 +283,7 @@ export class MigrateCommand {
265
283
 
266
284
  this.verifyEntities(dataSource);
267
285
 
286
+ // Setup migrations directory - FIXED: Proper directory extraction
268
287
  const migrationsPattern = migrations[0];
269
288
  const migrationsDir = this.extractMigrationDirectory(
270
289
  migrationsPattern,
@@ -283,6 +302,7 @@ export class MigrateCommand {
283
302
  : [];
284
303
  const isFirstMigration = existingMigrations.length === 0;
285
304
 
305
+ // Generate schema changes
286
306
  spinner.text = "Analyzing schema changes...";
287
307
  const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
288
308
  const upQueries = sqlInMemory.upQueries || [];
@@ -294,6 +314,7 @@ export class MigrateCommand {
294
314
  return;
295
315
  }
296
316
 
317
+ // Create migration
297
318
  const migrationName =
298
319
  options.name ||
299
320
  (nameOrPath ? path.basename(nameOrPath).replace(/\.ts$/, "") : null) ||
@@ -366,10 +387,11 @@ export class MigrateCommand {
366
387
  }
367
388
 
368
389
  private static async createMigration(name: string): Promise<void> {
369
- const useTypeScript = this.setupEnvironment();
390
+ // Always create TypeScript migrations (user can compile if needed)
391
+ const useTypeScript = true; // Force TS for creation
370
392
  const spinner = ora("Creating TypeScript migration...").start();
371
393
  try {
372
- const { migrations } = this.getPaths(true);
394
+ const { migrations } = this.getPaths(true); // Always use TS for creation
373
395
  const migrationsDir = this.extractMigrationDirectory(migrations[0], true);
374
396
  const timestamp = Date.now();
375
397
  const fileName = `${timestamp}-${name}.ts`;
@@ -489,6 +511,7 @@ export class ${name}${timestamp} implements MigrationInterface {
489
511
  });
490
512
  }
491
513
 
514
+ // Show migration directory from config
492
515
  const migrationsPattern = migrations[0];
493
516
  const migrationsDir = this.extractMigrationDirectory(
494
517
  migrationsPattern,
@@ -506,7 +529,11 @@ export class ${name}${timestamp} implements MigrationInterface {
506
529
  );
507
530
  }
508
531
 
532
+ // Show detection info
509
533
  console.log(chalk.gray(`\n Configuration:`));
534
+ console.log(
535
+ chalk.gray(` - Environment mode: ${EnvUtils.getEnvironmentMode()}`),
536
+ );
510
537
  console.log(
511
538
  chalk.gray(` - ts-node available: ${this.isTsNodeAvailable()}`),
512
539
  );
@@ -622,10 +649,10 @@ export class ${name}${timestamp} implements MigrationInterface {
622
649
  }
623
650
 
624
651
  private static async createSeed(name: string): Promise<void> {
625
- const useTypeScript = this.setupEnvironment();
652
+ const useTypeScript = true; // Always create TS seeds
626
653
  const spinner = ora("Creating TypeScript seed...").start();
627
654
  try {
628
- const { seeds: seedsDir } = this.getPaths(true);
655
+ const { seeds: seedsDir } = this.getPaths(true); // Always create TS seeds
629
656
  const fileName = `${name}.seed.ts`;
630
657
  const filePath = path.join(seedsDir, fileName);
631
658
 
@@ -647,6 +674,7 @@ export class ${name}${timestamp} implements MigrationInterface {
647
674
  }
648
675
  }
649
676
 
677
+ // Helper methods remain the same
650
678
  private static verifyEntities(dataSource: DataSource): void {
651
679
  const entities = dataSource.entityMetadatas;
652
680
  if (entities.length === 0) {
@@ -5,196 +5,231 @@ import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import { TsConfigUtils } from "../../shared/tsconfig.utils";
7
7
 
8
-
9
8
  export class ServeCommand {
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
- });
9
+ static register(program: Command): void {
10
+ program
11
+ .command("serve")
12
+ .description("Start the development server")
13
+ .option("-w, --watch", "Watch for changes", true)
14
+ .option("-p, --port <port>", "Port to run on", "3000")
15
+ .action(async (options) => {
16
+ await ServeCommand.execute(options);
17
+ });
18
+ }
19
+
20
+ static async execute(options: any): Promise<void> {
21
+ // If NODE_ENV=production, delegate to npm start
22
+ if (process.env.NODE_ENV === "production") {
23
+ const pkg = require(path.join(process.cwd(), "package.json"));
24
+ if (!pkg.scripts?.start) {
25
+ console.error(
26
+ chalk.red("โŒ package.json missing 'start' script for production"),
27
+ );
28
+ process.exit(1);
29
+ }
30
+
31
+ console.log(chalk.blue("๐Ÿš€ Running production server via npm start..."));
32
+
33
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
34
+
35
+ try {
36
+ const proc = spawn(npmCmd, ["start"], {
37
+ stdio: "inherit",
38
+ env: { ...process.env, PORT: options.port },
39
+ });
40
+ proc.on("exit", (code) => process.exit(code || 0));
41
+ return;
42
+ } catch {
43
+ console.error(chalk.red("Failed to run npm start"));
44
+ process.exit(1);
45
+ }
19
46
  }
20
47
 
21
- static async execute(options: any): Promise<void> {
22
- console.log(chalk.blue("๐Ÿš€ Starting Fragment development server...\n"));
48
+ console.log(chalk.blue("๐Ÿš€ Starting Fragment development server...\n"));
23
49
 
24
- // Use TsConfigUtils to get the correct main file path
25
- const srcDir = TsConfigUtils.getRootDir();
26
- const mainFile = path.join(srcDir, "main.ts");
50
+ const srcDir = TsConfigUtils.getRootDir();
51
+ const mainFile = path.join(srcDir, "main.ts");
27
52
 
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
- }
53
+ if (!fs.existsSync(mainFile)) {
54
+ console.error(
55
+ chalk.red(
56
+ `Main file not found at ${path.relative(process.cwd(), mainFile)}`,
57
+ ),
58
+ );
59
+ process.exit(1);
60
+ }
36
61
 
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
- }
62
+ const tsNode = this.findTsNode();
63
+ if (!tsNode) {
64
+ console.error(chalk.red("ts-node not found. Install it:"));
65
+ console.log(chalk.cyan(" npm install --save-dev ts-node"));
66
+ process.exit(1);
67
+ }
44
68
 
45
- let appProcess: ChildProcess | null = null;
46
- let isRestarting = false;
69
+ let appProcess: ChildProcess | null = null;
70
+ let isRestarting = false;
47
71
 
48
- const startServer = () => {
49
- if (isRestarting) return;
72
+ const startServer = () => {
73
+ if (isRestarting) return;
50
74
 
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
- });
75
+ appProcess = spawn(tsNode, [mainFile], {
76
+ stdio: "inherit",
77
+ env: { ...process.env, PORT: options.port },
78
+ });
56
79
 
57
- appProcess.on("error", (error) => {
58
- console.error(chalk.red(`Failed to start server: ${error.message}`));
59
- });
80
+ appProcess.on("error", (error) => {
81
+ console.error(chalk.red(`Failed to start server: ${error.message}`));
82
+ });
60
83
 
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
- });
66
- };
84
+ appProcess.on("exit", (code) => {
85
+ if (!isRestarting && code !== null && code !== 0) {
86
+ console.error(chalk.red(`Server exited with code ${code}`));
87
+ }
88
+ });
89
+ };
90
+
91
+ const killServer = (): Promise<void> => {
92
+ return new Promise((resolve) => {
93
+ if (!appProcess || appProcess.killed) {
94
+ resolve();
95
+ return;
96
+ }
67
97
 
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");
98
+ if (process.platform === "win32") {
99
+ try {
100
+ execSync(`taskkill /pid ${appProcess.pid} /T /F`, {
101
+ stdio: "ignore",
90
102
  });
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
+ } catch {}
104
+ appProcess = null;
105
+ resolve();
106
+ } else {
107
+ const timeout = setTimeout(() => {
108
+ if (appProcess && !appProcess.killed) {
109
+ console.log(chalk.yellow("Force killing server..."));
110
+ appProcess.kill("SIGKILL");
103
111
  }
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));
112
+ resolve();
113
+ }, 3000);
110
114
 
111
- isRestarting = false;
112
- startServer();
113
- };
115
+ appProcess.once("exit", () => {
116
+ clearTimeout(timeout);
117
+ appProcess = null;
118
+ resolve();
119
+ });
114
120
 
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);
121
+ appProcess.kill("SIGTERM");
176
122
  }
177
- }
178
-
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"
123
+ });
124
+ };
125
+
126
+ const restartServer = async (filePath?: string) => {
127
+ if (isRestarting) return;
128
+ isRestarting = true;
129
+
130
+ if (filePath) {
131
+ console.log(
132
+ chalk.yellow(
133
+ `\n๐Ÿ”„ File changed: ${path.relative(process.cwd(), filePath)}`,
134
+ ),
186
135
  );
136
+ }
137
+
138
+ console.log(chalk.blue("Restarting server...\n"));
139
+ await killServer();
140
+ await new Promise((r) => setTimeout(r, 500));
141
+ isRestarting = false;
142
+ startServer();
143
+ };
144
+
145
+ startServer();
146
+
147
+ if (options.watch) {
148
+ try {
149
+ const chokidar = await import("chokidar");
150
+ const watchPattern = path.join(srcDir, "**/*.ts");
151
+
152
+ const watcher = chokidar.watch(watchPattern, {
153
+ ignored: /(^|[/\\])\../,
154
+ persistent: true,
155
+ ignoreInitial: true,
156
+ });
157
+
158
+ let debounceTimer: NodeJS.Timeout | null = null;
159
+
160
+ watcher.on("change", (filePath) => {
161
+ if (debounceTimer) clearTimeout(debounceTimer);
162
+ debounceTimer = setTimeout(() => restartServer(filePath), 300);
163
+ });
164
+
165
+ watcher.on("error", (error) => {
166
+ console.error(chalk.red(`Watcher error: ${error}`));
167
+ });
168
+
169
+ const cleanup = async () => {
170
+ console.log(chalk.yellow("\n\n๐Ÿ›‘ Shutting down..."));
171
+ await watcher.close();
172
+ await killServer();
173
+ process.exit(0);
174
+ };
187
175
 
188
- if (fs.existsSync(localTsNode)) {
189
- return localTsNode;
190
- }
176
+ process.on("exit", cleanup);
177
+ process.on("SIGINT", () => process.exit(0));
178
+ process.on("SIGTERM", () => process.exit(0));
191
179
 
192
- // Try global ts-node
193
- try {
194
- execSync("ts-node --version", { stdio: "ignore" });
195
- return "ts-node";
196
- } catch {
197
- return null;
180
+ if (process.platform === "win32") {
181
+ const rl = require("readline").createInterface({
182
+ input: process.stdin,
183
+ output: process.stdout,
184
+ });
185
+ rl.on("close", () => process.exit(0));
198
186
  }
187
+ } catch {
188
+ console.error(
189
+ chalk.red(
190
+ "Failed to start file watcher. Install chokidar: npm install chokidar",
191
+ ),
192
+ );
193
+ process.exit(1);
194
+ }
195
+ } else {
196
+ const cleanup = async () => {
197
+ await killServer();
198
+ process.exit(0);
199
+ };
200
+
201
+ process.on("exit", cleanup);
202
+ process.on("SIGINT", () => process.exit(0));
203
+ process.on("SIGTERM", () => process.exit(0));
204
+
205
+ if (process.platform === "win32") {
206
+ const rl = require("readline").createInterface({
207
+ input: process.stdin,
208
+ output: process.stdout,
209
+ });
210
+ rl.on("close", () => process.exit(0));
211
+ }
212
+ }
213
+ }
214
+
215
+ private static findTsNode(): string | null {
216
+ const binName = process.platform === "win32" ? "ts-node.cmd" : "ts-node";
217
+ const localTsNode = path.join(
218
+ process.cwd(),
219
+ "node_modules",
220
+ ".bin",
221
+ binName,
222
+ );
223
+
224
+ if (fs.existsSync(localTsNode)) {
225
+ return localTsNode;
226
+ }
227
+
228
+ try {
229
+ execSync("ts-node --version", { stdio: "ignore" });
230
+ return binName;
231
+ } catch {
232
+ return null;
199
233
  }
234
+ }
200
235
  }
@@ -23,27 +23,49 @@ export class TestCommand {
23
23
 
24
24
  private static async runTests(options: any): Promise<void> {
25
25
  console.log(chalk.blue("\n๐Ÿงช Running Fragment Tests...\n"));
26
+
26
27
  const cwd = process.cwd();
27
28
  const hasSource = fs.existsSync(path.join(cwd, "src"));
28
29
  const hasDist = fs.existsSync(path.join(cwd, "dist"));
29
30
 
31
+ // Determine mode based on NODE_ENV
32
+ const isProduction = EnvUtils.getEnvironmentMode() === "production";
30
33
  let useTypeScript: boolean;
34
+ let mode: string;
31
35
  let basePath: string;
32
36
  let pattern: string;
33
37
 
34
- if (hasSource) {
35
- useTypeScript = true;
36
- basePath = "src";
37
- pattern = options.pattern || "**/*.spec.ts";
38
- } else if (hasDist) {
38
+ if (isProduction) {
39
+ if (!hasDist) {
40
+ console.log(
41
+ chalk.red(
42
+ "Production mode detected but dist/ directory not found. Run: fragment build",
43
+ ),
44
+ );
45
+ return;
46
+ }
39
47
  useTypeScript = false;
48
+ mode = "production (dist/)";
40
49
  basePath = "dist";
41
- pattern = (options.pattern || "**/*.spec.ts").replace(/\.ts$/, ".js");
50
+ pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
42
51
  } else {
43
- console.log(chalk.red("No src/ or dist/ found. Run: fragment init"));
44
- return;
52
+ if (!hasSource) {
53
+ console.log(
54
+ chalk.red(
55
+ "Development mode detected but src/ directory not found. Run: fragment init",
56
+ ),
57
+ );
58
+ return;
59
+ }
60
+ useTypeScript = true;
61
+ mode = "development (src/)";
62
+ basePath = "src";
63
+ pattern = options.pattern || "**/*.spec.ts";
45
64
  }
46
65
 
66
+ console.log(chalk.gray(` Mode: ${mode}`));
67
+ console.log(chalk.gray(` Pattern: ${basePath}/${pattern}`));
68
+
47
69
  // Check if we need to use ts-node for TypeScript files
48
70
  const hasTsConfig = TsConfigUtils.exists();
49
71
 
package/src/cli/index.ts CHANGED
@@ -4,21 +4,13 @@ import * as path from "path";
4
4
 
5
5
  const cwd = process.cwd();
6
6
 
7
- // Order: lowest priority โ†’ highest priority
8
- const envFiles = [
9
- ".env", // base
10
- ".env.local", // gitignored local overrides
11
- ".env.prod", // production (or use .env.staging, etc.)
12
- ]
13
- .map((file) => path.join(cwd, file))
7
+ const envFiles = [".env", ".env.local"]
8
+ .map((f) => path.join(cwd, f))
14
9
  .filter(fs.existsSync);
15
10
 
16
- envFiles.forEach((file) => {
17
- dotenv.config({ path: file });
18
- });
11
+ envFiles.forEach((f) => dotenv.config({ path: f }));
19
12
 
20
13
  import { Command } from "commander";
21
- import { EnvCommand } from "./commands/env.command";
22
14
  import { InitCommand } from "./commands/init.command";
23
15
  import { GenerateCommand } from "./commands/generate.command";
24
16
  import { ServeCommand } from "./commands/serve.command";
@@ -37,7 +29,6 @@ program
37
29
  .version("1.0.0");
38
30
 
39
31
  // Register all commands
40
- EnvCommand.register(program);
41
32
  InitCommand.register(program);
42
33
  GenerateCommand.register(program);
43
34
  ServeCommand.register(program);
@@ -196,4 +196,19 @@ export class ComponentScanner {
196
196
  }
197
197
  }
198
198
  }
199
+
200
+ /**
201
+ * Auto-scan based on current environment mode
202
+ */
203
+ static async autoScan(): Promise<void> {
204
+ // Detect environment using the same logic as other components
205
+ const isDevMode = EnvUtils.isDevelopmentMode();;
206
+
207
+ if (isDevMode) {
208
+ await this.scanSource();
209
+ } else {
210
+ await this.scan();
211
+ }
212
+ }
213
+
199
214
  }