@nitronjs/framework 0.2.2 → 0.2.4

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 (71) hide show
  1. package/README.md +3 -1
  2. package/cli/create.js +88 -72
  3. package/cli/njs.js +17 -19
  4. package/lib/Auth/Auth.js +167 -0
  5. package/lib/Build/CssBuilder.js +9 -0
  6. package/lib/Build/FileAnalyzer.js +16 -0
  7. package/lib/Build/HydrationBuilder.js +17 -0
  8. package/lib/Build/Manager.js +15 -0
  9. package/lib/Build/colors.js +4 -0
  10. package/lib/Build/plugins.js +84 -20
  11. package/lib/Console/Commands/DevCommand.js +13 -9
  12. package/lib/Console/Commands/MakeCommand.js +24 -10
  13. package/lib/Console/Commands/MigrateCommand.js +4 -3
  14. package/lib/Console/Commands/MigrateFreshCommand.js +22 -27
  15. package/lib/Console/Commands/MigrateRollbackCommand.js +8 -4
  16. package/lib/Console/Commands/MigrateStatusCommand.js +8 -4
  17. package/lib/Console/Commands/SeedCommand.js +8 -28
  18. package/lib/Console/Commands/StorageLinkCommand.js +20 -5
  19. package/lib/Console/Output.js +143 -0
  20. package/lib/Core/Config.js +2 -1
  21. package/lib/Core/Paths.js +8 -8
  22. package/lib/Database/DB.js +141 -51
  23. package/lib/Database/Drivers/MySQLDriver.js +102 -157
  24. package/lib/Database/Migration/Checksum.js +3 -8
  25. package/lib/Database/Migration/MigrationRepository.js +25 -35
  26. package/lib/Database/Migration/MigrationRunner.js +59 -67
  27. package/lib/Database/Model.js +165 -75
  28. package/lib/Database/QueryBuilder.js +43 -0
  29. package/lib/Database/QueryValidation.js +51 -30
  30. package/lib/Database/Schema/Blueprint.js +25 -36
  31. package/lib/Database/Schema/Manager.js +31 -68
  32. package/lib/Database/Seeder/SeederRunner.js +24 -145
  33. package/lib/Date/DateTime.js +9 -0
  34. package/lib/Encryption/Encryption.js +52 -0
  35. package/lib/Faker/Faker.js +11 -0
  36. package/lib/Filesystem/Storage.js +120 -0
  37. package/lib/HMR/Server.js +79 -9
  38. package/lib/Hashing/Hash.js +41 -0
  39. package/lib/Http/Server.js +179 -151
  40. package/lib/Logging/{Manager.js → Log.js} +68 -80
  41. package/lib/Mail/Mail.js +187 -0
  42. package/lib/Route/Router.js +416 -0
  43. package/lib/Session/File.js +135 -233
  44. package/lib/Session/Manager.js +117 -171
  45. package/lib/Session/Memory.js +28 -38
  46. package/lib/Session/Session.js +71 -107
  47. package/lib/Support/Str.js +103 -0
  48. package/lib/Translation/Lang.js +54 -0
  49. package/lib/View/Client/hmr-client.js +87 -51
  50. package/lib/View/Client/nitronjs-icon.png +0 -0
  51. package/lib/View/{Manager.js → View.js} +44 -29
  52. package/lib/index.d.ts +49 -27
  53. package/lib/index.js +19 -13
  54. package/package.json +1 -1
  55. package/skeleton/app/Controllers/HomeController.js +7 -1
  56. package/skeleton/package.json +2 -0
  57. package/skeleton/resources/css/global.css +1 -0
  58. package/skeleton/resources/views/Site/Home.tsx +456 -79
  59. package/skeleton/tsconfig.json +6 -1
  60. package/lib/Auth/Manager.js +0 -111
  61. package/lib/Database/Connection.js +0 -61
  62. package/lib/Database/Manager.js +0 -162
  63. package/lib/Database/Migration/migrations/0000_00_00_00_01_create_seeders_table.js +0 -20
  64. package/lib/Database/Seeder/SeederRepository.js +0 -45
  65. package/lib/Encryption/Manager.js +0 -47
  66. package/lib/Filesystem/Manager.js +0 -74
  67. package/lib/Hashing/Manager.js +0 -25
  68. package/lib/Mail/Manager.js +0 -120
  69. package/lib/Route/Loader.js +0 -80
  70. package/lib/Route/Manager.js +0 -286
  71. package/lib/Translation/Manager.js +0 -49
