@warlock.js/core 4.10.0 → 4.12.0

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 (144) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/esm/application/application.d.mts +19 -0
  3. package/esm/application/application.d.mts.map +1 -1
  4. package/esm/application/application.mjs +25 -0
  5. package/esm/application/application.mjs.map +1 -1
  6. package/esm/application/boot-signal.d.mts +77 -0
  7. package/esm/application/boot-signal.d.mts.map +1 -0
  8. package/esm/application/boot-signal.mjs +64 -0
  9. package/esm/application/boot-signal.mjs.map +1 -0
  10. package/esm/application/index.d.mts +1 -0
  11. package/esm/application/index.mjs +1 -0
  12. package/esm/bootstrap.mjs +2 -2
  13. package/esm/bootstrap.mjs.map +1 -1
  14. package/esm/cli/cli-commands.manager.mjs +31 -8
  15. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  16. package/esm/cli/cli-commands.utils.mjs +57 -1
  17. package/esm/cli/cli-commands.utils.mjs.map +1 -1
  18. package/esm/cli/commands/add.command.mjs +4 -2
  19. package/esm/cli/commands/add.command.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generate.command.mjs +42 -21
  21. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  22. package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
  23. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  24. package/esm/cli/commands/migrate.command.mjs +6 -2
  25. package/esm/cli/commands/migrate.command.mjs.map +1 -1
  26. package/esm/cli/commands/start-production.command.mjs +4 -25
  27. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  28. package/esm/cli/parse-cli-args.mjs +86 -9
  29. package/esm/cli/parse-cli-args.mjs.map +1 -1
  30. package/esm/cli/types.d.mts +6 -1
  31. package/esm/cli/types.d.mts.map +1 -1
  32. package/esm/config/config-getter.mjs +5 -5
  33. package/esm/config/config-getter.mjs.map +1 -1
  34. package/esm/config/config-loader.mjs +2 -2
  35. package/esm/config/config-loader.mjs.map +1 -1
  36. package/esm/config/config-setter.d.mts +27 -0
  37. package/esm/config/config-setter.d.mts.map +1 -0
  38. package/esm/config/config-setter.mjs +10 -0
  39. package/esm/config/config-setter.mjs.map +1 -0
  40. package/esm/config/index.mjs +1 -0
  41. package/esm/connectors/access-connector.mjs +2 -2
  42. package/esm/connectors/access-connector.mjs.map +1 -1
  43. package/esm/connectors/ai-connector.mjs +2 -2
  44. package/esm/connectors/ai-connector.mjs.map +1 -1
  45. package/esm/connectors/cache-connector.mjs +2 -2
  46. package/esm/connectors/cache-connector.mjs.map +1 -1
  47. package/esm/connectors/database-connector.mjs +2 -2
  48. package/esm/connectors/database-connector.mjs.map +1 -1
  49. package/esm/connectors/herald-connector.mjs +2 -2
  50. package/esm/connectors/herald-connector.mjs.map +1 -1
  51. package/esm/connectors/http-connector.d.mts.map +1 -1
  52. package/esm/connectors/http-connector.mjs +8 -7
  53. package/esm/connectors/http-connector.mjs.map +1 -1
  54. package/esm/connectors/logger-connector.mjs +2 -2
  55. package/esm/connectors/logger-connector.mjs.map +1 -1
  56. package/esm/connectors/mail-connector.mjs +2 -2
  57. package/esm/connectors/mail-connector.mjs.map +1 -1
  58. package/esm/connectors/notifications-connector.mjs +2 -2
  59. package/esm/connectors/notifications-connector.mjs.map +1 -1
  60. package/esm/connectors/socket-connector.mjs +3 -3
  61. package/esm/connectors/socket-connector.mjs.map +1 -1
  62. package/esm/connectors/storage.connector.d.mts +11 -5
  63. package/esm/connectors/storage.connector.d.mts.map +1 -1
  64. package/esm/connectors/storage.connector.mjs +11 -5
  65. package/esm/connectors/storage.connector.mjs.map +1 -1
  66. package/esm/database/migrate-action.mjs +53 -1
  67. package/esm/database/migrate-action.mjs.map +1 -1
  68. package/esm/database/pending-exit-code.mjs +39 -0
  69. package/esm/database/pending-exit-code.mjs.map +1 -0
  70. package/esm/database/resolve-pending-migrations.mjs +49 -0
  71. package/esm/database/resolve-pending-migrations.mjs.map +1 -0
  72. package/esm/dev-server/loader/load-hook.mjs +1 -1
  73. package/esm/generations/stubs.mjs +1 -1
  74. package/esm/generations/stubs.mjs.map +1 -1
  75. package/esm/http/config.mjs +2 -2
  76. package/esm/http/config.mjs.map +1 -1
  77. package/esm/http/createHttpApplication.mjs +2 -2
  78. package/esm/http/createHttpApplication.mjs.map +1 -1
  79. package/esm/http/index.d.mts +1 -0
  80. package/esm/http/index.mjs +1 -0
  81. package/esm/http/middleware/idempotency.middleware.mjs +5 -5
  82. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  83. package/esm/http/middleware/inject-request-context.mjs +2 -2
  84. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  85. package/esm/http/middleware/maintenance.middleware.mjs +4 -4
  86. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  87. package/esm/http/plugins.mjs +9 -9
  88. package/esm/http/plugins.mjs.map +1 -1
  89. package/esm/http/port-preflight.d.mts +34 -0
  90. package/esm/http/port-preflight.d.mts.map +1 -0
  91. package/esm/http/port-preflight.mjs +66 -0
  92. package/esm/http/port-preflight.mjs.map +1 -0
  93. package/esm/http/response.mjs +5 -5
  94. package/esm/http/response.mjs.map +1 -1
  95. package/esm/http/server.mjs +4 -4
  96. package/esm/http/server.mjs.map +1 -1
  97. package/esm/image/image.d.mts +3 -2
  98. package/esm/image/image.d.mts.map +1 -1
  99. package/esm/image/image.mjs +63 -17
  100. package/esm/image/image.mjs.map +1 -1
  101. package/esm/index.d.mts +6 -2
  102. package/esm/index.mjs +5 -1
  103. package/esm/mail/mailer-pool.d.mts.map +1 -1
  104. package/esm/mail/mailer-pool.mjs.map +1 -1
  105. package/esm/production/assert-generated-imports.mjs +94 -0
  106. package/esm/production/assert-generated-imports.mjs.map +1 -0
  107. package/esm/production/production-builder.mjs +29 -4
  108. package/esm/production/production-builder.mjs.map +1 -1
  109. package/esm/production/production-supervisor.mjs +108 -0
  110. package/esm/production/production-supervisor.mjs.map +1 -0
  111. package/esm/react/index.d.mts +4 -0
  112. package/esm/react/index.d.mts.map +1 -1
  113. package/esm/react/index.mjs +88 -14
  114. package/esm/react/index.mjs.map +1 -1
  115. package/esm/storage/storage.d.mts +16 -0
  116. package/esm/storage/storage.d.mts.map +1 -1
  117. package/esm/storage/storage.mjs +24 -1
  118. package/esm/storage/storage.mjs.map +1 -1
  119. package/esm/tests/index.d.mts +2 -1
  120. package/esm/tests/index.mjs +1 -0
  121. package/esm/tests/start-http-development-server.d.mts +15 -2
  122. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  123. package/esm/tests/start-http-development-server.mjs +43 -16
  124. package/esm/tests/start-http-development-server.mjs.map +1 -1
  125. package/esm/tests/test-helpers.d.mts.map +1 -1
  126. package/esm/tests/test-helpers.mjs +2 -1
  127. package/esm/tests/test-helpers.mjs.map +1 -1
  128. package/esm/tests/test-server-port-channel.d.mts +27 -0
  129. package/esm/tests/test-server-port-channel.d.mts.map +1 -0
  130. package/esm/tests/test-server-port-channel.mjs +32 -0
  131. package/esm/tests/test-server-port-channel.mjs.map +1 -0
  132. package/esm/utils/load-environment.mjs +59 -0
  133. package/esm/utils/load-environment.mjs.map +1 -0
  134. package/esm/utils/paths.mjs +2 -2
  135. package/esm/utils/paths.mjs.map +1 -1
  136. package/esm/validation/validateAll.mjs +2 -2
  137. package/esm/validation/validateAll.mjs.map +1 -1
  138. package/llms-full.txt +90 -3
  139. package/llms.txt +1 -1
  140. package/package.json +13 -13
  141. package/skills/process-image/SKILL.md +3 -1
  142. package/skills/run-app/SKILL.md +63 -0
  143. package/skills/test-http/SKILL.md +27 -3
  144. package/skills/write-cli-command/SKILL.md +19 -1
