@nexical/cli 0.11.8 → 0.11.10

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 (46) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/commands/deploy.d.ts +2 -0
  4. package/dist/src/commands/deploy.js +3 -3
  5. package/dist/src/commands/deploy.js.map +1 -1
  6. package/dist/src/commands/init.js +3 -3
  7. package/dist/src/commands/module/add.js +53 -22
  8. package/dist/src/commands/module/add.js.map +1 -1
  9. package/dist/src/commands/module/list.d.ts +1 -0
  10. package/dist/src/commands/module/list.js +54 -45
  11. package/dist/src/commands/module/list.js.map +1 -1
  12. package/dist/src/commands/module/remove.js +37 -12
  13. package/dist/src/commands/module/remove.js.map +1 -1
  14. package/dist/src/commands/module/update.js +15 -3
  15. package/dist/src/commands/module/update.js.map +1 -1
  16. package/dist/src/commands/run.js +18 -1
  17. package/dist/src/commands/run.js.map +1 -1
  18. package/package.json +2 -2
  19. package/src/commands/deploy.ts +3 -3
  20. package/src/commands/module/add.ts +74 -31
  21. package/src/commands/module/list.ts +80 -57
  22. package/src/commands/module/remove.ts +50 -14
  23. package/src/commands/module/update.ts +19 -5
  24. package/src/commands/run.ts +21 -1
  25. package/test/e2e/lifecycle.e2e.test.ts +3 -2
  26. package/test/integration/commands/deploy.integration.test.ts +102 -0
  27. package/test/integration/commands/init.integration.test.ts +16 -1
  28. package/test/integration/commands/module.integration.test.ts +81 -55
  29. package/test/integration/commands/run.integration.test.ts +69 -74
  30. package/test/integration/commands/setup.integration.test.ts +53 -0
  31. package/test/unit/commands/deploy.test.ts +285 -0
  32. package/test/unit/commands/init.test.ts +15 -0
  33. package/test/unit/commands/module/add.test.ts +363 -254
  34. package/test/unit/commands/module/list.test.ts +100 -99
  35. package/test/unit/commands/module/remove.test.ts +143 -58
  36. package/test/unit/commands/module/update.test.ts +45 -62
  37. package/test/unit/commands/run.test.ts +16 -1
  38. package/test/unit/commands/setup.test.ts +25 -66
  39. package/test/unit/deploy/config-manager.test.ts +65 -0
  40. package/test/unit/deploy/providers/cloudflare.test.ts +210 -0
  41. package/test/unit/deploy/providers/github.test.ts +139 -0
  42. package/test/unit/deploy/providers/railway.test.ts +328 -0
  43. package/test/unit/deploy/registry.test.ts +227 -0
  44. package/test/unit/deploy/utils.test.ts +30 -0
  45. package/test/unit/utils/command-discovery.test.ts +145 -142
  46. package/test/unit/utils/git_utils.test.ts +49 -0
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { fileURLToPath } from "url";
15
15
  // package.json
