@launch77/cli 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/cli.js +8 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/infrastructure/git.d.ts +37 -0
  5. package/dist/infrastructure/git.d.ts.map +1 -0
  6. package/dist/infrastructure/git.js +82 -0
  7. package/dist/infrastructure/git.js.map +1 -0
  8. package/dist/infrastructure/github.d.ts +43 -0
  9. package/dist/infrastructure/github.d.ts.map +1 -0
  10. package/dist/infrastructure/github.js +89 -0
  11. package/dist/infrastructure/github.js.map +1 -0
  12. package/dist/infrastructure/template-generator.d.ts +1 -1
  13. package/dist/infrastructure/template-generator.d.ts.map +1 -1
  14. package/dist/infrastructure/template.d.ts +5 -0
  15. package/dist/infrastructure/template.d.ts.map +1 -1
  16. package/dist/infrastructure/template.js +11 -0
  17. package/dist/infrastructure/template.js.map +1 -1
  18. package/dist/modules/app/commands/create-app.js +1 -1
  19. package/dist/modules/app/commands/create-app.js.map +1 -1
  20. package/dist/modules/app/commands/delete-app.js +1 -1
  21. package/dist/modules/app/commands/delete-app.js.map +1 -1
  22. package/dist/modules/app/services/app-svc.d.ts +1 -1
  23. package/dist/modules/app/services/app-svc.d.ts.map +1 -1
  24. package/dist/modules/app/services/manifest-svc.d.ts +1 -1
  25. package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
  26. package/dist/modules/catalog/config/catalog-config.test.js +1 -1
  27. package/dist/modules/catalog/config/catalog-config.test.js.map +1 -1
  28. package/dist/modules/catalog/schemas/catalog-ui-components.schema.json +2 -18
  29. package/dist/modules/git/commands/git-connect.d.ts +3 -0
  30. package/dist/modules/git/commands/git-connect.d.ts.map +1 -0
  31. package/dist/modules/git/commands/git-connect.js +156 -0
  32. package/dist/modules/git/commands/git-connect.js.map +1 -0
  33. package/dist/modules/git/errors/git-errors.d.ts +21 -0
  34. package/dist/modules/git/errors/git-errors.d.ts.map +1 -0
  35. package/dist/modules/git/errors/git-errors.js +41 -0
  36. package/dist/modules/git/errors/git-errors.js.map +1 -0
  37. package/dist/modules/git/index.d.ts +5 -0
  38. package/dist/modules/git/index.d.ts.map +1 -0
  39. package/dist/modules/git/index.js +8 -0
  40. package/dist/modules/git/index.js.map +1 -0
  41. package/dist/modules/git/services/git-service.d.ts +24 -0
  42. package/dist/modules/git/services/git-service.d.ts.map +1 -0
  43. package/dist/modules/git/services/git-service.js +56 -0
  44. package/dist/modules/git/services/git-service.js.map +1 -0
  45. package/dist/modules/git/services/github-service.d.ts +27 -0
  46. package/dist/modules/git/services/github-service.d.ts.map +1 -0
  47. package/dist/modules/git/services/github-service.js +45 -0
  48. package/dist/modules/git/services/github-service.js.map +1 -0
  49. package/dist/modules/plugin/commands/plugin-create.d.ts +3 -0
  50. package/dist/modules/plugin/commands/plugin-create.d.ts.map +1 -0
  51. package/dist/modules/plugin/commands/plugin-create.js +59 -0
  52. package/dist/modules/plugin/commands/plugin-create.js.map +1 -0
  53. package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -1
  54. package/dist/modules/plugin/commands/plugin-install.js +9 -24
  55. package/dist/modules/plugin/commands/plugin-install.js.map +1 -1
  56. package/dist/modules/plugin/errors/plugin-errors.d.ts +24 -1
  57. package/dist/modules/plugin/errors/plugin-errors.d.ts.map +1 -1
  58. package/dist/modules/plugin/errors/plugin-errors.js +79 -6
  59. package/dist/modules/plugin/errors/plugin-errors.js.map +1 -1
  60. package/dist/modules/plugin/index.d.ts +4 -2
  61. package/dist/modules/plugin/index.d.ts.map +1 -1
  62. package/dist/modules/plugin/index.js +4 -2
  63. package/dist/modules/plugin/index.js.map +1 -1
  64. package/dist/modules/plugin/lib/plugin-registry.d.ts +6 -12
  65. package/dist/modules/plugin/lib/plugin-registry.d.ts.map +1 -1
  66. package/dist/modules/plugin/lib/plugin-registry.js +13 -30
  67. package/dist/modules/plugin/lib/plugin-registry.js.map +1 -1
  68. package/dist/modules/plugin/lib/plugin-resolver.d.ts +76 -0
  69. package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +1 -0
  70. package/dist/modules/plugin/lib/plugin-resolver.js +128 -0
  71. package/dist/modules/plugin/lib/plugin-resolver.js.map +1 -0
  72. package/dist/modules/plugin/lib/plugin-resolver.test.d.ts +2 -0
  73. package/dist/modules/plugin/lib/plugin-resolver.test.d.ts.map +1 -0
  74. package/dist/modules/plugin/lib/plugin-resolver.test.js +175 -0
  75. package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -0
  76. package/dist/modules/plugin/services/plugin-create-service.d.ts +16 -0
  77. package/dist/modules/plugin/services/plugin-create-service.d.ts.map +1 -0
  78. package/dist/modules/plugin/services/plugin-create-service.js +47 -0
  79. package/dist/modules/plugin/services/plugin-create-service.js.map +1 -0
  80. package/dist/modules/plugin/services/plugin-svc.d.ts +8 -3
  81. package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -1
  82. package/dist/modules/plugin/services/plugin-svc.js +96 -15
  83. package/dist/modules/plugin/services/plugin-svc.js.map +1 -1
  84. package/dist/modules/release/commands/release-init.d.ts +3 -0
  85. package/dist/modules/release/commands/release-init.d.ts.map +1 -0
  86. package/dist/modules/release/commands/release-init.js +92 -0
  87. package/dist/modules/release/commands/release-init.js.map +1 -0
  88. package/dist/modules/release/errors/release-errors.d.ts +7 -0
  89. package/dist/modules/release/errors/release-errors.d.ts.map +1 -0
  90. package/dist/modules/release/errors/release-errors.js +13 -0
  91. package/dist/modules/release/errors/release-errors.js.map +1 -0
  92. package/dist/modules/release/index.d.ts +4 -0
  93. package/dist/modules/release/index.d.ts.map +1 -0
  94. package/dist/modules/release/index.js +7 -0
  95. package/dist/modules/release/index.js.map +1 -0
  96. package/dist/modules/release/services/release-service.d.ts +34 -0
  97. package/dist/modules/release/services/release-service.d.ts.map +1 -0
  98. package/dist/modules/release/services/release-service.js +154 -0
  99. package/dist/modules/release/services/release-service.js.map +1 -0
  100. package/dist/modules/workspace/commands/init-workspace.d.ts.map +1 -1
  101. package/dist/modules/workspace/commands/init-workspace.js +4 -5
  102. package/dist/modules/workspace/commands/init-workspace.js.map +1 -1
  103. package/dist/modules/workspace/services/workspace-service.d.ts +2 -1
  104. package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -1
  105. package/dist/modules/workspace/services/workspace-service.js +27 -1
  106. package/dist/modules/workspace/services/workspace-service.js.map +1 -1
  107. package/dist/templates/plugin/README.md.hbs +39 -0
  108. package/dist/{plugins/theme/package.json → templates/plugin/package.json.hbs} +5 -3
  109. package/dist/templates/plugin/plugin.json.hbs +7 -0
  110. package/dist/templates/plugin/src/generator.ts.hbs +64 -0
  111. package/dist/templates/plugin/templates/src/.gitkeep +0 -0
  112. package/dist/templates/plugin/tsconfig.json +10 -0
  113. package/dist/{plugins/theme → templates/plugin}/tsup.config.ts +0 -1
  114. package/dist/templates/workspace/.github/workflows/ci.yml +102 -0
  115. package/dist/templates/workspace/package.json +16 -1
  116. package/dist/templates/workspace/turbo.json +5 -0
  117. package/dist/utils/launch77-context.d.ts +1 -1
  118. package/dist/utils/launch77-context.d.ts.map +1 -1
  119. package/dist/utils/launch77-context.js +25 -2
  120. package/dist/utils/launch77-context.js.map +1 -1
  121. package/dist/utils/launch77-validation.d.ts +1 -1
  122. package/dist/utils/launch77-validation.d.ts.map +1 -1
  123. package/dist/utils/string.d.ts +13 -0
  124. package/dist/utils/string.d.ts.map +1 -0
  125. package/dist/utils/string.js +18 -0
  126. package/dist/utils/string.js.map +1 -0
  127. package/package.json +7 -10
  128. package/src/cli.ts +10 -1
  129. package/src/infrastructure/git.ts +86 -0
  130. package/src/infrastructure/github.ts +111 -0
  131. package/src/infrastructure/template-generator.ts +1 -1
  132. package/src/infrastructure/template.ts +14 -0
  133. package/src/modules/app/commands/create-app.ts +1 -1
  134. package/src/modules/app/commands/delete-app.ts +1 -1
  135. package/src/modules/app/services/app-svc.ts +1 -1
  136. package/src/modules/app/services/manifest-svc.ts +1 -1
  137. package/src/modules/catalog/config/catalog-config.test.ts +1 -1
  138. package/src/modules/git/commands/git-connect.ts +183 -0
  139. package/src/modules/git/errors/git-errors.ts +44 -0
  140. package/src/modules/git/index.ts +9 -0
  141. package/src/modules/git/services/git-service.ts +63 -0
  142. package/src/modules/git/services/github-service.ts +52 -0
  143. package/src/modules/plugin/commands/plugin-create.ts +68 -0
  144. package/src/modules/plugin/commands/plugin-install.ts +9 -26
  145. package/src/modules/plugin/errors/plugin-errors.ts +87 -6
  146. package/src/modules/plugin/index.ts +4 -2
  147. package/src/modules/plugin/lib/plugin-registry.ts +14 -37
  148. package/src/modules/plugin/lib/plugin-resolver.test.ts +215 -0
  149. package/src/modules/plugin/lib/plugin-resolver.ts +160 -0
  150. package/src/modules/plugin/services/plugin-create-service.ts +69 -0
  151. package/src/modules/plugin/services/plugin-svc.ts +108 -15
  152. package/src/modules/release/commands/release-init.ts +102 -0
  153. package/src/modules/release/errors/release-errors.ts +13 -0
  154. package/src/modules/release/index.ts +8 -0
  155. package/src/modules/release/services/release-service.ts +170 -0
  156. package/src/modules/workspace/commands/init-workspace.ts +4 -6
  157. package/src/modules/workspace/services/workspace-service.ts +30 -1
  158. package/src/utils/launch77-context.ts +29 -3
  159. package/src/utils/launch77-validation.ts +1 -1
  160. package/src/utils/string.ts +17 -0
  161. package/templates/plugin/README.md.hbs +39 -0
  162. package/templates/plugin/package.json.hbs +34 -0
  163. package/templates/plugin/plugin.json.hbs +7 -0
  164. package/templates/plugin/src/generator.ts.hbs +64 -0
  165. package/templates/plugin/templates/src/.gitkeep +0 -0
  166. package/templates/plugin/tsconfig.json +10 -0
  167. package/templates/plugin/tsup.config.ts +9 -0
  168. package/templates/workspace/.github/workflows/ci.yml +102 -0
  169. package/templates/workspace/package.json +5 -0
  170. package/templates/workspace/turbo.json +5 -0
  171. package/tests/integration/cli.test.ts +25 -0
  172. package/tests/integration/setup.ts +20 -0
  173. package/vitest.config.ts +9 -0
  174. package/vitest.integration.config.ts +9 -0
  175. package/dist/app-templates/webapp/.env.ci +0 -6
  176. package/dist/app-templates/webapp/.env.example +0 -9
  177. package/dist/app-templates/webapp/.eslintrc.json +0 -6
  178. package/dist/app-templates/webapp/README.md.hbs +0 -80
  179. package/dist/app-templates/webapp/app/about/page.tsx.hbs +0 -41
  180. package/dist/app-templates/webapp/app/dashboard/page.tsx.hbs +0 -51
  181. package/dist/app-templates/webapp/app/globals.css +0 -31
  182. package/dist/app-templates/webapp/app/layout.tsx.hbs +0 -26
  183. package/dist/app-templates/webapp/app/page.tsx.hbs +0 -30
  184. package/dist/app-templates/webapp/next.config.js +0 -99
  185. package/dist/app-templates/webapp/package.json.hbs +0 -30
  186. package/dist/app-templates/webapp/postcss.config.js +0 -6
  187. package/dist/app-templates/webapp/tailwind.config.ts +0 -24
  188. package/dist/app-templates/webapp/tsconfig.json +0 -29
  189. package/dist/app-templates/webapp/vercel.json.hbs +0 -7
  190. package/dist/modules/catalog/schemas/schemas/catalog-ui-components.schema.json +0 -145
  191. package/dist/plugins/theme/plugin.json +0 -9
  192. package/dist/plugins/theme/src/generator.ts +0 -92
  193. package/dist/plugins/theme/src/utils/config-modifier.ts +0 -142
  194. package/dist/plugins/theme/src/utils/css-modifier.ts +0 -89
  195. package/dist/plugins/theme/templates/app/theme-test/page.tsx +0 -156
  196. package/dist/plugins/theme/templates/src/modules/theme/README.md +0 -209
  197. package/dist/plugins/theme/templates/src/modules/theme/config/brand.css +0 -23
  198. package/dist/plugins/theme/tsconfig.json +0 -14
  199. package/dist/templates/templates/startup/apps/.gitkeep +0 -8
  200. package/dist/templates/templates/workspace/.launch77/workspace.json +0 -3
  201. package/dist/templates/templates/workspace/README.md +0 -62
  202. package/dist/templates/templates/workspace/app-templates/.gitkeep +0 -1
  203. package/dist/templates/templates/workspace/apps/.gitkeep +0 -1
  204. package/dist/templates/templates/workspace/libraries/.gitkeep +0 -1
  205. package/dist/templates/templates/workspace/package.json +0 -31
  206. package/dist/templates/templates/workspace/plugins/.gitkeep +0 -1
  207. package/dist/templates/templates/workspace/tsconfig.json +0 -22
  208. package/dist/templates/templates/workspace/turbo.json +0 -25
  209. package/launch77-cli-1.2.0.tgz +0 -0
  210. package/src/modules/plugin/lib/launch77-workspace.code-workspace +0 -14
  211. /package/dist/templates/{templates/workspace → workspace}/.eslintignore +0 -0
  212. /package/dist/templates/{templates/workspace → workspace}/.eslintrc.js +0 -0
  213. /package/dist/templates/{templates/workspace → workspace}/.husky/pre-push +0 -0
  214. /package/dist/templates/{templates/workspace → workspace}/.lintstagedrc.json +0 -0
  215. /package/dist/templates/{templates/workspace → workspace}/.prettierrc +0 -0