@@ -1 +1 @@
1
- {"version":3,"file":"cli-commands.manager.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { loadEnv } from \"@mongez/dotenv\";\r\nimport { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { bootstrap } from \"../bootstrap\";\r\nimport { loadConfigFiles } from \"../config/load-config-files\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { manifestManager } from \"../manifest/manifest-manager\";\r\nimport { appPath } from \"../utils\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { CLICommand } from \"./cli-command\";\r\nimport {\r\n displayBootError,\r\n displayCommandError,\r\n displayCommandHelp,\r\n displayCommandNotFound,\r\n displayCommandSuccess,\r\n displayExecutingCommand,\r\n displayHelp,\r\n displayMissingCommand,\r\n displayMissingOptions,\r\n displayWarlockVersionInTerminal,\r\n type HelpCommandInfo,\r\n isMatchingCommandName,\r\n} from \"./cli-commands.utils\";\r\nimport { cliCommandsLoader } from \"./commands-loader\";\r\nimport { frameworkCommands } from \"./framework-cli-commands\";\r\nimport { parseCliArgs } from \"./parse-cli-args\";\r\nimport { findSimilar } from \"./string-similarity\";\r\nimport { CommandActionData, ResolvedCLICommandOption } from \"./types\";\r\n\r\nexport class CLICommandsManager {\r\n /**\r\n * List of commands\r\n */\r\n public commands: CLICommand[] = [];\r\n\r\n /**\r\n * Commands map (name -> command)\r\n */\r\n public commandsMap: Map<string, CLICommand> = new Map();\r\n\r\n /**\r\n * Alias map (alias -> command name)\r\n */\r\n public aliasMap: Map<string, string> = new Map();\r\n\r\n /**\r\n * Register the given commands\r\n */\r\n public register(...commands: CLICommand[]): this {\r\n this.commands.push(...commands);\r\n\r\n commands.forEach((command) => {\r\n const commandKey = command.name.split(\" \")[0];\r\n\r\n manifestManager.addCommandToList(command.name, {\r\n relativePath: command.commandRelativePath,\r\n source: command.commandSource || \"project\",\r\n description: command.commandDescription,\r\n alias: command.commandAlias,\r\n options: command.commandOptions.length > 0 ? command.commandOptions : undefined,\r\n });\r\n\r\n this.commandsMap.set(commandKey, command);\r\n\r\n // Register alias if exists\r\n if (command.commandAlias) {\r\n this.aliasMap.set(command.commandAlias, commandKey);\r\n }\r\n });\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Get command by name or alias\r\n */\r\n public getCommand(name: string): CLICommand | undefined {\r\n // First try direct lookup\r\n let command = this.commandsMap.get(name);\r\n if (command) return command;\r\n\r\n // Try alias lookup\r\n const realName = this.aliasMap.get(name);\r\n if (realName) {\r\n return this.commandsMap.get(realName);\r\n }\r\n\r\n return;\r\n }\r\n\r\n /**\r\n * Get all command names and aliases\r\n * Used for fuzzy matching suggestions\r\n */\r\n public getAllCommandNames(): string[] {\r\n const names: string[] = [];\r\n\r\n // Add all command names\r\n for (const name of this.commandsMap.keys()) {\r\n names.push(name);\r\n }\r\n\r\n // Add all aliases\r\n for (const alias of this.aliasMap.keys()) {\r\n names.push(alias);\r\n }\r\n\r\n // Also include cached commands from manifest\r\n const manifestCommands = manifestManager.commandsJson?.commands || {};\r\n for (const name of Object.keys(manifestCommands)) {\r\n const baseName = name.split(\" \")[0];\r\n if (!names.includes(baseName)) {\r\n names.push(baseName);\r\n }\r\n const alias = manifestCommands[name].alias;\r\n if (alias && !names.includes(alias)) {\r\n names.push(alias);\r\n }\r\n }\r\n\r\n return names;\r\n }\r\n\r\n /**\r\n * Start the cli manager\r\n */\r\n public async start() {\r\n const { name, options, args } = parseCliArgs(process.argv);\r\n\r\n if (options.noCache) {\r\n manifestManager.clearCommandsCache();\r\n // remove the commands.json file as wel\r\n await manifestManager.removeCommandsFile();\r\n }\r\n\r\n if (options.version || options.v) {\r\n await displayWarlockVersionInTerminal();\r\n process.exit(0);\r\n }\r\n\r\n // Try to load from manifest first (fastest path)\r\n await manifestManager.loadCommands();\r\n\r\n // Register framework commands first (needed for help)\r\n this.register(\r\n ...frameworkCommands.map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"framework\";\r\n }\r\n return command;\r\n }),\r\n );\r\n\r\n const isHelpCommand = options.help || options.h;\r\n\r\n // Handle global help (no command)\r\n if (!name && isHelpCommand) {\r\n await this.showGlobalHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Handle warm cache\r\n if (options.warmCache) {\r\n await this.warmCache();\r\n process.exit(0);\r\n }\r\n\r\n if (!name) {\r\n displayMissingCommand();\r\n process.exit(1);\r\n }\r\n\r\n const command = await this.lazyGetCommand(name);\r\n\r\n if (manifestManager.hasChanges) {\r\n await manifestManager.saveCommands();\r\n }\r\n\r\n if (!command) {\r\n // Find similar commands for suggestions\r\n const allCommandNames = this.getAllCommandNames();\r\n const suggestions = findSimilar(name, allCommandNames).map((s) => s.value);\r\n\r\n displayCommandNotFound(name, suggestions);\r\n process.exit(1);\r\n }\r\n\r\n // Handle command-specific help\r\n if (isHelpCommand) {\r\n displayCommandHelp({\r\n name: command.name,\r\n alias: command.commandAlias,\r\n description: command.commandDescription,\r\n options: command.commandOptions,\r\n });\r\n process.exit(0);\r\n }\r\n\r\n await this.execute(command, {\r\n options,\r\n args,\r\n });\r\n }\r\n\r\n /**\r\n * Show global help with all commands\r\n * Uses manifest if available for fast display\r\n */\r\n protected async showGlobalHelp() {\r\n if (\r\n manifestManager.isCommandLoaded &&\r\n Object.keys(manifestManager.commandsJson?.commands || {}).length > 0\r\n ) {\r\n // Use manifest directly - no need to load command files\r\n const helpCommands: HelpCommandInfo[] = Object.entries(\r\n manifestManager.commandsJson?.commands || {},\r\n ).map(([name, cmd]) => ({\r\n name,\r\n alias: cmd.alias,\r\n description: cmd.description,\r\n source: cmd.source,\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n return;\r\n }\r\n\r\n // Fallback: No manifest, build from registered commands\r\n // This happens on first run before warm-cache\r\n await this.loadPluginsCommands();\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n // Build help info from registered commands\r\n const helpCommands: HelpCommandInfo[] = this.commands.map((cmd) => ({\r\n name: cmd.name,\r\n alias: cmd.commandAlias,\r\n description: cmd.commandDescription,\r\n source: cmd.commandSource || \"project\",\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n }\r\n\r\n /**\r\n * Warm cache - scan all project commands and save to manifest\r\n */\r\n protected async warmCache() {\r\n console.log();\r\n console.log(` ${colors.cyan(\"›\")} Scanning project commands...`);\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n console.log(\r\n ` ${colors.green(\"✔\")} Cached ${colors.bold(String(projectCommands.length))} project commands`,\r\n );\r\n console.log();\r\n }\r\n\r\n /**\r\n * Load plugins commands\r\n */\r\n protected async loadPluginsCommands() {\r\n await warlockConfigManager.load();\r\n\r\n if (warlockConfigManager.isLoaded) {\r\n this.register(\r\n ...(warlockConfigManager.get(\"cli\")?.commands || []).map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"plugin\";\r\n }\r\n return command;\r\n }),\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Try to find the command based on the given command name or alias\r\n */\r\n protected async lazyGetCommand(name: string) {\r\n // first step, try to find it directly through current commands\r\n let command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // second step, try to find it through warlock config commands\r\n await this.loadPluginsCommands();\r\n\r\n command = this.getCommand(name);\r\n\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // third step, try to find it through project commands\r\n await this.loadProjectCommands(name);\r\n\r\n command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Load project commands\r\n */\r\n protected async loadProjectCommands(name: string) {\r\n // first get the commands.json contents as an object\r\n const jsonCommandsFile = await manifestManager.loadCommands();\r\n\r\n if (jsonCommandsFile) {\r\n // Check by name or alias\r\n for (const fullCommandName in jsonCommandsFile.commands) {\r\n const cmdMeta = jsonCommandsFile.commands[fullCommandName];\r\n\r\n // Match by name or alias\r\n if (isMatchingCommandName(fullCommandName, name) || cmdMeta.alias === name) {\r\n const commandPath = cmdMeta.relativePath;\r\n if (commandPath) {\r\n const executedCommand = await cliCommandsLoader.load(commandPath);\r\n\r\n if (executedCommand) {\r\n executedCommand.$relativePath(commandPath);\r\n this.register(executedCommand);\r\n return;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const command = await cliCommandsLoader.locate(name);\r\n\r\n if (command) {\r\n this.register(command);\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * Validate required options\r\n */\r\n protected validateOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): ResolvedCLICommandOption[] {\r\n const missing: ResolvedCLICommandOption[] = [];\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.required) {\r\n // Check if option is provided by name or alias\r\n const hasOption =\r\n options[opt.name] !== undefined || (opt.alias && options[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n missing.push(opt);\r\n }\r\n }\r\n });\r\n\r\n return missing;\r\n }\r\n\r\n /**\r\n * Apply default values to options\r\n */\r\n protected applyDefaultOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): Record<string, string | boolean | number> {\r\n const result = { ...options };\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.defaultValue !== undefined) {\r\n const hasOption =\r\n result[opt.name] !== undefined || (opt.alias && result[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n result[opt.name] = opt.defaultValue;\r\n }\r\n }\r\n\r\n if (opt.alias !== undefined && result[opt.alias] && result[opt.name] === undefined) {\r\n result[opt.name] = result[opt.alias];\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute the given command\r\n */\r\n public async execute(command: CLICommand, data: CommandActionData) {\r\n const startTime = Date.now();\r\n\r\n // Validate required options\r\n const missingOptions = this.validateOptions(command, data.options);\r\n if (missingOptions.length > 0) {\r\n displayMissingOptions(missingOptions);\r\n process.exit(1);\r\n }\r\n\r\n // Apply default values\r\n data.options = this.applyDefaultOptions(command, data.options);\r\n\r\n displayExecutingCommand(command.name);\r\n\r\n if (command.commandPreAction) {\r\n await command.commandPreAction(data);\r\n }\r\n\r\n // load preloaders\r\n if (command.commandPreload) {\r\n try {\r\n await this.loadPreloaders(command);\r\n } catch (error) {\r\n // A preload failure (a bad import in a config file, a connector that\r\n // throws on boot, a missing module export) happens BEFORE the command's\r\n // action runs — there is no run loop to recover into, so it is ALWAYS\r\n // fatal, persistent command (dev/start) or not. Surface the real cause\r\n // and the offending file, then exit. This MUST stay outside the\r\n // command-execute try/catch below: letting a preload rejection escape\r\n // turned `warlock dev` into a silent hang — the unhandled rejection slid\r\n // past while the already-started loader worker thread kept the process\r\n // alive, freezing just after the banner with no error printed.\r\n displayBootError(command.name, error as Error);\r\n process.exit(1);\r\n }\r\n }\r\n\r\n try {\r\n await command.execute(data);\r\n\r\n if (!command.isPersistent) {\r\n displayCommandSuccess(command.name, Date.now() - startTime);\r\n\r\n process.exit(0);\r\n }\r\n } catch (error) {\r\n displayCommandError(command.name, error as Error);\r\n // Persistent commands (dev/start) own their startup-failure exits;\r\n // by the time an error reaches here they've already entered their\r\n // run loop, so it's a runtime error we log but don't crash on —\r\n // HMR or process supervisors can recover. Non-persistent\r\n // one-shot commands always exit on failure.\r\n if (!command.isPersistent) {\r\n process.exit(1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Load preloaders\r\n */\r\n protected async loadPreloaders(command: CLICommand) {\r\n const preloaders = command.commandPreload || {};\r\n\r\n if (preloaders.runtimeStrategy) {\r\n Application.setRuntimeStrategy(preloaders.runtimeStrategy);\r\n }\r\n\r\n if (preloaders.environemnt) {\r\n Application.setEnvironment(preloaders.environemnt);\r\n }\r\n\r\n await warlockConfigManager.load();\r\n\r\n if (preloaders.config || preloaders.bootstrap || preloaders.prestart) {\r\n await filesOrchestrator.init();\r\n }\r\n\r\n if (preloaders.env && !preloaders.bootstrap) {\r\n await loadEnv();\r\n } else if (preloaders.bootstrap) {\r\n await bootstrap();\r\n\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/bootstrap.ts\");\r\n }\r\n }\r\n\r\n // Load configuration files\r\n if (preloaders.config) {\r\n await loadConfigFiles(preloaders.config);\r\n }\r\n\r\n if (preloaders.prestart) {\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/prestart.ts\");\r\n }\r\n }\r\n\r\n // Initialize connectors.\r\n // `connectors: true` only starts the early phase here — late-phase\r\n // connectors (http, socket) start after the command's action loads\r\n // app code. Explicit lists bypass phase splitting (caller knows\r\n // exactly which connectors they want).\r\n if (preloaders.connectors) {\r\n if (preloaders.connectors === true) {\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\r\n } else {\r\n await connectorsManager.start(preloaders.connectors);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-commands.manager.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { bootstrap } from \"../bootstrap\";\r\nimport { loadConfigFiles } from \"../config/load-config-files\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { manifestManager } from \"../manifest/manifest-manager\";\r\nimport { appPath } from \"../utils\";\r\nimport { loadEnvironmentFiles } from \"../utils/load-environment\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { CLICommand } from \"./cli-command\";\r\nimport {\r\n displayBootError,\r\n displayCommandError,\r\n displayCommandHelp,\r\n displayCommandNotFound,\r\n displayCommandSuccess,\r\n displayExecutingCommand,\r\n displayHelp,\r\n displayMissingCommand,\r\n displayMissingOptions,\r\n displayWarlockVersionInTerminal,\r\n type HelpCommandInfo,\r\n isMatchingCommandName,\r\n} from \"./cli-commands.utils\";\r\nimport { cliCommandsLoader } from \"./commands-loader\";\r\nimport { frameworkCommands } from \"./framework-cli-commands\";\r\nimport { CliOptionValueError, ParsedCliArgs, parseCliArgs } from \"./parse-cli-args\";\r\nimport { findSimilar } from \"./string-similarity\";\r\nimport { CommandActionData, ResolvedCLICommandOption } from \"./types\";\r\n\r\nexport class CLICommandsManager {\r\n /**\r\n * List of commands\r\n */\r\n public commands: CLICommand[] = [];\r\n\r\n /**\r\n * Commands map (name -> command)\r\n */\r\n public commandsMap: Map<string, CLICommand> = new Map();\r\n\r\n /**\r\n * Alias map (alias -> command name)\r\n */\r\n public aliasMap: Map<string, string> = new Map();\r\n\r\n /**\r\n * Register the given commands\r\n */\r\n public register(...commands: CLICommand[]): this {\r\n this.commands.push(...commands);\r\n\r\n commands.forEach((command) => {\r\n const commandKey = command.name.split(\" \")[0];\r\n\r\n manifestManager.addCommandToList(command.name, {\r\n relativePath: command.commandRelativePath,\r\n source: command.commandSource || \"project\",\r\n description: command.commandDescription,\r\n alias: command.commandAlias,\r\n options: command.commandOptions.length > 0 ? command.commandOptions : undefined,\r\n });\r\n\r\n this.commandsMap.set(commandKey, command);\r\n\r\n // Register alias if exists\r\n if (command.commandAlias) {\r\n this.aliasMap.set(command.commandAlias, commandKey);\r\n }\r\n });\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Get command by name or alias\r\n */\r\n public getCommand(name: string): CLICommand | undefined {\r\n // First try direct lookup\r\n let command = this.commandsMap.get(name);\r\n if (command) return command;\r\n\r\n // Try alias lookup\r\n const realName = this.aliasMap.get(name);\r\n if (realName) {\r\n return this.commandsMap.get(realName);\r\n }\r\n\r\n return;\r\n }\r\n\r\n /**\r\n * Get all command names and aliases\r\n * Used for fuzzy matching suggestions\r\n */\r\n public getAllCommandNames(): string[] {\r\n const names: string[] = [];\r\n\r\n // Add all command names\r\n for (const name of this.commandsMap.keys()) {\r\n names.push(name);\r\n }\r\n\r\n // Add all aliases\r\n for (const alias of this.aliasMap.keys()) {\r\n names.push(alias);\r\n }\r\n\r\n // Also include cached commands from manifest\r\n const manifestCommands = manifestManager.commandsJson?.commands || {};\r\n for (const name of Object.keys(manifestCommands)) {\r\n const baseName = name.split(\" \")[0];\r\n if (!names.includes(baseName)) {\r\n names.push(baseName);\r\n }\r\n const alias = manifestCommands[name].alias;\r\n if (alias && !names.includes(alias)) {\r\n names.push(alias);\r\n }\r\n }\r\n\r\n return names;\r\n }\r\n\r\n /**\r\n * Start the cli manager\r\n */\r\n public async start() {\r\n // Schema-less first pass: it exists to discover the command name and the\r\n // command-independent flags (--help, --version, --no-cache). Values here are\r\n // raw strings; the typed pass below is the one whose options reach a command.\r\n const { name, options } = parseCliArgs(process.argv);\r\n\r\n if (options.noCache) {\r\n manifestManager.clearCommandsCache();\r\n // remove the commands.json file as wel\r\n await manifestManager.removeCommandsFile();\r\n }\r\n\r\n if (options.version || options.v) {\r\n await displayWarlockVersionInTerminal();\r\n process.exit(0);\r\n }\r\n\r\n // Try to load from manifest first (fastest path)\r\n await manifestManager.loadCommands();\r\n\r\n // Register framework commands first (needed for help)\r\n this.register(\r\n ...frameworkCommands.map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"framework\";\r\n }\r\n return command;\r\n }),\r\n );\r\n\r\n const isHelpCommand = options.help || options.h;\r\n\r\n // Handle global help (no command)\r\n if (!name && isHelpCommand) {\r\n await this.showGlobalHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Handle warm cache\r\n if (options.warmCache) {\r\n await this.warmCache();\r\n process.exit(0);\r\n }\r\n\r\n if (!name) {\r\n displayMissingCommand();\r\n process.exit(1);\r\n }\r\n\r\n const command = await this.lazyGetCommand(name);\r\n\r\n if (manifestManager.hasChanges) {\r\n await manifestManager.saveCommands();\r\n }\r\n\r\n if (!command) {\r\n // Find similar commands for suggestions\r\n const allCommandNames = this.getAllCommandNames();\r\n const suggestions = findSimilar(name, allCommandNames).map((s) => s.value);\r\n\r\n displayCommandNotFound(name, suggestions);\r\n process.exit(1);\r\n }\r\n\r\n // Handle command-specific help\r\n if (isHelpCommand) {\r\n displayCommandHelp({\r\n name: command.name,\r\n alias: command.commandAlias,\r\n description: command.commandDescription,\r\n options: command.commandOptions,\r\n });\r\n process.exit(0);\r\n }\r\n\r\n // The first parse above had no command to consult, so every value in it is\r\n // a raw string. Now that the command is resolved, re-read argv against its\r\n // declared options so a `type: \"boolean\"` option arrives as a boolean.\r\n const resolved = this.resolveCommandArgs(command);\r\n\r\n await this.execute(command, {\r\n options: resolved.options,\r\n args: resolved.args,\r\n });\r\n }\r\n\r\n /**\r\n * Re-parse argv against the resolved command's declared options.\r\n *\r\n * parseCliArgs runs twice on purpose. The first run produces the command\r\n * name, and there is no schema to consult yet; the second one, here, can be\r\n * type-aware. Re-reading argv (rather than post-processing the first result)\r\n * is what makes `--rollback users.ts` recoverable: by the time the first pass\r\n * returns, the swallowed positional is indistinguishable from a value.\r\n */\r\n protected resolveCommandArgs(command: CLICommand, argv: string[] = process.argv): ParsedCliArgs {\r\n try {\r\n return parseCliArgs(argv, command.commandOptions);\r\n } catch (error) {\r\n if (error instanceof CliOptionValueError) {\r\n console.log();\r\n console.log(` ${colors.redBright(\"Error:\")} ${error.message}`);\r\n console.log();\r\n process.exit(1);\r\n }\r\n\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Show global help with all commands\r\n * Uses manifest if available for fast display\r\n */\r\n protected async showGlobalHelp() {\r\n if (\r\n manifestManager.isCommandLoaded &&\r\n Object.keys(manifestManager.commandsJson?.commands || {}).length > 0\r\n ) {\r\n // Use manifest directly - no need to load command files\r\n const helpCommands: HelpCommandInfo[] = Object.entries(\r\n manifestManager.commandsJson?.commands || {},\r\n ).map(([name, cmd]) => ({\r\n name,\r\n alias: cmd.alias,\r\n description: cmd.description,\r\n source: cmd.source,\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n return;\r\n }\r\n\r\n // Fallback: No manifest, build from registered commands\r\n // This happens on first run before warm-cache\r\n await this.loadPluginsCommands();\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n // Build help info from registered commands\r\n const helpCommands: HelpCommandInfo[] = this.commands.map((cmd) => ({\r\n name: cmd.name,\r\n alias: cmd.commandAlias,\r\n description: cmd.commandDescription,\r\n source: cmd.commandSource || \"project\",\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n }\r\n\r\n /**\r\n * Warm cache - scan all project commands and save to manifest\r\n */\r\n protected async warmCache() {\r\n console.log();\r\n console.log(` ${colors.cyan(\"›\")} Scanning project commands...`);\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n console.log(\r\n ` ${colors.green(\"✔\")} Cached ${colors.bold(String(projectCommands.length))} project commands`,\r\n );\r\n console.log();\r\n }\r\n\r\n /**\r\n * Load plugins commands\r\n */\r\n protected async loadPluginsCommands() {\r\n await warlockConfigManager.load();\r\n\r\n if (warlockConfigManager.isLoaded) {\r\n this.register(\r\n ...(warlockConfigManager.get(\"cli\")?.commands || []).map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"plugin\";\r\n }\r\n return command;\r\n }),\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Try to find the command based on the given command name or alias\r\n */\r\n protected async lazyGetCommand(name: string) {\r\n // first step, try to find it directly through current commands\r\n let command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // second step, try to find it through warlock config commands\r\n await this.loadPluginsCommands();\r\n\r\n command = this.getCommand(name);\r\n\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // third step, try to find it through project commands\r\n await this.loadProjectCommands(name);\r\n\r\n command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Load project commands\r\n */\r\n protected async loadProjectCommands(name: string) {\r\n // first get the commands.json contents as an object\r\n const jsonCommandsFile = await manifestManager.loadCommands();\r\n\r\n if (jsonCommandsFile) {\r\n // Check by name or alias\r\n for (const fullCommandName in jsonCommandsFile.commands) {\r\n const cmdMeta = jsonCommandsFile.commands[fullCommandName];\r\n\r\n // Match by name or alias\r\n if (isMatchingCommandName(fullCommandName, name) || cmdMeta.alias === name) {\r\n const commandPath = cmdMeta.relativePath;\r\n if (commandPath) {\r\n const executedCommand = await cliCommandsLoader.load(commandPath);\r\n\r\n if (executedCommand) {\r\n executedCommand.$relativePath(commandPath);\r\n this.register(executedCommand);\r\n return;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const command = await cliCommandsLoader.locate(name);\r\n\r\n if (command) {\r\n this.register(command);\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * Validate required options\r\n */\r\n protected validateOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): ResolvedCLICommandOption[] {\r\n const missing: ResolvedCLICommandOption[] = [];\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.required) {\r\n // Check if option is provided by name or alias\r\n const hasOption =\r\n options[opt.name] !== undefined || (opt.alias && options[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n missing.push(opt);\r\n }\r\n }\r\n });\r\n\r\n return missing;\r\n }\r\n\r\n /**\r\n * Apply default values to options\r\n */\r\n protected applyDefaultOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): Record<string, string | boolean | number> {\r\n const result = { ...options };\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.defaultValue !== undefined) {\r\n const hasOption =\r\n result[opt.name] !== undefined || (opt.alias && result[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n result[opt.name] = opt.defaultValue;\r\n }\r\n }\r\n\r\n // `!== undefined`, not truthiness: `-r=false` now resolves to the boolean\r\n // `false`, and a truthiness test would drop it before it ever reached the\r\n // canonical name — leaving `rollback` undefined instead of explicitly off.\r\n if (\r\n opt.alias !== undefined &&\r\n result[opt.alias] !== undefined &&\r\n result[opt.name] === undefined\r\n ) {\r\n result[opt.name] = result[opt.alias];\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute the given command\r\n */\r\n public async execute(command: CLICommand, data: CommandActionData) {\r\n const startTime = Date.now();\r\n\r\n // Validate required options\r\n const missingOptions = this.validateOptions(command, data.options);\r\n if (missingOptions.length > 0) {\r\n displayMissingOptions(missingOptions);\r\n process.exit(1);\r\n }\r\n\r\n // Apply default values\r\n data.options = this.applyDefaultOptions(command, data.options);\r\n\r\n displayExecutingCommand(command.name);\r\n\r\n if (command.commandPreAction) {\r\n await command.commandPreAction(data);\r\n }\r\n\r\n // load preloaders\r\n if (command.commandPreload) {\r\n try {\r\n await this.loadPreloaders(command);\r\n } catch (error) {\r\n // A preload failure (a bad import in a config file, a connector that\r\n // throws on boot, a missing module export) happens BEFORE the command's\r\n // action runs — there is no run loop to recover into, so it is ALWAYS\r\n // fatal, persistent command (dev/start) or not. Surface the real cause\r\n // and the offending file, then exit. This MUST stay outside the\r\n // command-execute try/catch below: letting a preload rejection escape\r\n // turned `warlock dev` into a silent hang — the unhandled rejection slid\r\n // past while the already-started loader worker thread kept the process\r\n // alive, freezing just after the banner with no error printed.\r\n displayBootError(command.name, error as Error);\r\n process.exit(1);\r\n }\r\n }\r\n\r\n try {\r\n await command.execute(data);\r\n\r\n if (!command.isPersistent) {\r\n displayCommandSuccess(command.name, Date.now() - startTime);\r\n\r\n process.exit(0);\r\n }\r\n } catch (error) {\r\n displayCommandError(command.name, error as Error);\r\n // Persistent commands (dev/start) own their startup-failure exits;\r\n // by the time an error reaches here they've already entered their\r\n // run loop, so it's a runtime error we log but don't crash on —\r\n // HMR or process supervisors can recover. Non-persistent\r\n // one-shot commands always exit on failure.\r\n if (!command.isPersistent) {\r\n process.exit(1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Load preloaders\r\n */\r\n protected async loadPreloaders(command: CLICommand) {\r\n const preloaders = command.commandPreload || {};\r\n\r\n if (preloaders.runtimeStrategy) {\r\n Application.setRuntimeStrategy(preloaders.runtimeStrategy);\r\n }\r\n\r\n if (preloaders.environemnt) {\r\n Application.setEnvironment(preloaders.environemnt);\r\n }\r\n\r\n // Env BEFORE config, unconditionally, for every command.\r\n //\r\n // `warlock.config.ts` is a module whose body runs on import, and projects\r\n // legitimately call `env(\"BUILD_OUT\", \"dist\")` in it. Loading config first\r\n // evaluated that body against an empty env store, so every such call\r\n // silently returned its default — under `build` and `start`, which never\r\n // loaded env at all, and under `dev` too, because config was loaded first\r\n // regardless. The ordering is load-bearing in the other direction as well:\r\n // `loadEnv()` reads `process.env.NODE_ENV` at call time to choose\r\n // `.env.<NODE_ENV>` over `.env`, so it must follow `setEnvironment` above.\r\n await loadEnvironmentFiles();\r\n\r\n await warlockConfigManager.load();\r\n\r\n if (preloaders.config || preloaders.bootstrap || preloaders.prestart) {\r\n await filesOrchestrator.init();\r\n }\r\n\r\n // `preloaders.env` is gone: env is loaded above for every command, so the\r\n // branch that used to serve it could only re-parse the same files and\r\n // re-override `process.env` a second time.\r\n if (preloaders.bootstrap) {\r\n await bootstrap();\r\n\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/bootstrap.ts\");\r\n }\r\n }\r\n\r\n // Load configuration files\r\n if (preloaders.config) {\r\n await loadConfigFiles(preloaders.config);\r\n }\r\n\r\n if (preloaders.prestart) {\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/prestart.ts\");\r\n }\r\n }\r\n\r\n // Initialize connectors.\r\n // `connectors: true` only starts the early phase here — late-phase\r\n // connectors (http, socket) start after the command's action loads\r\n // app code. Explicit lists bypass phase splitting (caller knows\r\n // exactly which connectors they want).\r\n if (preloaders.connectors) {\r\n if (preloaders.connectors === true) {\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\r\n } else {\r\n await connectorsManager.start(preloaders.connectors);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -30,6 +30,62 @@ async function displayStartupBanner({ environment }) {
30
30
  console.log();
31
31
  }