@@ -1,3 +1,7 @@
1
+ /**
2
+ * ANSI color codes for terminal output.
3
+ * @type {Object<string, string>}
4
+ */
1
5
  const COLORS = {
2
6
  reset: "\x1b[0m",
3
7
  dim: "\x1b[2m",
@@ -7,39 +7,71 @@ import COLORS from "./colors.js";
7
7
 
8
8
  const _traverse = traverse.default;
9
9
 
10
+ /**
11
+ * Creates an esbuild plugin for path alias resolution.
12
+ * Supports @/, @css/, @views/, @models/, @controllers/, @middlewares/ aliases.
13
+ * @returns {import("esbuild").Plugin}
14
+ */
10
15
  export function createPathAliasPlugin() {
11
16
  const root = Paths.project;
17
+
18
+ // Define path aliases for developer convenience
19
+ const aliases = {
20
+ "@/": root, // Project root
21
+ "@css/": path.join(root, "resources/css"), // CSS files
22
+ "@views/": path.join(root, "resources/views"), // View components
23
+ "@models/": path.join(root, "app/Models"), // Models
24
+ "@controllers/": path.join(root, "app/Controllers"), // Controllers
25
+ "@middlewares/": path.join(root, "app/Middlewares"), // Middlewares
26
+ };
27
+
12
28
  return {
13
29
  name: "path-alias",
14
30
  setup: (build) => {
15
- // @ resolves to project root
16
- build.onResolve({ filter: /^@\// }, async (args) => {
17
- const relativePath = args.path.replace(/^@\//, "");
18
- const absolutePath = path.join(root, relativePath);
19
-
20
- const extensions = [".js", ".ts", ".jsx", ".tsx", ""];
21
- for (const ext of extensions) {
22
- const fullPath = absolutePath + ext;
23
- if (fs.existsSync(fullPath)) {
24
- return { path: fullPath, external: false };
25
- }
26
- }
27
-
28
- if (fs.existsSync(absolutePath) && fs.statSync(absolutePath).isDirectory()) {
29
- for (const ext of [".js", ".ts", ".jsx", ".tsx"]) {
30
- const indexPath = path.join(absolutePath, "index" + ext);
31
- if (fs.existsSync(indexPath)) {
32
- return { path: indexPath, external: false };
31
+ // Handle all @ prefixed imports
32
+ build.onResolve({ filter: /^@[a-z]*\// }, async (args) => {
33
+ // Find matching alias (longest match first)
34
+ const sortedPrefixes = Object.keys(aliases).sort((a, b) => b.length - a.length);
35
+
36
+ for (const prefix of sortedPrefixes) {
37
+ if (args.path.startsWith(prefix)) {
38
+ const relativePath = args.path.slice(prefix.length);
39
+ const absolutePath = path.join(aliases[prefix], relativePath);
40
+ const extensions = [".js", ".ts", ".jsx", ".tsx", ".css", ""];
41
+
42
+ for (const ext of extensions) {
43
+ const fullPath = absolutePath + ext;
44
+
45
+ if (fs.existsSync(fullPath)) {
46
+ return { path: fullPath, external: false };
47
+ }
48
+ }
49
+
50
+ // Check for directory with index file
51
+ if (fs.existsSync(absolutePath) && fs.statSync(absolutePath).isDirectory()) {
52
+ for (const ext of [".js", ".ts", ".jsx", ".tsx"]) {
53
+ const indexPath = path.join(absolutePath, "index" + ext);
54
+
55
+ if (fs.existsSync(indexPath)) {
56
+ return { path: indexPath, external: false };
57
+ }
58
+ }
33
59
  }
60
+
61
+ return { path: absolutePath, external: false };
34
62
  }
35
63
  }
36
-
37
- return { path: absolutePath + ".js", external: false };
64
+
65
+ return null;
38
66
  });
39
67
  }
40
68
  };
41
69
  }
42
70
 
71
+ /**
72
+ * Creates an esbuild plugin for original JSX runtime resolution.
73
+ * @returns {import("esbuild").Plugin}
74
+ */
43
75
  export function createOriginalJsxPlugin() {
44
76
  return {
45
77
  name: "original-jsx",
@@ -52,6 +84,11 @@ export function createOriginalJsxPlugin() {
52
84
  };
53
85
  }
54
86
 
87
+ /**
88
+ * Creates an esbuild plugin that maps React packages to window globals.
89
+ * Used for client bundles to share React instance across islands.
90
+ * @returns {import("esbuild").Plugin}
91
+ */
55
92
  export function createVendorGlobalsPlugin() {
56
93
  const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\\/]/g, "\\$&");
57
94
 
@@ -86,6 +123,11 @@ export function createVendorGlobalsPlugin() {
86
123
  };
87
124
  }
88
125
 
126
+ /**
127
+ * Creates an esbuild plugin that blocks server-only modules in client bundles.
128
+ * Provides helpful error messages when server modules are imported incorrectly.
129
+ * @returns {import("esbuild").Plugin}
130
+ */
89
131
  export function createServerModuleBlockerPlugin() {
90
132
  const SERVER_MODULES = /lib\/(DB|Mail|Log|Hash|Environment|Server|Model|Validator|Storage)\.js$/;
91
133
 
@@ -118,6 +160,11 @@ export function createServerModuleBlockerPlugin() {
118
160
  };
119
161
  }
120
162
 
163
+ /**
164
+ * Creates an esbuild plugin that transforms server functions for client use.
165
+ * Replaces csrf() and route() calls with runtime lookups.
166
+ * @returns {import("esbuild").Plugin}
167
+ */
121
168
  export function createServerFunctionsPlugin() {
122
169
  return {
123
170
  name: "server-functions",
@@ -152,6 +199,11 @@ export function createServerFunctionsPlugin() {
152
199
  };
153
200
  }
154
201
 
202
+ /**
203
+ * Creates an esbuild plugin that stubs CSS imports.
204
+ * Prevents CSS from being bundled into JS files.
205
+ * @returns {import("esbuild").Plugin}
206
+ */
155
207
  export function createCssStubPlugin() {
156
208
  return {
157
209
  name: "css-stub",
@@ -169,6 +221,13 @@ export function createCssStubPlugin() {
169
221
  };
170
222
  }
171
223
 
224
+ /**
225
+ * Creates an esbuild plugin that marks client components with a symbol.
226
+ * Used for Islands Architecture to identify hydration boundaries.
227
+ * @param {import("esbuild").BuildOptions} options - Build options.
228
+ * @param {boolean} isDev - Whether in development mode.
229
+ * @returns {import("esbuild").Plugin}
230
+ */
172
231
  export function createMarkerPlugin(options, isDev) {
173
232
  return {
174
233
  name: "client-marker",
@@ -215,6 +274,11 @@ export function createMarkerPlugin(options, isDev) {
215
274
  };
216
275
  }
217
276
 
277
+ /**
278
+ * Finds all exported functions/components in an AST.
279
+ * @param {import("@babel/parser").ParseResult} ast - Parsed AST.
280
+ * @returns {Array<{name: string}>} Array of export names.
281
+ */
218
282
  function findExports(ast) {
219
283
  const exports = [];
220
284
 
@@ -11,14 +11,16 @@ import Builder from "../../Build/Manager.js";
11
11
  dotenv.config({ quiet: true });
12
12
  Environment.setDev(true);
13
13
 
14
- const C = { r: "\x1b[0m", d: "\x1b[2m", red: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", b: "\x1b[34m", m: "\x1b[35m", c: "\x1b[36m" };
14
+ const C = { r: "\x1b[0m", d: "\x1b[2m", bold: "\x1b[1m", red: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", b: "\x1b[34m", m: "\x1b[35m", c: "\x1b[36m" };
15
+
16
+ // Using consistent-width characters for alignment
15
17
  const ICONS = {
16
- info: `${C.b}ℹ${C.r} `,
17
- ok: `${C.g}✓${C.r} `,
18
- err: `${C.red}✗${C.r} `,
19
- build: `${C.m}⟳${C.r} `,
20
- hmr: `${C.c}⚡${C.r}`,
21
- watch: `${C.y}○${C.r} `
18
+ info: `${C.b}i${C.r}`,
19
+ ok: `${C.g}✓${C.r}`,
20
+ err: `${C.red}✗${C.r}`,
21
+ build: `${C.y}○${C.r}`,
22
+ hmr: `${C.m}●${C.r}`,
23
+ watch: `${C.c}◐${C.r}`
22
24
  };
23
25
 
24
26
  const PATTERNS = {
@@ -35,8 +37,10 @@ class DevServer {
35
37
  #debounce = { build: null, restart: null };
36
38
 
37
39
  #log(icon, msg, extra) {
38
- const t = `${C.d}${new Date().toLocaleTimeString()}${C.r}`;
39
- console.log(`${t} ${ICONS[icon] || ICONS.info} ${msg}${extra ? ` ${C.d}(${extra})${C.r}` : ""}`);
40
+ const time = new Date().toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
41
+ const iconStr = ICONS[icon] || ICONS.info;
42
+ const extraStr = extra ? ` ${C.d}(${extra})${C.r}` : "";
43
+ console.log(`${C.d}${time}${C.r} ${iconStr} ${msg}${extraStr}`);
40
44
  }
41
45
 
42
46
  async #build(only = null) {
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import Paths from '../../Core/Paths.js';
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import Paths from "../../Core/Paths.js";
5
+ import Output from "../Output.js";
5
6
 
6
7
  const baseDirs = {
7
8
  controller: 'app/Controllers',
@@ -47,22 +48,30 @@ function toPascalCase(str) {
47
48
  .join('');
48
49
  }
49
50
 
51
+ /**
52
+ * Creates a new file from a template.
53
+ * @param {string} type - The type of file (controller, model, etc.).
54
+ * @param {string} rawName - The name/path of the file to create.
55
+ * @returns {Promise<boolean>}
56
+ */
50
57
  export default async function make(type, rawName) {
51
58
  if (!type || !rawName) {
52
- console.error("\x1b[31m%s\x1b[0m", "Usage: njs make:<type> <Name/Path> (e.g., njs make:controller Admin/HomeController)");
59
+ Output.error("Usage: njs make:<type> <Name/Path>");
60
+ Output.dim(" Example: njs make:controller Admin/HomeController");
61
+
53
62
  return false;
54
63
  }
55
64
 
56
- const parts = rawName.split('/');
57
- const subDirs = parts.slice(0, -1).join('/');
65
+ const parts = rawName.split("/");
66
+ const subDirs = parts.slice(0, -1).join("/");
58
67
  const outputDir = path.join(Paths.project, baseDirs[type]);
59
68
 
60
69
  let className = parts[parts.length - 1];
61
70
  let fileName = `${className}.js`;
62
71
 
63
- if (type === 'migration') {
72
+ if (type === "migration") {
64
73
  const now = new Date();
65
- const pad = (n) => n.toString().padStart(2, '0');
74
+ const pad = (n) => n.toString().padStart(2, "0");
66
75
 
67
76
  const year = now.getFullYear();
68
77
  const month = pad(now.getMonth() + 1);
@@ -78,7 +87,7 @@ export default async function make(type, rawName) {
78
87
  const fullPath = path.join(fullDir, fileName);
79
88
 
80
89
  const templatePath = path.join(Paths.frameworkTemplates, templates[type]);
81
- let template = fs.readFileSync(templatePath, 'utf-8');
90
+ let template = fs.readFileSync(templatePath, "utf-8");
82
91
 
83
92
  if (type === "model") {
84
93
  template = template.replace(/__CLASS__/g, className).replace(/__TABLE__/g, toTableName(className));
@@ -93,7 +102,12 @@ export default async function make(type, rawName) {
93
102
  fs.mkdirSync(fullDir, { recursive: true });
94
103
  fs.writeFileSync(fullPath, template);
95
104
 
96
- console.log("\x1b[32m%s\x1b[0m", `${type[0].toUpperCase() + type.slice(1)} successfully created: ${fullPath}`);
105
+ const typeLabel = type.charAt(0).toUpperCase() + type.slice(1);
106
+ const relativePath = path.relative(Paths.project, fullPath);
107
+
108
+ Output.success(`${typeLabel} created`);
109
+ Output.dim(` ${relativePath}`);
110
+
97
111
  return true;
98
112
  }
99
113
 
@@ -12,7 +12,8 @@ export default async function migrate(options = {}) {
12
12
 
13
13
  try {
14
14
  await DB.setup();
15
- } catch (error) {
15
+ }
16
+ catch (error) {
16
17
  console.error('❌ Database setup failed:', error.message);
17
18
  console.error('Check your .env file and ensure the database exists and is accessible');
18
19
  return false;
@@ -27,14 +28,14 @@ export default async function migrate(options = {}) {
27
28
  }
28
29
 
29
30
  if (shouldSeed && result.ran.length > 0) {
30
- console.log('\n');
31
31
  await seed();
32
32
  }
33
33
 
34
34
  await DB.close();
35
35
  return true;
36
36
 
37
- } catch (error) {
37
+ }
38
+ catch (error) {
38
39
  console.error('❌ Migration error:', error.message);
39
40
  await DB.close();
40
41
  return false;
@@ -2,16 +2,9 @@ import dotenv from 'dotenv';
2
2
  import DB from '../../Database/DB.js';
3
3
  import Config from '../../Core/Config.js';
4
4
  import MigrationRunner from '../../Database/Migration/MigrationRunner.js';
5
+ import Output from '../../Console/Output.js';
5
6
  import seed from './SeedCommand.js';
6
7
 
7
- const COLORS = {
8
- reset: '\x1b[0m',
9
- green: '\x1b[32m',
10
- yellow: '\x1b[33m',
11
- cyan: '\x1b[36m',
12
- dim: '\x1b[2m'
13
- };
14
-
15
8
  export default async function migrateFresh(options = {}) {
16
9
  const { seed: shouldSeed = false } = options;
17
10
 
@@ -20,28 +13,29 @@ export default async function migrateFresh(options = {}) {
20
13
 
21
14
  try {
22
15
  await DB.setup();
23
- } catch (error) {
24
- console.error('❌ Database setup failed:', error.message);
25
- console.error('Check your .env file and ensure the database exists and is accessible');
16
+ }
17
+ catch (error) {
18
+ Output.newline();
19
+ Output.error(`Database setup failed: ${error.message}`);
20
+ Output.errorDetail('Check your .env file and ensure the database exists and is accessible');
26
21
  return false;
27
22
  }
28
23
 
29
24
  try {
30
- console.log(`${COLORS.yellow}⚠️ Dropping all tables...${COLORS.reset}\n`);
31
-
32
- const [tableRows] = await DB.query("SHOW TABLES");
33
- await DB.rawQuery("SET FOREIGN_KEY_CHECKS = 0");
34
-
35
- for (const row of tableRows) {
36
- const tableName = Object.values(row)[0];
37
- console.log(`${COLORS.dim}Dropping:${COLORS.reset} ${COLORS.cyan}${tableName}${COLORS.reset}`);
38
- await DB.rawQuery(`DROP TABLE IF EXISTS \`${tableName}\``);
39
- }
25
+ const [tableRows] = await DB.rawQuery("SHOW TABLES");
26
+
27
+ if (tableRows.length > 0) {
28
+ Output.dropTablesHeader();
29
+ await DB.rawQuery("SET FOREIGN_KEY_CHECKS = 0");
40
30
 
41
- await DB.rawQuery("SET FOREIGN_KEY_CHECKS = 1");
31
+ for (const row of tableRows) {
32
+ const tableName = Object.values(row)[0];
33
+ Output.droppingTable(tableName);
34
+ await DB.rawQuery(`DROP TABLE IF EXISTS \`${tableName}\``);
35
+ }
42
36
 
43
- if (tableRows.length > 0) {
44
- console.log(`\n${COLORS.green}✅ Dropped ${tableRows.length} table(s)${COLORS.reset}\n`);
37
+ await DB.rawQuery("SET FOREIGN_KEY_CHECKS = 1");
38
+ Output.dropTablesSuccess(tableRows.length);
45
39
  }
46
40
 
47
41
  const result = await MigrationRunner.run();
@@ -52,15 +46,16 @@ export default async function migrateFresh(options = {}) {
52
46
  }
53
47
 
54
48
  if (shouldSeed && result.ran.length > 0) {
55
- console.log('\n');
56
49
  await seed();
57
50
  }
58
51
 
59
52
  await DB.close();
60
53
  return true;
61
54
 
62
- } catch (error) {
63
- console.error('❌ Fresh migration error:', error.message);
55
+ }
56
+ catch (error) {
57
+ Output.newline();
58
+ Output.error(`Fresh migration error: ${error.message}`);
64
59
  await DB.close();
65
60
  return false;
66
61
  }
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
2
2
  import DB from '../../Database/DB.js';
3
3
  import Config from '../../Core/Config.js';
4
4
  import MigrationRunner from '../../Database/Migration/MigrationRunner.js';
5
+ import Output from '../../Console/Output.js';
5
6
 
6
7
  export default async function rollback(options = {}) {
7
8
  const { step = 1, all = false } = options;
@@ -11,9 +12,11 @@ export default async function rollback(options = {}) {
11
12
 
12
13
  try {
13
14
  await DB.setup();
14
- } catch (error) {
15
- console.error('❌ Database setup failed:', error.message);
16
- console.error('Check your .env file and ensure the database exists and is accessible');
15
+ }
16
+ catch (error) {
17
+ Output.newline();
18
+ Output.error(`Database setup failed: ${error.message}`);
19
+ Output.errorDetail('Check your .env file and ensure the database exists and is accessible');
17
20
  return false;
18
21
  }
19
22
 
@@ -30,7 +33,8 @@ export default async function rollback(options = {}) {
30
33
  return result.success;
31
34
 
32
35
  } catch (error) {
33
- console.error('❌ Rollback error:', error.message);
36
+ Output.newline();
37
+ Output.error(`Rollback error: ${error.message}`);
34
38
  await DB.close();
35
39
  return false;
36
40
  }
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
2
2
  import DB from '../../Database/DB.js';
3
3
  import Config from '../../Core/Config.js';
4
4
  import MigrationRunner from '../../Database/Migration/MigrationRunner.js';
5
+ import Output from '../../Console/Output.js';
5
6
 
6
7
  export default async function status() {
7
8
  dotenv.config({ quiet: true });
@@ -9,9 +10,11 @@ export default async function status() {
9
10
 
10
11
  try {
11
12
  await DB.setup();
12
- } catch (error) {
13
- console.error('❌ Database setup failed:', error.message);
14
- console.error('Check your .env file and ensure the database exists and is accessible');
13
+ }
14
+ catch (error) {
15
+ Output.newline();
16
+ Output.error(`Database setup failed: ${error.message}`);
17
+ Output.errorDetail('Check your .env file and ensure the database exists and is accessible');
15
18
  return false;
16
19
  }
17
20
 
@@ -21,7 +24,8 @@ export default async function status() {
21
24
  return true;
22
25
 
23
26
  } catch (error) {
24
- console.error('❌ Status error:', error.message);
27
+ Output.newline();
28
+ Output.error(`Status error: ${error.message}`);
25
29
  await DB.close();
26
30
  return false;
27
31
  }
@@ -3,39 +3,26 @@ import DB from '../../Database/DB.js';
3
3
  import Config from '../../Core/Config.js';
4
4
  import SeederRunner from '../../Database/Seeder/SeederRunner.js';
5
5
 
6
- export default async function seed(options = {}) {
7
- const { environment = 'prod', status = false } = options;
8
-
6
+ export default async function seed(seederName = null) {
9
7
  dotenv.config({ quiet: true });
10
8
  await Config.initialize();
11
9
 
12
10
  try {
13
11
  await DB.setup();
14
- } catch (error) {
12
+ }
13
+ catch (error) {
15
14
  console.error('❌ Database setup failed:', error.message);
16
15
  console.error('Check your .env file and ensure the database exists and is accessible');
17
16
  return false;
18
17
  }
19
18
 
20
19
  try {
21
- if (status) {
22
- await SeederRunner.printStatus();
23
- await DB.close();
24
- return true;
25
- }
26
-
27
- let result;
28
-
29
- if (environment === 'all') {
30
- result = await SeederRunner.runAll();
31
- } else {
32
- result = await SeederRunner.run(environment);
33
- }
34
-
20
+ const result = await SeederRunner.run(seederName);
35
21
  await DB.close();
36
22
  return result.success;
37
23
 
38
- } catch (error) {
24
+ }
25
+ catch (error) {
39
26
  console.error('❌ Seeding error:', error.message);
40
27
  await DB.close();
41
28
  return false;
@@ -45,16 +32,9 @@ export default async function seed(options = {}) {
45
32
  const isMain = process.argv[1]?.endsWith("SeedCommand.js");
46
33
  if (isMain) {
47
34
  const args = process.argv.slice(2);
35
+ const seederName = args.find(a => !a.startsWith('--')) || null;
48
36
 
49
- const showStatus = args.includes('--status');
50
- const isDev = args.includes('--dev');
51
- const isAll = args.includes('--all');
52
-
53
- let environment = 'prod';
54
- if (isDev) environment = 'dev';
55
- if (isAll) environment = 'all';
56
-
57
- seed({ environment, status: showStatus })
37
+ seed(seederName)
58
38
  .then(success => process.exit(success ? 0 : 1))
59
39
  .catch(err => {
60
40
  console.error(err);
@@ -1,21 +1,36 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import os from "os";
4
+ import Output from "../Output.js";
4
5
 
6
+ /**
7
+ * Creates a symbolic link from storage/app/public to public/storage.
8
+ * @returns {Promise<boolean>}
9
+ */
5
10
  export default async function storageLink() {
6
11
  const source = path.join(process.cwd(), "storage", "app", "public");
7
12
  const target = path.join(process.cwd(), "public", "storage");
8
-
9
- let linkType = os.platform() === 'win32' ? 'junction' : 'dir';
13
+ const linkType = os.platform() === "win32" ? "junction" : "dir";
10
14
 
11
15
  return new Promise((resolve, reject) => {
12
16
  fs.symlink(source, target, linkType, (err) => {
13
17
  if (err) {
14
- console.error('Error creating symbolic link:', err);
15
- reject(err);
18
+ if (err.code === "EEXIST") {
19
+ Output.warn("Symbolic link already exists");
20
+ Output.dim(` ${source} → ${target}`);
21
+ Output.newline();
22
+ resolve(true);
23
+ }
24
+ else {
25
+ Output.error("Failed to create symbolic link");
26
+ Output.dim(` ${err.message}`);
27
+ reject(err);
28
+ }
16
29
  }
17
30
  else {
18
- console.log('Symbolic link has been created successfully.');
31
+ Output.success("Symbolic link created");
32
+ Output.dim(` ${source} → ${target}`);
33
+ Output.newline();
19
34
  resolve(true);
20
35
  }
21
36
  });