@@ -14,19 +14,65 @@ export class InvalidPluginContextError extends Error {
14
14
  }
15
15
  /**
16
16
  * Factory function to create standardized InvalidPluginContextError
17
- * for when plugin:install is run outside of an app directory
17
+ * for when plugin:install is run outside of a package directory
18
18
  */
19
19
  export function createInvalidContextError(currentLocation) {
20
- return new InvalidPluginContextError(`plugin:install must be run from within an app directory.
20
+ return new InvalidPluginContextError(`plugin:install must be run from within a package directory.
21
21
 
22
22
  Current location: ${currentLocation}
23
- Expected: apps/<app-name>/
23
+ Expected: apps/<name>/, libraries/<name>/, plugins/<name>/, or app-templates/<name>/
24
24
 
25
- Navigate to an app directory:
25
+ Navigate to a package directory:
26
26
  cd apps/<app-name>/
27
+ cd libraries/<lib-name>/
28
+ cd plugins/<plugin-name>/
29
+ cd app-templates/<template-name>/`);
30
+ }
31
+ /**
32
+ * Error when plugin.json is missing the required 'targets' field
33
+ */
34
+ export class MissingPluginTargetsError extends Error {
35
+ constructor(pluginName) {
36
+ super(`Plugin '${pluginName}' is missing the required 'targets' field in plugin.json.
37
+
38
+ The plugin.json file must include a 'targets' array specifying which package types
39
+ the plugin can be installed into.
40
+
41
+ Example plugin.json:
42
+ {
43
+ "name": "${pluginName}",
44
+ "version": "1.0.0",
45
+ "targets": ["app", "library", "plugin", "app-template"],
46
+ "pluginDependencies": {},
47
+ "libraryDependencies": {}
48
+ }`);
49
+ this.name = 'MissingPluginTargetsError';
50
+ }
51
+ }
52
+ /**
53
+ * Factory function to create error when plugin targets don't match current location
54
+ */
55
+ export function createInvalidTargetError(pluginName, currentTarget, allowedTargets) {
56
+ const targetLocations = allowedTargets.map((target) => {
57
+ switch (target) {
58
+ case 'app':
59
+ return 'apps/<name>/';
60
+ case 'library':
61
+ return 'libraries/<name>/';
62
+ case 'plugin':
63
+ return 'plugins/<name>/';
64
+ case 'app-template':
65
+ return 'app-templates/<name>/';
66
+ default:
67
+ return target;
68
+ }
69
+ });
70
+ return new InvalidPluginContextError(`Plugin '${pluginName}' cannot be installed in a '${currentTarget}' package.
71
+
72
+ This plugin can only be installed in: ${allowedTargets.join(', ')}
27
73
 
28
- Or create an app first:
29
- launch77 app:create api <app-name>`);
74
+ Allowed locations:
75
+ ${targetLocations.map((loc) => ` ${loc}`).join('\n')}`);
30
76
  }