32
32
  /**
33
+ * Announce that the production server is up — printed on **stdout**, and only
34
+ * after the child process reported a completed boot.
35
+ *
36
+ * Stdout is the channel a supervisor or CI gate greps to decide the service is
37
+ * healthy, so nothing optimistic may be written to it: this function is the
38
+ * single place allowed to say "started", and it is called from exactly one
39
+ * place, the `warlock:ready` handler in the `start` command.
40
+ */
41
+ async function displayProductionReadyBanner({ bootDurationMs } = {}) {
42
+ const version = await getWarlockVersion();
43
+ const duration = bootDurationMs ? colors.dim(` in ${bootDurationMs}ms`) : "";
44
+ console.log(` ⚡ ${colors.bold(colors.greenBright("Warlock.js"))} ${colors.dim(`v${version}`)} ${colors.green("✔")} production server started${duration}`);
45
+ console.log();
46
+ }
47
+ /**
48
+ * Report that the production server died before it ever served anything.
49
+ *
50
+ * Written to **both** streams on purpose. Stderr is where a human and a log
51
+ * collector look; stdout is where a supervisor that greps for the success
52
+ * banner looks, and it must find a failure there rather than silence — silence
53
+ * on stdout is what let a boot failure be recorded as a healthy start.
54
+ */
55
+ function displayProductionStartFailure(exitCode) {
56
+ const lines = [
57
+ "",
58
+ ` ${colors.red("✖")} ${colors.bold("warlock start")} failed — the server never finished booting`,
59
+ ` ${colors.dim(`the application process exited with code ${exitCode}`)}`,
60
+ ` ${colors.dim("the cause is printed above, in the application's own output")}`,
61
+ ""
62
+ ];
63
+ for (const line of lines) {
64
+ console.error(line);
65
+ console.log(line);
66
+ }
67
+ }
68
+ /**
69
+ * Note that a running child has not reported readiness yet.
70
+ *
71
+ * Emitted on **stderr** only. The process may be perfectly healthy — an older
72
+ * bundle has no readiness signal at all — so saying anything on stdout would be
73
+ * the very false-green this channel exists to prevent.
74
+ *
75
+ * The wording offers the likely causes rather than asserting one. It cannot
76
+ * distinguish an old bundle from a slow boot from a boot that is about to fail,
77
+ * and an earlier version claimed "the bundle predates readiness reporting" —
78
+ * which was simply wrong when a current bundle was mid-crash, and sent the
79
+ * reader after the wrong thing.
80
+ */
81
+ function displayMissingReadinessNotice(waitedMs) {
82
+ console.error();
83
+ console.error(` ${colors.yellow("!")} still running after ${Math.round(waitedMs / 1e3)}s with no readiness signal`);
84
+ console.error(` ${colors.dim("either the boot is still in progress, or this bundle was built")}`);
85
+ console.error(` ${colors.dim("before readiness reporting — re-run")} ${colors.cyan("warlock build")} ${colors.dim("if the banner never appears")}`);
86
+ console.error();
87
+ }
88
+ /**
33
89
  * Display command execution header
34
90
  */