16
16
  var package_default = {
17
17
  name: "@nexical/cli",
18
- version: "0.11.8",
18
+ version: "0.11.10",
19
19
  type: "module",
20
20
  bin: {
21
21
  nexical: "./dist/index.js"
@@ -43,7 +43,7 @@ var package_default = {
43
43
  ]
44
44
  },
45
45
  dependencies: {
46
- "@nexical/cli-core": "^0.1.12",
46
+ "@nexical/cli-core": "^0.1.15",
47
47
  dotenv: "^17.3.1",
48
48
  "fast-glob": "^3.3.3",
49
49
  jiti: "^2.6.1",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI, findProjectRoot } from '@nexical/cli-core';\nimport { fileURLToPath } from 'node:url';\nimport { discoverCommandDirectories } from './src/utils/discovery.js';\nimport pkg from './package.json';\nimport path from 'node:path';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst commandName = 'nexical';\nconst projectRoot = (await findProjectRoot(commandName, process.cwd())) || process.cwd();\nconst coreCommandsDir = path.resolve(__dirname, './src/commands');\nconst additionalCommands = discoverCommandDirectories(projectRoot);\n\n// Filter out duplicate core commands and source versions\nconst filteredAdditional = additionalCommands.filter((dir) => {\n const resolvedDir = path.resolve(dir);\n const resolvedCore = path.resolve(coreCommandsDir);\n\n if (resolvedDir === resolvedCore) return false;\n\n // Check if this is another instance of the core CLI commands (by checking path suffix)\n const coreSuffix = path.join('@nexical', 'cli', 'dist', 'src', 'commands');\n const coreSuffixSrc = path.join('packages', 'cli', 'dist', 'src', 'commands');\n const coreSuffixRawSrc = path.join('packages', 'cli', 'src', 'commands');\n\n if (\n resolvedDir.endsWith(coreSuffix) ||\n resolvedDir.endsWith(coreSuffixSrc) ||\n resolvedDir.endsWith(coreSuffixRawSrc)\n ) {\n return false;\n }\n\n // Handle mismatch between dist/src and src/\n if (resolvedCore.includes(path.join(path.sep, 'dist', 'src', 'commands'))) {\n const srcVersion = resolvedCore.replace(\n path.join(path.sep, 'dist', 'src', 'commands'),\n path.join(path.sep, 'src', 'commands'),\n );\n if (resolvedDir === srcVersion) return false;\n }\n\n return true;\n});\n\nconst app = new CLI({\n version: pkg.version,\n commandName: commandName,\n searchDirectories: [...new Set([coreCommandsDir, ...filteredAdditional])],\n});\napp.start();\n","{\n \"name\": \"@nexical/cli\",\n \"version\": \"0.11.8\",\n \"type\": \"module\",\n \"bin\": {\n \"nexical\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\",\n \"format\": \"prettier --write .\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"prepare\": \"husky\"\n },\n \"lint-staged\": {\n \"**/*\": [\n \"prettier --write --ignore-unknown\"\n ],\n \"**/*.{js,jsx,ts,tsx,astro}\": [\n \"eslint --fix\"\n ]\n },\n \"dependencies\": {\n \"@nexical/cli-core\": \"^0.1.12\",\n \"dotenv\": \"^17.3.1\",\n \"fast-glob\": \"^3.3.3\",\n \"jiti\": \"^2.6.1\",\n \"yaml\": \"^2.3.4\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.10.0\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-astro\": \"^1.5.0\",\n \"eslint-plugin-jsx-a11y\": \"^6.10.2\",\n \"eslint-plugin-react\": \"^7.37.5\",\n \"eslint-plugin-react-hooks\": \"^7.0.1\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.2\",\n \"globals\": \"^17.2.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.2.7\",\n \"prettier\": \"^3.8.1\",\n \"tsup\": \"^8.0.1\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.3.3\",\n \"typescript-eslint\": \"^8.54.0\",\n \"vitest\": \"^4.0.15\"\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AACA,SAAS,KAAK,uBAAuB;AACrC,SAAS,qBAAqB;;;ACF9B;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,SAAW;AAAA,EACb;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,KAAO;AAAA,IACP,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,QAAU;AAAA,IACV,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAW;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,IACF;AAAA,IACA,8BAA8B;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAgB;AAAA,IACd,qBAAqB;AAAA,IACrB,QAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAQ;AAAA,IACR,MAAQ;AAAA,EACV;AAAA,EACA,iBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,uBAAuB;AAAA,IACvB,QAAU;AAAA,IACV,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,6BAA6B;AAAA,IAC7B,OAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAW;AAAA,IACX,OAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,YAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,QAAU;AAAA,EACZ;AACF;;;ADtDA,OAAO,UAAU;AAEjB,IAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAE7D,IAAM,cAAc;AACpB,IAAM,cAAe,MAAM,gBAAgB,aAAa,QAAQ,IAAI,CAAC,KAAM,QAAQ,IAAI;AACvF,IAAM,kBAAkB,KAAK,QAAQ,WAAW,gBAAgB;AAChE,IAAM,qBAAqB,2BAA2B,WAAW;AAGjE,IAAM,qBAAqB,mBAAmB,OAAO,CAAC,QAAQ;AAC5D,QAAM,cAAc,KAAK,QAAQ,GAAG;AACpC,QAAM,eAAe,KAAK,QAAQ,eAAe;AAEjD,MAAI,gBAAgB,aAAc,QAAO;AAGzC,QAAM,aAAa,KAAK,KAAK,YAAY,OAAO,QAAQ,OAAO,UAAU;AACzE,QAAM,gBAAgB,KAAK,KAAK,YAAY,OAAO,QAAQ,OAAO,UAAU;AAC5E,QAAM,mBAAmB,KAAK,KAAK,YAAY,OAAO,OAAO,UAAU;AAEvE,MACE,YAAY,SAAS,UAAU,KAC/B,YAAY,SAAS,aAAa,KAClC,YAAY,SAAS,gBAAgB,GACrC;AACA,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,SAAS,KAAK,KAAK,KAAK,KAAK,QAAQ,OAAO,UAAU,CAAC,GAAG;AACzE,UAAM,aAAa,aAAa;AAAA,MAC9B,KAAK,KAAK,KAAK,KAAK,QAAQ,OAAO,UAAU;AAAA,MAC7C,KAAK,KAAK,KAAK,KAAK,OAAO,UAAU;AAAA,IACvC;AACA,QAAI,gBAAgB,WAAY,QAAO;AAAA,EACzC;AAEA,SAAO;AACT,CAAC;AAED,IAAM,MAAM,IAAI,IAAI;AAAA,EAClB,SAAS,gBAAI;AAAA,EACb;AAAA,EACA,mBAAmB,CAAC,GAAG,oBAAI,IAAI,CAAC,iBAAiB,GAAG,kBAAkB,CAAC,CAAC;AAC1E,CAAC;AACD,IAAI,MAAM;","names":[]}
1
+ {"version":3,"sources":["../index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI, findProjectRoot } from '@nexical/cli-core';\nimport { fileURLToPath } from 'node:url';\nimport { discoverCommandDirectories } from './src/utils/discovery.js';\nimport pkg from './package.json';\nimport path from 'node:path';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst commandName = 'nexical';\nconst projectRoot = (await findProjectRoot(commandName, process.cwd())) || process.cwd();\nconst coreCommandsDir = path.resolve(__dirname, './src/commands');\nconst additionalCommands = discoverCommandDirectories(projectRoot);\n\n// Filter out duplicate core commands and source versions\nconst filteredAdditional = additionalCommands.filter((dir) => {\n const resolvedDir = path.resolve(dir);\n const resolvedCore = path.resolve(coreCommandsDir);\n\n if (resolvedDir === resolvedCore) return false;\n\n // Check if this is another instance of the core CLI commands (by checking path suffix)\n const coreSuffix = path.join('@nexical', 'cli', 'dist', 'src', 'commands');\n const coreSuffixSrc = path.join('packages', 'cli', 'dist', 'src', 'commands');\n const coreSuffixRawSrc = path.join('packages', 'cli', 'src', 'commands');\n\n if (\n resolvedDir.endsWith(coreSuffix) ||\n resolvedDir.endsWith(coreSuffixSrc) ||\n resolvedDir.endsWith(coreSuffixRawSrc)\n ) {\n return false;\n }\n\n // Handle mismatch between dist/src and src/\n if (resolvedCore.includes(path.join(path.sep, 'dist', 'src', 'commands'))) {\n const srcVersion = resolvedCore.replace(\n path.join(path.sep, 'dist', 'src', 'commands'),\n path.join(path.sep, 'src', 'commands'),\n );\n if (resolvedDir === srcVersion) return false;\n }\n\n return true;\n});\n\nconst app = new CLI({\n version: pkg.version,\n commandName: commandName,\n searchDirectories: [...new Set([coreCommandsDir, ...filteredAdditional])],\n});\napp.start();\n","{\n \"name\": \"@nexical/cli\",\n \"version\": \"0.11.10\",\n \"type\": \"module\",\n \"bin\": {\n \"nexical\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\",\n \"format\": \"prettier --write .\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"prepare\": \"husky\"\n },\n \"lint-staged\": {\n \"**/*\": [\n \"prettier --write --ignore-unknown\"\n ],\n \"**/*.{js,jsx,ts,tsx,astro}\": [\n \"eslint --fix\"\n ]\n },\n \"dependencies\": {\n \"@nexical/cli-core\": \"^0.1.15\",\n \"dotenv\": \"^17.3.1\",\n \"fast-glob\": \"^3.3.3\",\n \"jiti\": \"^2.6.1\",\n \"yaml\": \"^2.3.4\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.10.0\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-astro\": \"^1.5.0\",\n \"eslint-plugin-jsx-a11y\": \"^6.10.2\",\n \"eslint-plugin-react\": \"^7.37.5\",\n \"eslint-plugin-react-hooks\": \"^7.0.1\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.2\",\n \"globals\": \"^17.2.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.2.7\",\n \"prettier\": \"^3.8.1\",\n \"tsup\": \"^8.0.1\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.3.3\",\n \"typescript-eslint\": \"^8.54.0\",\n \"vitest\": \"^4.0.15\"\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AACA,SAAS,KAAK,uBAAuB;AACrC,SAAS,qBAAqB;;;ACF9B;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,SAAW;AAAA,EACb;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,KAAO;AAAA,IACP,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,QAAU;AAAA,IACV,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAW;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,IACF;AAAA,IACA,8BAA8B;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAgB;AAAA,IACd,qBAAqB;AAAA,IACrB,QAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAQ;AAAA,IACR,MAAQ;AAAA,EACV;AAAA,EACA,iBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,uBAAuB;AAAA,IACvB,QAAU;AAAA,IACV,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,6BAA6B;AAAA,IAC7B,OAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAW;AAAA,IACX,OAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,YAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,QAAU;AAAA,EACZ;AACF;;;ADtDA,OAAO,UAAU;AAEjB,IAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAE7D,IAAM,cAAc;AACpB,IAAM,cAAe,MAAM,gBAAgB,aAAa,QAAQ,IAAI,CAAC,KAAM,QAAQ,IAAI;AACvF,IAAM,kBAAkB,KAAK,QAAQ,WAAW,gBAAgB;AAChE,IAAM,qBAAqB,2BAA2B,WAAW;AAGjE,IAAM,qBAAqB,mBAAmB,OAAO,CAAC,QAAQ;AAC5D,QAAM,cAAc,KAAK,QAAQ,GAAG;AACpC,QAAM,eAAe,KAAK,QAAQ,eAAe;AAEjD,MAAI,gBAAgB,aAAc,QAAO;AAGzC,QAAM,aAAa,KAAK,KAAK,YAAY,OAAO,QAAQ,OAAO,UAAU;AACzE,QAAM,gBAAgB,KAAK,KAAK,YAAY,OAAO,QAAQ,OAAO,UAAU;AAC5E,QAAM,mBAAmB,KAAK,KAAK,YAAY,OAAO,OAAO,UAAU;AAEvE,MACE,YAAY,SAAS,UAAU,KAC/B,YAAY,SAAS,aAAa,KAClC,YAAY,SAAS,gBAAgB,GACrC;AACA,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,SAAS,KAAK,KAAK,KAAK,KAAK,QAAQ,OAAO,UAAU,CAAC,GAAG;AACzE,UAAM,aAAa,aAAa;AAAA,MAC9B,KAAK,KAAK,KAAK,KAAK,QAAQ,OAAO,UAAU;AAAA,MAC7C,KAAK,KAAK,KAAK,KAAK,OAAO,UAAU;AAAA,IACvC;AACA,QAAI,gBAAgB,WAAY,QAAO;AAAA,EACzC;AAEA,SAAO;AACT,CAAC;AAED,IAAM,MAAM,IAAI,IAAI;AAAA,EAClB,SAAS,gBAAI;AAAA,EACb;AAAA,EACA,mBAAmB,CAAC,GAAG,oBAAI,IAAI,CAAC,iBAAiB,GAAG,kBAAkB,CAAC,CAAC;AAC1E,CAAC;AACD,IAAI,MAAM;","names":[]}
@@ -1,7 +1,9 @@
1
1
  import { BaseCommand } from '@nexical/cli-core';
2
2
 
3
3
  declare class DeployCommand extends BaseCommand {
4
+ static usage: string;
4
5
  static description: string;
6
+ static help: string;
5
7
  static args: {
6
8
  options: ({
7
9
  name: string;
@@ -15,9 +15,9 @@ import path from "path";
15
15
  import dotenv from "dotenv";
16
16
  import { BaseCommand } from "@nexical/cli-core";
17
17
  var DeployCommand = class extends BaseCommand {
18
- static description = `Deploy the application based on nexical.yaml configuration.
19
-
20
- This command orchestrates the deployment of your frontend and backend applications
18
+ static usage = "deploy";
19
+ static description = "Deploy the application based on nexical.yaml configuration.";
20
+ static help = `This command orchestrates the deployment of your frontend and backend applications
21
21
  by interacting with the providers specified in your configuration file.
22
22
 
23
23
  CONFIGURATION:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/deploy.ts"],"sourcesContent":["import path from 'node:path';\nimport dotenv from 'dotenv';\nimport { BaseCommand } from '@nexical/cli-core';\nimport { ConfigManager } from '../deploy/config-manager';\nimport { ProviderRegistry } from '../deploy/registry';\nimport { DeploymentContext } from '../deploy/types';\n\nexport default class DeployCommand extends BaseCommand {\n static description = `Deploy the application based on nexical.yaml configuration.\n\nThis command orchestrates the deployment of your frontend and backend applications \nby interacting with the providers specified in your configuration file.\n\nCONFIGURATION:\n- Requires a 'nexical.yaml' file in the project root.\n- If the file or specific sections are missing, the CLI will prompt you to run an interactive setup \n and save the configuration for future uses.\n- Supports loading environment variables from a .env file in the project root.\n\nPROVIDERS:\n- Backend: Railway, etc.\n- Frontend: Cloudflare Pages, etc.\n- Repository: GitHub, GitLab, etc.\n\nPROCESS:\n1. Loads environment variables from '.env'.\n2. Loads configuration from 'nexical.yaml'.\n3. Provisions resources via the selected providers.\n4. Configures the repository (secrets/variables) for CI/CD.\n5. Generates CI/CD workflow files.`;\n\n static args = {\n options: [\n {\n name: '--backend <provider>',\n description: 'Override backend provider',\n },\n {\n name: '--frontend <provider>',\n description: 'Override frontend provider',\n },\n {\n name: '--repo <provider>',\n description: 'Override repositroy provider',\n },\n {\n name: '--env <environment>',\n description: 'Deployment environment (e.g. production, staging)',\n default: 'production',\n },\n {\n name: '--dry-run',\n description: 'Simulate the deployment process',\n default: false,\n },\n ],\n };\n\n async run(options: Record<string, unknown>) {\n this.info('Starting Nexical Deployment...');\n\n // Load environment variables from .env\n dotenv.config({ path: path.join(process.cwd(), '.env') });\n\n const configManager = new ConfigManager(process.cwd());\n const config = await configManager.load();\n const registry = new ProviderRegistry();\n\n // Register core and local providers\n await registry.loadCoreProviders();\n await registry.loadLocalProviders(process.cwd());\n\n // Resolve providers (CLI flags > Config > Error)\n const backendProviderName =\n (options.backend as string | undefined) || config.deploy?.backend?.provider;\n if (!backendProviderName) {\n this.error(\n \"Backend provider not specified. Use --backend flag or configure 'deploy.backend.provider' in nexical.yaml.\",\n );\n }\n\n const frontendProviderName =\n (options.frontend as string | undefined) || config.deploy?.frontend?.provider;\n if (!frontendProviderName) {\n this.error(\n \"Frontend provider not specified. Use --frontend flag or configure 'deploy.frontend.provider' in nexical.yaml.\",\n );\n }\n\n const repoProviderName =\n (options.repo as string | undefined) || config.deploy?.repository?.provider;\n if (!repoProviderName) {\n this.error(\n \"Repository provider not specified. Use --repo flag or configure 'deploy.repository.provider' in nexical.yaml.\",\n );\n }\n\n const backendProvider = registry.getDeploymentProvider(backendProviderName!);\n const frontendProvider = registry.getDeploymentProvider(frontendProviderName!);\n const repoProvider = registry.getRepositoryProvider(repoProviderName!);\n\n if (!backendProvider) throw new Error(`Backend provider '${backendProviderName}' not found.`);\n if (!frontendProvider)\n throw new Error(`Frontend provider '${frontendProviderName}' not found.`);\n if (!repoProvider) throw new Error(`Repository provider '${repoProviderName}' not found.`);\n\n const context: DeploymentContext = {\n cwd: process.cwd(),\n config,\n options,\n };\n\n // Provision\n this.info(`Provisioning Backend with ${backendProvider.name}...`);\n await backendProvider.provision(context);\n\n this.info(`Provisioning Frontend with ${frontendProvider.name}...`);\n await frontendProvider.provision(context);\n\n // Configure Repo\n this.info(`Configuring Repository with ${repoProvider.name}...`);\n\n const secrets: Record<string, string> = {};\n\n // Collect secrets from Backend Provider\n this.info(`Resolving secrets from ${backendProvider.name}...`);\n try {\n const backendSecrets = await backendProvider.getSecrets(context);\n Object.assign(secrets, backendSecrets);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve secrets for ${backendProvider.name}: ${message}`);\n }\n\n // Collect secrets from Frontend Provider\n this.info(`Resolving secrets from ${frontendProvider.name}...`);\n try {\n const frontendSecrets = await frontendProvider.getSecrets(context);\n Object.assign(secrets, frontendSecrets);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve secrets for ${frontendProvider.name}: ${message}`);\n }\n\n await repoProvider.configureSecrets(context, secrets);\n\n const variables: Record<string, string> = {};\n\n // Collect variables from Backend Provider\n try {\n const backendVars = await backendProvider.getVariables(context);\n Object.assign(variables, backendVars);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve variables for ${backendProvider.name}: ${message}`);\n }\n\n // Collect variables from Frontend Provider\n try {\n const frontendVars = await frontendProvider.getVariables(context);\n Object.assign(variables, frontendVars);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve variables for ${frontendProvider.name}: ${message}`);\n }\n\n await repoProvider.configureVariables(context, variables);\n\n // Generate Workflows\n this.info('Generating CI/CD Workflows...');\n await repoProvider.generateWorkflow(context, [backendProvider, frontendProvider]);\n\n this.success('Deployment configuration complete!');\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/commands/deploy.ts"],"sourcesContent":["import path from 'node:path';\nimport dotenv from 'dotenv';\nimport { BaseCommand } from '@nexical/cli-core';\nimport { ConfigManager } from '../deploy/config-manager';\nimport { ProviderRegistry } from '../deploy/registry';\nimport { DeploymentContext } from '../deploy/types';\n\nexport default class DeployCommand extends BaseCommand {\n static usage = 'deploy';\n static description = 'Deploy the application based on nexical.yaml configuration.';\n static help = `This command orchestrates the deployment of your frontend and backend applications \nby interacting with the providers specified in your configuration file.\n\nCONFIGURATION:\n- Requires a 'nexical.yaml' file in the project root.\n- If the file or specific sections are missing, the CLI will prompt you to run an interactive setup \n and save the configuration for future uses.\n- Supports loading environment variables from a .env file in the project root.\n\nPROVIDERS:\n- Backend: Railway, etc.\n- Frontend: Cloudflare Pages, etc.\n- Repository: GitHub, GitLab, etc.\n\nPROCESS:\n1. Loads environment variables from '.env'.\n2. Loads configuration from 'nexical.yaml'.\n3. Provisions resources via the selected providers.\n4. Configures the repository (secrets/variables) for CI/CD.\n5. Generates CI/CD workflow files.`;\n\n static args = {\n options: [\n {\n name: '--backend <provider>',\n description: 'Override backend provider',\n },\n {\n name: '--frontend <provider>',\n description: 'Override frontend provider',\n },\n {\n name: '--repo <provider>',\n description: 'Override repositroy provider',\n },\n {\n name: '--env <environment>',\n description: 'Deployment environment (e.g. production, staging)',\n default: 'production',\n },\n {\n name: '--dry-run',\n description: 'Simulate the deployment process',\n default: false,\n },\n ],\n };\n\n async run(options: Record<string, unknown>) {\n this.info('Starting Nexical Deployment...');\n\n // Load environment variables from .env\n dotenv.config({ path: path.join(process.cwd(), '.env') });\n\n const configManager = new ConfigManager(process.cwd());\n const config = await configManager.load();\n const registry = new ProviderRegistry();\n\n // Register core and local providers\n await registry.loadCoreProviders();\n await registry.loadLocalProviders(process.cwd());\n\n // Resolve providers (CLI flags > Config > Error)\n const backendProviderName =\n (options.backend as string | undefined) || config.deploy?.backend?.provider;\n if (!backendProviderName) {\n this.error(\n \"Backend provider not specified. Use --backend flag or configure 'deploy.backend.provider' in nexical.yaml.\",\n );\n }\n\n const frontendProviderName =\n (options.frontend as string | undefined) || config.deploy?.frontend?.provider;\n if (!frontendProviderName) {\n this.error(\n \"Frontend provider not specified. Use --frontend flag or configure 'deploy.frontend.provider' in nexical.yaml.\",\n );\n }\n\n const repoProviderName =\n (options.repo as string | undefined) || config.deploy?.repository?.provider;\n if (!repoProviderName) {\n this.error(\n \"Repository provider not specified. Use --repo flag or configure 'deploy.repository.provider' in nexical.yaml.\",\n );\n }\n\n const backendProvider = registry.getDeploymentProvider(backendProviderName!);\n const frontendProvider = registry.getDeploymentProvider(frontendProviderName!);\n const repoProvider = registry.getRepositoryProvider(repoProviderName!);\n\n if (!backendProvider) throw new Error(`Backend provider '${backendProviderName}' not found.`);\n if (!frontendProvider)\n throw new Error(`Frontend provider '${frontendProviderName}' not found.`);\n if (!repoProvider) throw new Error(`Repository provider '${repoProviderName}' not found.`);\n\n const context: DeploymentContext = {\n cwd: process.cwd(),\n config,\n options,\n };\n\n // Provision\n this.info(`Provisioning Backend with ${backendProvider.name}...`);\n await backendProvider.provision(context);\n\n this.info(`Provisioning Frontend with ${frontendProvider.name}...`);\n await frontendProvider.provision(context);\n\n // Configure Repo\n this.info(`Configuring Repository with ${repoProvider.name}...`);\n\n const secrets: Record<string, string> = {};\n\n // Collect secrets from Backend Provider\n this.info(`Resolving secrets from ${backendProvider.name}...`);\n try {\n const backendSecrets = await backendProvider.getSecrets(context);\n Object.assign(secrets, backendSecrets);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve secrets for ${backendProvider.name}: ${message}`);\n }\n\n // Collect secrets from Frontend Provider\n this.info(`Resolving secrets from ${frontendProvider.name}...`);\n try {\n const frontendSecrets = await frontendProvider.getSecrets(context);\n Object.assign(secrets, frontendSecrets);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve secrets for ${frontendProvider.name}: ${message}`);\n }\n\n await repoProvider.configureSecrets(context, secrets);\n\n const variables: Record<string, string> = {};\n\n // Collect variables from Backend Provider\n try {\n const backendVars = await backendProvider.getVariables(context);\n Object.assign(variables, backendVars);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve variables for ${backendProvider.name}: ${message}`);\n }\n\n // Collect variables from Frontend Provider\n try {\n const frontendVars = await frontendProvider.getVariables(context);\n Object.assign(variables, frontendVars);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n this.error(`Failed to resolve variables for ${frontendProvider.name}: ${message}`);\n }\n\n await repoProvider.configureVariables(context, variables);\n\n // Generate Workflows\n this.info('Generating CI/CD Workflows...');\n await repoProvider.generateWorkflow(context, [backendProvider, frontendProvider]);\n\n this.success('Deployment configuration complete!');\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,4 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
- import {
3
- require_lib
4
- } from "../../chunk-OUGA4CB4.js";
5
2
  import {
6
3
  addAll,
7
4
  clone,
@@ -12,6 +9,9 @@ import {
12
9
  import {
13
10
  resolveGitUrl
14
11
  } from "../../chunk-PJIOCW2A.js";
12
+ import {
13
+ require_lib
14
+ } from "../../chunk-OUGA4CB4.js";
15
15
  import {
16
16
  __toESM,
17
17
  init_esm_shims
@@ -1,7 +1,4 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
- import {
3
- require_lib
4
- } from "../../../chunk-OUGA4CB4.js";
5
2
  import {
6
3
  clone,
7
4
  getRemoteUrl
@@ -9,6 +6,9 @@ import {
9
6
  import {
10
7
  resolveGitUrl
11
8
  } from "../../../chunk-PJIOCW2A.js";
9
+ import {
10
+ require_lib
11
+ } from "../../../chunk-OUGA4CB4.js";
12
12
  import {
13
13
  __toESM,
14
14
  init_esm_shims
@@ -66,6 +66,7 @@ var ModuleAddCommand = class extends BaseCommand {
66
66
  `staging-${Date.now()}-${Math.random().toString(36).substring(7)}`
67
67
  );
68
68
  let moduleName = "";
69
+ let moduleType = "backend";
69
70
  let dependencies = [];
70
71
  try {
71
72
  await import_fs_extra.default.ensureDir(stagingDir);
@@ -73,42 +74,67 @@ var ModuleAddCommand = class extends BaseCommand {
73
74
  const searchPath = subPath ? path.join(stagingDir, subPath) : stagingDir;
74
75
  const moduleYamlPath = path.join(searchPath, "module.yaml");
75
76
  const moduleYmlPath = path.join(searchPath, "module.yml");
77
+ const pkgJsonPath = path.join(searchPath, "package.json");
76
78
  let configPath = "";
77
79
  if (await import_fs_extra.default.pathExists(moduleYamlPath)) configPath = moduleYamlPath;
78
80
  else if (await import_fs_extra.default.pathExists(moduleYmlPath)) configPath = moduleYmlPath;
79
- else {
80
- throw new Error(`No module.yaml found in ${cleanUrl}${subPath ? "//" + subPath : ""}`);
81
+ if (configPath) {
82
+ const configContent = await import_fs_extra.default.readFile(configPath, "utf8");
83
+ const config = YAML.parse(configContent);
84
+ if (config.name) moduleName = config.name;
85
+ dependencies = config.dependencies || [];
86
+ }
87
+ if (!moduleName && await import_fs_extra.default.pathExists(pkgJsonPath)) {
88
+ try {
89
+ const pkg = await import_fs_extra.default.readJson(pkgJsonPath);
90
+ if (pkg.name) {
91
+ moduleName = pkg.name.startsWith("@modules/") ? pkg.name.split("/")[1] : pkg.name;
92
+ }
93
+ } catch {
94
+ }
81
95
  }
82
- const configContent = await import_fs_extra.default.readFile(configPath, "utf8");
83
- const config = YAML.parse(configContent);
84
- if (!config.name) {
85
- throw new Error(`Module at ${url} is missing 'name' in module.yaml`);
96
+ if (!moduleName) {
97
+ moduleName = path.basename(cleanUrl, ".git");
98
+ }
99
+ const hasUiYaml = await import_fs_extra.default.pathExists(path.join(searchPath, "ui.yaml"));
100
+ const hasModelsYaml = await import_fs_extra.default.pathExists(path.join(searchPath, "models.yaml"));
101
+ const hasApiYaml = await import_fs_extra.default.pathExists(path.join(searchPath, "api.yaml"));
102
+ if (hasUiYaml) {
103
+ moduleType = "frontend";
104
+ } else if (hasModelsYaml || hasApiYaml) {
105
+ moduleType = "backend";
106
+ } else {
107
+ if (await import_fs_extra.default.pathExists(path.join(searchPath, "src", "components"))) {
108
+ moduleType = "frontend";
109
+ } else {
110
+ moduleType = "backend";
111
+ }
86
112
  }
87
- moduleName = config.name;
88
- dependencies = config.dependencies || [];
89
113
  if (dependencies && !Array.isArray(dependencies)) {
90
114
  dependencies = Object.keys(dependencies);
91
115
  }
92
116
  } finally {
93
117
  await import_fs_extra.default.remove(stagingDir);
94
118
  }
95
- const targetDir = path.join(projectRoot, "modules", moduleName);
96
- const relativeTargetDir = path.relative(projectRoot, targetDir);
119
+ const modulesBaseDir = moduleType === "frontend" ? "apps/frontend/modules" : "apps/backend/modules";
120
+ const relativeTargetDir = path.join(modulesBaseDir, moduleName);
121
+ const targetDir = path.join(projectRoot, relativeTargetDir);
97
122
  if (await import_fs_extra.default.pathExists(targetDir)) {
98
123
  const existingRemote = await getRemoteUrl(targetDir);
99
124
  const normExisting = existingRemote.replace(/\.git$/, "");
100
125
  const normNew = cleanUrl.replace(/\.git$/, "");
101
126
  if (normExisting !== normNew && existingRemote !== "") {
102
127
  throw new Error(
103
- `Dependency Conflict! Module '${moduleName}' exists but remote '${existingRemote}' does not match '${cleanUrl}'.`
128
+ `Dependency Conflict! Module '${moduleName}' exists in ${moduleType} but remote '${existingRemote}' does not match '${cleanUrl}'.`
104
129
  );
105
130
  }
106
- this.info(`Module ${moduleName} already installed.`);
131
+ this.info(`Module ${moduleName} already installed in ${moduleType}.`);
107
132
  } else {
108
- this.info(`Installing ${moduleName} to ${relativeTargetDir}...`);
133
+ this.info(`Installing ${moduleName} (${moduleType}) to ${relativeTargetDir}...`);
134
+ await import_fs_extra.default.ensureDir(path.dirname(targetDir));
109
135
  await runCommand(`git submodule add ${cleanUrl} ${relativeTargetDir}`, projectRoot);
110
136
  }
111
- await this.addToConfig(moduleName);
137
+ await this.addToConfig(moduleName, moduleType);
112
138
  if (dependencies.length > 0) {
113
139
  this.info(`Resolving ${dependencies.length} dependencies for ${moduleName}...`);
114
140
  for (const depUrl of dependencies) {
@@ -116,7 +142,7 @@ var ModuleAddCommand = class extends BaseCommand {
116
142
  }
117
143
  }
118
144
  }
119
- async addToConfig(moduleName) {
145
+ async addToConfig(moduleName, type) {
120
146
  const projectRoot = this.projectRoot;
121
147
  const configPath = path.join(projectRoot, "nexical.yaml");
122
148
  if (!await import_fs_extra.default.pathExists(configPath)) {
@@ -126,11 +152,16 @@ var ModuleAddCommand = class extends BaseCommand {
126
152
  try {
127
153
  const content = await import_fs_extra.default.readFile(configPath, "utf8");
128
154
  const config = YAML.parse(content) || {};
129
- if (!config.modules) config.modules = [];
130
- if (!config.modules.includes(moduleName)) {
131
- config.modules.push(moduleName);
155
+ if (!config.modules) config.modules = {};
156
+ if (Array.isArray(config.modules)) {
157
+ const oldModules = config.modules;
158
+ config.modules = { backend: oldModules, frontend: [] };
159
+ }
160
+ if (!config.modules[type]) config.modules[type] = [];
161
+ if (!config.modules[type].includes(moduleName)) {
162
+ config.modules[type].push(moduleName);
132
163
  await import_fs_extra.default.writeFile(configPath, YAML.stringify(config));
133
- logger.debug(`Added ${moduleName} to nexical.yaml modules list.`);
164
+ logger.debug(`Added ${moduleName} to nexical.yaml modules.${type} list.`);
134
165
  }
135
166
  } catch (e) {
136
167
  if (e instanceof Error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/commands/module/add.ts"],"sourcesContent":["import { type CommandDefinition, BaseCommand, logger, runCommand } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { clone, getRemoteUrl } from '../../utils/git.js';\nimport { resolveGitUrl } from '../../utils/url-resolver.js';\nimport YAML from 'yaml';\n\nexport default class ModuleAddCommand extends BaseCommand {\n static usage = 'module add <url>';\n static description = 'Add a module and its dependencies as git submodules.';\n static requiresProject = true;\n\n static args: CommandDefinition = {\n args: [{ name: 'url', required: true, description: 'Git repository URL or gh@org/repo' }],\n };\n\n private visited = new Set<string>();\n\n async run(options: { url: string }) {\n const projectRoot = this.projectRoot as string;\n const { url } = options;\n\n if (!url) {\n this.error('Please specify a repository URL.');\n return;\n }\n\n try {\n await this.installModule(url);\n\n this.info('Syncing workspace dependencies...');\n await runCommand('npm install', projectRoot);\n\n this.success('All modules installed successfully.');\n } catch (e: unknown) {\n if (e instanceof Error) {\n this.error(`Failed to add module: ${e.message}`);\n } else {\n this.error(`Failed to add module: ${String(e)}`);\n }\n }\n }\n\n private async installModule(url: string) {\n const projectRoot = this.projectRoot as string;\n\n // Resolve URL using utility\n url = resolveGitUrl(url);\n\n const [repoUrl, subPath] = url.split('.git//');\n const cleanUrl = subPath ? repoUrl + '.git' : url;\n\n if (this.visited.has(cleanUrl)) {\n logger.debug(`Already visited ${cleanUrl}, skipping.`);\n return;\n }\n this.visited.add(cleanUrl);\n\n this.info(`Inspecting ${cleanUrl}...`);\n\n // Stage 1: Inspect (Temp Clone)\n const stagingDir = path.resolve(\n projectRoot!,\n '.nexical',\n 'cache',\n `staging-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n );\n let moduleName = '';\n let dependencies: string[] = [];\n\n try {\n await fs.ensureDir(stagingDir);\n\n // Shallow clone to inspect\n await clone(cleanUrl, stagingDir, { depth: 1 });\n\n // Read module.yaml\n const searchPath = subPath ? path.join(stagingDir, subPath) : stagingDir;\n const moduleYamlPath = path.join(searchPath, 'module.yaml');\n const moduleYmlPath = path.join(searchPath, 'module.yml');\n\n let configPath = '';\n if (await fs.pathExists(moduleYamlPath)) configPath = moduleYamlPath;\n else if (await fs.pathExists(moduleYmlPath)) configPath = moduleYmlPath;\n else {\n throw new Error(`No module.yaml found in ${cleanUrl}${subPath ? '//' + subPath : ''}`);\n }\n\n const configContent = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(configContent);\n\n if (!config.name) {\n throw new Error(`Module at ${url} is missing 'name' in module.yaml`);\n }\n moduleName = config.name;\n dependencies = config.dependencies || [];\n\n // Normalize dependencies to array if object (though spec says list of strings, defensiveness is good)\n if (dependencies && !Array.isArray(dependencies)) {\n dependencies = Object.keys(dependencies);\n }\n } finally {\n // Cleanup staging always\n await fs.remove(stagingDir);\n }\n\n // Stage 2: Conflict Detection\n const targetDir = path.join(projectRoot!, 'modules', moduleName);\n const relativeTargetDir = path.relative(projectRoot!, targetDir);\n\n if (await fs.pathExists(targetDir)) {\n // Check origin\n const existingRemote = await getRemoteUrl(targetDir);\n // We compare cleanUrl (the repo root).\n // normalize both\n const normExisting = existingRemote.replace(/\\.git$/, '');\n const normNew = cleanUrl.replace(/\\.git$/, '');\n\n if (normExisting !== normNew && existingRemote !== '') {\n throw new Error(\n `Dependency Conflict! Module '${moduleName}' exists but remote '${existingRemote}' does not match '${cleanUrl}'.`,\n );\n }\n\n this.info(`Module ${moduleName} already installed.`);\n // Proceed to recurse, but skip add\n } else {\n // Stage 3: Submodule Add\n this.info(`Installing ${moduleName} to ${relativeTargetDir}...`);\n // We install the ROOT repo.\n // IMPORTANT: If subPath exists, \"Identity is Internal\" means we name the folder `moduleName`.\n // But the CONTENT will be the whole repo.\n // If the user meant to only have the subdir, we can't do that with submodule add easily without manual git plumbing.\n // Given instructions, I will proceed with submodule add of root repo to target dir.\n await runCommand(`git submodule add ${cleanUrl} ${relativeTargetDir}`, projectRoot!);\n }\n\n // Update nexical.yaml\n await this.addToConfig(moduleName);\n\n // Stage 4: Recurse\n if (dependencies.length > 0) {\n this.info(`Resolving ${dependencies.length} dependencies for ${moduleName}...`);\n for (const depUrl of dependencies) {\n await this.installModule(depUrl);\n }\n }\n }\n\n private async addToConfig(moduleName: string) {\n const projectRoot = this.projectRoot as string;\n const configPath = path.join(projectRoot, 'nexical.yaml');\n\n if (!(await fs.pathExists(configPath))) {\n // Not strictly required to exist for all operations, but good to have if we are tracking modules.\n logger.warn('nexical.yaml not found, skipping module list update.');\n return;\n }\n\n try {\n const content = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(content) || {};\n\n if (!config.modules) config.modules = [];\n\n if (!config.modules.includes(moduleName)) {\n config.modules.push(moduleName);\n await fs.writeFile(configPath, YAML.stringify(config));\n logger.debug(`Added ${moduleName} to nexical.yaml modules list.`);\n }\n } catch (e: unknown) {\n if (e instanceof Error) {\n logger.warn(`Failed to update nexical.yaml: ${e.message}`);\n } else {\n logger.warn(`Failed to update nexical.yaml: ${String(e)}`);\n }\n }\n }\n}\n"],"mappings":"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","names":["fs"]}
1
+ {"version":3,"sources":["../../../../src/commands/module/add.ts"],"sourcesContent":["import { type CommandDefinition, BaseCommand, logger, runCommand } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { clone, getRemoteUrl } from '../../utils/git.js';\nimport { resolveGitUrl } from '../../utils/url-resolver.js';\nimport YAML from 'yaml';\n\nexport default class ModuleAddCommand extends BaseCommand {\n static usage = 'module add <url>';\n static description = 'Add a module and its dependencies as git submodules.';\n static requiresProject = true;\n\n static args: CommandDefinition = {\n args: [{ name: 'url', required: true, description: 'Git repository URL or gh@org/repo' }],\n };\n\n private visited = new Set<string>();\n\n async run(options: { url: string }) {\n const projectRoot = this.projectRoot as string;\n const { url } = options;\n\n if (!url) {\n this.error('Please specify a repository URL.');\n return;\n }\n\n try {\n await this.installModule(url);\n\n this.info('Syncing workspace dependencies...');\n await runCommand('npm install', projectRoot);\n\n this.success('All modules installed successfully.');\n } catch (e: unknown) {\n if (e instanceof Error) {\n this.error(`Failed to add module: ${e.message}`);\n } else {\n this.error(`Failed to add module: ${String(e)}`);\n }\n }\n }\n\n private async installModule(url: string) {\n const projectRoot = this.projectRoot as string;\n\n // Resolve URL using utility\n url = resolveGitUrl(url);\n\n const [repoUrl, subPath] = url.split('.git//');\n const cleanUrl = subPath ? repoUrl + '.git' : url;\n\n if (this.visited.has(cleanUrl)) {\n logger.debug(`Already visited ${cleanUrl}, skipping.`);\n return;\n }\n this.visited.add(cleanUrl);\n\n this.info(`Inspecting ${cleanUrl}...`);\n\n // Stage 1: Inspect (Temp Clone)\n const stagingDir = path.resolve(\n projectRoot!,\n '.nexical',\n 'cache',\n `staging-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n );\n let moduleName = '';\n let moduleType: 'backend' | 'frontend' = 'backend'; // Default to backend if uncertain, but we should detect.\n let dependencies: string[] = [];\n\n try {\n await fs.ensureDir(stagingDir);\n\n // Shallow clone to inspect\n await clone(cleanUrl, stagingDir, { depth: 1 });\n\n // Search path handling\n const searchPath = subPath ? path.join(stagingDir, subPath) : stagingDir;\n\n // 1. Detect Module Name & Dependencies\n const moduleYamlPath = path.join(searchPath, 'module.yaml');\n const moduleYmlPath = path.join(searchPath, 'module.yml');\n const pkgJsonPath = path.join(searchPath, 'package.json');\n\n let configPath = '';\n if (await fs.pathExists(moduleYamlPath)) configPath = moduleYamlPath;\n else if (await fs.pathExists(moduleYmlPath)) configPath = moduleYmlPath;\n\n // Try to get name from module.yaml/yml\n if (configPath) {\n const configContent = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(configContent);\n if (config.name) moduleName = config.name;\n dependencies = config.dependencies || [];\n }\n\n // If no name yet, try package.json\n if (!moduleName && (await fs.pathExists(pkgJsonPath))) {\n try {\n const pkg = await fs.readJson(pkgJsonPath);\n if (pkg.name) {\n // Handle scoped packages @modules/name -> name\n moduleName = pkg.name.startsWith('@modules/') ? pkg.name.split('/')[1] : pkg.name;\n }\n } catch {\n /* ignore */\n }\n }\n\n // Fallback to git repo name if still no name\n if (!moduleName) {\n moduleName = path.basename(cleanUrl, '.git');\n }\n\n // 2. Detect Module Type\n // Frontend indicators: ui.yaml, or specifically typed in module.config.mjs (harder to parse statically), or package.json dependencies like 'react'/'astro' (maybe too broad).\n // Backend indicators: models.yaml, api.yaml, access.yaml.\n\n const hasUiYaml = await fs.pathExists(path.join(searchPath, 'ui.yaml'));\n const hasModelsYaml = await fs.pathExists(path.join(searchPath, 'models.yaml'));\n const hasApiYaml = await fs.pathExists(path.join(searchPath, 'api.yaml'));\n\n if (hasUiYaml) {\n moduleType = 'frontend';\n } else if (hasModelsYaml || hasApiYaml) {\n moduleType = 'backend';\n } else {\n // Fallback: Check checking package.json for \"auth-astro\" which is common in both, but maybe \"react\" or \"vue\" for frontend?\n // Let's assume Backend default if ambiguous for now, or check for specific folder structure?\n // Let's look for `src/components` vs `src/services`.\n if (await fs.pathExists(path.join(searchPath, 'src', 'components'))) {\n moduleType = 'frontend';\n } else {\n moduleType = 'backend';\n }\n }\n\n // Normalize dependencies\n if (dependencies && !Array.isArray(dependencies)) {\n dependencies = Object.keys(dependencies);\n }\n } finally {\n // Cleanup staging always\n await fs.remove(stagingDir);\n }\n\n // Stage 2: Conflict Detection & Path Resolution\n const modulesBaseDir =\n moduleType === 'frontend' ? 'apps/frontend/modules' : 'apps/backend/modules';\n const relativeTargetDir = path.join(modulesBaseDir, moduleName);\n const targetDir = path.join(projectRoot!, relativeTargetDir);\n\n if (await fs.pathExists(targetDir)) {\n // Check origin\n const existingRemote = await getRemoteUrl(targetDir);\n const normExisting = existingRemote.replace(/\\.git$/, '');\n const normNew = cleanUrl.replace(/\\.git$/, '');\n\n if (normExisting !== normNew && existingRemote !== '') {\n throw new Error(\n `Dependency Conflict! Module '${moduleName}' exists in ${moduleType} but remote '${existingRemote}' does not match '${cleanUrl}'.`,\n );\n }\n\n this.info(`Module ${moduleName} already installed in ${moduleType}.`);\n } else {\n // Stage 3: Submodule Add\n this.info(`Installing ${moduleName} (${moduleType}) to ${relativeTargetDir}...`);\n await fs.ensureDir(path.dirname(targetDir)); // Ensure apps/backend/modules exists\n await runCommand(`git submodule add ${cleanUrl} ${relativeTargetDir}`, projectRoot!);\n }\n\n // Update nexical.yaml\n await this.addToConfig(moduleName, moduleType);\n\n // Stage 4: Recurse\n if (dependencies.length > 0) {\n this.info(`Resolving ${dependencies.length} dependencies for ${moduleName}...`);\n for (const depUrl of dependencies) {\n await this.installModule(depUrl);\n }\n }\n }\n\n private async addToConfig(moduleName: string, type: 'backend' | 'frontend') {\n const projectRoot = this.projectRoot as string;\n const configPath = path.join(projectRoot, 'nexical.yaml');\n\n if (!(await fs.pathExists(configPath))) {\n logger.warn('nexical.yaml not found, skipping module list update.');\n return;\n }\n\n try {\n const content = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(content) || {};\n\n if (!config.modules) config.modules = {};\n\n // Migration: If modules is array, convert to object\n if (Array.isArray(config.modules)) {\n const oldModules = config.modules;\n config.modules = { backend: oldModules, frontend: [] }; // Assume old were backend? Or just move them to backend for safety.\n }\n\n if (!config.modules[type]) config.modules[type] = [];\n\n if (!config.modules[type].includes(moduleName)) {\n config.modules[type].push(moduleName);\n await fs.writeFile(configPath, YAML.stringify(config));\n logger.debug(`Added ${moduleName} to nexical.yaml modules.${type} list.`);\n }\n } catch (e: unknown) {\n if (e instanceof Error) {\n logger.warn(`Failed to update nexical.yaml: ${e.message}`);\n } else {\n logger.warn(`Failed to update nexical.yaml: ${String(e)}`);\n }\n }\n }\n}\n"],"mappings":"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","names":["fs"]}
@@ -5,6 +5,7 @@ declare class ModuleListCommand extends BaseCommand {
5
5
  static description: string;
6
6
  static requiresProject: boolean;
7
7
  run(): Promise<void>;
8
+ private getModuleInfo;
8
9
  }
9
10
 
10
11
  export { ModuleListCommand as default };
@@ -10,7 +10,7 @@ import {
10
10
  // src/commands/module/list.ts
11
11
  init_esm_shims();
12
12
  var import_fs_extra = __toESM(require_lib(), 1);
13
- import { BaseCommand, logger } from "@nexical/cli-core";
13
+ import { BaseCommand } from "@nexical/cli-core";
14
14
  import path from "path";
15
15
  import YAML from "yaml";
16
16
  var ModuleListCommand = class extends BaseCommand {
@@ -19,56 +19,65 @@ var ModuleListCommand = class extends BaseCommand {
19
19
  static requiresProject = true;
20
20
  async run() {
21
21
  const projectRoot = this.projectRoot;
22
- const modulesDir = path.resolve(projectRoot, "modules");
23
- logger.debug(`Scanning for modules in: ${modulesDir}`);
24
- if (!await import_fs_extra.default.pathExists(modulesDir)) {
25
- this.info("No modules installed (modules directory missing).");
26
- return;
27
- }
28
- try {
29
- const modules = await import_fs_extra.default.readdir(modulesDir);
30
- const validModules = [];
31
- for (const moduleName of modules) {
32
- const modulePath = path.join(modulesDir, moduleName);
33
- if ((await import_fs_extra.default.stat(modulePath)).isDirectory()) {
34
- let version = "unknown";
35
- let description = "";
36
- const pkgJsonPath = path.join(modulePath, "package.json");
37
- const moduleYamlPath = path.join(modulePath, "module.yaml");
38
- const moduleYmlPath = path.join(modulePath, "module.yml");
39
- let pkg = {};
40
- let modConfig = {};
41
- if (await import_fs_extra.default.pathExists(pkgJsonPath)) {
42
- try {
43
- pkg = await import_fs_extra.default.readJson(pkgJsonPath);
44
- } catch {
45
- }
46
- }
47
- if (await import_fs_extra.default.pathExists(moduleYamlPath) || await import_fs_extra.default.pathExists(moduleYmlPath)) {
48
- try {
49
- const configPath = await import_fs_extra.default.pathExists(moduleYamlPath) ? moduleYamlPath : moduleYmlPath;
50
- const content = await import_fs_extra.default.readFile(configPath, "utf8");
51
- modConfig = YAML.parse(content) || {};
52
- } catch {
53
- }
22
+ const builtInLocations = [
23
+ { type: "backend", path: path.join(projectRoot, "apps/backend/modules") },
24
+ { type: "frontend", path: path.join(projectRoot, "apps/frontend/modules") },
25
+ // Check legacy `modules` folder just in case?
26
+ { type: "legacy", path: path.join(projectRoot, "modules") }
27
+ ];
28
+ const allModules = [];
29
+ for (const loc of builtInLocations) {
30
+ if (await import_fs_extra.default.pathExists(loc.path)) {
31
+ const modules = await import_fs_extra.default.readdir(loc.path);
32
+ for (const moduleName of modules) {
33
+ const modulePath = path.join(loc.path, moduleName);
34
+ if ((await import_fs_extra.default.stat(modulePath)).isDirectory()) {
35
+ const info = await this.getModuleInfo(
36
+ modulePath,
37
+ moduleName,
38
+ loc.type
39
+ );
40
+ allModules.push(info);
54
41
  }
55
- version = pkg.version || modConfig.version || "unknown";
56
- description = pkg.description || modConfig.description || "";
57
- validModules.push({ name: moduleName, version, description });
58
42
  }
59
43
  }
60
- if (validModules.length === 0) {
61
- this.info("No modules installed.");
62
- } else {
63
- console.table(validModules);
44
+ }
45
+ if (allModules.length === 0) {
46
+ this.info("No modules installed.");
47
+ } else {
48
+ allModules.sort((a, b) => {
49
+ if (a.type !== b.type) return a.type.localeCompare(b.type);
50
+ return a.name.localeCompare(b.name);
51
+ });
52
+ console.table(allModules);
53
+ }
54
+ }
55
+ async getModuleInfo(modulePath, dirName, type) {
56
+ let version = "unknown";
57
+ let description = "";
58
+ const pkgJsonPath = path.join(modulePath, "package.json");
59
+ const moduleYamlPath = path.join(modulePath, "module.yaml");
60
+ const moduleYmlPath = path.join(modulePath, "module.yml");
61
+ let pkg = {};
62
+ let modConfig = {};
63
+ if (await import_fs_extra.default.pathExists(pkgJsonPath)) {
64
+ try {
65
+ pkg = await import_fs_extra.default.readJson(pkgJsonPath) || {};
66
+ } catch {
64
67
  }
65
- } catch (error) {
66
- if (error instanceof Error) {
67
- this.error(`Failed to list modules: ${error.message}`);
68
- } else {
69
- this.error(`Failed to list modules: ${String(error)}`);
68
+ }
69
+ if (await import_fs_extra.default.pathExists(moduleYamlPath) || await import_fs_extra.default.pathExists(moduleYmlPath)) {
70
+ try {
71
+ const configPath = await import_fs_extra.default.pathExists(moduleYamlPath) ? moduleYamlPath : moduleYmlPath;
72
+ const content = await import_fs_extra.default.readFile(configPath, "utf8");
73
+ modConfig = YAML.parse(content) || {};
74
+ } catch {
70
75
  }
71
76
  }
77
+ version = pkg.version || modConfig.version || "unknown";
78
+ description = pkg.description || modConfig.description || "";
79
+ const name = modConfig.name || dirName;
80
+ return { name, version, description, type };
72
81
  }
73
82
  };
74
83
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/commands/module/list.ts"],"sourcesContent":["import { BaseCommand, logger } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport YAML from 'yaml';\n\nexport default class ModuleListCommand extends BaseCommand {\n static usage = 'module list';\n static description = 'List installed modules.';\n static requiresProject = true;\n\n async run() {\n const projectRoot = this.projectRoot as string;\n const modulesDir = path.resolve(projectRoot, 'modules');\n logger.debug(`Scanning for modules in: ${modulesDir}`);\n\n if (!(await fs.pathExists(modulesDir))) {\n this.info('No modules installed (modules directory missing).');\n return;\n }\n\n try {\n const modules = await fs.readdir(modulesDir);\n const validModules: { name: string; version: string; description: string }[] = [];\n\n for (const moduleName of modules) {\n const modulePath = path.join(modulesDir, moduleName);\n if ((await fs.stat(modulePath)).isDirectory()) {\n let version = 'unknown';\n let description = '';\n\n const pkgJsonPath = path.join(modulePath, 'package.json');\n const moduleYamlPath = path.join(modulePath, 'module.yaml');\n const moduleYmlPath = path.join(modulePath, 'module.yml');\n\n let pkg: Record<string, unknown> = {};\n let modConfig: Record<string, unknown> = {};\n\n if (await fs.pathExists(pkgJsonPath)) {\n try {\n pkg = await fs.readJson(pkgJsonPath);\n } catch {\n /* ignore */\n }\n }\n\n if ((await fs.pathExists(moduleYamlPath)) || (await fs.pathExists(moduleYmlPath))) {\n try {\n const configPath = (await fs.pathExists(moduleYamlPath))\n ? moduleYamlPath\n : moduleYmlPath;\n const content = await fs.readFile(configPath, 'utf8');\n modConfig = YAML.parse(content) || {};\n } catch {\n /* ignore */\n }\n }\n\n version = (pkg.version as string) || (modConfig.version as string) || 'unknown';\n description = (pkg.description as string) || (modConfig.description as string) || '';\n // Optionally use display name from module.yaml if present, but strictly list is usually dir name.\n // Let's stick to dir name for \"name\" column, but description from module.yaml is good.\n validModules.push({ name: moduleName, version, description });\n }\n }\n\n if (validModules.length === 0) {\n this.info('No modules installed.');\n } else {\n // eslint-disable-next-line no-console\n console.table(validModules);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n this.error(`Failed to list modules: ${error.message}`);\n } else {\n this.error(`Failed to list modules: ${String(error)}`);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AACA,sBAAe;AADf,SAAS,aAAa,cAAc;AAEpC,OAAO,UAAU;AACjB,OAAO,UAAU;AAEjB,IAAqB,oBAArB,cAA+C,YAAY;AAAA,EACzD,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,kBAAkB;AAAA,EAEzB,MAAM,MAAM;AACV,UAAM,cAAc,KAAK;AACzB,UAAM,aAAa,KAAK,QAAQ,aAAa,SAAS;AACtD,WAAO,MAAM,4BAA4B,UAAU,EAAE;AAErD,QAAI,CAAE,MAAM,gBAAAA,QAAG,WAAW,UAAU,GAAI;AACtC,WAAK,KAAK,mDAAmD;AAC7D;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,gBAAAA,QAAG,QAAQ,UAAU;AAC3C,YAAM,eAAyE,CAAC;AAEhF,iBAAW,cAAc,SAAS;AAChC,cAAM,aAAa,KAAK,KAAK,YAAY,UAAU;AACnD,aAAK,MAAM,gBAAAA,QAAG,KAAK,UAAU,GAAG,YAAY,GAAG;AAC7C,cAAI,UAAU;AACd,cAAI,cAAc;AAElB,gBAAM,cAAc,KAAK,KAAK,YAAY,cAAc;AACxD,gBAAM,iBAAiB,KAAK,KAAK,YAAY,aAAa;AAC1D,gBAAM,gBAAgB,KAAK,KAAK,YAAY,YAAY;AAExD,cAAI,MAA+B,CAAC;AACpC,cAAI,YAAqC,CAAC;AAE1C,cAAI,MAAM,gBAAAA,QAAG,WAAW,WAAW,GAAG;AACpC,gBAAI;AACF,oBAAM,MAAM,gBAAAA,QAAG,SAAS,WAAW;AAAA,YACrC,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,cAAK,MAAM,gBAAAA,QAAG,WAAW,cAAc,KAAO,MAAM,gBAAAA,QAAG,WAAW,aAAa,GAAI;AACjF,gBAAI;AACF,oBAAM,aAAc,MAAM,gBAAAA,QAAG,WAAW,cAAc,IAClD,iBACA;AACJ,oBAAM,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,MAAM;AACpD,0BAAY,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,YACtC,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,oBAAW,IAAI,WAAuB,UAAU,WAAsB;AACtE,wBAAe,IAAI,eAA2B,UAAU,eAA0B;AAGlF,uBAAa,KAAK,EAAE,MAAM,YAAY,SAAS,YAAY,CAAC;AAAA,QAC9D;AAAA,MACF;AAEA,UAAI,aAAa,WAAW,GAAG;AAC7B,aAAK,KAAK,uBAAuB;AAAA,MACnC,OAAO;AAEL,gBAAQ,MAAM,YAAY;AAAA,MAC5B;AAAA,IACF,SAAS,OAAgB;AACvB,UAAI,iBAAiB,OAAO;AAC1B,aAAK,MAAM,2BAA2B,MAAM,OAAO,EAAE;AAAA,MACvD,OAAO;AACL,aAAK,MAAM,2BAA2B,OAAO,KAAK,CAAC,EAAE;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AACF;","names":["fs"]}
1
+ {"version":3,"sources":["../../../../src/commands/module/list.ts"],"sourcesContent":["import { BaseCommand } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport YAML from 'yaml';\n\ninterface ModuleInfo {\n name: string;\n version: string;\n description: string;\n type: 'backend' | 'frontend' | 'legacy';\n}\n\nexport default class ModuleListCommand extends BaseCommand {\n static usage = 'module list';\n static description = 'List installed modules.';\n static requiresProject = true;\n\n async run() {\n const projectRoot = this.projectRoot as string;\n\n // Define locations to scan\n const builtInLocations = [\n { type: 'backend', path: path.join(projectRoot, 'apps/backend/modules') },\n { type: 'frontend', path: path.join(projectRoot, 'apps/frontend/modules') },\n // Check legacy `modules` folder just in case?\n { type: 'legacy', path: path.join(projectRoot, 'modules') },\n ];\n\n const allModules: ModuleInfo[] = [];\n\n for (const loc of builtInLocations) {\n if (await fs.pathExists(loc.path)) {\n const modules = await fs.readdir(loc.path);\n\n for (const moduleName of modules) {\n const modulePath = path.join(loc.path, moduleName);\n if ((await fs.stat(modulePath)).isDirectory()) {\n const info = await this.getModuleInfo(\n modulePath,\n moduleName,\n loc.type as 'backend' | 'frontend' | 'legacy',\n );\n allModules.push(info);\n }\n }\n }\n }\n\n if (allModules.length === 0) {\n this.info('No modules installed.');\n } else {\n // Sort by type then name\n allModules.sort((a, b) => {\n if (a.type !== b.type) return a.type.localeCompare(b.type);\n return a.name.localeCompare(b.name);\n });\n // eslint-disable-next-line no-console\n console.table(allModules);\n }\n }\n\n private async getModuleInfo(\n modulePath: string,\n dirName: string,\n type: 'backend' | 'frontend' | 'legacy',\n ): Promise<ModuleInfo> {\n let version = 'unknown';\n let description = '';\n\n const pkgJsonPath = path.join(modulePath, 'package.json');\n const moduleYamlPath = path.join(modulePath, 'module.yaml');\n const moduleYmlPath = path.join(modulePath, 'module.yml');\n\n let pkg: Record<string, unknown> = {};\n let modConfig: Record<string, unknown> = {};\n\n if (await fs.pathExists(pkgJsonPath)) {\n try {\n pkg = (await fs.readJson(pkgJsonPath)) || {};\n } catch {\n /* ignore */\n }\n }\n\n if ((await fs.pathExists(moduleYamlPath)) || (await fs.pathExists(moduleYmlPath))) {\n try {\n const configPath = (await fs.pathExists(moduleYamlPath)) ? moduleYamlPath : moduleYmlPath;\n const content = await fs.readFile(configPath, 'utf8');\n modConfig = YAML.parse(content) || {};\n } catch {\n /* ignore */\n }\n }\n\n version = (pkg.version as string) || (modConfig.version as string) || 'unknown';\n description = (pkg.description as string) || (modConfig.description as string) || '';\n\n // Use config name if available, else dirName\n const name = (modConfig.name as string) || dirName;\n\n return { name, version, description, type };\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AACA,sBAAe;AADf,SAAS,mBAAmB;AAE5B,OAAO,UAAU;AACjB,OAAO,UAAU;AASjB,IAAqB,oBAArB,cAA+C,YAAY;AAAA,EACzD,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,kBAAkB;AAAA,EAEzB,MAAM,MAAM;AACV,UAAM,cAAc,KAAK;AAGzB,UAAM,mBAAmB;AAAA,MACvB,EAAE,MAAM,WAAW,MAAM,KAAK,KAAK,aAAa,sBAAsB,EAAE;AAAA,MACxE,EAAE,MAAM,YAAY,MAAM,KAAK,KAAK,aAAa,uBAAuB,EAAE;AAAA;AAAA,MAE1E,EAAE,MAAM,UAAU,MAAM,KAAK,KAAK,aAAa,SAAS,EAAE;AAAA,IAC5D;AAEA,UAAM,aAA2B,CAAC;AAElC,eAAW,OAAO,kBAAkB;AAClC,UAAI,MAAM,gBAAAA,QAAG,WAAW,IAAI,IAAI,GAAG;AACjC,cAAM,UAAU,MAAM,gBAAAA,QAAG,QAAQ,IAAI,IAAI;AAEzC,mBAAW,cAAc,SAAS;AAChC,gBAAM,aAAa,KAAK,KAAK,IAAI,MAAM,UAAU;AACjD,eAAK,MAAM,gBAAAA,QAAG,KAAK,UAAU,GAAG,YAAY,GAAG;AAC7C,kBAAM,OAAO,MAAM,KAAK;AAAA,cACtB;AAAA,cACA;AAAA,cACA,IAAI;AAAA,YACN;AACA,uBAAW,KAAK,IAAI;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,WAAW,GAAG;AAC3B,WAAK,KAAK,uBAAuB;AAAA,IACnC,OAAO;AAEL,iBAAW,KAAK,CAAC,GAAG,MAAM;AACxB,YAAI,EAAE,SAAS,EAAE,KAAM,QAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AACzD,eAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,MACpC,CAAC;AAED,cAAQ,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,YACA,SACA,MACqB;AACrB,QAAI,UAAU;AACd,QAAI,cAAc;AAElB,UAAM,cAAc,KAAK,KAAK,YAAY,cAAc;AACxD,UAAM,iBAAiB,KAAK,KAAK,YAAY,aAAa;AAC1D,UAAM,gBAAgB,KAAK,KAAK,YAAY,YAAY;AAExD,QAAI,MAA+B,CAAC;AACpC,QAAI,YAAqC,CAAC;AAE1C,QAAI,MAAM,gBAAAA,QAAG,WAAW,WAAW,GAAG;AACpC,UAAI;AACF,cAAO,MAAM,gBAAAA,QAAG,SAAS,WAAW,KAAM,CAAC;AAAA,MAC7C,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAK,MAAM,gBAAAA,QAAG,WAAW,cAAc,KAAO,MAAM,gBAAAA,QAAG,WAAW,aAAa,GAAI;AACjF,UAAI;AACF,cAAM,aAAc,MAAM,gBAAAA,QAAG,WAAW,cAAc,IAAK,iBAAiB;AAC5E,cAAM,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,MAAM;AACpD,oBAAY,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,MACtC,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,cAAW,IAAI,WAAuB,UAAU,WAAsB;AACtE,kBAAe,IAAI,eAA2B,UAAU,eAA0B;AAGlF,UAAM,OAAQ,UAAU,QAAmB;AAE3C,WAAO,EAAE,MAAM,SAAS,aAAa,KAAK;AAAA,EAC5C;AACF;","names":["fs"]}
@@ -23,21 +23,31 @@ var ModuleRemoveCommand = class extends BaseCommand {
23
23
  async run(options) {
24
24
  const projectRoot = this.projectRoot;
25
25
  const { name } = options;
26
- const relativePath = `modules/${name}`;
27
- const fullPath = path.resolve(projectRoot, relativePath);
28
- logger.debug("Removing module at:", fullPath);
29
- if (!await import_fs_extra.default.pathExists(fullPath)) {
30
- this.error(`Module ${name} not found at ${relativePath}.`);
26
+ const locations = [
27
+ { type: "backend", path: `apps/backend/modules/${name}` },
28
+ { type: "frontend", path: `apps/frontend/modules/${name}` },
29
+ { type: "legacy", path: `modules/${name}` }
30
+ ];
31
+ let targetLoc = null;
32
+ let fullPath = "";
33
+ for (const loc of locations) {
34
+ const absPath = path.resolve(projectRoot, loc.path);
35
+ if (await import_fs_extra.default.pathExists(absPath)) {
36
+ targetLoc = loc;
37
+ fullPath = absPath;
38
+ break;
39
+ }
40
+ }
41
+ if (!targetLoc) {
42
+ this.error(`Module ${name} not found in any standard location.`);
31
43
  return;
32
44
  }
33
- this.info(`Removing module ${name}...`);
45
+ const relativePath = targetLoc.path;
46
+ logger.debug("Removing module at:", fullPath);
47
+ this.info(`Removing module ${name} (${targetLoc.type})...`);
34
48
  try {
35
49
  await runCommand(`git submodule deinit -f ${relativePath}`, projectRoot);
36
50
  await runCommand(`git rm -f ${relativePath}`, projectRoot);
37
- const gitModulesDir = path.resolve(projectRoot, ".git", "modules", "modules", name);
38
- if (await import_fs_extra.default.pathExists(gitModulesDir)) {
39
- await import_fs_extra.default.remove(gitModulesDir);
40
- }
41
51
  this.info("Syncing workspace dependencies...");
42
52
  await runCommand("npm install", projectRoot);
43
53
  await this.removeFromConfig(name);
@@ -57,8 +67,23 @@ var ModuleRemoveCommand = class extends BaseCommand {
57
67
  try {
58
68
  const content = await import_fs_extra.default.readFile(configPath, "utf8");
59
69
  const config = YAML.parse(content) || {};
60
- if (config.modules && config.modules.includes(moduleName)) {
61
- config.modules = config.modules.filter((m) => m !== moduleName);
70
+ let changed = false;
71
+ if (config.modules) {
72
+ if (!Array.isArray(config.modules)) {
73
+ for (const key of Object.keys(config.modules)) {
74
+ if (Array.isArray(config.modules[key]) && config.modules[key].includes(moduleName)) {
75
+ config.modules[key] = config.modules[key].filter((m) => m !== moduleName);
76
+ changed = true;
77
+ }
78
+ }
79
+ } else {
80
+ if (config.modules.includes(moduleName)) {
81
+ config.modules = config.modules.filter((m) => m !== moduleName);
82
+ changed = true;
83
+ }
84
+ }
85
+ }
86
+ if (changed) {
62
87
  await import_fs_extra.default.writeFile(configPath, YAML.stringify(config));
63
88
  logger.debug(`Removed ${moduleName} from nexical.yaml modules list.`);
64
89
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/commands/module/remove.ts"],"sourcesContent":["import { type CommandDefinition, BaseCommand, logger, runCommand } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport YAML from 'yaml';\n\nexport default class ModuleRemoveCommand extends BaseCommand {\n static usage = 'module remove <name>';\n static description = 'Remove an installed module.';\n static requiresProject = true;\n\n static args: CommandDefinition = {\n args: [{ name: 'name', required: true, description: 'Name of the module to remove' }],\n };\n\n async run(options: { name: string }) {\n const projectRoot = this.projectRoot as string;\n const { name } = options;\n\n const relativePath = `modules/${name}`;\n const fullPath = path.resolve(projectRoot, relativePath);\n\n logger.debug('Removing module at:', fullPath);\n\n if (!(await fs.pathExists(fullPath))) {\n this.error(`Module ${name} not found at ${relativePath}.`);\n return;\n }\n\n this.info(`Removing module ${name}...`);\n\n try {\n await runCommand(`git submodule deinit -f ${relativePath}`, projectRoot);\n await runCommand(`git rm -f ${relativePath}`, projectRoot);\n\n // Clean up .git/modules\n const gitModulesDir = path.resolve(projectRoot, '.git', 'modules', 'modules', name);\n if (await fs.pathExists(gitModulesDir)) {\n await fs.remove(gitModulesDir);\n }\n\n this.info('Syncing workspace dependencies...');\n await runCommand('npm install', projectRoot);\n\n await this.removeFromConfig(name);\n\n this.success(`Module ${name} removed successfully.`);\n } catch (e: unknown) {\n if (e instanceof Error) {\n this.error(`Failed to remove module: ${e.message}`);\n } else {\n this.error(`Failed to remove module: ${String(e)}`);\n }\n }\n }\n\n private async removeFromConfig(moduleName: string) {\n const projectRoot = this.projectRoot as string;\n const configPath = path.join(projectRoot, 'nexical.yaml');\n\n if (!(await fs.pathExists(configPath))) return;\n\n try {\n const content = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(content) || {};\n\n if (config.modules && config.modules.includes(moduleName)) {\n config.modules = config.modules.filter((m: string) => m !== moduleName);\n await fs.writeFile(configPath, YAML.stringify(config));\n logger.debug(`Removed ${moduleName} from nexical.yaml modules list.`);\n }\n } catch (e: unknown) {\n if (e instanceof Error) {\n logger.warn(`Failed to update nexical.yaml: ${e.message}`);\n } else {\n logger.warn(`Failed to update nexical.yaml: ${String(e)}`);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AACA,sBAAe;AADf,SAAiC,aAAa,QAAQ,kBAAkB;AAExE,OAAO,UAAU;AACjB,OAAO,UAAU;AAEjB,IAAqB,sBAArB,cAAiD,YAAY;AAAA,EAC3D,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,kBAAkB;AAAA,EAEzB,OAAO,OAA0B;AAAA,IAC/B,MAAM,CAAC,EAAE,MAAM,QAAQ,UAAU,MAAM,aAAa,+BAA+B,CAAC;AAAA,EACtF;AAAA,EAEA,MAAM,IAAI,SAA2B;AACnC,UAAM,cAAc,KAAK;AACzB,UAAM,EAAE,KAAK,IAAI;AAEjB,UAAM,eAAe,WAAW,IAAI;AACpC,UAAM,WAAW,KAAK,QAAQ,aAAa,YAAY;AAEvD,WAAO,MAAM,uBAAuB,QAAQ;AAE5C,QAAI,CAAE,MAAM,gBAAAA,QAAG,WAAW,QAAQ,GAAI;AACpC,WAAK,MAAM,UAAU,IAAI,iBAAiB,YAAY,GAAG;AACzD;AAAA,IACF;AAEA,SAAK,KAAK,mBAAmB,IAAI,KAAK;AAEtC,QAAI;AACF,YAAM,WAAW,2BAA2B,YAAY,IAAI,WAAW;AACvE,YAAM,WAAW,aAAa,YAAY,IAAI,WAAW;AAGzD,YAAM,gBAAgB,KAAK,QAAQ,aAAa,QAAQ,WAAW,WAAW,IAAI;AAClF,UAAI,MAAM,gBAAAA,QAAG,WAAW,aAAa,GAAG;AACtC,cAAM,gBAAAA,QAAG,OAAO,aAAa;AAAA,MAC/B;AAEA,WAAK,KAAK,mCAAmC;AAC7C,YAAM,WAAW,eAAe,WAAW;AAE3C,YAAM,KAAK,iBAAiB,IAAI;AAEhC,WAAK,QAAQ,UAAU,IAAI,wBAAwB;AAAA,IACrD,SAAS,GAAY;AACnB,UAAI,aAAa,OAAO;AACtB,aAAK,MAAM,4BAA4B,EAAE,OAAO,EAAE;AAAA,MACpD,OAAO;AACL,aAAK,MAAM,4BAA4B,OAAO,CAAC,CAAC,EAAE;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,YAAoB;AACjD,UAAM,cAAc,KAAK;AACzB,UAAM,aAAa,KAAK,KAAK,aAAa,cAAc;AAExD,QAAI,CAAE,MAAM,gBAAAA,QAAG,WAAW,UAAU,EAAI;AAExC,QAAI;AACF,YAAM,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,MAAM;AACpD,YAAM,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC;AAEvC,UAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,UAAU,GAAG;AACzD,eAAO,UAAU,OAAO,QAAQ,OAAO,CAAC,MAAc,MAAM,UAAU;AACtE,cAAM,gBAAAA,QAAG,UAAU,YAAY,KAAK,UAAU,MAAM,CAAC;AACrD,eAAO,MAAM,WAAW,UAAU,kCAAkC;AAAA,MACtE;AAAA,IACF,SAAS,GAAY;AACnB,UAAI,aAAa,OAAO;AACtB,eAAO,KAAK,kCAAkC,EAAE,OAAO,EAAE;AAAA,MAC3D,OAAO;AACL,eAAO,KAAK,kCAAkC,OAAO,CAAC,CAAC,EAAE;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACF;","names":["fs"]}
1
+ {"version":3,"sources":["../../../../src/commands/module/remove.ts"],"sourcesContent":["import { type CommandDefinition, BaseCommand, logger, runCommand } from '@nexical/cli-core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport YAML from 'yaml';\n\nexport default class ModuleRemoveCommand extends BaseCommand {\n static usage = 'module remove <name>';\n static description = 'Remove an installed module.';\n static requiresProject = true;\n\n static args: CommandDefinition = {\n args: [{ name: 'name', required: true, description: 'Name of the module to remove' }],\n };\n\n async run(options: { name: string }) {\n const projectRoot = this.projectRoot as string;\n const { name } = options;\n\n // Check locations\n const locations = [\n { type: 'backend', path: `apps/backend/modules/${name}` },\n { type: 'frontend', path: `apps/frontend/modules/${name}` },\n { type: 'legacy', path: `modules/${name}` },\n ];\n\n let targetLoc: { type: string; path: string } | null = null;\n let fullPath = '';\n\n for (const loc of locations) {\n const absPath = path.resolve(projectRoot, loc.path);\n if (await fs.pathExists(absPath)) {\n targetLoc = loc;\n fullPath = absPath;\n break;\n }\n }\n\n if (!targetLoc) {\n this.error(`Module ${name} not found in any standard location.`);\n return;\n }\n\n const relativePath = targetLoc.path;\n\n logger.debug('Removing module at:', fullPath);\n this.info(`Removing module ${name} (${targetLoc.type})...`);\n\n try {\n await runCommand(`git submodule deinit -f ${relativePath}`, projectRoot);\n await runCommand(`git rm -f ${relativePath}`, projectRoot);\n\n // Clean up .git/modules if needed (git rm often handles this but sometimes leaves stale dirs in .git/modules)\n // The path in .git/modules depends on how it was added.\n // Usually .git/modules/apps/backend/modules/name\n // We'll leave strict git cleanup to git, manually removing can be risky if path structure varies.\n // But we can check for the directory itself just in case.\n\n this.info('Syncing workspace dependencies...');\n await runCommand('npm install', projectRoot);\n\n await this.removeFromConfig(name);\n\n this.success(`Module ${name} removed successfully.`);\n } catch (e: unknown) {\n if (e instanceof Error) {\n this.error(`Failed to remove module: ${e.message}`);\n } else {\n this.error(`Failed to remove module: ${String(e)}`);\n }\n }\n }\n\n private async removeFromConfig(moduleName: string) {\n const projectRoot = this.projectRoot as string;\n const configPath = path.join(projectRoot, 'nexical.yaml');\n\n if (!(await fs.pathExists(configPath))) return;\n\n try {\n const content = await fs.readFile(configPath, 'utf8');\n const config = YAML.parse(content) || {};\n\n let changed = false;\n\n if (config.modules) {\n // Check if object\n if (!Array.isArray(config.modules)) {\n for (const key of Object.keys(config.modules)) {\n if (Array.isArray(config.modules[key]) && config.modules[key].includes(moduleName)) {\n config.modules[key] = config.modules[key].filter((m: string) => m !== moduleName);\n changed = true;\n }\n }\n } else {\n // Legacy array\n if (config.modules.includes(moduleName)) {\n config.modules = config.modules.filter((m: string) => m !== moduleName);\n changed = true;\n }\n }\n }\n\n if (changed) {\n await fs.writeFile(configPath, YAML.stringify(config));\n logger.debug(`Removed ${moduleName} from nexical.yaml modules list.`);\n }\n } catch (e: unknown) {\n if (e instanceof Error) {\n logger.warn(`Failed to update nexical.yaml: ${e.message}`);\n } else {\n logger.warn(`Failed to update nexical.yaml: ${String(e)}`);\n }\n }\n }\n}\n"],"mappings":"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","names":["fs"]}