31
77
  export class PluginInstallationError extends Error {
32
78
  cause;
@@ -36,4 +82,31 @@ export class PluginInstallationError extends Error {
36
82
  this.name = 'PluginInstallationError';
37
83
  }
38
84
  }
85
+ /**
86
+ * Error when plugin resolution fails
87
+ */
88
+ export class PluginResolutionError extends Error {
89
+ constructor(pluginName, reason) {
90
+ super(`Failed to resolve plugin '${pluginName}': ${reason}`);
91
+ this.name = 'PluginResolutionError';
92
+ }
93
+ }
94
+ /**
95
+ * Error when npm package installation fails
96
+ */
97
+ export class NpmInstallationError extends Error {
98
+ cause;
99
+ constructor(packageName, cause) {
100
+ super(`Failed to install npm package '${packageName}'.
101
+
102
+ Please check:
103
+ - Your internet connection
104
+ - npm registry access (https://registry.npmjs.org)
105
+ - Package exists: https://www.npmjs.com/package/${packageName}
106
+
107
+ ${cause ? `\nOriginal error: ${cause.message}` : ''}`);
108
+ this.cause = cause;
109
+ this.name = 'NpmInstallationError';
110
+ }
111
+ }
39
112
  //# sourceMappingURL=plugin-errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-errors.js","sourceRoot":"","sources":["../../../../src/modules/plugin/errors/plugin-errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,UAAkB;QAC5B,KAAK,CAAC,WAAW,UAAU;;qDAEsB,CAAC,CAAA;QAClD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAA;IACzC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,eAAuB;IAC/D,OAAO,IAAI,yBAAyB,CAClC;;oBAEgB,eAAe;;;;;;;qCAOE,CAClC,CAAA;AACH,CAAC;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAG9B;IAFlB,YACE,OAAe,EACC,KAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAA;QAFE,UAAK,GAAL,KAAK,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;IACvC,CAAC;CACF"}