35
91
  function displayExecutingCommand(commandName) {
@@ -213,5 +269,5 @@ function displayCommandHelp(command) {
213
269
  }
214
270
 
215
271
  //#endregion
216
- export { displayBootError, displayCommandError, displayCommandHelp, displayCommandNotFound, displayCommandSuccess, displayExecutingCommand, displayHelp, displayMissingCommand, displayMissingOptions, displayStartupBanner, displayWarlockVersionInTerminal, isMatchingCommandName };
272
+ export { displayBootError, displayCommandError, displayCommandHelp, displayCommandNotFound, displayCommandSuccess, displayExecutingCommand, displayHelp, displayMissingCommand, displayMissingOptions, displayMissingReadinessNotice, displayProductionReadyBanner, displayProductionStartFailure, displayStartupBanner, displayWarlockVersionInTerminal, isMatchingCommandName };
217
273
  //# sourceMappingURL=cli-commands.utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-commands.utils.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.utils.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { Environment } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\n\nexport function isMatchingCommandName(commandName: string, targetingCommandName: string) {\n return commandName.split(\" \")[0] === targetingCommandName;\n}\n\n/**\n * Display the Warlock.js version banner\n */\nexport async function displayWarlockVersionInTerminal() {\n const version = await getWarlockVersion();\n console.log(`⚡ ${colors.bold(\"Warlock.js\")} ${colors.greenBright(`v${version}`)}`);\n}\n\ntype StartBannerOptions = {\n environment: Environment;\n};\n\nfunction getTextColorMethod(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.yellowBright;\n case \"production\":\n return colors.greenBright;\n case \"test\":\n return colors.blueBright;\n default:\n return colors.whiteBright;\n }\n}\n\n/**\n * Display CLI startup banner\n */\nexport async function displayStartupBanner({ environment }: StartBannerOptions) {\n const version = await getWarlockVersion();\n const textColorMethod = getTextColorMethod(environment);\n console.log(` ⚡ ${colors.bold(textColorMethod(\"Warlock.js\"))} ${colors.dim(`v${version}`)}`);\n console.log();\n}\n\n/**\n * Display command execution header\n */\nexport function displayExecutingCommand(commandName: string) {\n console.log(` ${colors.cyan(\"›\")} Running ${colors.bold(colors.white(commandName))}...`);\n console.log();\n}\n\n/**\n * Display command not found error with optional suggestions\n */\nexport function displayCommandNotFound(commandName: string, suggestions?: string[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Command ${colors.magenta(commandName)} not found`);\n\n if (suggestions && suggestions.length > 0) {\n console.log();\n console.log(` ${colors.yellow(\"Did you mean?\")}`);\n suggestions.forEach((suggestion) => {\n console.log(` ${colors.cyan(\"→\")} ${colors.white(suggestion)}`);\n });\n }\n\n console.log();\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display missing command error\n */\nexport function displayMissingCommand() {\n console.log();\n console.log(` ${colors.red(\"✖\")} No command specified`);\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display command success message\n */\nexport function displayCommandSuccess(commandName: string, durationMs?: number) {\n const duration = durationMs ? colors.dim(` (${durationMs}ms)`) : \"\";\n console.log();\n console.log(\n ` ${colors.green(\"✔\")} ${colors.bold(commandName)} completed successfully${duration}`,\n );\n console.log();\n}\n\n/**\n * Display command error message\n */\nexport function displayCommandError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed`);\n console.log(` ${colors.dim(error.message)}`);\n console.log();\n}\n\n/**\n * Display a FATAL boot/preload failure and exit information.\n *\n * Preload failures — a bad import in a config file, a connector that throws\n * on startup, a missing module export — happen BEFORE the command's run loop\n * exists, so there is nothing to recover into: they are always fatal. Unlike\n * `displayCommandError`, this prints the full stack (not just `error.message`)\n * because the stack names the exact file + line of the offending import, which\n * is the single most useful clue when a config file pulls in a broken module.\n *\n * Surfacing this loudly is the difference between a clear error and a silent\n * hang: an unhandled preload rejection used to escape while the already-started\n * loader worker thread kept the process alive, leaving `warlock dev` frozen\n * just after the banner with no message at all.\n *\n * @example\n * // SyntaxError: The requested module '@warlock.js/cascade' does not\n * // provide an export named 'belongsTo'\n * // at src/app/.../permission.model.ts:2\n */\nexport function displayBootError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed to start`);\n console.log(` ${colors.red(error.message)}`);\n if (error.stack) {\n console.log();\n console.log(colors.dim(error.stack));\n }\n console.log();\n}\n\n/**\n * Display missing required options error\n */\nexport function displayMissingOptions(options: { name: string; text: string }[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Missing required options:`);\n options.forEach((opt) => {\n console.log(` ${colors.yellow(opt.text)} ${colors.dim(`(--${opt.name})`)}`);\n });\n console.log();\n}\n\n/**\n * Command info for help display\n */\nexport type HelpCommandInfo = {\n name: string;\n alias?: string;\n description?: string;\n source: \"framework\" | \"plugin\" | \"project\";\n};\n\n/**\n * Display global help with all commands grouped by source\n */\nexport async function displayHelp(commands: HelpCommandInfo[]) {\n const version = await getWarlockVersion();\n\n console.log();\n console.log(\n ` ⚡ ${colors.bold(colors.yellowBright(\"Warlock.js\"))} CLI ${colors.dim(`v${version}`)}`,\n );\n console.log();\n console.log(\n ` ${colors.bold(\"Usage:\")} ${colors.cyan(\"warlock\")} ${colors.dim(\"<command>\")} ${colors.dim(\"[options]\")}`,\n );\n console.log();\n\n // Group by source\n const grouped: Record<string, HelpCommandInfo[]> = {\n framework: [],\n plugin: [],\n project: [],\n };\n\n commands.forEach((cmd) => {\n grouped[cmd.source]?.push(cmd);\n });\n\n // Display each group\n const groupLabels: Record<string, string> = {\n framework: \"Framework Commands\",\n plugin: \"Plugin Commands\",\n project: \"Project Commands\",\n };\n\n for (const [source, cmds] of Object.entries(grouped)) {\n if (cmds.length === 0) continue;\n\n console.log(` ${colors.bold(colors.white(groupLabels[source]))}`);\n console.log();\n\n // Find max name length for alignment\n const maxLen = Math.max(...cmds.map((c) => c.name.length + (c.alias ? c.alias.length + 4 : 0)));\n\n cmds.forEach((cmd) => {\n const aliasStr = cmd.alias ? colors.dim(` (${cmd.alias})`) : \"\";\n const nameWithAlias = cmd.name + (cmd.alias ? ` (${cmd.alias})` : \"\");\n const padding = \" \".repeat(maxLen - nameWithAlias.length + 2);\n // const desc = cmd.description || colors.dim(\"No description\");\n const desc = cmd.description || \"\";\n console.log(` ${colors.cyan(cmd.name)}${aliasStr}${padding}${desc}`);\n });\n console.log();\n }\n\n // Display global flags\n console.log(` ${colors.bold(colors.white(\"Global Flags\"))}`);\n console.log();\n\n const globalFlags = [\n { flag: \"--help, -h\", description: \"Show help for a command\" },\n { flag: \"--version, -v\", description: \"Show Warlock version\" },\n { flag: \"--no-cache\", description: \"Force reload without cache\" },\n { flag: \"--warm-cache\", description: \"Pre-cache all project commands\" },\n ];\n\n const maxFlagLen = Math.max(...globalFlags.map((f) => f.flag.length));\n\n globalFlags.forEach(({ flag, description }) => {\n const padding = \" \".repeat(maxFlagLen - flag.length + 2);\n console.log(` ${colors.yellow(flag)}${padding}${description}`);\n });\n console.log();\n\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock <command> --help\")} ${colors.dim(\"for command-specific help\")}`,\n );\n console.log();\n}\n\n/**\n * Display help for a specific command\n */\nexport function displayCommandHelp(command: {\n name: string;\n alias?: string;\n description?: string;\n options?: { name: string; text: string; description?: string; required?: boolean }[];\n}) {\n console.log();\n console.log(\n ` ${colors.bold(colors.cyan(command.name))}${command.alias ? colors.dim(` (${command.alias})`) : \"\"}`,\n );\n\n if (command.description) {\n console.log(` ${command.description}`);\n }\n console.log();\n\n if (command.options && command.options.length > 0) {\n console.log(` ${colors.bold(\"Options:\")}`);\n console.log();\n\n const maxLen = Math.max(...command.options.map((o) => o.text.length));\n\n command.options.forEach((opt) => {\n const padding = \" \".repeat(maxLen - opt.text.length + 2);\n const required = opt.required ? colors.red(\" (required)\") : \"\";\n const desc = opt.description || \"\";\n console.log(` ${colors.green(opt.text)}${padding}${desc}${required}`);\n });\n console.log();\n } else {\n console.log(` ${colors.dim(\"No options available\")}`);\n console.log();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-commands.utils.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.utils.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { Environment } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\n\nexport function isMatchingCommandName(commandName: string, targetingCommandName: string) {\n return commandName.split(\" \")[0] === targetingCommandName;\n}\n\n/**\n * Display the Warlock.js version banner\n */\nexport async function displayWarlockVersionInTerminal() {\n const version = await getWarlockVersion();\n console.log(`⚡ ${colors.bold(\"Warlock.js\")} ${colors.greenBright(`v${version}`)}`);\n}\n\ntype StartBannerOptions = {\n environment: Environment;\n};\n\nfunction getTextColorMethod(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.yellowBright;\n case \"production\":\n return colors.greenBright;\n case \"test\":\n return colors.blueBright;\n default:\n return colors.whiteBright;\n }\n}\n\n/**\n * Display CLI startup banner\n */\nexport async function displayStartupBanner({ environment }: StartBannerOptions) {\n const version = await getWarlockVersion();\n const textColorMethod = getTextColorMethod(environment);\n console.log(` ⚡ ${colors.bold(textColorMethod(\"Warlock.js\"))} ${colors.dim(`v${version}`)}`);\n console.log();\n}\n\ntype ProductionReadyBannerOptions = {\n bootDurationMs?: number;\n};\n\n/**\n * Announce that the production server is up — printed on **stdout**, and only\n * after the child process reported a completed boot.\n *\n * Stdout is the channel a supervisor or CI gate greps to decide the service is\n * healthy, so nothing optimistic may be written to it: this function is the\n * single place allowed to say \"started\", and it is called from exactly one\n * place, the `warlock:ready` handler in the `start` command.\n */\nexport async function displayProductionReadyBanner({\n bootDurationMs,\n}: ProductionReadyBannerOptions = {}) {\n const version = await getWarlockVersion();\n const duration = bootDurationMs ? colors.dim(` in ${bootDurationMs}ms`) : \"\";\n\n console.log(\n ` ⚡ ${colors.bold(colors.greenBright(\"Warlock.js\"))} ${colors.dim(`v${version}`)} ${colors.green(\"✔\")} production server started${duration}`,\n );\n console.log();\n}\n\n/**\n * Report that the production server died before it ever served anything.\n *\n * Written to **both** streams on purpose. Stderr is where a human and a log\n * collector look; stdout is where a supervisor that greps for the success\n * banner looks, and it must find a failure there rather than silence — silence\n * on stdout is what let a boot failure be recorded as a healthy start.\n */\nexport function displayProductionStartFailure(exitCode: number) {\n const lines = [\n \"\",\n ` ${colors.red(\"✖\")} ${colors.bold(\"warlock start\")} failed — the server never finished booting`,\n ` ${colors.dim(`the application process exited with code ${exitCode}`)}`,\n ` ${colors.dim(\"the cause is printed above, in the application's own output\")}`,\n \"\",\n ];\n\n for (const line of lines) {\n console.error(line);\n console.log(line);\n }\n}\n\n/**\n * Note that a running child has not reported readiness yet.\n *\n * Emitted on **stderr** only. The process may be perfectly healthy — an older\n * bundle has no readiness signal at all — so saying anything on stdout would be\n * the very false-green this channel exists to prevent.\n *\n * The wording offers the likely causes rather than asserting one. It cannot\n * distinguish an old bundle from a slow boot from a boot that is about to fail,\n * and an earlier version claimed \"the bundle predates readiness reporting\" —\n * which was simply wrong when a current bundle was mid-crash, and sent the\n * reader after the wrong thing.\n */\nexport function displayMissingReadinessNotice(waitedMs: number) {\n console.error();\n console.error(\n ` ${colors.yellow(\"!\")} still running after ${Math.round(waitedMs / 1000)}s with no readiness signal`,\n );\n console.error(` ${colors.dim(\"either the boot is still in progress, or this bundle was built\")}`);\n console.error(\n ` ${colors.dim(\"before readiness reporting — re-run\")} ${colors.cyan(\"warlock build\")} ${colors.dim(\"if the banner never appears\")}`,\n );\n console.error();\n}\n\n/**\n * Display command execution header\n */\nexport function displayExecutingCommand(commandName: string) {\n console.log(` ${colors.cyan(\"›\")} Running ${colors.bold(colors.white(commandName))}...`);\n console.log();\n}\n\n/**\n * Display command not found error with optional suggestions\n */\nexport function displayCommandNotFound(commandName: string, suggestions?: string[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Command ${colors.magenta(commandName)} not found`);\n\n if (suggestions && suggestions.length > 0) {\n console.log();\n console.log(` ${colors.yellow(\"Did you mean?\")}`);\n suggestions.forEach((suggestion) => {\n console.log(` ${colors.cyan(\"→\")} ${colors.white(suggestion)}`);\n });\n }\n\n console.log();\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display missing command error\n */\nexport function displayMissingCommand() {\n console.log();\n console.log(` ${colors.red(\"✖\")} No command specified`);\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock --help\")} ${colors.dim(\"to see available commands\")}`,\n );\n console.log();\n}\n\n/**\n * Display command success message\n */\nexport function displayCommandSuccess(commandName: string, durationMs?: number) {\n const duration = durationMs ? colors.dim(` (${durationMs}ms)`) : \"\";\n console.log();\n console.log(\n ` ${colors.green(\"✔\")} ${colors.bold(commandName)} completed successfully${duration}`,\n );\n console.log();\n}\n\n/**\n * Display command error message\n */\nexport function displayCommandError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed`);\n console.log(` ${colors.dim(error.message)}`);\n console.log();\n}\n\n/**\n * Display a FATAL boot/preload failure and exit information.\n *\n * Preload failures — a bad import in a config file, a connector that throws\n * on startup, a missing module export — happen BEFORE the command's run loop\n * exists, so there is nothing to recover into: they are always fatal. Unlike\n * `displayCommandError`, this prints the full stack (not just `error.message`)\n * because the stack names the exact file + line of the offending import, which\n * is the single most useful clue when a config file pulls in a broken module.\n *\n * Surfacing this loudly is the difference between a clear error and a silent\n * hang: an unhandled preload rejection used to escape while the already-started\n * loader worker thread kept the process alive, leaving `warlock dev` frozen\n * just after the banner with no message at all.\n *\n * @example\n * // SyntaxError: The requested module '@warlock.js/cascade' does not\n * // provide an export named 'belongsTo'\n * // at src/app/.../permission.model.ts:2\n */\nexport function displayBootError(commandName: string, error: Error) {\n console.log();\n console.log(` ${colors.red(\"✖\")} ${colors.bold(commandName)} failed to start`);\n console.log(` ${colors.red(error.message)}`);\n if (error.stack) {\n console.log();\n console.log(colors.dim(error.stack));\n }\n console.log();\n}\n\n/**\n * Display missing required options error\n */\nexport function displayMissingOptions(options: { name: string; text: string }[]) {\n console.log();\n console.log(` ${colors.red(\"✖\")} Missing required options:`);\n options.forEach((opt) => {\n console.log(` ${colors.yellow(opt.text)} ${colors.dim(`(--${opt.name})`)}`);\n });\n console.log();\n}\n\n/**\n * Command info for help display\n */\nexport type HelpCommandInfo = {\n name: string;\n alias?: string;\n description?: string;\n source: \"framework\" | \"plugin\" | \"project\";\n};\n\n/**\n * Display global help with all commands grouped by source\n */\nexport async function displayHelp(commands: HelpCommandInfo[]) {\n const version = await getWarlockVersion();\n\n console.log();\n console.log(\n ` ⚡ ${colors.bold(colors.yellowBright(\"Warlock.js\"))} CLI ${colors.dim(`v${version}`)}`,\n );\n console.log();\n console.log(\n ` ${colors.bold(\"Usage:\")} ${colors.cyan(\"warlock\")} ${colors.dim(\"<command>\")} ${colors.dim(\"[options]\")}`,\n );\n console.log();\n\n // Group by source\n const grouped: Record<string, HelpCommandInfo[]> = {\n framework: [],\n plugin: [],\n project: [],\n };\n\n commands.forEach((cmd) => {\n grouped[cmd.source]?.push(cmd);\n });\n\n // Display each group\n const groupLabels: Record<string, string> = {\n framework: \"Framework Commands\",\n plugin: \"Plugin Commands\",\n project: \"Project Commands\",\n };\n\n for (const [source, cmds] of Object.entries(grouped)) {\n if (cmds.length === 0) continue;\n\n console.log(` ${colors.bold(colors.white(groupLabels[source]))}`);\n console.log();\n\n // Find max name length for alignment\n const maxLen = Math.max(...cmds.map((c) => c.name.length + (c.alias ? c.alias.length + 4 : 0)));\n\n cmds.forEach((cmd) => {\n const aliasStr = cmd.alias ? colors.dim(` (${cmd.alias})`) : \"\";\n const nameWithAlias = cmd.name + (cmd.alias ? ` (${cmd.alias})` : \"\");\n const padding = \" \".repeat(maxLen - nameWithAlias.length + 2);\n // const desc = cmd.description || colors.dim(\"No description\");\n const desc = cmd.description || \"\";\n console.log(` ${colors.cyan(cmd.name)}${aliasStr}${padding}${desc}`);\n });\n console.log();\n }\n\n // Display global flags\n console.log(` ${colors.bold(colors.white(\"Global Flags\"))}`);\n console.log();\n\n const globalFlags = [\n { flag: \"--help, -h\", description: \"Show help for a command\" },\n { flag: \"--version, -v\", description: \"Show Warlock version\" },\n { flag: \"--no-cache\", description: \"Force reload without cache\" },\n { flag: \"--warm-cache\", description: \"Pre-cache all project commands\" },\n ];\n\n const maxFlagLen = Math.max(...globalFlags.map((f) => f.flag.length));\n\n globalFlags.forEach(({ flag, description }) => {\n const padding = \" \".repeat(maxFlagLen - flag.length + 2);\n console.log(` ${colors.yellow(flag)}${padding}${description}`);\n });\n console.log();\n\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"warlock <command> --help\")} ${colors.dim(\"for command-specific help\")}`,\n );\n console.log();\n}\n\n/**\n * Display help for a specific command\n */\nexport function displayCommandHelp(command: {\n name: string;\n alias?: string;\n description?: string;\n options?: { name: string; text: string; description?: string; required?: boolean }[];\n}) {\n console.log();\n console.log(\n ` ${colors.bold(colors.cyan(command.name))}${command.alias ? colors.dim(` (${command.alias})`) : \"\"}`,\n );\n\n if (command.description) {\n console.log(` ${command.description}`);\n }\n console.log();\n\n if (command.options && command.options.length > 0) {\n console.log(` ${colors.bold(\"Options:\")}`);\n console.log();\n\n const maxLen = Math.max(...command.options.map((o) => o.text.length));\n\n command.options.forEach((opt) => {\n const padding = \" \".repeat(maxLen - opt.text.length + 2);\n const required = opt.required ? colors.red(\" (required)\") : \"\";\n const desc = opt.description || \"\";\n console.log(` ${colors.green(opt.text)}${padding}${desc}${required}`);\n });\n console.log();\n } else {\n console.log(` ${colors.dim(\"No options available\")}`);\n console.log();\n }\n}\n"],"mappings":"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"}
@@ -13,11 +13,13 @@ const addCommand = command({
13
13
  },
14
14
  {
15
15
  text: "--list, -l",
16
- description: "List available features"
16
+ description: "List available features",
17
+ type: "boolean"
17
18
  },
18
19
  {
19
20
  text: "--no-install",
20
- description: "Record dependencies in package.json without installing them — still ejects configs, adds scripts, and runs setup hooks. Pass it last, after the feature list. Used by scaffolders that run a single install afterwards."
21
+ description: "Record dependencies in package.json without installing them — still ejects configs, adds scripts, and runs setup hooks. Used by scaffolders that run a single install afterwards.",
22
+ type: "boolean"
21
23
  }
22
24
  ]
23
25
  });
