@open-mercato/cli 0.4.9-develop-94fb251ed3 → 0.4.9-develop-d989387b7a

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 (58) hide show
  1. package/dist/agentic/shared/AGENTS.md.template +2 -0
  2. package/dist/agentic/shared/ai/skills/eject-and-customize/SKILL.md +3 -1
  3. package/dist/bin.js +1 -0
  4. package/dist/bin.js.map +2 -2
  5. package/dist/lib/__fixtures__/official-module-package/src/index.js +1 -0
  6. package/dist/lib/__fixtures__/official-module-package/src/index.js.map +7 -0
  7. package/dist/lib/__fixtures__/official-module-package/src/modules/test_package/index.js +10 -0
  8. package/dist/lib/__fixtures__/official-module-package/src/modules/test_package/index.js.map +7 -0
  9. package/dist/lib/eject.js +30 -38
  10. package/dist/lib/eject.js.map +2 -2
  11. package/dist/lib/generators/index.js +2 -0
  12. package/dist/lib/generators/index.js.map +2 -2
  13. package/dist/lib/generators/module-package-sources.js +45 -0
  14. package/dist/lib/generators/module-package-sources.js.map +7 -0
  15. package/dist/lib/module-install-args.js +40 -0
  16. package/dist/lib/module-install-args.js.map +7 -0
  17. package/dist/lib/module-install.js +157 -0
  18. package/dist/lib/module-install.js.map +7 -0
  19. package/dist/lib/module-package.js +245 -0
  20. package/dist/lib/module-package.js.map +7 -0
  21. package/dist/lib/modules-config.js +255 -0
  22. package/dist/lib/modules-config.js.map +7 -0
  23. package/dist/lib/resolver.js +19 -5
  24. package/dist/lib/resolver.js.map +2 -2
  25. package/dist/lib/testing/integration-discovery.js +20 -9
  26. package/dist/lib/testing/integration-discovery.js.map +2 -2
  27. package/dist/lib/testing/integration.js +86 -47
  28. package/dist/lib/testing/integration.js.map +2 -2
  29. package/dist/mercato.js +120 -43
  30. package/dist/mercato.js.map +3 -3
  31. package/package.json +5 -4
  32. package/src/__tests__/mercato.test.ts +6 -1
  33. package/src/bin.ts +1 -0
  34. package/src/lib/__fixtures__/official-module-package/dist/modules/test_package/index.js +2 -0
  35. package/src/lib/__fixtures__/official-module-package/package.json +33 -0
  36. package/src/lib/__fixtures__/official-module-package/src/index.ts +1 -0
  37. package/src/lib/__fixtures__/official-module-package/src/modules/test_package/index.ts +6 -0
  38. package/src/lib/__fixtures__/official-module-package/src/modules/test_package/widgets/injection/test/widget.tsx +3 -0
  39. package/src/lib/__tests__/eject.test.ts +107 -1
  40. package/src/lib/__tests__/module-install-args.test.ts +35 -0
  41. package/src/lib/__tests__/module-install.test.ts +217 -0
  42. package/src/lib/__tests__/module-package.test.ts +215 -0
  43. package/src/lib/__tests__/modules-config.test.ts +104 -0
  44. package/src/lib/__tests__/resolve-environment.test.ts +141 -0
  45. package/src/lib/eject.ts +45 -55
  46. package/src/lib/generators/__tests__/generators.test.ts +11 -0
  47. package/src/lib/generators/__tests__/module-package-sources.test.ts +121 -0
  48. package/src/lib/generators/index.ts +1 -0
  49. package/src/lib/generators/module-package-sources.ts +59 -0
  50. package/src/lib/module-install-args.ts +50 -0
  51. package/src/lib/module-install.ts +234 -0
  52. package/src/lib/module-package.ts +355 -0
  53. package/src/lib/modules-config.ts +393 -0
  54. package/src/lib/resolver.ts +46 -4
  55. package/src/lib/testing/__tests__/integration-discovery.test.ts +30 -0
  56. package/src/lib/testing/integration-discovery.ts +23 -8
  57. package/src/lib/testing/integration.ts +97 -57
  58. package/src/mercato.ts +128 -49
@@ -7,6 +7,7 @@ Do NOT load the entire src/ tree — Open Mercato apps can have many modules.
7
7
 
8
8
  A standalone Open Mercato application built ON TOP of the framework.
9
9
  The framework lives in `node_modules/@open-mercato/*`. Never edit `node_modules` directly.
10
+ Install official packages with `yarn mercato module add @open-mercato/<package>`.
10
11
  To customise a built-in module beyond extensions, eject with `yarn mercato eject <module>`.
11
12
 
12
13
  ## Task → Context Map
@@ -158,6 +159,7 @@ import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-intercept
158
159
  |---|---|
159
160
  | `yarn dev` | Start dev server |
160
161
  | `yarn generate` | Regenerate `.mercato/generated/` |
162
+ | `yarn mercato module add <package>` | Install and enable an official module package |
161
163
  | `yarn db:generate` | Create migration for entity changes |
162
164
  | `yarn db:migrate` | Apply pending migrations |
163
165
  | `yarn initialize` | Bootstrap DB + first admin account |
@@ -99,11 +99,13 @@ Save this in `.ai/specs/` or a project README for future reference.
99
99
  ### Run the Eject Command
100
100
 
101
101
  ```bash
102
- yarn mercato eject <module-id>
102
+ yarn mercato module eject <module-id>
103
103
  ```
104
104
 
105
105
  This copies the module from `node_modules/@open-mercato/core/dist/modules/<module-id>/` to `src/modules/<module-id>/`.
106
106
 
107
+ The legacy alias `yarn mercato eject <module-id>` remains supported.
108
+
107
109
  ### Post-Ejection Steps
108
110
 