1
+ {"version":3,"file":"plugin-errors.js","sourceRoot":"","sources":["../../../../src/modules/plugin/errors/plugin-errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,UAAkB;QAC5B,KAAK,CAAC,WAAW,UAAU;;qDAEsB,CAAC,CAAA;QAClD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAA;IACzC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,eAAuB;IAC/D,OAAO,IAAI,yBAAyB,CAClC;;oBAEgB,eAAe;;;;;;;oCAOC,CACjC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAY,UAAkB;QAC5B,KAAK,CAAC,WAAW,UAAU;;;;;;;aAOlB,UAAU;;;;;EAKrB,CAAC,CAAA;QACC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAA;IACzC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAkB,EAAE,aAAqB,EAAE,cAAwB;IAC1G,MAAM,eAAe,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACpD,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,KAAK;gBACR,OAAO,cAAc,CAAA;YACvB,KAAK,SAAS;gBACZ,OAAO,mBAAmB,CAAA;YAC5B,KAAK,QAAQ;gBACX,OAAO,iBAAiB,CAAA;YAC1B,KAAK,cAAc;gBACjB,OAAO,uBAAuB,CAAA;YAChC;gBACE,OAAO,MAAM,CAAA;QACjB,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,IAAI,yBAAyB,CAClC,WAAW,UAAU,+BAA+B,aAAa;;wCAE7B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;;;EAG/D,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpD,CAAA;AACH,CAAC;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAG9B;IAFlB,YACE,OAAe,EACC,KAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAA;QAFE,UAAK,GAAL,KAAK,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;IACvC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,UAAkB,EAAE,MAAc;QAC5C,KAAK,CAAC,6BAA6B,UAAU,MAAM,MAAM,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;IACrC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAG3B;IAFlB,YACE,WAAmB,EACH,KAAa;QAE7B,KAAK,CAAC,kCAAkC,WAAW;;;;;kDAKL,WAAW;;EAE3D,KAAK,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QATlC,UAAK,GAAL,KAAK,CAAQ;QAU7B,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF"}
@@ -1,6 +1,8 @@
1
1
  export { PluginService } from './services/plugin-svc.js';
2
+ export { PluginCreateService } from './services/plugin-create-service.js';
2
3
  export type { InstallPluginRequest, InstallPluginResult, PluginMetadata, HookResult } from './types/plugin-types.js';
3
- export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError } from './errors/plugin-errors.js';
4
+ export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError, PluginResolutionError, NpmInstallationError } from './errors/plugin-errors.js';
4
5
  export { pluginInstallCommand } from './commands/plugin-install.js';
5
- export { getPluginPath, pluginExists, listAvailablePlugins } from './lib/plugin-registry.js';
6
+ export { pluginCreateCommand } from './commands/plugin-create.js';
7
+ export { listAvailablePlugins } from './lib/plugin-registry.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/plugin/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGxD,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAGpH,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAGnH,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAGnE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/plugin/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AAGzE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAGpH,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAGhK,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAGjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -1,9 +1,11 @@
1
1
  // Services
2
2
  export { PluginService } from './services/plugin-svc.js';
3
+ export { PluginCreateService } from './services/plugin-create-service.js';
3
4
  // Errors
4
- export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError } from './errors/plugin-errors.js';
5
+ export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError, PluginResolutionError, NpmInstallationError } from './errors/plugin-errors.js';
5
6
  // Commands
6
7
  export { pluginInstallCommand } from './commands/plugin-install.js';
8
+ export { pluginCreateCommand } from './commands/plugin-create.js';
7
9
  // Utilities