@@ -1 +1 @@
1
- {"version":3,"file":"add.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/add.command.ts"],"sourcesContent":["import { addCommandAction } from \"../../generations/add-command.action\";\r\nimport { command } from \"../cli-command\";\r\n\r\nexport const addCommand = command({\r\n name: \"add <features...>\",\r\n description: \"Add new feature(s) to the project\",\r\n action: addCommandAction,\r\n options: [\r\n {\r\n text: \"--package-manager -pm\",\r\n description: \"Package manager to use, if not passed, it will be detected automatically\",\r\n },\r\n {\r\n text: \"--list, -l\",\r\n description: \"List available features\",\r\n },\r\n {\r\n text: \"--no-install\",\r\n description:\r\n \"Record dependencies in package.json without installing them — still ejects configs, adds scripts, and runs setup hooks. Pass it last, after the feature list. Used by scaffolders that run a single install afterwards.\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aACE;EACJ;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"add.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/add.command.ts"],"sourcesContent":["import { addCommandAction } from \"../../generations/add-command.action\";\r\nimport { command } from \"../cli-command\";\r\n\r\nexport const addCommand = command({\r\n name: \"add <features...>\",\r\n description: \"Add new feature(s) to the project\",\r\n action: addCommandAction,\r\n options: [\r\n {\r\n text: \"--package-manager -pm\",\r\n description: \"Package manager to use, if not passed, it will be detected automatically\",\r\n },\r\n {\r\n text: \"--list, -l\",\r\n description: \"List available features\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--no-install\",\r\n description:\r\n \"Record dependencies in package.json without installing them — still ejects configs, adds scripts, and runs setup hooks. Used by scaffolders that run a single install afterwards.\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;CACF;AACF,CAAC"}
@@ -50,10 +50,12 @@ const generateCommand = command({
50
50
  action: generateCommandAction,
51
51
  options: [{
52
52
  text: "--force, -f",
53
- description: "Overwrite existing files"
53
+ description: "Overwrite existing files",
54
+ type: "boolean"
54
55
  }, {
55
56
  text: "--dry-run",
56
- description: "Preview the files that would be generated without writing anything"
57
+ description: "Preview the files that would be generated without writing anything",
58
+ type: "boolean"
57
59
  }]
58
60
  });