109
111
  ```bash
package/dist/bin.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { run } from "./mercato.js";
3
3
  const BOOTSTRAP_FREE_COMMANDS = [
4
4
  "generate",
5
+ "module",
5
6
  "db",
6
7
  "init",
7
8
  "eject",
package/dist/bin.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/bin.ts"],
4
- "sourcesContent": ["/**\n * CLI binary entry point for @open-mercato/cli package.\n *\n * Called from within a Next.js app directory as: yarn mercato <command>\n * Uses dynamic app resolution to find generated files at .mercato/generated/\n */\nimport { run } from './mercato.js'\n\n// Commands that can run without bootstrap (without generated files)\n// - generate: creates the generated files\n// - db: uses resolver directly to find modules and migrations\n// - init: runs yarn commands to set up the app\n// - help: just shows help text\nconst BOOTSTRAP_FREE_COMMANDS = [\n 'generate',\n 'db',\n 'init',\n 'eject',\n 'test',\n 'test:integration',\n 'test:integration:coverage',\n 'test:integration:spec-coverage',\n 'test:ephemeral',\n 'test:integration:interactive',\n 'umes:list',\n 'umes:inspect',\n 'umes:check',\n 'help',\n '--help',\n '-h',\n]\n\nfunction assertNode24Runtime(): void {\n const detectedNodeVersion = process.versions.node\n const majorVersion = Number.parseInt(detectedNodeVersion.split('.')[0] ?? '0', 10)\n if (majorVersion >= 24) {\n return\n }\n throw new Error(\n [\n 'Unsupported Node.js runtime.',\n `Cause: Detected Node ${detectedNodeVersion}, but Open Mercato requires Node 24.x.`,\n 'What to do: switch your shell to Node 24 (for example `nvm use 24`), run `yarn install`, then retry.',\n ].join(' '),\n )\n}\n\nfunction needsBootstrap(argv: string[]): boolean {\n const [, , first] = argv\n if (!first) return false // help screen\n return !BOOTSTRAP_FREE_COMMANDS.includes(first)\n}\n\nasync function tryBootstrap(): Promise<boolean> {\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n const { registerCliModules } = await import('./mercato.js')\n // Use the CLI resolver to find the app directory (handles monorepo detection)\n const { createResolver } = await import('./lib/resolver.js')\n const resolver = createResolver()\n const appDir = resolver.getAppDir()\n const data = await bootstrapFromAppRoot(appDir)\n // Register CLI modules directly to avoid module resolution issues\n registerCliModules(data.modules)\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n // Check if the error is about missing generated files\n if (\n message.includes('Cannot find module') &&\n (message.includes('/generated/') || message.includes('.generated') || message.includes('.mercato'))\n ) {\n return false\n }\n // Re-throw other errors\n throw err\n }\n}\n\nasync function main(): Promise<void> {\n assertNode24Runtime()\n const requiresBootstrap = needsBootstrap(process.argv)\n\n if (requiresBootstrap) {\n const bootstrapSucceeded = await tryBootstrap()\n if (!bootstrapSucceeded) {\n console.error('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557')\n console.error('\u2551 Generated files not found! \u2551')\n console.error('\u2551 \u2551')\n console.error('\u2551 The CLI requires generated files to discover modules. \u2551')\n console.error('\u2551 Please run the following command first: \u2551')\n console.error('\u2551 \u2551')\n console.error('\u2551 yarn mercato generate \u2551')\n console.error('\u2551 \u2551')\n console.error('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D')\n process.exit(1)\n }\n }\n\n const code = await run(process.argv)\n process.exit(code ?? 0)\n}\n\nmain().catch((error: unknown) => {\n if (error instanceof Error) {\n console.error(error.message)\n } else {\n console.error(error)\n }\n process.exit(1)\n})\n"],
5
- "mappings": "AAMA,SAAS,WAAW;AAOpB,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,sBAA4B;AACnC,QAAM,sBAAsB,QAAQ,SAAS;AAC7C,QAAM,eAAe,OAAO,SAAS,oBAAoB,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AACjF,MAAI,gBAAgB,IAAI;AACtB;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR;AAAA,MACE;AAAA,MACA,wBAAwB,mBAAmB;AAAA,MAC3C;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,EACZ;AACF;AAEA,SAAS,eAAe,MAAyB;AAC/C,QAAM,CAAC,EAAE,EAAE,KAAK,IAAI;AACpB,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,wBAAwB,SAAS,KAAK;AAChD;AAEA,eAAe,eAAiC;AAC9C,MAAI;AACF,UAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,kDAAkD;AAChG,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,cAAc;AAE1D,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,UAAM,WAAW,eAAe;AAChC,UAAM,SAAS,SAAS,UAAU;AAClC,UAAM,OAAO,MAAM,qBAAqB,MAAM;AAE9C,uBAAmB,KAAK,OAAO;AAC/B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE/D,QACE,QAAQ,SAAS,oBAAoB,MACpC,QAAQ,SAAS,aAAa,KAAK,QAAQ,SAAS,YAAY,KAAK,QAAQ,SAAS,UAAU,IACjG;AACA,aAAO;AAAA,IACT;AAEA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,OAAsB;AACnC,sBAAoB;AACpB,QAAM,oBAAoB,eAAe,QAAQ,IAAI;AAErD,MAAI,mBAAmB;AACrB,UAAM,qBAAqB,MAAM,aAAa;AAC9C,QAAI,CAAC,oBAAoB;AACvB,cAAQ,MAAM,gaAAuE;AACrF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gaAAuE;AACrF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,IAAI,QAAQ,IAAI;AACnC,UAAQ,KAAK,QAAQ,CAAC;AACxB;AAEA,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,MAAI,iBAAiB,OAAO;AAC1B,YAAQ,MAAM,MAAM,OAAO;AAAA,EAC7B,OAAO;AACL,YAAQ,MAAM,KAAK;AAAA,EACrB;AACA,UAAQ,KAAK,CAAC;AAChB,CAAC;",
4
+ "sourcesContent": ["/**\n * CLI binary entry point for @open-mercato/cli package.\n *\n * Called from within a Next.js app directory as: yarn mercato <command>\n * Uses dynamic app resolution to find generated files at .mercato/generated/\n */\nimport { run } from './mercato.js'\n\n// Commands that can run without bootstrap (without generated files)\n// - generate: creates the generated files\n// - db: uses resolver directly to find modules and migrations\n// - init: runs yarn commands to set up the app\n// - help: just shows help text\nconst BOOTSTRAP_FREE_COMMANDS = [\n 'generate',\n 'module',\n 'db',\n 'init',\n 'eject',\n 'test',\n 'test:integration',\n 'test:integration:coverage',\n 'test:integration:spec-coverage',\n 'test:ephemeral',\n 'test:integration:interactive',\n 'umes:list',\n 'umes:inspect',\n 'umes:check',\n 'help',\n '--help',\n '-h',\n]\n\nfunction assertNode24Runtime(): void {\n const detectedNodeVersion = process.versions.node\n const majorVersion = Number.parseInt(detectedNodeVersion.split('.')[0] ?? '0', 10)\n if (majorVersion >= 24) {\n return\n }\n throw new Error(\n [\n 'Unsupported Node.js runtime.',\n `Cause: Detected Node ${detectedNodeVersion}, but Open Mercato requires Node 24.x.`,\n 'What to do: switch your shell to Node 24 (for example `nvm use 24`), run `yarn install`, then retry.',\n ].join(' '),\n )\n}\n\nfunction needsBootstrap(argv: string[]): boolean {\n const [, , first] = argv\n if (!first) return false // help screen\n return !BOOTSTRAP_FREE_COMMANDS.includes(first)\n}\n\nasync function tryBootstrap(): Promise<boolean> {\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n const { registerCliModules } = await import('./mercato.js')\n // Use the CLI resolver to find the app directory (handles monorepo detection)\n const { createResolver } = await import('./lib/resolver.js')\n const resolver = createResolver()\n const appDir = resolver.getAppDir()\n const data = await bootstrapFromAppRoot(appDir)\n // Register CLI modules directly to avoid module resolution issues\n registerCliModules(data.modules)\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n // Check if the error is about missing generated files\n if (\n message.includes('Cannot find module') &&\n (message.includes('/generated/') || message.includes('.generated') || message.includes('.mercato'))\n ) {\n return false\n }\n // Re-throw other errors\n throw err\n }\n}\n\nasync function main(): Promise<void> {\n assertNode24Runtime()\n const requiresBootstrap = needsBootstrap(process.argv)\n\n if (requiresBootstrap) {\n const bootstrapSucceeded = await tryBootstrap()\n if (!bootstrapSucceeded) {\n console.error('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557')\n console.error('\u2551 Generated files not found! \u2551')\n console.error('\u2551 \u2551')\n console.error('\u2551 The CLI requires generated files to discover modules. \u2551')\n console.error('\u2551 Please run the following command first: \u2551')\n console.error('\u2551 \u2551')\n console.error('\u2551 yarn mercato generate \u2551')\n console.error('\u2551 \u2551')\n console.error('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D')\n process.exit(1)\n }\n }\n\n const code = await run(process.argv)\n process.exit(code ?? 0)\n}\n\nmain().catch((error: unknown) => {\n if (error instanceof Error) {\n console.error(error.message)\n } else {\n console.error(error)\n }\n process.exit(1)\n})\n"],
5
+ "mappings": "AAMA,SAAS,WAAW;AAOpB,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,sBAA4B;AACnC,QAAM,sBAAsB,QAAQ,SAAS;AAC7C,QAAM,eAAe,OAAO,SAAS,oBAAoB,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AACjF,MAAI,gBAAgB,IAAI;AACtB;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR;AAAA,MACE;AAAA,MACA,wBAAwB,mBAAmB;AAAA,MAC3C;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,EACZ;AACF;AAEA,SAAS,eAAe,MAAyB;AAC/C,QAAM,CAAC,EAAE,EAAE,KAAK,IAAI;AACpB,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,wBAAwB,SAAS,KAAK;AAChD;AAEA,eAAe,eAAiC;AAC9C,MAAI;AACF,UAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,kDAAkD;AAChG,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,cAAc;AAE1D,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,UAAM,WAAW,eAAe;AAChC,UAAM,SAAS,SAAS,UAAU;AAClC,UAAM,OAAO,MAAM,qBAAqB,MAAM;AAE9C,uBAAmB,KAAK,OAAO;AAC/B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE/D,QACE,QAAQ,SAAS,oBAAoB,MACpC,QAAQ,SAAS,aAAa,KAAK,QAAQ,SAAS,YAAY,KAAK,QAAQ,SAAS,UAAU,IACjG;AACA,aAAO;AAAA,IACT;AAEA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,OAAsB;AACnC,sBAAoB;AACpB,QAAM,oBAAoB,eAAe,QAAQ,IAAI;AAErD,MAAI,mBAAmB;AACrB,UAAM,qBAAqB,MAAM,aAAa;AAC9C,QAAI,CAAC,oBAAoB;AACvB,cAAQ,MAAM,gaAAuE;AACrF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gFAAsE;AACpF,cAAQ,MAAM,iFAAuE;AACrF,cAAQ,MAAM,gaAAuE;AACrF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,IAAI,QAAQ,IAAI;AACnC,UAAQ,KAAK,QAAQ,CAAC;AACxB;AAEA,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,MAAI,iBAAiB,OAAO;AAC1B,YAAQ,MAAM,MAAM,OAAO;AAAA,EAC7B,OAAO;AACL,YAAQ,MAAM,KAAK;AAAA,EACrB;AACA,UAAQ,KAAK,CAAC;AAChB,CAAC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ const metadata = {
2
+ id: "test_package",
3
+ title: "Test Package",
4
+ description: "Fixture official module package for CLI install tests.",
5
+ ejectable: true
6
+ };
7
+ export {
8
+ metadata
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/lib/__fixtures__/official-module-package/src/modules/test_package/index.ts"],
4
+ "sourcesContent": ["export const metadata = {\n id: 'test_package',\n title: 'Test Package',\n description: 'Fixture official module package for CLI install tests.',\n ejectable: true,\n}\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,WAAW;AACb;",
6
+ "names": []
7
+ }
package/dist/lib/eject.js CHANGED
@@ -1,12 +1,17 @@
1
1
  import path from "node:path";
2
2
  import fs from "node:fs";
3
+ import { setModuleRegistrationSource } from "./modules-config.js";
4
+ import { resolveInstalledOfficialModulePackage } from "./module-package.js";
3
5
  const SKIP_DIRS = /* @__PURE__ */ new Set(["__tests__", "__mocks__", "node_modules"]);
4
6
  const SOURCE_FILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
7
+ function shouldSkipEntryName(name) {
8
+ return SKIP_DIRS.has(name) || name === ".DS_Store" || name.startsWith("._");
9
+ }
5
10
  function collectSourceFiles(dir) {
6
11
  const files = [];
7
12
  const entries = fs.readdirSync(dir, { withFileTypes: true });
8
13
  for (const entry of entries) {
9
- if (SKIP_DIRS.has(entry.name)) continue;
14
+ if (shouldSkipEntryName(entry.name)) continue;
10
15
  const fullPath = path.join(dir, entry.name);
11
16
  if (entry.isDirectory()) {
12
17
  files.push(...collectSourceFiles(fullPath));
@@ -114,7 +119,7 @@ function copyDirRecursive(src, dest) {
114
119
  fs.mkdirSync(dest, { recursive: true });
115
120
  const entries = fs.readdirSync(src, { withFileTypes: true });
116
121
  for (const entry of entries) {
117
- if (SKIP_DIRS.has(entry.name)) continue;
122
+ if (shouldSkipEntryName(entry.name)) continue;
118
123
  const srcPath = path.join(src, entry.name);
119
124
  const destPath = path.join(dest, entry.name);
120
125
  if (entry.isDirectory()) {
@@ -125,47 +130,35 @@ function copyDirRecursive(src, dest) {
125
130
  }
126
131
  }
127
132
  function updateModulesTs(modulesPath, moduleId) {
128
- if (!fs.existsSync(modulesPath)) {
129
- throw new Error(`modules.ts not found at ${modulesPath}`);
130
- }
131
- const source = fs.readFileSync(modulesPath, "utf8");
132
- const objectPattern = /\{[^{}]*\}/g;
133
- let match;
134
- let updatedSource = null;
135
- while ((match = objectPattern.exec(source)) !== null) {
136
- const objectLiteral = match[0];
137
- const idMatch = objectLiteral.match(/\bid\s*:\s*(['"])([^'"]+)\1/);
138
- if (!idMatch || idMatch[2] !== moduleId) {
139
- continue;
140
- }
141
- const updatedObject = upsertModuleSource(objectLiteral);
142
- updatedSource = source.slice(0, match.index) + updatedObject + source.slice(match.index + objectLiteral.length);
143
- break;
144
- }
145
- if (!updatedSource) {
146
- throw new Error(
147
- `Could not find module entry for "${moduleId}" in ${modulesPath}. Expected a pattern like: { id: '${moduleId}', from: '...' } or { id: '${moduleId}' }`
148
- );
149
- }
150
- fs.writeFileSync(modulesPath, updatedSource);
133
+ setModuleRegistrationSource(modulesPath, moduleId, "@app");
151
134
  }
152
- function upsertModuleSource(objectLiteral) {
153
- if (/from\s*:\s*'[^']*'/.test(objectLiteral)) {
154
- return objectLiteral.replace(/from\s*:\s*'[^']*'/, "from: '@app'");
155
- }
156
- if (/from\s*:\s*"[^"]*"/.test(objectLiteral)) {
157
- return objectLiteral.replace(/from\s*:\s*"[^"]*"/, 'from: "@app"');
135
+ function resolveModuleSource(resolver, entry) {
136
+ const { pkgBase } = resolver.getModulePaths(entry);
137
+ const fallbackMetadata = parseModuleMetadata(path.join(pkgBase, "index.ts"));
138
+ const from = entry.from || "@open-mercato/core";
139
+ if (from === "@app" || from === "@open-mercato/core") {
140
+ return { pkgBase, metadata: fallbackMetadata };
141
+ }
142
+ try {
143
+ const modulePackage = resolveInstalledOfficialModulePackage(resolver, from, entry.id);
144
+ return {
145
+ pkgBase: modulePackage.sourceModuleDir,
146
+ metadata: {
147
+ ejectable: modulePackage.metadata.ejectable,
148
+ title: modulePackage.moduleInfo.title ?? modulePackage.metadata.moduleId,
149
+ description: modulePackage.moduleInfo.description
150
+ }
151
+ };
152
+ } catch {
153
+ return { pkgBase, metadata: fallbackMetadata };
158
154
  }
159
- return objectLiteral.replace(/\}\s*$/, ", from: '@app' }");
160
155
  }
161
156
  function listEjectableModules(resolver) {
162
157
  const modules = resolver.loadEnabledModules();
163
158
  const ejectable = [];
164
159
  for (const entry of modules) {
165
160
  if (entry.from === "@app") continue;
166
- const { pkgBase } = resolver.getModulePaths(entry);
167
- const indexPath = path.join(pkgBase, "index.ts");
168
- const metadata = parseModuleMetadata(indexPath);
161
+ const { metadata } = resolveModuleSource(resolver, entry);
169
162
  if (metadata.ejectable) {
170
163
  ejectable.push({
171
164
  id: entry.id,
@@ -190,14 +183,13 @@ function ejectModule(resolver, moduleId) {
190
183
  `Module "${moduleId}" is already local (from: '@app'). Nothing to eject.`
191
184
  );
192
185
  }
193
- const { pkgBase, appBase } = resolver.getModulePaths(entry);
186
+ const { appBase } = resolver.getModulePaths(entry);
187
+ const { pkgBase, metadata } = resolveModuleSource(resolver, entry);
194
188
  if (!fs.existsSync(pkgBase)) {
195
189
  throw new Error(
196
190
  `Package source directory not found: ${pkgBase}. Make sure the package is installed.`
197
191
  );
198
192
  }
199
- const indexPath = path.join(pkgBase, "index.ts");
200
- const metadata = parseModuleMetadata(indexPath);
201
193
  if (!metadata.ejectable) {
202
194
  throw new Error(
203
195
  `Module "${moduleId}" is not marked as ejectable. Only modules with \`ejectable: true\` in their metadata can be ejected.`
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/eject.ts"],
4
- "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport type { PackageResolver, ModuleEntry } from './resolver'\n\ntype ModuleMetadata = {\n ejectable?: boolean\n title?: string\n description?: string\n}\n\nconst SKIP_DIRS = new Set(['__tests__', '__mocks__', 'node_modules'])\nconst SOURCE_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']\n\nfunction collectSourceFiles(dir: string): string[] {\n const files: string[] = []\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (SKIP_DIRS.has(entry.name)) continue\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n files.push(...collectSourceFiles(fullPath))\n continue\n }\n const ext = path.extname(entry.name)\n if (!SOURCE_FILE_EXTENSIONS.includes(ext)) continue\n files.push(fullPath)\n }\n return files\n}\n\nfunction resolveRelativeImportTarget(sourceFile: string, importPath: string): string | null {\n if (!importPath.startsWith('.')) return null\n\n const basePath = path.resolve(path.dirname(sourceFile), importPath)\n const candidates = [basePath]\n\n for (const ext of SOURCE_FILE_EXTENSIONS) {\n candidates.push(`${basePath}${ext}`)\n candidates.push(path.join(basePath, `index${ext}`))\n }\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\nfunction rewriteRelativeSpecifier(\n sourceFile: string,\n specifier: string,\n modulesRoot: string,\n moduleId: string,\n packageName: string,\n): string {\n const resolvedTarget = resolveRelativeImportTarget(sourceFile, specifier)\n if (!resolvedTarget) return specifier\n\n const modulesRootPrefix = `${modulesRoot}${path.sep}`\n if (!resolvedTarget.startsWith(modulesRootPrefix)) return specifier\n\n const relativeFromModules = path.relative(modulesRoot, resolvedTarget)\n let normalizedRelative = relativeFromModules.split(path.sep).join('/')\n const matchedExt = SOURCE_FILE_EXTENSIONS.find((ext) => normalizedRelative.endsWith(ext))\n if (matchedExt) {\n normalizedRelative = normalizedRelative.slice(0, -matchedExt.length)\n }\n if (normalizedRelative.endsWith('/index')) {\n normalizedRelative = normalizedRelative.slice(0, -'/index'.length)\n }\n const segments = normalizedRelative.split('/')\n const targetModuleId = segments[0]\n\n if (!targetModuleId || targetModuleId === moduleId) return specifier\n\n return `${packageName}/modules/${normalizedRelative}`\n}\n\nexport function rewriteCrossModuleImports(\n pkgBase: string,\n appBase: string,\n moduleId: string,\n packageName: string,\n): void {\n const modulesRoot = path.resolve(pkgBase, '..')\n const appFiles = collectSourceFiles(appBase)\n\n for (const appFile of appFiles) {\n const relativePath = path.relative(appBase, appFile)\n const sourceFile = path.join(pkgBase, relativePath)\n\n if (!fs.existsSync(sourceFile)) continue\n\n const content = fs.readFileSync(appFile, 'utf8')\n let updated = content\n\n updated = updated.replace(\n /(\\bfrom\\s*['\"])([^'\"]+)(['\"])/g,\n (_match, prefix: string, specifier: string, suffix: string) => {\n const rewritten = rewriteRelativeSpecifier(\n sourceFile,\n specifier,\n modulesRoot,\n moduleId,\n packageName,\n )\n return `${prefix}${rewritten}${suffix}`\n },\n )\n\n updated = updated.replace(\n /(\\bimport\\s*\\(\\s*['\"])([^'\"]+)(['\"]\\s*\\))/g,\n (_match, prefix: string, specifier: string, suffix: string) => {\n const rewritten = rewriteRelativeSpecifier(\n sourceFile,\n specifier,\n modulesRoot,\n moduleId,\n packageName,\n )\n return `${prefix}${rewritten}${suffix}`\n },\n )\n\n if (updated !== content) {\n fs.writeFileSync(appFile, updated)\n }\n }\n}\n\nexport function parseModuleMetadata(indexPath: string): ModuleMetadata {\n if (!fs.existsSync(indexPath)) return {}\n\n const source = fs.readFileSync(indexPath, 'utf8')\n const result: ModuleMetadata = {}\n\n const ejectableMatch = source.match(/ejectable\\s*:\\s*(true|false)/)\n if (ejectableMatch) {\n result.ejectable = ejectableMatch[1] === 'true'\n }\n\n const titleMatch = source.match(/title\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (titleMatch) {\n result.title = titleMatch[1]\n }\n\n const descMatch = source.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) {\n result.description = descMatch[1]\n }\n\n return result\n}\n\nexport function copyDirRecursive(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true })\n\n const entries = fs.readdirSync(src, { withFileTypes: true })\n for (const entry of entries) {\n if (SKIP_DIRS.has(entry.name)) continue\n\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n\n if (entry.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else {\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\nexport function updateModulesTs(modulesPath: string, moduleId: string): void {\n if (!fs.existsSync(modulesPath)) {\n throw new Error(`modules.ts not found at ${modulesPath}`)\n }\n\n const source = fs.readFileSync(modulesPath, 'utf8')\n const objectPattern = /\\{[^{}]*\\}/g\n\n let match: RegExpExecArray | null\n let updatedSource: string | null = null\n\n while ((match = objectPattern.exec(source)) !== null) {\n const objectLiteral = match[0]\n const idMatch = objectLiteral.match(/\\bid\\s*:\\s*(['\"])([^'\"]+)\\1/)\n if (!idMatch || idMatch[2] !== moduleId) {\n continue\n }\n\n const updatedObject = upsertModuleSource(objectLiteral)\n updatedSource =\n source.slice(0, match.index) +\n updatedObject +\n source.slice(match.index + objectLiteral.length)\n break\n }\n\n if (!updatedSource) {\n throw new Error(\n `Could not find module entry for \"${moduleId}\" in ${modulesPath}. ` +\n `Expected a pattern like: { id: '${moduleId}', from: '...' } or { id: '${moduleId}' }`,\n )\n }\n\n fs.writeFileSync(modulesPath, updatedSource)\n}\n\nfunction upsertModuleSource(objectLiteral: string): string {\n if (/from\\s*:\\s*'[^']*'/.test(objectLiteral)) {\n return objectLiteral.replace(/from\\s*:\\s*'[^']*'/, \"from: '@app'\")\n }\n\n if (/from\\s*:\\s*\"[^\"]*\"/.test(objectLiteral)) {\n return objectLiteral.replace(/from\\s*:\\s*\"[^\"]*\"/, 'from: \"@app\"')\n }\n\n return objectLiteral.replace(/\\}\\s*$/, \", from: '@app' }\")\n}\n\nexport type EjectableModule = {\n id: string\n title?: string\n description?: string\n from: string\n}\n\nexport function listEjectableModules(resolver: PackageResolver): EjectableModule[] {\n const modules = resolver.loadEnabledModules()\n const ejectable: EjectableModule[] = []\n\n for (const entry of modules) {\n if (entry.from === '@app') continue\n\n const { pkgBase } = resolver.getModulePaths(entry)\n const indexPath = path.join(pkgBase, 'index.ts')\n const metadata = parseModuleMetadata(indexPath)\n\n if (metadata.ejectable) {\n ejectable.push({\n id: entry.id,\n title: metadata.title,\n description: metadata.description,\n from: entry.from || '@open-mercato/core',\n })\n }\n }\n\n return ejectable\n}\n\nexport function ejectModule(resolver: PackageResolver, moduleId: string): void {\n const modules = resolver.loadEnabledModules()\n const entry = modules.find((m: ModuleEntry) => m.id === moduleId)\n\n if (!entry) {\n throw new Error(\n `Module \"${moduleId}\" is not listed in src/modules.ts. ` +\n `Available modules: ${modules.map((m: ModuleEntry) => m.id).join(', ')}`,\n )\n }\n\n if (entry.from === '@app') {\n throw new Error(\n `Module \"${moduleId}\" is already local (from: '@app'). Nothing to eject.`,\n )\n }\n\n const { pkgBase, appBase } = resolver.getModulePaths(entry)\n\n if (!fs.existsSync(pkgBase)) {\n throw new Error(\n `Package source directory not found: ${pkgBase}. ` +\n `Make sure the package is installed.`,\n )\n }\n\n const indexPath = path.join(pkgBase, 'index.ts')\n const metadata = parseModuleMetadata(indexPath)\n\n if (!metadata.ejectable) {\n throw new Error(\n `Module \"${moduleId}\" is not marked as ejectable. ` +\n `Only modules with \\`ejectable: true\\` in their metadata can be ejected.`,\n )\n }\n\n if (fs.existsSync(appBase)) {\n throw new Error(\n `Destination directory already exists: ${appBase}. ` +\n `Remove it first or resolve the conflict manually.`,\n )\n }\n\n copyDirRecursive(pkgBase, appBase)\n rewriteCrossModuleImports(pkgBase, appBase, moduleId, entry.from || '@open-mercato/core')\n\n const modulesPath = resolver.getModulesConfigPath()\n updateModulesTs(modulesPath, moduleId)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { setModuleRegistrationSource } from './modules-config'\nimport type { PackageResolver, ModuleEntry } from './resolver'\nimport { resolveInstalledOfficialModulePackage } from './module-package'\n\ntype ModuleMetadata = {\n ejectable?: boolean\n title?: string\n description?: string\n}\n\nconst SKIP_DIRS = new Set(['__tests__', '__mocks__', 'node_modules'])\nconst SOURCE_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']\n\nfunction shouldSkipEntryName(name: string): boolean {\n return SKIP_DIRS.has(name) || name === '.DS_Store' || name.startsWith('._')\n}\n\nfunction collectSourceFiles(dir: string): string[] {\n const files: string[] = []\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (shouldSkipEntryName(entry.name)) continue\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n files.push(...collectSourceFiles(fullPath))\n continue\n }\n const ext = path.extname(entry.name)\n if (!SOURCE_FILE_EXTENSIONS.includes(ext)) continue\n files.push(fullPath)\n }\n return files\n}\n\nfunction resolveRelativeImportTarget(sourceFile: string, importPath: string): string | null {\n if (!importPath.startsWith('.')) return null\n\n const basePath = path.resolve(path.dirname(sourceFile), importPath)\n const candidates = [basePath]\n\n for (const ext of SOURCE_FILE_EXTENSIONS) {\n candidates.push(`${basePath}${ext}`)\n candidates.push(path.join(basePath, `index${ext}`))\n }\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\nfunction rewriteRelativeSpecifier(\n sourceFile: string,\n specifier: string,\n modulesRoot: string,\n moduleId: string,\n packageName: string,\n): string {\n const resolvedTarget = resolveRelativeImportTarget(sourceFile, specifier)\n if (!resolvedTarget) return specifier\n\n const modulesRootPrefix = `${modulesRoot}${path.sep}`\n if (!resolvedTarget.startsWith(modulesRootPrefix)) return specifier\n\n const relativeFromModules = path.relative(modulesRoot, resolvedTarget)\n let normalizedRelative = relativeFromModules.split(path.sep).join('/')\n const matchedExt = SOURCE_FILE_EXTENSIONS.find((ext) => normalizedRelative.endsWith(ext))\n if (matchedExt) {\n normalizedRelative = normalizedRelative.slice(0, -matchedExt.length)\n }\n if (normalizedRelative.endsWith('/index')) {\n normalizedRelative = normalizedRelative.slice(0, -'/index'.length)\n }\n const segments = normalizedRelative.split('/')\n const targetModuleId = segments[0]\n\n if (!targetModuleId || targetModuleId === moduleId) return specifier\n\n return `${packageName}/modules/${normalizedRelative}`\n}\n\nexport function rewriteCrossModuleImports(\n pkgBase: string,\n appBase: string,\n moduleId: string,\n packageName: string,\n): void {\n const modulesRoot = path.resolve(pkgBase, '..')\n const appFiles = collectSourceFiles(appBase)\n\n for (const appFile of appFiles) {\n const relativePath = path.relative(appBase, appFile)\n const sourceFile = path.join(pkgBase, relativePath)\n\n if (!fs.existsSync(sourceFile)) continue\n\n const content = fs.readFileSync(appFile, 'utf8')\n let updated = content\n\n updated = updated.replace(\n /(\\bfrom\\s*['\"])([^'\"]+)(['\"])/g,\n (_match, prefix: string, specifier: string, suffix: string) => {\n const rewritten = rewriteRelativeSpecifier(\n sourceFile,\n specifier,\n modulesRoot,\n moduleId,\n packageName,\n )\n return `${prefix}${rewritten}${suffix}`\n },\n )\n\n updated = updated.replace(\n /(\\bimport\\s*\\(\\s*['\"])([^'\"]+)(['\"]\\s*\\))/g,\n (_match, prefix: string, specifier: string, suffix: string) => {\n const rewritten = rewriteRelativeSpecifier(\n sourceFile,\n specifier,\n modulesRoot,\n moduleId,\n packageName,\n )\n return `${prefix}${rewritten}${suffix}`\n },\n )\n\n if (updated !== content) {\n fs.writeFileSync(appFile, updated)\n }\n }\n}\n\nexport function parseModuleMetadata(indexPath: string): ModuleMetadata {\n if (!fs.existsSync(indexPath)) return {}\n\n const source = fs.readFileSync(indexPath, 'utf8')\n const result: ModuleMetadata = {}\n\n const ejectableMatch = source.match(/ejectable\\s*:\\s*(true|false)/)\n if (ejectableMatch) {\n result.ejectable = ejectableMatch[1] === 'true'\n }\n\n const titleMatch = source.match(/title\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (titleMatch) {\n result.title = titleMatch[1]\n }\n\n const descMatch = source.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) {\n result.description = descMatch[1]\n }\n\n return result\n}\n\nexport function copyDirRecursive(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true })\n\n const entries = fs.readdirSync(src, { withFileTypes: true })\n for (const entry of entries) {\n if (shouldSkipEntryName(entry.name)) continue\n\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n\n if (entry.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else {\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\nexport function updateModulesTs(modulesPath: string, moduleId: string): void {\n setModuleRegistrationSource(modulesPath, moduleId, '@app')\n}\n\nexport type EjectableModule = {\n id: string\n title?: string\n description?: string\n from: string\n}\n\ntype ResolvedModuleSource = {\n pkgBase: string\n metadata: ModuleMetadata\n}\n\nfunction resolveModuleSource(\n resolver: PackageResolver,\n entry: ModuleEntry,\n): ResolvedModuleSource {\n const { pkgBase } = resolver.getModulePaths(entry)\n const fallbackMetadata = parseModuleMetadata(path.join(pkgBase, 'index.ts'))\n const from = entry.from || '@open-mercato/core'\n\n if (from === '@app' || from === '@open-mercato/core') {\n return { pkgBase, metadata: fallbackMetadata }\n }\n\n try {\n const modulePackage = resolveInstalledOfficialModulePackage(resolver, from, entry.id)\n\n return {\n pkgBase: modulePackage.sourceModuleDir,\n metadata: {\n ejectable: modulePackage.metadata.ejectable,\n title: modulePackage.moduleInfo.title ?? modulePackage.metadata.moduleId,\n description: modulePackage.moduleInfo.description,\n },\n }\n } catch {\n return { pkgBase, metadata: fallbackMetadata }\n }\n}\n\nexport function listEjectableModules(resolver: PackageResolver): EjectableModule[] {\n const modules = resolver.loadEnabledModules()\n const ejectable: EjectableModule[] = []\n\n for (const entry of modules) {\n if (entry.from === '@app') continue\n\n const { metadata } = resolveModuleSource(resolver, entry)\n\n if (metadata.ejectable) {\n ejectable.push({\n id: entry.id,\n title: metadata.title,\n description: metadata.description,\n from: entry.from || '@open-mercato/core',\n })\n }\n }\n\n return ejectable\n}\n\nexport function ejectModule(resolver: PackageResolver, moduleId: string): void {\n const modules = resolver.loadEnabledModules()\n const entry = modules.find((m: ModuleEntry) => m.id === moduleId)\n\n if (!entry) {\n throw new Error(\n `Module \"${moduleId}\" is not listed in src/modules.ts. ` +\n `Available modules: ${modules.map((m: ModuleEntry) => m.id).join(', ')}`,\n )\n }\n\n if (entry.from === '@app') {\n throw new Error(\n `Module \"${moduleId}\" is already local (from: '@app'). Nothing to eject.`,\n )\n }\n\n const { appBase } = resolver.getModulePaths(entry)\n const { pkgBase, metadata } = resolveModuleSource(resolver, entry)\n\n if (!fs.existsSync(pkgBase)) {\n throw new Error(\n `Package source directory not found: ${pkgBase}. ` +\n `Make sure the package is installed.`,\n )\n }\n\n if (!metadata.ejectable) {\n throw new Error(\n `Module \"${moduleId}\" is not marked as ejectable. Only modules with \\`ejectable: true\\` in their metadata can be ejected.`,\n )\n }\n\n if (fs.existsSync(appBase)) {\n throw new Error(\n `Destination directory already exists: ${appBase}. ` +\n `Remove it first or resolve the conflict manually.`,\n )\n }\n\n copyDirRecursive(pkgBase, appBase)\n rewriteCrossModuleImports(pkgBase, appBase, moduleId, entry.from || '@open-mercato/core')\n\n const modulesPath = resolver.getModulesConfigPath()\n updateModulesTs(modulesPath, moduleId)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,11 +2,13 @@ import { generateEntityIds } from "./entity-ids.js";
2
2
  import { generateModuleRegistry, generateModuleRegistryCli } from "./module-registry.js";