8
- export { getPluginPath, pluginExists, listAvailablePlugins } from './lib/plugin-registry.js';
10
+ export { listAvailablePlugins } from './lib/plugin-registry.js';
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/plugin/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAKxD,SAAS;AACT,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAEnH,WAAW;AACX,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAEnE,YAAY;AACZ,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/plugin/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AAKzE,SAAS;AACT,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAEhK,WAAW;AACX,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,YAAY;AACZ,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -1,15 +1,9 @@
1
1
  /**
2
- * Get the path to a plugin directory
3
- * Plugins are bundled with the CLI at dist/plugins/
2
+ * List all available plugins in the workspace
3
+ * Scans the workspace plugins/ directory for valid plugins
4
+ *
5
+ * @param workspaceRoot - The root directory of the Launch77 workspace
6
+ * @returns Array of plugin names found in the workspace
4
7
  */
5
- export declare function getPluginPath(pluginName: string): string;
6
- /**
7
- * Check if a plugin exists
8
- * A plugin must have: dist/generator.js and plugin.json
9
- */
10
- export declare function pluginExists(pluginName: string): Promise<boolean>;
11
- /**
12
- * List all available plugins
13
- */
14
- export declare function listAvailablePlugins(): Promise<string[]>;
8
+ export declare function listAvailablePlugins(workspaceRoot: string): Promise<string[]>;
15
9
  //# sourceMappingURL=plugin-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-registry.d.ts","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-registry.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOvE;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAuB9D"}
1
+ {"version":3,"file":"plugin-registry.d.ts","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-registry.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnF"}
@@ -1,43 +1,26 @@
1
1
  import * as path from 'path';
2
- import { fileURLToPath } from 'url';
3
2
  import fs from 'fs-extra';
4
3
  /**
5
- * Get the path to a plugin directory
6
- * Plugins are bundled with the CLI at dist/plugins/
4
+ * List all available plugins in the workspace
5
+ * Scans the workspace plugins/ directory for valid plugins
6
+ *
7
+ * @param workspaceRoot - The root directory of the Launch77 workspace
8
+ * @returns Array of plugin names found in the workspace
7
9
  */