59
61
  const generateModuleCommand = command({
@@ -64,15 +66,18 @@ const generateModuleCommand = command({
64
66
  options: [
65
67
  {
66
68
  text: "--minimal, -m",
67
- description: "Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold"
69
+ description: "Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold",
70
+ type: "boolean"
68
71
  },
69
72
  {
70
73
  text: "--force, -f",
71
- description: "Overwrite existing files"
74
+ description: "Overwrite existing files",
75
+ type: "boolean"
72
76
  },
73
77
  {
74
78
  text: "--dry-run",
75
- description: "Preview the files that would be generated without writing anything"
79
+ description: "Preview the files that would be generated without writing anything",
80
+ type: "boolean"
76
81
  }
77
82
  ]
78
83
  });
@@ -84,15 +89,18 @@ const generateControllerCommand = command({
84
89
  options: [
85
90
  {
86
91
  text: "--with-validation, -v",
87
- description: "Generate a validation schema and bind it to the controller"
92
+ description: "Generate a validation schema and bind it to the controller",
93
+ type: "boolean"
88
94
  },
89
95
  {
90
96
  text: "--force, -f",
91
- description: "Overwrite existing files"
97
+ description: "Overwrite existing files",
98
+ type: "boolean"
92
99
  },
93
100
  {
94
101
  text: "--dry-run",
95
- description: "Preview the files that would be generated without writing anything"
102
+ description: "Preview the files that would be generated without writing anything",
103
+ type: "boolean"
96
104
  }
97
105
  ]
98
106
  });
@@ -103,10 +111,12 @@ const generateServiceCommand = command({
103
111
  action: generateService,
104
112
  options: [{
105
113
  text: "--force, -f",
106
- description: "Overwrite existing files"
114
+ description: "Overwrite existing files",
115
+ type: "boolean"
107
116
  }, {
108
117
  text: "--dry-run",
109
- description: "Preview the files that would be generated without writing anything"
118
+ description: "Preview the files that would be generated without writing anything",
119
+ type: "boolean"
110
120
  }]
111
121
  });
112
122
  const generateModelCommand = command({
@@ -117,7 +127,8 @@ const generateModelCommand = command({
117
127
  options: [
118
128
  {
119
129
  text: "--with-resource, -rs",
120
- description: "Generate resource"
130
+ description: "Generate resource",
131
+ type: "boolean"
121
132
  },
122
133
  {
123
134
  text: "--table <name>",
@@ -125,15 +136,18 @@ const generateModelCommand = command({
125
136
  },
126
137
  {
127
138
  text: "--force, -f",
128
- description: "Overwrite existing files"
139
+ description: "Overwrite existing files",
140
+ type: "boolean"
129
141
  },
130
142
  {
131
143
  text: "--dry-run",
132
- description: "Preview the files that would be generated without writing anything"
144
+ description: "Preview the files that would be generated without writing anything",
145
+ type: "boolean"
133
146
  },
134
147
  {
135
148
  text: "--timestamps [bool]",
136
- description: "Include timestamps in migration (default: true)"
149
+ description: "Include timestamps in migration (default: true)",
150
+ type: "boolean"
137
151
  }
138
152
  ]
139
153
  });
@@ -144,10 +158,12 @@ const generateRepositoryCommand = command({
144
158
  action: generateRepository,
145
159
  options: [{
146
160
  text: "--force, -f",
147
- description: "Overwrite existing files"
161
+ description: "Overwrite existing files",
162
+ type: "boolean"
148
163
  }, {
149
164
  text: "--dry-run",
150
- description: "Preview the files that would be generated without writing anything"
165
+ description: "Preview the files that would be generated without writing anything",
166
+ type: "boolean"
151
167
  }]
152
168
  });
153
169
  const generateResourceCommand = command({
@@ -157,10 +173,12 @@ const generateResourceCommand = command({
157
173
  action: generateResource,
158
174
  options: [{
159
175
  text: "--force, -f",
160
- description: "Overwrite existing files"
176
+ description: "Overwrite existing files",
177
+ type: "boolean"
161
178
  }, {
162
179
  text: "--dry-run",
163
- description: "Preview the files that would be generated without writing anything"
180
+ description: "Preview the files that would be generated without writing anything",
181
+ type: "boolean"
164
182
  }]
165
183
  });
166
184
  const generateMigrationCommand = command({
@@ -171,11 +189,13 @@ const generateMigrationCommand = command({
171
189
  options: [
172
190
  {
173
191
  text: "--force, -f",
174
- description: "Overwrite existing files"
192
+ description: "Overwrite existing files",
193
+ type: "boolean"
175
194
  },
176
195
  {
177
196
  text: "--dry-run",
178
- description: "Preview the files that would be generated without writing anything"
197
+ description: "Preview the files that would be generated without writing anything",
198
+ type: "boolean"
179
199
  },
180
200
  {
181
201
  text: "--add <columns>",
@@ -191,7 +211,8 @@ const generateMigrationCommand = command({
191
211
  },
192
212
  {
193
213
  text: "--timestamps [bool]",
194
- description: "Include timestamps (default: true)"
214
+ description: "Include timestamps (default: true)",
215
+ type: "boolean"
195
216
  }
196
217
  ]
197
218
  });
@@ -1 +1 @@
1
- {"version":3,"file":"generate.command.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/generate/generate.command.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { command } from \"../../cli-command\";\r\nimport type { CommandActionData } from \"../../types\";\r\nimport { generateController } from \"./generators/controller.generator\";\r\nimport { generateMigration } from \"./generators/migration.generator\";\r\nimport { generateModel } from \"./generators/model.generator\";\r\nimport { generateModule } from \"./generators/module.generator\";\r\nimport { generateRepository } from \"./generators/repository.generator\";\r\nimport { generateResource } from \"./generators/resource.generator\";\r\nimport { generateService } from \"./generators/service.generator\";\r\n\r\nconst generators: Record<string, (data: CommandActionData) => Promise<void>> = {\r\n module: generateModule,\r\n controller: generateController,\r\n service: generateService,\r\n model: generateModel,\r\n migration: generateMigration,\r\n repository: generateRepository,\r\n resource: generateResource,\r\n};\r\n\r\nasync function generateCommandAction(data: CommandActionData) {\r\n const commandName = data.args[0];\r\n\r\n if (!commandName) {\r\n console.log(colors.red(\"Error: Generator name is required\"));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n console.log(colors.yellow(\"\\nUsage: warlock generate <generator> <name> [options]\"));\r\n process.exit(1);\r\n }\r\n\r\n const generator = generators[commandName];\r\n\r\n if (!generator) {\r\n console.log(colors.red(`Error: Unknown generator \"${commandName}\"`));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n process.exit(1);\r\n }\r\n\r\n // Remove the command name from args for the generator\r\n const generatorData: CommandActionData = {\r\n ...data,\r\n args: data.args.slice(1),\r\n };\r\n\r\n await generator(generatorData);\r\n}\r\n\r\n// Master generate command\r\nexport const generateCommand = command({\r\n name: \"generate <generator> [args...]\",\r\n alias: \"g\", // Allows 'warlock g module ...'\r\n description: \"Scaffold a new component\",\r\n action: generateCommandAction,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\n// Individual commands\r\nexport const generateModuleCommand = command({\r\n name: \"generate.module <name>\",\r\n alias: \"gen.m\",\r\n description: \"Generate a new module\",\r\n action: generateModule,\r\n options: [\r\n {\r\n text: \"--minimal, -m\",\r\n description:\r\n \"Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateControllerCommand = command({\r\n name: \"generate.controller <module>/<name>\",\r\n alias: \"gen.c\",\r\n description: \"Generate a new controller\",\r\n action: generateController,\r\n options: [\r\n {\r\n text: \"--with-validation, -v\",\r\n description: \"Generate a validation schema and bind it to the controller\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateServiceCommand = command({\r\n name: \"generate.service <module>/<name>\",\r\n alias: \"gen.s\",\r\n description: \"Generate a new service\",\r\n action: generateService,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateModelCommand = command({\r\n name: \"generate.model <module>/<name>\",\r\n alias: \"gen.md\",\r\n description: \"Generate a new model with migration\",\r\n action: generateModel,\r\n options: [\r\n {\r\n text: \"--with-resource, -rs\",\r\n description: \"Generate resource\",\r\n },\r\n {\r\n text: \"--table <name>\",\r\n description: \"Specify table name\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps in migration (default: true)\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateRepositoryCommand = command({\r\n name: \"generate.repository <module>/<name>\",\r\n alias: \"gen.r\",\r\n description: \"Generate a new repository\",\r\n action: generateRepository,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateResourceCommand = command({\r\n name: \"generate.resource <module>/<name>\",\r\n alias: \"gen.rs\",\r\n description: \"Generate a new resource\",\r\n action: generateResource,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateMigrationCommand = command({\r\n name: \"generate.migration <model-path>\",\r\n alias: \"gen.mig\",\r\n description: \"Generate a new migration for a model\",\r\n action: generateMigration,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n },\r\n {\r\n text: \"--add <columns>\",\r\n description: \"Columns to add (DSL: name:type:modifier,...)\",\r\n },\r\n {\r\n text: \"--drop <columns>\",\r\n description: \"Columns to drop (comma-separated names)\",\r\n },\r\n {\r\n text: \"--rename <columns>\",\r\n description: \"Columns to rename (DSL: old:new,...)\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps (default: true)\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,aAAyE;CAC7E,QAAQ;CACR,YAAY;CACZ,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,UAAU;AACZ;AAEA,eAAe,sBAAsB,MAAyB;CAC5D,MAAM,cAAc,KAAK,KAAK;CAE9B,IAAI,CAAC,aAAa;EAChB,QAAQ,IAAI,OAAO,IAAI,mCAAmC,CAAC;EAC3D,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,IAAI,OAAO,OAAO,wDAAwD,CAAC;EACnF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,YAAY,WAAW;CAE7B,IAAI,CAAC,WAAW;EACd,QAAQ,IAAI,OAAO,IAAI,6BAA6B,YAAY,EAAE,CAAC;EACnE,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,KAAK,CAAC;CAChB;CAQA,MAAM,UAAU;EAJd,GAAG;EACH,MAAM,KAAK,KAAK,MAAM,CAAC;CAGG,CAAC;AAC/B;AAGA,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;CACf,GACA;EACE,MAAM;EACN,aAAa;CACf,CACF;AACF,CAAC;AAGD,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aACE;EACJ;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;CACF;AACF,CAAC;AAED,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;CACf,GACA;EACE,MAAM;EACN,aAAa;CACf,CACF;AACF,CAAC;AAED,MAAa,uBAAuB,QAAQ;CAC1C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;CACf,GACA;EACE,MAAM;EACN,aAAa;CACf,CACF;AACF,CAAC;AAED,MAAa,0BAA0B,QAAQ;CAC7C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;CACf,GACA;EACE,MAAM;EACN,aAAa;CACf,CACF;AACF,CAAC;AAED,MAAa,2BAA2B,QAAQ;CAC9C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"generate.command.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/generate/generate.command.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { command } from \"../../cli-command\";\r\nimport type { CommandActionData } from \"../../types\";\r\nimport { generateController } from \"./generators/controller.generator\";\r\nimport { generateMigration } from \"./generators/migration.generator\";\r\nimport { generateModel } from \"./generators/model.generator\";\r\nimport { generateModule } from \"./generators/module.generator\";\r\nimport { generateRepository } from \"./generators/repository.generator\";\r\nimport { generateResource } from \"./generators/resource.generator\";\r\nimport { generateService } from \"./generators/service.generator\";\r\n\r\nconst generators: Record<string, (data: CommandActionData) => Promise<void>> = {\r\n module: generateModule,\r\n controller: generateController,\r\n service: generateService,\r\n model: generateModel,\r\n migration: generateMigration,\r\n repository: generateRepository,\r\n resource: generateResource,\r\n};\r\n\r\nasync function generateCommandAction(data: CommandActionData) {\r\n const commandName = data.args[0];\r\n\r\n if (!commandName) {\r\n console.log(colors.red(\"Error: Generator name is required\"));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n console.log(colors.yellow(\"\\nUsage: warlock generate <generator> <name> [options]\"));\r\n process.exit(1);\r\n }\r\n\r\n const generator = generators[commandName];\r\n\r\n if (!generator) {\r\n console.log(colors.red(`Error: Unknown generator \"${commandName}\"`));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n process.exit(1);\r\n }\r\n\r\n // Remove the command name from args for the generator\r\n const generatorData: CommandActionData = {\r\n ...data,\r\n args: data.args.slice(1),\r\n };\r\n\r\n await generator(generatorData);\r\n}\r\n\r\n// Master generate command\r\nexport const generateCommand = command({\r\n name: \"generate <generator> [args...]\",\r\n alias: \"g\", // Allows 'warlock g module ...'\r\n description: \"Scaffold a new component\",\r\n action: generateCommandAction,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\n// Individual commands\r\nexport const generateModuleCommand = command({\r\n name: \"generate.module <name>\",\r\n alias: \"gen.m\",\r\n description: \"Generate a new module\",\r\n action: generateModule,\r\n options: [\r\n {\r\n text: \"--minimal, -m\",\r\n description:\r\n \"Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateControllerCommand = command({\r\n name: \"generate.controller <module>/<name>\",\r\n alias: \"gen.c\",\r\n description: \"Generate a new controller\",\r\n action: generateController,\r\n options: [\r\n {\r\n text: \"--with-validation, -v\",\r\n description: \"Generate a validation schema and bind it to the controller\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateServiceCommand = command({\r\n name: \"generate.service <module>/<name>\",\r\n alias: \"gen.s\",\r\n description: \"Generate a new service\",\r\n action: generateService,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateModelCommand = command({\r\n name: \"generate.model <module>/<name>\",\r\n alias: \"gen.md\",\r\n description: \"Generate a new model with migration\",\r\n action: generateModel,\r\n options: [\r\n {\r\n text: \"--with-resource, -rs\",\r\n description: \"Generate resource\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--table <name>\",\r\n description: \"Specify table name\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps in migration (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateRepositoryCommand = command({\r\n name: \"generate.repository <module>/<name>\",\r\n alias: \"gen.r\",\r\n description: \"Generate a new repository\",\r\n action: generateRepository,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateResourceCommand = command({\r\n name: \"generate.resource <module>/<name>\",\r\n alias: \"gen.rs\",\r\n description: \"Generate a new resource\",\r\n action: generateResource,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateMigrationCommand = command({\r\n name: \"generate.migration <model-path>\",\r\n alias: \"gen.mig\",\r\n description: \"Generate a new migration for a model\",\r\n action: generateMigration,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--add <columns>\",\r\n description: \"Columns to add (DSL: name:type:modifier,...)\",\r\n },\r\n {\r\n text: \"--drop <columns>\",\r\n description: \"Columns to drop (comma-separated names)\",\r\n },\r\n {\r\n text: \"--rename <columns>\",\r\n description: \"Columns to rename (DSL: old:new,...)\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,aAAyE;CAC7E,QAAQ;CACR,YAAY;CACZ,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,UAAU;AACZ;AAEA,eAAe,sBAAsB,MAAyB;CAC5D,MAAM,cAAc,KAAK,KAAK;CAE9B,IAAI,CAAC,aAAa;EAChB,QAAQ,IAAI,OAAO,IAAI,mCAAmC,CAAC;EAC3D,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,IAAI,OAAO,OAAO,wDAAwD,CAAC;EACnF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,YAAY,WAAW;CAE7B,IAAI,CAAC,WAAW;EACd,QAAQ,IAAI,OAAO,IAAI,6BAA6B,YAAY,EAAE,CAAC;EACnE,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,KAAK,CAAC;CAChB;CAQA,MAAM,UAAU;EAJd,GAAG;EACH,MAAM,KAAK,KAAK,MAAM,CAAC;CAGG,CAAC;AAC/B;AAGA,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAGD,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,uBAAuB,QAAQ;CAC1C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,0BAA0B,QAAQ;CAC7C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,2BAA2B,QAAQ;CAC9C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
@@ -51,7 +51,7 @@ import { guarded } from "app/shared/utils/router";
51
51
  // router.get("/${name.kebab}", listController);
52
52
  `;
53
53
  await putFileAsync(path.join(modulePath, "routes.ts"), routesContent);
54
- const localesContent = `import { groupedTranslations } from "@mongez/localization";
54
+ const localesContent = `import { groupedTranslations } from "@warlock.js/core";
55
55
 
56
56
  groupedTranslations("${name.camel}", {
57
57
  // Add your translations here
@@ -1 +1 @@
1
- {"version":3,"file":"module.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/module.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"../../../types\";\r\nimport {\r\n crudCreateControllerStub,\r\n crudCreateSchemaStub,\r\n crudCreateServiceStub,\r\n crudDeleteControllerStub,\r\n crudDeleteServiceStub,\r\n crudGetServiceStub,\r\n crudListControllerStub,\r\n crudListServiceStub,\r\n crudModelStub,\r\n crudRepositoryStub,\r\n crudResourceStub,\r\n crudRoutesStub,\r\n crudSeedStub,\r\n crudShowControllerStub,\r\n crudUpdateControllerStub,\r\n crudUpdateSchemaStub,\r\n crudUpdateServiceStub,\r\n} from \"../templates/stubs\";\r\nimport { pluralName, singularName } from \"../utils/name-parser\";\r\nimport { moduleExists } from \"../utils/path-resolver\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\nimport { createMigrationFile } from \"./migration.generator\";\r\n\r\nexport async function generateModule(data: CommandActionData): Promise<void> {\r\n const moduleName = data.args[0];\r\n\r\n if (!moduleName) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock generate.module <name> [options]\"));\r\n console.log(colors.yellow(\"Example: warlock generate.module products\"));\r\n console.log(colors.yellow(\" warlock generate.module products --minimal\"));\r\n process.exit(1);\r\n }\r\n\r\n const name = pluralName(moduleName);\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n // Full CRUD is the default; --minimal (-m) opts down to a bare skeleton.\r\n const minimal = data.options.minimal || data.options.m;\r\n const withCrud = !minimal;\r\n\r\n // Check if module already exists\r\n if ((await moduleExists(name.kebab)) && !force) {\r\n console.log(colors.red(`Error: Module \"${name.kebab}\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n const modulePath = appPath(name.kebab);\r\n\r\n // Create module directory structure\r\n const directories = [\r\n \"controllers\",\r\n \"services\",\r\n \"models\",\r\n \"repositories\",\r\n \"schema\",\r\n \"resources\",\r\n \"events\",\r\n \"types\",\r\n \"utils\",\r\n ];\r\n\r\n for (const dir of directories) {\r\n await ensureDirectoryAsync(path.join(modulePath, dir));\r\n }\r\n\r\n // Create main.ts\r\n const mainContent = `// You may use this file as a custom entry point to be executed when the app starts.\r\n// it runs only once, and you don't need to import the routes.ts file, it will be imported automatically.\r\n// Use it to regsiter or boot up any custom logic for this module.\r\n`;\r\n await putFileAsync(path.join(modulePath, \"main.ts\"), mainContent);\r\n\r\n // Create routes.ts\r\n const routesContent = withCrud\r\n ? crudRoutesStub(name)\r\n : `import { router } from \"@warlock.js/core\";\r\nimport { guarded } from \"app/shared/utils/router\";\r\n\r\n// Define your routes here\r\n// Example:\r\n// router.get(\"/${name.kebab}\", listController);\r\n`;\r\n await putFileAsync(path.join(modulePath, \"routes.ts\"), routesContent);\r\n\r\n // Create utils/locales.ts\r\n const localesContent = `import { groupedTranslations } from \"@mongez/localization\";\r\n\r\ngroupedTranslations(\"${name.camel}\", {\r\n // Add your translations here\r\n // Example:\r\n // welcome: {\r\n // en: \"Welcome\",\r\n // ar: \"مرحبا\",\r\n // },\r\n});\r\n`;\r\n await putFileAsync(path.join(modulePath, \"utils\", \"locales.ts\"), localesContent);\r\n\r\n // Full CRUD scaffold (the default) unless --minimal was passed\r\n if (withCrud) {\r\n const entity = singularName(moduleName);\r\n\r\n // Create controllers\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `create-${entity.kebab}.controller.ts`),\r\n crudCreateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `update-${entity.kebab}.controller.ts`),\r\n crudUpdateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `list-${name.kebab}.controller.ts`),\r\n crudListControllerStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `get-${entity.kebab}.controller.ts`),\r\n crudShowControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `delete-${entity.kebab}.controller.ts`),\r\n crudDeleteControllerStub(entity),\r\n );\r\n\r\n // Create schema files — each exports both the schema value and its\r\n // inferred type; controllers import them directly (no requests/ alias).\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `create-${entity.kebab}.schema.ts`),\r\n crudCreateSchemaStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `update-${entity.kebab}.schema.ts`),\r\n crudUpdateSchemaStub(entity),\r\n );\r\n\r\n // Create model\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab));\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, `${entity.kebab}.model.ts`),\r\n crudModelStub(entity),\r\n );\r\n\r\n // Create model index\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, \"index.ts\"),\r\n `export * from \"./${entity.kebab}.model\";\\n`,\r\n );\r\n\r\n // Create migrations folder\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab, \"migrations\"));\r\n\r\n // Create resource\r\n await putFileAsync(\r\n path.join(modulePath, \"resources\", `${entity.kebab}.resource.ts`),\r\n crudResourceStub(name),\r\n );\r\n\r\n // Create repository\r\n await putFileAsync(\r\n path.join(modulePath, \"repositories\", `${name.kebab}.repository.ts`),\r\n crudRepositoryStub(name),\r\n );\r\n\r\n // Create services\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `create-${entity.kebab}.service.ts`),\r\n crudCreateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `update-${entity.kebab}.service.ts`),\r\n crudUpdateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `list-${name.kebab}.service.ts`),\r\n crudListServiceStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `get-${entity.kebab}.service.ts`),\r\n crudGetServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `delete-${entity.kebab}.service.ts`),\r\n crudDeleteServiceStub(entity),\r\n );\r\n\r\n // Create seeds\r\n await ensureDirectoryAsync(path.join(modulePath, \"seeds\"));\r\n await putFileAsync(path.join(modulePath, \"seeds\", `${name.kebab}.seed.ts`), crudSeedStub(name));\r\n\r\n // Create migration\r\n await createMigrationFile(name.kebab, entity.kebab);\r\n }\r\n\r\n console.log(colors.cyan(`\\n✨ Module \"${name.kebab}\" generated successfully!`));\r\n\r\n if (withCrud) {\r\n console.log(colors.gray(`\\n📦 CRUD scaffold created with:`));\r\n console.log(colors.gray(` - List, Get, Create, Update & Delete controllers`));\r\n console.log(colors.gray(` - Repository`));\r\n console.log(colors.gray(` - Validation schemas in schema/`));\r\n console.log(colors.gray(` - Model with resource`));\r\n console.log(colors.gray(` - Routes configured`));\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(\r\n colors.gray(` 1. Update model schema in models/${name.kebab}/${name.kebab}.model.ts`),\r\n );\r\n console.log(colors.gray(` 2. Update schema rules in schema/*.schema.ts`));\r\n console.log(\r\n colors.gray(` 3. Create migration: warlock generate.model ${name.kebab}/${name.kebab}`),\r\n );\r\n } else {\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(colors.gray(` 1. Define routes in ${name.kebab}/routes.ts`));\r\n console.log(\r\n colors.gray(` 2. Create controllers: warlock generate.controller ${name.kebab}/<name>`),\r\n );\r\n console.log(colors.gray(` 3. Create models: warlock generate.model ${name.kebab}/<name>`));\r\n console.log(colors.gray(`\\nTip: omit --minimal to generate a full CRUD scaffold`));\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AA4BA,eAAsB,eAAe,MAAwC;CAC3E,MAAM,aAAa,KAAK,KAAK;CAE7B,IAAI,CAAC,YAAY;EACf,QAAQ,IAAI,OAAO,IAAI,gCAAgC,CAAC;EACxD,QAAQ,IAAI,OAAO,OAAO,iDAAiD,CAAC;EAC5E,QAAQ,IAAI,OAAO,OAAO,2CAA2C,CAAC;EACtE,QAAQ,IAAI,OAAO,OAAO,qDAAqD,CAAC;EAChF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,OAAO,WAAW,UAAU;CAClC,MAAM,QAAQ,KAAK,QAAQ,SAAS,KAAK,QAAQ;CACjD,UAAU,QAAQ,KAAK,QAAQ,MAAM,CAAC;CAItC,MAAM,WAAW,EADD,KAAK,QAAQ,WAAW,KAAK,QAAQ;CAIrD,IAAK,MAAM,aAAa,KAAK,KAAK,KAAM,CAAC,OAAO;EAC9C,QAAQ,IAAI,OAAO,IAAI,kBAAkB,KAAK,MAAM,iBAAiB,CAAC;EACtE,QAAQ,IAAI,OAAO,OAAO,0BAA0B,CAAC;EACrD,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,aAAa,QAAQ,KAAK,KAAK;CAerC,KAAK,MAAM,OAAO;EAXhB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAG0B,GAC1B,MAAM,qBAAqB,KAAK,KAAK,YAAY,GAAG,CAAC;CAQvD,MAAM,aAAa,KAAK,KAAK,YAAY,SAAS,GAAG;;;CAAW;CAGhE,MAAM,gBAAgB,WAClB,eAAe,IAAI,IACnB;;;;;kBAKY,KAAK,MAAM;;CAE3B,MAAM,aAAa,KAAK,KAAK,YAAY,WAAW,GAAG,aAAa;CAGpE,MAAM,iBAAiB;;uBAEF,KAAK,MAAM;;;;;;;;;CAShC,MAAM,aAAa,KAAK,KAAK,YAAY,SAAS,YAAY,GAAG,cAAc;CAG/E,IAAI,UAAU;EACZ,MAAM,SAAS,aAAa,UAAU;EAGtC,MAAM,aACJ,KAAK,KAAK,YAAY,eAAe,UAAU,OAAO,MAAM,eAAe,GAC3E,yBAAyB,MAAM,CACjC;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,eAAe,UAAU,OAAO,MAAM,eAAe,GAC3E,yBAAyB,MAAM,CACjC;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,eAAe,QAAQ,KAAK,MAAM,eAAe,GACvE,uBAAuB,IAAI,CAC7B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,eAAe,OAAO,OAAO,MAAM,eAAe,GACxE,uBAAuB,MAAM,CAC/B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,eAAe,UAAU,OAAO,MAAM,eAAe,GAC3E,yBAAyB,MAAM,CACjC;EAIA,MAAM,aACJ,KAAK,KAAK,YAAY,UAAU,UAAU,OAAO,MAAM,WAAW,GAClE,qBAAqB,MAAM,CAC7B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,UAAU,UAAU,OAAO,MAAM,WAAW,GAClE,qBAAqB,MAAM,CAC7B;EAGA,MAAM,qBAAqB,KAAK,KAAK,YAAY,UAAU,OAAO,KAAK,CAAC;EACxE,MAAM,aACJ,KAAK,KAAK,YAAY,UAAU,OAAO,OAAO,GAAG,OAAO,MAAM,UAAU,GACxE,cAAc,MAAM,CACtB;EAGA,MAAM,aACJ,KAAK,KAAK,YAAY,UAAU,OAAO,OAAO,UAAU,GACxD,oBAAoB,OAAO,MAAM,WACnC;EAGA,MAAM,qBAAqB,KAAK,KAAK,YAAY,UAAU,OAAO,OAAO,YAAY,CAAC;EAGtF,MAAM,aACJ,KAAK,KAAK,YAAY,aAAa,GAAG,OAAO,MAAM,aAAa,GAChE,iBAAiB,IAAI,CACvB;EAGA,MAAM,aACJ,KAAK,KAAK,YAAY,gBAAgB,GAAG,KAAK,MAAM,eAAe,GACnE,mBAAmB,IAAI,CACzB;EAGA,MAAM,aACJ,KAAK,KAAK,YAAY,YAAY,UAAU,OAAO,MAAM,YAAY,GACrE,sBAAsB,MAAM,CAC9B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,YAAY,UAAU,OAAO,MAAM,YAAY,GACrE,sBAAsB,MAAM,CAC9B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,YAAY,QAAQ,KAAK,MAAM,YAAY,GACjE,oBAAoB,IAAI,CAC1B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,YAAY,OAAO,OAAO,MAAM,YAAY,GAClE,mBAAmB,MAAM,CAC3B;EAEA,MAAM,aACJ,KAAK,KAAK,YAAY,YAAY,UAAU,OAAO,MAAM,YAAY,GACrE,sBAAsB,MAAM,CAC9B;EAGA,MAAM,qBAAqB,KAAK,KAAK,YAAY,OAAO,CAAC;EACzD,MAAM,aAAa,KAAK,KAAK,YAAY,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,aAAa,IAAI,CAAC;EAG9F,MAAM,oBAAoB,KAAK,OAAO,OAAO,KAAK;CACpD;CAEA,QAAQ,IAAI,OAAO,KAAK,iBAAiB,KAAK,MAAM,0BAA0B,CAAC;CAE/E,IAAI,UAAU;EACZ,QAAQ,IAAI,OAAO,KAAK,oCAAoC,CAAC;EAC7D,QAAQ,IAAI,OAAO,KAAK,oDAAoD,CAAC;EAC7E,QAAQ,IAAI,OAAO,KAAK,gBAAgB,CAAC;EACzC,QAAQ,IAAI,OAAO,KAAK,mCAAmC,CAAC;EAC5D,QAAQ,IAAI,OAAO,KAAK,yBAAyB,CAAC;EAClD,QAAQ,IAAI,OAAO,KAAK,uBAAuB,CAAC;EAChD,QAAQ,IAAI,OAAO,KAAK,eAAe,CAAC;EACxC,QAAQ,IACN,OAAO,KAAK,sCAAsC,KAAK,MAAM,GAAG,KAAK,MAAM,UAAU,CACvF;EACA,QAAQ,IAAI,OAAO,KAAK,gDAAgD,CAAC;EACzE,QAAQ,IACN,OAAO,KAAK,iDAAiD,KAAK,MAAM,GAAG,KAAK,OAAO,CACzF;CACF,OAAO;EACL,QAAQ,IAAI,OAAO,KAAK,eAAe,CAAC;EACxC,QAAQ,IAAI,OAAO,KAAK,yBAAyB,KAAK,MAAM,WAAW,CAAC;EACxE,QAAQ,IACN,OAAO,KAAK,wDAAwD,KAAK,MAAM,QAAQ,CACzF;EACA,QAAQ,IAAI,OAAO,KAAK,8CAA8C,KAAK,MAAM,QAAQ,CAAC;EAC1F,QAAQ,IAAI,OAAO,KAAK,wDAAwD,CAAC;CACnF;AACF"}
1
+ {"version":3,"file":"module.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/module.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"../../../types\";\r\nimport {\r\n crudCreateControllerStub,\r\n crudCreateSchemaStub,\r\n crudCreateServiceStub,\r\n crudDeleteControllerStub,\r\n crudDeleteServiceStub,\r\n crudGetServiceStub,\r\n crudListControllerStub,\r\n crudListServiceStub,\r\n crudModelStub,\r\n crudRepositoryStub,\r\n crudResourceStub,\r\n crudRoutesStub,\r\n crudSeedStub,\r\n crudShowControllerStub,\r\n crudUpdateControllerStub,\r\n crudUpdateSchemaStub,\r\n crudUpdateServiceStub,\r\n} from \"../templates/stubs\";\r\nimport { pluralName, singularName } from \"../utils/name-parser\";\r\nimport { moduleExists } from \"../utils/path-resolver\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\nimport { createMigrationFile } from \"./migration.generator\";\r\n\r\nexport async function generateModule(data: CommandActionData): Promise<void> {\r\n const moduleName = data.args[0];\r\n\r\n if (!moduleName) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock generate.module <name> [options]\"));\r\n console.log(colors.yellow(\"Example: warlock generate.module products\"));\r\n console.log(colors.yellow(\" warlock generate.module products --minimal\"));\r\n process.exit(1);\r\n }\r\n\r\n const name = pluralName(moduleName);\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n // Full CRUD is the default; --minimal (-m) opts down to a bare skeleton.\r\n const minimal = data.options.minimal || data.options.m;\r\n const withCrud = !minimal;\r\n\r\n // Check if module already exists\r\n if ((await moduleExists(name.kebab)) && !force) {\r\n console.log(colors.red(`Error: Module \"${name.kebab}\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n const modulePath = appPath(name.kebab);\r\n\r\n // Create module directory structure\r\n const directories = [\r\n \"controllers\",\r\n \"services\",\r\n \"models\",\r\n \"repositories\",\r\n \"schema\",\r\n \"resources\",\r\n \"events\",\r\n \"types\",\r\n \"utils\",\r\n ];\r\n\r\n for (const dir of directories) {\r\n await ensureDirectoryAsync(path.join(modulePath, dir));\r\n }\r\n\r\n // Create main.ts\r\n const mainContent = `// You may use this file as a custom entry point to be executed when the app starts.\r\n// it runs only once, and you don't need to import the routes.ts file, it will be imported automatically.\r\n// Use it to regsiter or boot up any custom logic for this module.\r\n`;\r\n await putFileAsync(path.join(modulePath, \"main.ts\"), mainContent);\r\n\r\n // Create routes.ts\r\n const routesContent = withCrud\r\n ? crudRoutesStub(name)\r\n : `import { router } from \"@warlock.js/core\";\r\nimport { guarded } from \"app/shared/utils/router\";\r\n\r\n// Define your routes here\r\n// Example:\r\n// router.get(\"/${name.kebab}\", listController);\r\n`;\r\n await putFileAsync(path.join(modulePath, \"routes.ts\"), routesContent);\r\n\r\n // Create utils/locales.ts\r\n const localesContent = `import { groupedTranslations } from \"@warlock.js/core\";\r\n\r\ngroupedTranslations(\"${name.camel}\", {\r\n // Add your translations here\r\n // Example:\r\n // welcome: {\r\n // en: \"Welcome\",\r\n // ar: \"مرحبا\",\r\n // },\r\n});\r\n`;\r\n await putFileAsync(path.join(modulePath, \"utils\", \"locales.ts\"), localesContent);\r\n\r\n // Full CRUD scaffold (the default) unless --minimal was passed\r\n if (withCrud) {\r\n const entity = singularName(moduleName);\r\n\r\n // Create controllers\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `create-${entity.kebab}.controller.ts`),\r\n crudCreateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `update-${entity.kebab}.controller.ts`),\r\n crudUpdateControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `list-${name.kebab}.controller.ts`),\r\n crudListControllerStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `get-${entity.kebab}.controller.ts`),\r\n crudShowControllerStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"controllers\", `delete-${entity.kebab}.controller.ts`),\r\n crudDeleteControllerStub(entity),\r\n );\r\n\r\n // Create schema files — each exports both the schema value and its\r\n // inferred type; controllers import them directly (no requests/ alias).\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `create-${entity.kebab}.schema.ts`),\r\n crudCreateSchemaStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"schema\", `update-${entity.kebab}.schema.ts`),\r\n crudUpdateSchemaStub(entity),\r\n );\r\n\r\n // Create model\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab));\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, `${entity.kebab}.model.ts`),\r\n crudModelStub(entity),\r\n );\r\n\r\n // Create model index\r\n await putFileAsync(\r\n path.join(modulePath, \"models\", entity.kebab, \"index.ts\"),\r\n `export * from \"./${entity.kebab}.model\";\\n`,\r\n );\r\n\r\n // Create migrations folder\r\n await ensureDirectoryAsync(path.join(modulePath, \"models\", entity.kebab, \"migrations\"));\r\n\r\n // Create resource\r\n await putFileAsync(\r\n path.join(modulePath, \"resources\", `${entity.kebab}.resource.ts`),\r\n crudResourceStub(name),\r\n );\r\n\r\n // Create repository\r\n await putFileAsync(\r\n path.join(modulePath, \"repositories\", `${name.kebab}.repository.ts`),\r\n crudRepositoryStub(name),\r\n );\r\n\r\n // Create services\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `create-${entity.kebab}.service.ts`),\r\n crudCreateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `update-${entity.kebab}.service.ts`),\r\n crudUpdateServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `list-${name.kebab}.service.ts`),\r\n crudListServiceStub(name),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `get-${entity.kebab}.service.ts`),\r\n crudGetServiceStub(entity),\r\n );\r\n\r\n await putFileAsync(\r\n path.join(modulePath, \"services\", `delete-${entity.kebab}.service.ts`),\r\n crudDeleteServiceStub(entity),\r\n );\r\n\r\n // Create seeds\r\n await ensureDirectoryAsync(path.join(modulePath, \"seeds\"));\r\n await putFileAsync(path.join(modulePath, \"seeds\", `${name.kebab}.seed.ts`), crudSeedStub(name));\r\n\r\n // Create migration\r\n await createMigrationFile(name.kebab, entity.kebab);\r\n }\r\n\r\n console.log(colors.cyan(`\\n✨ Module \"${name.kebab}\" generated successfully!`));\r\n\r\n if (withCrud) {\r\n console.log(colors.gray(`\\n📦 CRUD scaffold created with:`));\r\n console.log(colors.gray(` - List, Get, Create, Update & Delete controllers`));\r\n console.log(colors.gray(` - Repository`));\r\n console.log(colors.gray(` - Validation schemas in schema/`));\r\n console.log(colors.gray(` - Model with resource`));\r\n console.log(colors.gray(` - Routes configured`));\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(\r\n colors.gray(` 1. Update model schema in models/${name.kebab}/${name.kebab}.model.ts`),\r\n );\r\n console.log(colors.gray(` 2. Update schema rules in schema/*.schema.ts`));\r\n console.log(\r\n colors.gray(` 3. Create migration: warlock generate.model ${name.kebab}/${name.kebab}`),\r\n );\r\n } else {\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(colors.gray(` 1. Define routes in ${name.kebab}/routes.ts`));\r\n console.log(\r\n colors.gray(` 2. Create controllers: warlock generate.controller ${name.kebab}/<name>`),\r\n );\r\n console.log(colors.gray(` 3. Create models: warlock generate.model ${name.kebab}/<name>`));\r\n console.log(colors.gray(`\\nTip: omit --minimal to generate a full CRUD scaffold`));\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -8,7 +8,6 @@ const migrateCommand = command({
8
8
  description: "Run database migrations",
9
9
  preload: {
10
10
  config: ["database", "log"],
11
- env: true,
12
11
  connectors: ["database", "logger"]
13
12
  },
14
13
  options: [
@@ -29,7 +28,12 @@ const migrateCommand = command({
29
28
  },
30
29
  {
31
30
  text: "--list, -l",
32
- description: "List all executed migrations",
31
+ description: "Show migration state: executed migrations, then what will run next",
32
+ type: "boolean"
33
+ },
34
+ {
35
+ text: "--pending",
36
+ description: "List only what will run next, in execution order. Exits 0 if nothing is pending, 1 if something is, 2 if it could not be determined",
33
37
  type: "boolean"
34
38
  },
35
39
  {
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/migrate.command.ts"],"sourcesContent":["import { migrateAction } from \"../../database/migrate-action\";\nimport { command } from \"../cli-command\";\n\nexport const migrateCommand = command({\n action: migrateAction,\n name: \"migrate\",\n description: \"Run database migrations\",\n preload: {\n config: [\"database\", \"log\"],\n env: true,\n connectors: [\"database\", \"logger\"],\n },\n options: [\n {\n text: \"--fresh, -f\",\n description: \"Drop all tables and re-run migrations\",\n type: \"boolean\",\n },\n {\n text: \"--rollback, -r\",\n description: \"Rollback migrations, drop all tables\",\n type: \"boolean\",\n },\n {\n text: \"--path, -p\",\n description: \"Migration file path, if not provided, all migrations will be wroking\",\n type: \"string\",\n },\n {\n text: \"--list, -l\",\n description: \"List all executed migrations\",\n type: \"boolean\",\n },\n {\n text: \"--all, -a\",\n description: \"List all migrations files in the app\",\n type: \"boolean\",\n },\n {\n text: \"--sql, -s\",\n description: \"Export all migrations as phase-ordered SQL files instead of executing them\",\n type: \"boolean\",\n },\n {\n text: \"--pending-only\",\n description: \"When used with --sql, exports only pending migrations instead of all\",\n type: \"boolean\",\n },\n {\n text: \"--compact, -c\",\n description:\n \"When used with --sql, strips out generated comments and blank lines to reduce file size\",\n type: \"boolean\",\n },\n ],\n});\n"],"mappings":";;;;AAGA,MAAa,iBAAiB,QAAQ;CACpC,QAAQ;CACR,MAAM;CACN,aAAa;CACb,SAAS;EACP,QAAQ,CAAC,YAAY,KAAK;EAC1B,KAAK;EACL,YAAY,CAAC,YAAY,QAAQ;CACnC;CACA,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"migrate.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/migrate.command.ts"],"sourcesContent":["import { migrateAction } from \"../../database/migrate-action\";\nimport { command } from \"../cli-command\";\n\nexport const migrateCommand = command({\n action: migrateAction,\n name: \"migrate\",\n description: \"Run database migrations\",\n preload: {\n config: [\"database\", \"log\"],\n connectors: [\"database\", \"logger\"],\n },\n options: [\n {\n text: \"--fresh, -f\",\n description: \"Drop all tables and re-run migrations\",\n type: \"boolean\",\n },\n {\n text: \"--rollback, -r\",\n description: \"Rollback migrations, drop all tables\",\n type: \"boolean\",\n },\n {\n text: \"--path, -p\",\n description: \"Migration file path, if not provided, all migrations will be wroking\",\n type: \"string\",\n },\n {\n text: \"--list, -l\",\n description: \"Show migration state: executed migrations, then what will run next\",\n type: \"boolean\",\n },\n {\n text: \"--pending\",\n description:\n \"List only what will run next, in execution order. Exits 0 if nothing is pending, 1 if something is, 2 if it could not be determined\",\n type: \"boolean\",\n },\n {\n text: \"--all, -a\",\n description: \"List all migrations files in the app\",\n type: \"boolean\",\n },\n {\n text: \"--sql, -s\",\n description: \"Export all migrations as phase-ordered SQL files instead of executing them\",\n type: \"boolean\",\n },\n {\n text: \"--pending-only\",\n description: \"When used with --sql, exports only pending migrations instead of all\",\n type: \"boolean\",\n },\n {\n text: \"--compact, -c\",\n description:\n \"When used with --sql, strips out generated comments and blank lines to reduce file size\",\n type: \"boolean\",\n },\n ],\n});\n"],"mappings":";;;;AAGA,MAAa,iBAAiB,QAAQ;CACpC,QAAQ;CACR,MAAM;CACN,aAAa;CACb,SAAS;EACP,QAAQ,CAAC,YAAY,KAAK;EAC1B,YAAY,CAAC,YAAY,QAAQ;CACnC;CACA,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;CACF;AACF,CAAC"}