3
3
  import { generateModuleEntities } from "./module-entities.js";
4
4
  import { generateModuleDi } from "./module-di.js";
5
+ import { generateModulePackageSources } from "./module-package-sources.js";
5
6
  import { generateOpenApi } from "./openapi.js";
6
7
  export {
7
8
  generateEntityIds,
8
9
  generateModuleDi,
9
10
  generateModuleEntities,
11
+ generateModulePackageSources,
10
12
  generateModuleRegistry,
11
13
  generateModuleRegistryCli,
12
14
  generateOpenApi
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/generators/index.ts"],
4
- "sourcesContent": ["export { generateEntityIds, type EntityIdsOptions } from './entity-ids'\nexport { generateModuleRegistry, generateModuleRegistryCli, type ModuleRegistryOptions } from './module-registry'\nexport { generateModuleEntities, type ModuleEntitiesOptions } from './module-entities'\nexport { generateModuleDi, type ModuleDiOptions } from './module-di'\nexport { generateOpenApi, type GenerateOpenApiOptions } from './openapi'\n"],
5
- "mappings": "AAAA,SAAS,yBAAgD;AACzD,SAAS,wBAAwB,iCAA6D;AAC9F,SAAS,8BAA0D;AACnE,SAAS,wBAA8C;AACvD,SAAS,uBAAoD;",
4
+ "sourcesContent": ["export { generateEntityIds, type EntityIdsOptions } from './entity-ids'\nexport { generateModuleRegistry, generateModuleRegistryCli, type ModuleRegistryOptions } from './module-registry'\nexport { generateModuleEntities, type ModuleEntitiesOptions } from './module-entities'\nexport { generateModuleDi, type ModuleDiOptions } from './module-di'\nexport { generateModulePackageSources, type ModulePackageSourcesOptions } from './module-package-sources'\nexport { generateOpenApi, type GenerateOpenApiOptions } from './openapi'\n"],
5
+ "mappings": "AAAA,SAAS,yBAAgD;AACzD,SAAS,wBAAwB,iCAA6D;AAC9F,SAAS,8BAA0D;AACnE,SAAS,wBAA8C;AACvD,SAAS,oCAAsE;AAC/E,SAAS,uBAAoD;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,45 @@
1
+ import path from "node:path";
2
+ import { readOfficialModulePackageFromRoot, resolveInstalledPackageRoot } from "../module-package.js";
3
+ import { calculateStructureChecksum, createGeneratorResult, writeGeneratedFile } from "../utils.js";
4
+ function normalizeCssSourcePath(value) {
5
+ const normalized = value.split(path.sep).join("/");
6
+ return normalized.startsWith(".") ? normalized : `./${normalized}`;
7
+ }
8
+ async function generateModulePackageSources(options) {
9
+ const { resolver, quiet } = options;
10
+ const result = createGeneratorResult();
11
+ const outputDir = resolver.getOutputDir();
12
+ const outFile = path.join(outputDir, "module-package-sources.css");
13
+ const checksumFile = path.join(outputDir, "module-package-sources.checksum");
14
+ const sourcePaths = /* @__PURE__ */ new Set();
15
+ const checksumTargets = [];
16
+ for (const entry of resolver.loadEnabledModules()) {
17
+ if (!entry.from || entry.from === "@app" || entry.from === "@open-mercato/core") continue;
18
+ const packageRoot = resolveInstalledPackageRoot(resolver, entry.from);
19
+ checksumTargets.push(packageRoot);
20
+ let modulePackage;
21
+ try {
22
+ modulePackage = readOfficialModulePackageFromRoot(packageRoot, entry.from, entry.id);
23
+ } catch {
24
+ continue;
25
+ }
26
+ const packageSourceRoot = path.join(modulePackage.packageRoot, "src");
27
+ const relativeSourcePath = normalizeCssSourcePath(path.relative(path.dirname(outFile), packageSourceRoot));
28
+ sourcePaths.add(`@source "${relativeSourcePath}/**/*.{ts,tsx}";`);
29
+ }
30
+ const content = `${Array.from(sourcePaths).sort((left, right) => left.localeCompare(right)).join("\n")}${sourcePaths.size > 0 ? "\n" : ""}`;
31
+ const structureChecksum = calculateStructureChecksum(checksumTargets);
32
+ writeGeneratedFile({
33
+ outFile,
34
+ checksumFile,
35
+ content,
36
+ structureChecksum,
37
+ result,
38
+ quiet
39
+ });
40
+ return result;
41
+ }
42
+ export {
43
+ generateModulePackageSources
44
+ };
45
+ //# sourceMappingURL=module-package-sources.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/generators/module-package-sources.ts"],
4
+ "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport { readOfficialModulePackageFromRoot, resolveInstalledPackageRoot } from '../module-package'\nimport type { PackageResolver } from '../resolver'\nimport { calculateStructureChecksum, createGeneratorResult, type GeneratorResult, writeGeneratedFile } from '../utils'\n\nexport interface ModulePackageSourcesOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\nfunction normalizeCssSourcePath(value: string): string {\n const normalized = value.split(path.sep).join('/')\n return normalized.startsWith('.') ? normalized : `./${normalized}`\n}\n\nexport async function generateModulePackageSources(\n options: ModulePackageSourcesOptions,\n): Promise<GeneratorResult> {\n const { resolver, quiet } = options\n const result = createGeneratorResult()\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'module-package-sources.css')\n const checksumFile = path.join(outputDir, 'module-package-sources.checksum')\n const sourcePaths = new Set<string>()\n const checksumTargets: string[] = []\n\n for (const entry of resolver.loadEnabledModules()) {\n if (!entry.from || entry.from === '@app' || entry.from === '@open-mercato/core') continue\n\n const packageRoot = resolveInstalledPackageRoot(resolver, entry.from)\n checksumTargets.push(packageRoot)\n\n let modulePackage\n try {\n modulePackage = readOfficialModulePackageFromRoot(packageRoot, entry.from, entry.id)\n } catch {\n continue\n }\n\n const packageSourceRoot = path.join(modulePackage.packageRoot, 'src')\n const relativeSourcePath = normalizeCssSourcePath(path.relative(path.dirname(outFile), packageSourceRoot))\n sourcePaths.add(`@source \"${relativeSourcePath}/**/*.{ts,tsx}\";`)\n }\n\n const content = `${Array.from(sourcePaths).sort((left, right) => left.localeCompare(right)).join('\\n')}${sourcePaths.size > 0 ? '\\n' : ''}`\n const structureChecksum = calculateStructureChecksum(checksumTargets)\n\n writeGeneratedFile({\n outFile,\n checksumFile,\n content,\n structureChecksum,\n result,\n quiet,\n })\n\n return result\n}\n"],
5
+ "mappings": "AACA,OAAO,UAAU;AACjB,SAAS,mCAAmC,mCAAmC;AAE/E,SAAS,4BAA4B,uBAA6C,0BAA0B;AAO5G,SAAS,uBAAuB,OAAuB;AACrD,QAAM,aAAa,MAAM,MAAM,KAAK,GAAG,EAAE,KAAK,GAAG;AACjD,SAAO,WAAW,WAAW,GAAG,IAAI,aAAa,KAAK,UAAU;AAClE;AAEA,eAAsB,6BACpB,SAC0B;AAC1B,QAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,QAAM,SAAS,sBAAsB;AACrC,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,UAAU,KAAK,KAAK,WAAW,4BAA4B;AACjE,QAAM,eAAe,KAAK,KAAK,WAAW,iCAAiC;AAC3E,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,kBAA4B,CAAC;AAEnC,aAAW,SAAS,SAAS,mBAAmB,GAAG;AACjD,QAAI,CAAC,MAAM,QAAQ,MAAM,SAAS,UAAU,MAAM,SAAS,qBAAsB;AAEjF,UAAM,cAAc,4BAA4B,UAAU,MAAM,IAAI;AACpE,oBAAgB,KAAK,WAAW;AAEhC,QAAI;AACJ,QAAI;AACF,sBAAgB,kCAAkC,aAAa,MAAM,MAAM,MAAM,EAAE;AAAA,IACrF,QAAQ;AACN;AAAA,IACF;AAEA,UAAM,oBAAoB,KAAK,KAAK,cAAc,aAAa,KAAK;AACpE,UAAM,qBAAqB,uBAAuB,KAAK,SAAS,KAAK,QAAQ,OAAO,GAAG,iBAAiB,CAAC;AACzG,gBAAY,IAAI,YAAY,kBAAkB,kBAAkB;AAAA,EAClE;AAEA,QAAM,UAAU,GAAG,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,YAAY,OAAO,IAAI,OAAO,EAAE;AACzI,QAAM,oBAAoB,2BAA2B,eAAe;AAEpE,qBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,40 @@
1
+ function parseModuleInstallArgs(args) {
2
+ let packageSpec = null;
3
+ let eject = false;
4
+ let moduleId = null;
5
+ for (let index = 0; index < args.length; index += 1) {
6
+ const arg = args[index];
7
+ if (!arg) continue;
8
+ if (arg === "--eject") {
9
+ eject = true;
10
+ continue;
11
+ }
12
+ if (arg.startsWith("--eject=")) {
13
+ throw new Error("--eject does not accept a value");
14
+ }
15
+ if (arg === "--module") {
16
+ const next = args[index + 1];
17
+ if (next && !next.startsWith("-")) {
18
+ moduleId = next;
19
+ index += 1;
20
+ continue;
21
+ }
22
+ throw new Error(`--module requires a moduleId value`);
23
+ }
24
+ if (arg.startsWith("--module=")) {
25
+ moduleId = arg.slice("--module=".length) || null;
26
+ continue;
27
+ }
28
+ if (arg.startsWith("-")) {
29
+ throw new Error(`Unsupported option: ${arg}`);
30
+ }
31
+ if (!arg.startsWith("-") && !packageSpec) {
32
+ packageSpec = arg;
33
+ }
34
+ }
35
+ return { packageSpec, eject, moduleId };
36
+ }
37
+ export {
38
+ parseModuleInstallArgs
39
+ };
40
+ //# sourceMappingURL=module-install-args.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/module-install-args.ts"],
4
+ "sourcesContent": ["export type ParsedModuleInstallArgs = {\n packageSpec: string | null\n eject: boolean\n moduleId: string | null\n}\n\nexport function parseModuleInstallArgs(args: string[]): ParsedModuleInstallArgs {\n let packageSpec: string | null = null\n let eject = false\n let moduleId: string | null = null\n\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index]\n if (!arg) continue\n\n if (arg === '--eject') {\n eject = true\n continue\n }\n\n if (arg.startsWith('--eject=')) {\n throw new Error('--eject does not accept a value')\n }\n\n if (arg === '--module') {\n const next = args[index + 1]\n if (next && !next.startsWith('-')) {\n moduleId = next\n index += 1\n continue\n }\n throw new Error(`--module requires a moduleId value`)\n }\n\n if (arg.startsWith('--module=')) {\n moduleId = arg.slice('--module='.length) || null\n continue\n }\n\n if (arg.startsWith('-')) {\n throw new Error(`Unsupported option: ${arg}`)\n }\n\n if (!arg.startsWith('-') && !packageSpec) {\n packageSpec = arg\n }\n }\n\n return { packageSpec, eject, moduleId }\n}\n"],
5
+ "mappings": "AAMO,SAAS,uBAAuB,MAAyC;AAC9E,MAAI,cAA6B;AACjC,MAAI,QAAQ;AACZ,MAAI,WAA0B;AAE9B,WAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;AACnD,UAAM,MAAM,KAAK,KAAK;AACtB,QAAI,CAAC,IAAK;AAEV,QAAI,QAAQ,WAAW;AACrB,cAAQ;AACR;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,UAAU,GAAG;AAC9B,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,QAAI,QAAQ,YAAY;AACtB,YAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,UAAI,QAAQ,CAAC,KAAK,WAAW,GAAG,GAAG;AACjC,mBAAW;AACX,iBAAS;AACT;AAAA,MACF;AACA,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,QAAI,IAAI,WAAW,WAAW,GAAG;AAC/B,iBAAW,IAAI,MAAM,YAAY,MAAM,KAAK;AAC5C;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,GAAG,GAAG;AACvB,YAAM,IAAI,MAAM,uBAAuB,GAAG,EAAE;AAAA,IAC9C;AAEA,QAAI,CAAC,IAAI,WAAW,GAAG,KAAK,CAAC,aAAa;AACxC,oBAAc;AAAA,IAChB;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,OAAO,SAAS;AACxC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,157 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { copyDirRecursive, rewriteCrossModuleImports } from "./eject.js";
5
+ import {
6
+ parsePackageNameFromSpec,
7
+ resolveInstalledOfficialModulePackage,
8
+ validateEjectBoundaries
9
+ } from "./module-package.js";
10
+ import { ensureModuleRegistration } from "./modules-config.js";
11
+ function resolveYarnBinary() {
12
+ return process.platform === "win32" ? "yarn.cmd" : "yarn";
13
+ }
14
+ function readAppPackageName(appDir) {
15
+ const packageJsonPath = path.join(appDir, "package.json");
16
+ if (!fs.existsSync(packageJsonPath)) {
17
+ throw new Error(`App package.json not found at ${packageJsonPath}.`);
18
+ }
19
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
20
+ if (!packageJson.name) {
21
+ throw new Error(`App package.json at ${packageJsonPath} is missing the "name" field.`);
22
+ }
23
+ return packageJson.name;
24
+ }
25
+ function resolveInstallTarget(resolver) {
26
+ if (!resolver.isMonorepo()) {
27
+ return {
28
+ cwd: resolver.getAppDir(),
29
+ args: ["add"]
30
+ };
31
+ }
32
+ return {
33
+ cwd: resolver.getRootDir(),
34
+ args: ["workspace", readAppPackageName(resolver.getAppDir()), "add"]
35
+ };
36
+ }
37
+ function runCommand(command, args, cwd) {
38
+ return new Promise((resolve, reject) => {
39
+ const child = spawn(command, args, {
40
+ cwd,
41
+ env: process.env,
42
+ stdio: "inherit"
43
+ });
44
+ child.on("error", reject);
45
+ child.on("exit", (code) => {
46
+ if (code === 0) {
47
+ resolve();
48
+ return;
49
+ }
50
+ reject(new Error(`Command failed: ${command} ${args.join(" ")} (exit ${code ?? "unknown"}).`));
51
+ });
52
+ });
53
+ }
54
+ async function installPackageSpec(resolver, packageSpec) {
55
+ const target = resolveInstallTarget(resolver);
56
+ await runCommand(resolveYarnBinary(), [...target.args, packageSpec], target.cwd);
57
+ }
58
+ async function runModuleGenerators(resolver, quiet = true) {
59
+ const {
60
+ generateEntityIds,
61
+ generateModuleDi,
62
+ generateModuleEntities,
63
+ generateModulePackageSources,
64
+ generateModuleRegistry,
65
+ generateModuleRegistryCli,
66
+ generateOpenApi
67
+ } = await import("./generators/index.js");
68
+ await generateEntityIds({ resolver, quiet });
69
+ await generateModuleRegistry({ resolver, quiet });
70
+ await generateModuleRegistryCli({ resolver, quiet });
71
+ await generateModuleEntities({ resolver, quiet });
72
+ await generateModuleDi({ resolver, quiet });
73
+ await generateModulePackageSources({ resolver, quiet });
74
+ await generateOpenApi({ resolver, quiet });
75
+ }
76
+ async function runGeneratorsWithRegistrationNotice(resolver, moduleId) {
77
+ try {
78
+ await runModuleGenerators(resolver);
79
+ } catch (error) {
80
+ const message = error instanceof Error ? error.message : String(error);
81
+ throw new Error(
82
+ `Module "${moduleId}" is enabled, but generated artifacts are stale because generation failed. Rerun "yarn mercato generate" after fixing the underlying error. ${message}`
83
+ );
84
+ }
85
+ }
86
+ function assertPackageName(packageName) {
87
+ if (!packageName || !packageName.startsWith("@open-mercato/")) {
88
+ throw new Error('Only @open-mercato/* package specs are supported by "mercato module add".');
89
+ }
90
+ }
91
+ function validateBeforeRegistration(modulePackage, resolver, eject) {
92
+ const appModuleDir = path.join(resolver.getAppDir(), "src", "modules", modulePackage.metadata.moduleId);
93
+ if (eject && fs.existsSync(appModuleDir)) {
94
+ throw new Error(`Destination directory already exists: ${appModuleDir}. Remove it first or rerun without --eject.`);
95
+ }
96
+ return appModuleDir;
97
+ }
98
+ function prepareRegistrationSource(modulePackage, resolver, packageName, eject) {
99
+ const appModuleDir = validateBeforeRegistration(modulePackage, resolver, eject);
100
+ if (!eject) {
101
+ return packageName;
102
+ }
103
+ if (!modulePackage.metadata.ejectable) {
104
+ throw new Error(`Package "${packageName}" is not ejectable. --eject requires open-mercato.ejectable === true.`);
105
+ }
106
+ validateEjectBoundaries(modulePackage);
107
+ copyDirRecursive(modulePackage.sourceModuleDir, appModuleDir);
108
+ rewriteCrossModuleImports(
109
+ modulePackage.sourceModuleDir,
110
+ appModuleDir,
111
+ modulePackage.metadata.moduleId,
112
+ packageName
113
+ );
114
+ return "@app";
115
+ }
116
+ async function registerResolvedOfficialModule(resolver, modulePackage, packageName, eject) {
117
+ const from = prepareRegistrationSource(modulePackage, resolver, packageName, eject);
118
+ const registration = ensureModuleRegistration(
119
+ resolver.getModulesConfigPath(),
120
+ {
121
+ id: modulePackage.metadata.moduleId,
122
+ from
123
+ }
124
+ );
125
+ if (!registration.changed) {
126
+ throw new Error(
127
+ `Module "${modulePackage.metadata.moduleId}" from "${from}" is already enabled in modules.ts.`
128
+ );
129
+ }
130
+ await runGeneratorsWithRegistrationNotice(resolver, modulePackage.metadata.moduleId);
131
+ return {
132
+ moduleId: modulePackage.metadata.moduleId,
133
+ packageName,
134
+ from,
135
+ registrationChanged: registration.changed
136
+ };
137
+ }
138
+ async function addOfficialModule(resolver, packageSpec, eject, moduleId) {
139
+ const packageName = parsePackageNameFromSpec(packageSpec);
140
+ assertPackageName(packageName);
141
+ await installPackageSpec(resolver, packageSpec);
142
+ const modulePackage = resolveInstalledOfficialModulePackage(resolver, packageName, moduleId);
143
+ return registerResolvedOfficialModule(resolver, modulePackage, packageName, eject);
144
+ }
145
+ async function enableOfficialModule(resolver, packageName, moduleId, eject = false) {
146
+ if (!packageName.startsWith("@open-mercato/")) {
147
+ throw new Error('Only @open-mercato/* packages can be enabled with "mercato module enable".');
148
+ }
149
+ const modulePackage = resolveInstalledOfficialModulePackage(resolver, packageName, moduleId);
150
+ return registerResolvedOfficialModule(resolver, modulePackage, packageName, eject);
151
+ }
152
+ export {
153
+ addOfficialModule,
154
+ enableOfficialModule,
155
+ runModuleGenerators
156
+ };
157
+ //# sourceMappingURL=module-install.js.map