8
- export function getPluginPath(pluginName) {
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
- // From dist/modules/plugin/lib/ to dist/plugins/
12
- const baseDir = path.join(__dirname, '../../../plugins');
13
- return path.join(baseDir, pluginName);
14
- }
15
- /**
16
- * Check if a plugin exists
17
- * A plugin must have: dist/generator.js and plugin.json
18
- */
19
- export async function pluginExists(pluginName) {
20
- const pluginPath = getPluginPath(pluginName);
21
- const hasGenerator = await fs.pathExists(path.join(pluginPath, 'dist/generator.js'));
22
- const hasPluginJson = await fs.pathExists(path.join(pluginPath, 'plugin.json'));
23
- return hasGenerator && hasPluginJson;
24
- }
25
- /**
26
- * List all available plugins
27
- */
28
- export async function listAvailablePlugins() {
29
- const __filename = fileURLToPath(import.meta.url);
30
- const __dirname = path.dirname(__filename);
31
- const baseDir = path.join(__dirname, '../../../plugins');
32
- if (!(await fs.pathExists(baseDir))) {
10
+ export async function listAvailablePlugins(workspaceRoot) {
11
+ const pluginsDir = path.join(workspaceRoot, 'plugins');
12
+ if (!(await fs.pathExists(pluginsDir))) {
33
13
  return [];
34
14
  }
35
- const items = await fs.readdir(baseDir, { withFileTypes: true });
15
+ const items = await fs.readdir(pluginsDir, { withFileTypes: true });
36
16
  const plugins = [];
37
17
  for (const item of items) {
38
18
  if (item.isDirectory()) {
39
19
  // Verify it's a valid plugin
40
- if (await pluginExists(item.name)) {
20
+ const pluginPath = path.join(pluginsDir, item.name);
21
+ const hasPluginJson = await fs.pathExists(path.join(pluginPath, 'plugin.json'));
22
+ const hasGenerator = await fs.pathExists(path.join(pluginPath, 'dist/generator.js'));
23
+ if (hasPluginJson && hasGenerator) {
41
24
  plugins.push(item.name);
42
25
  }
43
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-registry.js","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,OAAO,EAAE,MAAM,UAAU,CAAA;AAEzB;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAE1C,iDAAiD;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAA;IAExD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IAE5C,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC,CAAA;IACpF,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAA;IAE/E,OAAO,YAAY,IAAI,aAAa,CAAA;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAE1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAA;IAExD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAChE,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,6BAA6B;YAC7B,IAAI,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"plugin-registry.js","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,OAAO,EAAE,MAAM,UAAU,CAAA;AAEzB;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,aAAqB;IAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;IAEtD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IACnE,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,6BAA6B;YAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;YACnD,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAA;YAC/E,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC,CAAA;YAEpF,IAAI,aAAa,IAAI,YAAY,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { ValidationResult } from '@launch77/plugin-runtime';
2
+ /**
3
+ * Plugin resolution result
4
+ */
5
+ export interface PluginResolution {
6
+ /** The source of the plugin */
7
+ source: 'local' | 'npm';
8
+ /** The resolved name/package to use */
9
+ resolvedName: string;
10
+ /** The local path if source is 'local' */
11
+ localPath?: string;
12
+ /** The npm package name if source is 'npm' */
13
+ npmPackage?: string;
14
+ }
15
+ /**
16
+ * Validate plugin input name
17
+ *
18
+ * Accepts:
19
+ * - Unscoped names (e.g., "release", "my-plugin")
20
+ * - Scoped npm packages (e.g., "@ibm/plugin-name")
21
+ *
22
+ * Rejects:
23
+ * - Invalid formats
24
+ * - Empty strings
25
+ * - Names with invalid characters
26
+ *
27
+ * @param name - The plugin name to validate
28
+ * @returns ValidationResult with isValid and optional error message
29
+ *
30
+ * @example
31
+ * validatePluginInput('release') // { isValid: true }
32
+ * validatePluginInput('@ibm/analytics') // { isValid: true }
33
+ * validatePluginInput('@invalid') // { isValid: false, error: '...' }
34
+ */
35
+ export declare function validatePluginInput(name: string): ValidationResult;
36
+ /**
37
+ * Convert an unscoped plugin name to an npm package name
38
+ *
39
+ * Rules:
40
+ * - Unscoped names: prefix with @launch77-shared/plugin-
41
+ * - Scoped names: use as-is
42
+ *
43
+ * @param name - The plugin name (must be validated first)
44
+ * @returns The npm package name
45
+ *
46
+ * @example
47
+ * toNpmPackageName('release') // '@launch77-shared/plugin-release'
48
+ * toNpmPackageName('@ibm/analytics') // '@ibm/analytics'
49
+ */
50
+ export declare function toNpmPackageName(name: string): string;
51
+ /**
52
+ * Resolve plugin location from name
53
+ *
54
+ * Resolution order:
55
+ * 1. Check local workspace plugins directory
56
+ * 2. Resolve to npm package name
57
+ *
58
+ * @param name - The plugin name to resolve
59
+ * @param workspaceRoot - The workspace root directory
60
+ * @returns PluginResolution with source and resolved location
61
+ *
62
+ * @example
63
+ * // Local plugin found
64
+ * await resolvePluginLocation('my-plugin', '/workspace')
65
+ * // { source: 'local', resolvedName: 'my-plugin', localPath: '/workspace/plugins/my-plugin' }
66
+ *
67
+ * // Not found locally, resolve to npm
68
+ * await resolvePluginLocation('release', '/workspace')
69
+ * // { source: 'npm', resolvedName: 'release', npmPackage: '@launch77-shared/plugin-release' }
70
+ *
71
+ * // Scoped package always resolves to npm
72
+ * await resolvePluginLocation('@ibm/analytics', '/workspace')
73
+ * // { source: 'npm', resolvedName: '@ibm/analytics', npmPackage: '@ibm/analytics' }
74
+ */
75
+ export declare function resolvePluginLocation(name: string, workspaceRoot: string): Promise<PluginResolution>;
76
+ //# sourceMappingURL=plugin-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-resolver.d.ts","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,MAAM,EAAE,OAAO,GAAG,KAAK,CAAA;IACvB,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CA2BlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuC1G"}
@@ -0,0 +1,128 @@
1
+ import * as path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { parsePluginName, isValidNpmPackageName } from '@launch77/plugin-runtime';
4
+ /**
5
+ * Validate plugin input name
6
+ *
7
+ * Accepts:
8
+ * - Unscoped names (e.g., "release", "my-plugin")
9
+ * - Scoped npm packages (e.g., "@ibm/plugin-name")
10
+ *
11
+ * Rejects:
12
+ * - Invalid formats
13
+ * - Empty strings
14
+ * - Names with invalid characters
15
+ *
16
+ * @param name - The plugin name to validate
17
+ * @returns ValidationResult with isValid and optional error message
18
+ *
19
+ * @example
20
+ * validatePluginInput('release') // { isValid: true }
21
+ * validatePluginInput('@ibm/analytics') // { isValid: true }
22
+ * validatePluginInput('@invalid') // { isValid: false, error: '...' }
23
+ */
24
+ export function validatePluginInput(name) {
25
+ if (!name || name.trim().length === 0) {
26
+ return {
27
+ isValid: false,
28
+ error: 'Plugin name cannot be empty',
29
+ };
30
+ }
31
+ const trimmedName = name.trim();
32
+ // Parse the name to determine type and validate
33
+ const parsed = parsePluginName(trimmedName);
34
+ if (!parsed.isValid) {
35
+ return {
36
+ isValid: false,
37
+ error: parsed.error,
38
+ };
39
+ }
40
+ // If it's scoped, it must be a valid npm package name
41
+ if (parsed.type === 'scoped') {
42
+ return isValidNpmPackageName(trimmedName);
43
+ }
44
+ // Unscoped names are valid for both local and npm
45
+ return { isValid: true };
46
+ }
47
+ /**
48
+ * Convert an unscoped plugin name to an npm package name
49
+ *
50
+ * Rules:
51
+ * - Unscoped names: prefix with @launch77-shared/plugin-
52
+ * - Scoped names: use as-is
53
+ *
54
+ * @param name - The plugin name (must be validated first)
55
+ * @returns The npm package name
56
+ *
57
+ * @example
58
+ * toNpmPackageName('release') // '@launch77-shared/plugin-release'
59
+ * toNpmPackageName('@ibm/analytics') // '@ibm/analytics'
60
+ */
61
+ export function toNpmPackageName(name) {
62
+ const trimmedName = name.trim();
63
+ // If already scoped, use as-is
64
+ if (trimmedName.startsWith('@')) {
65
+ return trimmedName;
66
+ }
67
+ // Otherwise, convert to @launch77-shared/plugin-<name>
68
+ return `@launch77-shared/plugin-${trimmedName}`;
69
+ }
70
+ /**
71
+ * Resolve plugin location from name
72
+ *
73
+ * Resolution order:
74
+ * 1. Check local workspace plugins directory
75
+ * 2. Resolve to npm package name
76
+ *
77
+ * @param name - The plugin name to resolve
78
+ * @param workspaceRoot - The workspace root directory
79
+ * @returns PluginResolution with source and resolved location
80
+ *
81
+ * @example
82
+ * // Local plugin found
83
+ * await resolvePluginLocation('my-plugin', '/workspace')
84
+ * // { source: 'local', resolvedName: 'my-plugin', localPath: '/workspace/plugins/my-plugin' }
85
+ *
86
+ * // Not found locally, resolve to npm
87
+ * await resolvePluginLocation('release', '/workspace')
88
+ * // { source: 'npm', resolvedName: 'release', npmPackage: '@launch77-shared/plugin-release' }
89
+ *
90
+ * // Scoped package always resolves to npm
91
+ * await resolvePluginLocation('@ibm/analytics', '/workspace')
92
+ * // { source: 'npm', resolvedName: '@ibm/analytics', npmPackage: '@ibm/analytics' }
93
+ */
94
+ export async function resolvePluginLocation(name, workspaceRoot) {
95
+ const trimmedName = name.trim();
96
+ const parsed = parsePluginName(trimmedName);
97
+ // If scoped, always use npm (local plugins are never scoped)
98
+ if (parsed.type === 'scoped') {
99
+ return {
100
+ source: 'npm',
101
+ resolvedName: trimmedName,
102
+ npmPackage: trimmedName,
103
+ };
104
+ }
105
+ // Check local workspace plugins directory
106
+ const localPath = path.join(workspaceRoot, 'plugins', trimmedName);
107
+ const localExists = await fs.pathExists(localPath);
108
+ if (localExists) {
109
+ // Verify it's a valid plugin (has plugin.json and dist/generator.js)
110
+ const hasPluginJson = await fs.pathExists(path.join(localPath, 'plugin.json'));
111
+ const hasGenerator = await fs.pathExists(path.join(localPath, 'dist/generator.js'));
112
+ if (hasPluginJson && hasGenerator) {
113
+ return {
114
+ source: 'local',
115
+ resolvedName: trimmedName,
116
+ localPath,
117
+ };
118
+ }
119
+ }
120
+ // Not found locally, resolve to npm package
121
+ const npmPackage = toNpmPackageName(trimmedName);
122
+ return {
123
+ source: 'npm',
124
+ resolvedName: trimmedName,
125
+ npmPackage,
126
+ };
127
+ }
128
+ //# sourceMappingURL=plugin-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-resolver.js","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,OAAO,EAAE,MAAM,UAAU,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAkBjF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,6BAA6B;SACrC,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAE/B,gDAAgD;IAChD,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,CAAA;IAE3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAA;IACH,CAAC;IAED,sDAAsD;IACtD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,qBAAqB,CAAC,WAAW,CAAC,CAAA;IAC3C,CAAC;IAED,kDAAkD;IAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAE/B,+BAA+B;IAC/B,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,uDAAuD;IACvD,OAAO,2BAA2B,WAAW,EAAE,CAAA;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY,EAAE,aAAqB;IAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,CAAA;IAE3C,6DAA6D;IAC7D,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO;YACL,MAAM,EAAE,KAAK;YACb,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,WAAW;SACxB,CAAA;IACH,CAAC;IAED,0CAA0C;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,WAAW,CAAC,CAAA;IAClE,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;IAElD,IAAI,WAAW,EAAE,CAAC;QAChB,qEAAqE;QACrE,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;QAC9E,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAA;QAEnF,IAAI,aAAa,IAAI,YAAY,EAAE,CAAC;YAClC,OAAO;gBACL,MAAM,EAAE,OAAO;gBACf,YAAY,EAAE,WAAW;gBACzB,SAAS;aACV,CAAA;QACH,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAA;IAEhD,OAAO;QACL,MAAM,EAAE,KAAK;QACb,YAAY,EAAE,WAAW;QACzB,UAAU;KACX,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin-resolver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-resolver.test.d.ts","sourceRoot":"","sources":["../../../../src/modules/plugin/lib/plugin-resolver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,175 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import fs from 'fs-extra';
5
+ import { validatePluginInput, toNpmPackageName, resolvePluginLocation } from './plugin-resolver.js';
6
+ describe('Plugin Resolver', () => {
7
+ describe('validatePluginInput', () => {
8
+ it('should accept valid unscoped plugin names', () => {
9
+ expect(validatePluginInput('release')).toEqual({ isValid: true });
10
+ expect(validatePluginInput('my-plugin')).toEqual({ isValid: true });
11
+ expect(validatePluginInput('analytics-v2')).toEqual({ isValid: true });
12
+ });
13
+ it('should accept valid scoped npm packages', () => {
14
+ expect(validatePluginInput('@ibm/plugin-name')).toEqual({ isValid: true });
15
+ expect(validatePluginInput('@launch77-shared/plugin-release')).toEqual({ isValid: true });
16
+ expect(validatePluginInput('@org/analytics')).toEqual({ isValid: true });
17
+ });
18
+ it('should reject empty names', () => {
19
+ const result = validatePluginInput('');
20
+ expect(result.isValid).toBe(false);
21
+ expect(result.error).toBeDefined();
22
+ });
23
+ it('should reject whitespace-only names', () => {
24
+ const result = validatePluginInput(' ');
25
+ expect(result.isValid).toBe(false);
26
+ expect(result.error).toBeDefined();
27
+ });
28
+ it('should reject invalid scoped packages', () => {
29
+ const result1 = validatePluginInput('@invalid');
30
+ expect(result1.isValid).toBe(false);
31
+ expect(result1.error).toBeDefined();
32
+ const result2 = validatePluginInput('@/package');
33
+ expect(result2.isValid).toBe(false);
34
+ expect(result2.error).toBeDefined();
35
+ const result3 = validatePluginInput('@org/');
36
+ expect(result3.isValid).toBe(false);
37
+ expect(result3.error).toBeDefined();
38
+ });
39
+ it('should reject names with uppercase letters', () => {
40
+ const result = validatePluginInput('MyPlugin');
41
+ expect(result.isValid).toBe(false);
42
+ expect(result.error).toContain('lowercase');
43
+ });
44
+ it('should reject names starting with numbers', () => {
45
+ const result = validatePluginInput('123plugin');
46
+ expect(result.isValid).toBe(false);
47
+ expect(result.error).toBeDefined();
48
+ });
49
+ it('should reject names with special characters', () => {
50
+ const result1 = validatePluginInput('plugin_name');
51
+ expect(result1.isValid).toBe(false);
52
+ const result2 = validatePluginInput('plugin.name');
53
+ expect(result2.isValid).toBe(false);
54
+ const result3 = validatePluginInput('plugin name');
55
+ expect(result3.isValid).toBe(false);
56
+ });
57
+ it('should trim whitespace before validation', () => {
58
+ expect(validatePluginInput(' release ')).toEqual({ isValid: true });
59
+ expect(validatePluginInput(' @ibm/analytics ')).toEqual({ isValid: true });
60
+ });
61
+ });
62
+ describe('toNpmPackageName', () => {
63
+ it('should prefix unscoped names with @launch77-shared/plugin-', () => {
64
+ expect(toNpmPackageName('release')).toBe('@launch77-shared/plugin-release');
65
+ expect(toNpmPackageName('my-plugin')).toBe('@launch77-shared/plugin-my-plugin');
66
+ expect(toNpmPackageName('analytics-v2')).toBe('@launch77-shared/plugin-analytics-v2');
67
+ });
68
+ it('should return scoped packages as-is', () => {
69
+ expect(toNpmPackageName('@ibm/analytics')).toBe('@ibm/analytics');
70
+ expect(toNpmPackageName('@launch77-shared/plugin-release')).toBe('@launch77-shared/plugin-release');
71
+ expect(toNpmPackageName('@org/package')).toBe('@org/package');
72
+ });
73
+ it('should trim whitespace', () => {
74
+ expect(toNpmPackageName(' release ')).toBe('@launch77-shared/plugin-release');
75
+ expect(toNpmPackageName(' @ibm/analytics ')).toBe('@ibm/analytics');
76
+ });
77
+ });
78
+ describe('resolvePluginLocation', () => {
79
+ let tempDir;
80
+ beforeEach(async () => {
81
+ // Create a temporary workspace directory
82
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'plugin-resolver-test-'));
83
+ await fs.ensureDir(path.join(tempDir, 'plugins'));
84
+ });
85
+ afterEach(async () => {
86
+ // Clean up
87
+ await fs.remove(tempDir);
88
+ });
89
+ it('should resolve local plugins first', async () => {
90
+ // Create a valid local plugin
91
+ const pluginPath = path.join(tempDir, 'plugins', 'my-plugin');
92
+ await fs.ensureDir(pluginPath);
93
+ await fs.ensureDir(path.join(pluginPath, 'dist'));
94
+ await fs.writeFile(path.join(pluginPath, 'plugin.json'), JSON.stringify({ name: 'my-plugin', version: '1.0.0' }));
95
+ await fs.writeFile(path.join(pluginPath, 'dist/generator.js'), 'console.log("test")');
96
+ const result = await resolvePluginLocation('my-plugin', tempDir);
97
+ expect(result).toEqual({
98
+ source: 'local',
99
+ resolvedName: 'my-plugin',
100
+ localPath: pluginPath,
101
+ });
102
+ });
103
+ it('should resolve to npm if local plugin does not exist', async () => {
104
+ const result = await resolvePluginLocation('release', tempDir);
105
+ expect(result).toEqual({
106
+ source: 'npm',
107
+ resolvedName: 'release',
108
+ npmPackage: '@launch77-shared/plugin-release',
109
+ });
110
+ });
111
+ it('should resolve to npm if local plugin is incomplete (missing plugin.json)', async () => {
112
+ // Create incomplete local plugin (no plugin.json)
113
+ const pluginPath = path.join(tempDir, 'plugins', 'incomplete');
114
+ await fs.ensureDir(pluginPath);
115
+ await fs.ensureDir(path.join(pluginPath, 'dist'));
116
+ await fs.writeFile(path.join(pluginPath, 'dist/generator.js'), 'console.log("test")');
117
+ const result = await resolvePluginLocation('incomplete', tempDir);
118
+ expect(result).toEqual({
119
+ source: 'npm',
120
+ resolvedName: 'incomplete',
121
+ npmPackage: '@launch77-shared/plugin-incomplete',
122
+ });
123
+ });
124
+ it('should resolve to npm if local plugin is incomplete (missing generator.js)', async () => {
125
+ // Create incomplete local plugin (no generator.js)
126
+ const pluginPath = path.join(tempDir, 'plugins', 'incomplete');
127
+ await fs.ensureDir(pluginPath);
128
+ await fs.writeFile(path.join(pluginPath, 'plugin.json'), JSON.stringify({ name: 'incomplete', version: '1.0.0' }));
129
+ const result = await resolvePluginLocation('incomplete', tempDir);
130
+ expect(result).toEqual({
131
+ source: 'npm',
132
+ resolvedName: 'incomplete',
133
+ npmPackage: '@launch77-shared/plugin-incomplete',
134
+ });
135
+ });
136
+ it('should always resolve scoped packages to npm', async () => {
137
+ // Even if a directory exists locally, scoped names go to npm
138
+ const pluginPath = path.join(tempDir, 'plugins', '@ibm');
139
+ await fs.ensureDir(pluginPath);
140
+ const result = await resolvePluginLocation('@ibm/analytics', tempDir);
141
+ expect(result).toEqual({
142
+ source: 'npm',
143
+ resolvedName: '@ibm/analytics',
144
+ npmPackage: '@ibm/analytics',
145
+ });
146
+ });
147
+ it('should resolve scoped @launch77-shared packages to npm', async () => {
148
+ const result = await resolvePluginLocation('@launch77-shared/plugin-release', tempDir);
149
+ expect(result).toEqual({
150
+ source: 'npm',
151
+ resolvedName: '@launch77-shared/plugin-release',
152
+ npmPackage: '@launch77-shared/plugin-release',
153
+ });
154
+ });
155
+ it('should handle plugins directory not existing', async () => {
156
+ // Remove plugins directory
157
+ await fs.remove(path.join(tempDir, 'plugins'));
158
+ const result = await resolvePluginLocation('release', tempDir);
159
+ expect(result).toEqual({
160
+ source: 'npm',
161
+ resolvedName: 'release',
162
+ npmPackage: '@launch77-shared/plugin-release',
163
+ });
164
+ });
165
+ it('should trim whitespace from plugin names', async () => {
166
+ const result = await resolvePluginLocation(' release ', tempDir);
167
+ expect(result).toEqual({
168
+ source: 'npm',
169
+ resolvedName: 'release',
170
+ npmPackage: '@launch77-shared/plugin-release',
171
+ });
172
+ });
173
+ });
174
+ });
175
+ //# sourceMappingURL=plugin-resolver.test.js.map