@launch77/cli 1.7.2 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/infrastructure/package-resolver.test.js +9 -7
  2. package/dist/infrastructure/package-resolver.test.js.map +1 -1
  3. package/dist/modules/app/commands/create-app.d.ts.map +1 -1
  4. package/dist/modules/app/commands/create-app.js +1 -1
  5. package/dist/modules/app/commands/create-app.js.map +1 -1
  6. package/dist/modules/app/commands/delete-app.d.ts.map +1 -1
  7. package/dist/modules/app/commands/delete-app.js +1 -1
  8. package/dist/modules/app/commands/delete-app.js.map +1 -1
  9. package/dist/modules/app/lib/app-template-resolver.d.ts +1 -1
  10. package/dist/modules/app/lib/app-template-resolver.d.ts.map +1 -1
  11. package/dist/modules/app/lib/app-template-resolver.js +1 -1
  12. package/dist/modules/app/lib/app-template-resolver.js.map +1 -1
  13. package/dist/modules/app/services/app-svc.d.ts +1 -0
  14. package/dist/modules/app/services/app-svc.d.ts.map +1 -1
  15. package/dist/modules/app/services/app-svc.js +4 -4
  16. package/dist/modules/app/services/app-svc.js.map +1 -1
  17. package/dist/modules/app/services/manifest-svc.d.ts +1 -1
  18. package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
  19. package/dist/modules/catalog/commands/scan.d.ts.map +1 -1
  20. package/dist/modules/catalog/commands/scan.js +1 -1
  21. package/dist/modules/catalog/commands/scan.js.map +1 -1
  22. package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -1
  23. package/dist/modules/deploy/commands/deploy-init-action.js +1 -1
  24. package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -1
  25. package/dist/modules/deploy/commands/deploy-logs-action.js +1 -1
  26. package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -1
  27. package/dist/modules/deploy/commands/deploy-status-action.js +1 -1
  28. package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -1
  29. package/dist/modules/git/commands/git-connect.d.ts.map +1 -1
  30. package/dist/modules/git/commands/git-connect.js +3 -3
  31. package/dist/modules/git/commands/git-connect.js.map +1 -1
  32. package/dist/modules/library/commands/create-library.d.ts.map +1 -1
  33. package/dist/modules/library/commands/create-library.js +1 -1
  34. package/dist/modules/library/commands/create-library.js.map +1 -1
  35. package/dist/modules/library/commands/delete-library.d.ts.map +1 -1
  36. package/dist/modules/library/commands/delete-library.js +1 -1
  37. package/dist/modules/library/commands/delete-library.js.map +1 -1
  38. package/dist/modules/library/services/library-create-svc.js +1 -1
  39. package/dist/modules/library/services/library-create-svc.js.map +1 -1
  40. package/dist/modules/library/services/library-svc.d.ts +1 -0
  41. package/dist/modules/library/services/library-svc.d.ts.map +1 -1
  42. package/dist/modules/library/services/library-svc.js +3 -2
  43. package/dist/modules/library/services/library-svc.js.map +1 -1
  44. package/dist/modules/plugin/commands/delete-plugin.d.ts.map +1 -1
  45. package/dist/modules/plugin/commands/delete-plugin.js +1 -2
  46. package/dist/modules/plugin/commands/delete-plugin.js.map +1 -1
  47. package/dist/modules/plugin/commands/plugin-create.d.ts.map +1 -1
  48. package/dist/modules/plugin/commands/plugin-create.js +1 -1
  49. package/dist/modules/plugin/commands/plugin-create.js.map +1 -1
  50. package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -1
  51. package/dist/modules/plugin/commands/plugin-install.js +1 -2
  52. package/dist/modules/plugin/commands/plugin-install.js.map +1 -1
  53. package/dist/modules/plugin/index.d.ts +3 -3
  54. package/dist/modules/plugin/index.d.ts.map +1 -1
  55. package/dist/modules/plugin/index.js +2 -3
  56. package/dist/modules/plugin/index.js.map +1 -1
  57. package/dist/modules/plugin/lib/plugin-resolver.test.js +10 -6
  58. package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -1
  59. package/dist/modules/plugin/services/plugin-create-service.d.ts +1 -0
  60. package/dist/modules/plugin/services/plugin-create-service.d.ts.map +1 -1
  61. package/dist/modules/plugin/services/plugin-create-service.js +4 -3
  62. package/dist/modules/plugin/services/plugin-create-service.js.map +1 -1
  63. package/dist/modules/plugin/services/plugin-svc.test.js +34 -30
  64. package/dist/modules/plugin/services/plugin-svc.test.js.map +1 -1
  65. package/dist/modules/release/commands/release-init.d.ts.map +1 -1
  66. package/dist/modules/release/commands/release-init.js +2 -2
  67. package/dist/modules/release/commands/release-init.js.map +1 -1
  68. package/dist/modules/release/services/release-service.d.ts.map +1 -1
  69. package/dist/modules/release/services/release-service.js +3 -3
  70. package/dist/modules/release/services/release-service.js.map +1 -1
  71. package/dist/modules/workspace/services/workspace-service.d.ts +1 -0
  72. package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -1
  73. package/dist/modules/workspace/services/workspace-service.js +3 -2
  74. package/dist/modules/workspace/services/workspace-service.js.map +1 -1
  75. package/dist/templates/plugin/README.md.hbs +10 -0
  76. package/dist/templates/plugin/package.json.hbs +1 -1
  77. package/dist/templates/plugin/scripts/validate-plugin-json.js +13 -2
  78. package/dist/templates/workspace/.eslintignore +2 -1
  79. package/package.json +3 -3
  80. package/src/infrastructure/package-resolver.test.ts +9 -8
  81. package/src/modules/app/commands/create-app.ts +1 -1
  82. package/src/modules/app/commands/delete-app.ts +1 -1
  83. package/src/modules/app/lib/app-template-resolver.ts +1 -2
  84. package/src/modules/app/services/app-svc.ts +6 -7
  85. package/src/modules/app/services/manifest-svc.ts +1 -1
  86. package/src/modules/catalog/commands/scan.ts +1 -2
  87. package/src/modules/catalog/services/catalog-svc.ts +1 -1
  88. package/src/modules/deploy/commands/deploy-init-action.ts +1 -1
  89. package/src/modules/deploy/commands/deploy-logs-action.ts +1 -1
  90. package/src/modules/deploy/commands/deploy-status-action.ts +1 -1
  91. package/src/modules/git/commands/git-connect.ts +3 -3
  92. package/src/modules/library/commands/create-library.ts +1 -1
  93. package/src/modules/library/commands/delete-library.ts +1 -1
  94. package/src/modules/library/services/library-create-svc.ts +1 -1
  95. package/src/modules/library/services/library-svc.ts +3 -2
  96. package/src/modules/plugin/commands/delete-plugin.ts +1 -3
  97. package/src/modules/plugin/commands/plugin-create.ts +1 -1
  98. package/src/modules/plugin/commands/plugin-install.ts +1 -3
  99. package/src/modules/plugin/index.ts +4 -6
  100. package/src/modules/plugin/lib/plugin-resolver.test.ts +10 -7
  101. package/src/modules/plugin/services/plugin-create-service.ts +4 -3
  102. package/src/modules/plugin/services/plugin-svc.test.ts +52 -32
  103. package/src/modules/release/commands/release-init.ts +2 -2
  104. package/src/modules/release/services/release-service.ts +4 -3
  105. package/src/modules/workspace/services/workspace-service.ts +3 -2
  106. package/templates/plugin/README.md.hbs +10 -0
  107. package/templates/plugin/package.json.hbs +1 -1
  108. package/templates/plugin/scripts/validate-plugin-json.js +13 -2
  109. package/templates/workspace/.eslintignore +2 -1
  110. package/dist/infrastructure/npm-package.d.ts +0 -42
  111. package/dist/infrastructure/npm-package.d.ts.map +0 -1
  112. package/dist/infrastructure/npm-package.js +0 -46
  113. package/dist/infrastructure/npm-package.js.map +0 -1
  114. package/dist/infrastructure/npm.d.ts +0 -9
  115. package/dist/infrastructure/npm.d.ts.map +0 -1
  116. package/dist/infrastructure/npm.js +0 -17
  117. package/dist/infrastructure/npm.js.map +0 -1
  118. package/dist/infrastructure/package-resolver.d.ts +0 -117
  119. package/dist/infrastructure/package-resolver.d.ts.map +0 -1
  120. package/dist/infrastructure/package-resolver.js +0 -170
  121. package/dist/infrastructure/package-resolver.js.map +0 -1
  122. package/dist/modules/plugin/errors/plugin-errors.d.ts +0 -51
  123. package/dist/modules/plugin/errors/plugin-errors.d.ts.map +0 -1
  124. package/dist/modules/plugin/errors/plugin-errors.js +0 -130
  125. package/dist/modules/plugin/errors/plugin-errors.js.map +0 -1
  126. package/dist/modules/plugin/lib/plugin-resolver.d.ts +0 -14
  127. package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +0 -1
  128. package/dist/modules/plugin/lib/plugin-resolver.js +0 -36
  129. package/dist/modules/plugin/lib/plugin-resolver.js.map +0 -1
  130. package/dist/modules/plugin/services/plugin-svc.d.ts +0 -42
  131. package/dist/modules/plugin/services/plugin-svc.d.ts.map +0 -1
  132. package/dist/modules/plugin/services/plugin-svc.js +0 -257
  133. package/dist/modules/plugin/services/plugin-svc.js.map +0 -1
  134. package/dist/modules/plugin/types/plugin-types.d.ts +0 -25
  135. package/dist/modules/plugin/types/plugin-types.d.ts.map +0 -1
  136. package/dist/modules/plugin/types/plugin-types.js +0 -2
  137. package/dist/modules/plugin/types/plugin-types.js.map +0 -1
  138. package/src/infrastructure/npm-package.ts +0 -73
  139. package/src/infrastructure/npm.ts +0 -18
  140. package/src/infrastructure/package-resolver.ts +0 -223
  141. package/src/modules/plugin/errors/plugin-errors.ts +0 -145
  142. package/src/modules/plugin/lib/plugin-resolver.ts +0 -41
  143. package/src/modules/plugin/services/plugin-svc.ts +0 -303
  144. package/src/modules/plugin/types/plugin-types.ts +0 -29
@@ -1,145 +0,0 @@
1
- export class PluginNotFoundError extends Error {
2
- constructor(pluginName: string) {
3
- super(`Plugin '${pluginName}' not found.
4
-
5
- Run 'launch77 plugin:list' to see available plugins.`)
6
- this.name = 'PluginNotFoundError'
7
- }
8
- }
9
-
10
- export class InvalidPluginContextError extends Error {
11
- constructor(message: string) {
12
- super(message)
13
- this.name = 'InvalidPluginContextError'
14
- }
15
- }
16
-
17
- /**
18
- * Factory function to create standardized InvalidPluginContextError
19
- * for when plugin:install is run outside of a package directory
20
- */
21
- export function createInvalidContextError(currentLocation: string): InvalidPluginContextError {
22
- return new InvalidPluginContextError(
23
- `plugin:install must be run from within a package directory.
24
-
25
- Current location: ${currentLocation}
26
- Expected: apps/<name>/, libraries/<name>/, plugins/<name>/, or app-templates/<name>/
27
-
28
- Navigate to a package directory:
29
- cd apps/<app-name>/
30
- cd libraries/<lib-name>/
31
- cd plugins/<plugin-name>/
32
- cd app-templates/<template-name>/`
33
- )
34
- }
35
-
36
- /**
37
- * Error when plugin.json is missing the required 'targets' field
38
- */
39
- export class MissingPluginTargetsError extends Error {
40
- constructor(pluginName: string) {
41
- super(`Plugin '${pluginName}' is missing the required 'targets' field in plugin.json.
42
-
43
- The plugin.json file must include a 'targets' array specifying which package types
44
- the plugin can be installed into.
45
-
46
- Example plugin.json:
47
- {
48
- "name": "${pluginName}",
49
- "version": "1.0.0",
50
- "targets": ["app", "library", "plugin", "app-template"],
51
- "pluginDependencies": {},
52
- "libraryDependencies": {}
53
- }`)
54
- this.name = 'MissingPluginTargetsError'
55
- }
56
- }
57
-
58
- /**
59
- * Factory function to create error when plugin targets don't match current location
60
- */
61
- export function createInvalidTargetError(pluginName: string, currentTarget: string, allowedTargets: string[]): InvalidPluginContextError {
62
- const targetLocations = allowedTargets.map((target) => {
63
- switch (target) {
64
- case 'app':
65
- return 'apps/<name>/'
66
- case 'library':
67
- return 'libraries/<name>/'
68
- case 'plugin':
69
- return 'plugins/<name>/'
70
- case 'app-template':
71
- return 'app-templates/<name>/'
72
- default:
73
- return target
74
- }
75
- })
76
-
77
- return new InvalidPluginContextError(
78
- `Plugin '${pluginName}' cannot be installed in a '${currentTarget}' package.
79
-
80
- This plugin can only be installed in: ${allowedTargets.join(', ')}
81
-
82
- Allowed locations:
83
- ${targetLocations.map((loc) => ` ${loc}`).join('\n')}`
84
- )
85
- }
86
-
87
- export class PluginInstallationError extends Error {
88
- constructor(
89
- message: string,
90
- public readonly cause?: Error
91
- ) {
92
- super(message)
93
- this.name = 'PluginInstallationError'
94
- }
95
- }
96
-
97
- /**
98
- * Error when plugin resolution fails
99
- */
100
- export class PluginResolutionError extends Error {
101
- constructor(pluginName: string, reason: string) {
102
- super(`Failed to resolve plugin '${pluginName}': ${reason}`)
103
- this.name = 'PluginResolutionError'
104
- }
105
- }
106
-
107
- /**
108
- * Error when npm package installation fails
109
- */
110
- export class NpmInstallationError extends Error {
111
- constructor(
112
- packageName: string,
113
- public readonly cause?: Error
114
- ) {
115
- super(`Failed to install npm package '${packageName}'.
116
-
117
- Please check:
118
- - Your internet connection
119
- - npm registry access (https://registry.npmjs.org)
120
- - Package exists: https://www.npmjs.com/package/${packageName}
121
-
122
- ${cause ? `\nOriginal error: ${cause.message}` : ''}`)
123
- this.name = 'NpmInstallationError'
124
- }
125
- }
126
-
127
- /**
128
- * Error when plugin directory is not found for deletion
129
- */
130
- export class PluginDirectoryNotFoundError extends Error {
131
- constructor(pluginName: string, expectedPath: string) {
132
- super(`Plugin '${pluginName}' does not exist.\n\n` + `Expected location: ${expectedPath}\n\n` + `Available plugins:\n` + ` cd plugins/\n` + ` ls`)
133
- this.name = 'PluginDirectoryNotFoundError'
134
- }
135
- }
136
-
137
- /**
138
- * Error when plugin name validation fails
139
- */
140
- export class InvalidPluginNameError extends Error {
141
- constructor(message: string) {
142
- super(message)
143
- this.name = 'InvalidPluginNameError'
144
- }
145
- }
@@ -1,41 +0,0 @@
1
- import * as path from 'path'
2
-
3
- import fs from 'fs-extra'
4
-
5
- import { PackageResolver } from '../../../infrastructure/package-resolver.js'
6
-
7
- /**
8
- * Plugin resolver implementation
9
- *
10
- * Resolves plugins from:
11
- * - Local workspace plugins/ directory
12
- * - npm packages with @launch77-shared/plugin- prefix
13
- */
14
- export class PluginResolver extends PackageResolver {
15
- protected getFolderName(): string {
16
- return 'plugins'
17
- }
18
-
19
- protected getPackagePrefix(): string {
20
- return '@launch77-shared/plugin-'
21
- }
22
-
23
- protected async verify(localPath: string): Promise<boolean> {
24
- // Verify it's a valid plugin (has plugin.json, dist/generator.js, and package.json with version)
25
- const hasPluginJson = await fs.pathExists(path.join(localPath, 'plugin.json'))
26
- const hasGenerator = await fs.pathExists(path.join(localPath, 'dist/generator.js'))
27
- const hasPackageJson = await fs.pathExists(path.join(localPath, 'package.json'))
28
-
29
- if (!hasPluginJson || !hasGenerator || !hasPackageJson) {
30
- return false
31
- }
32
-
33
- // Verify package.json has a version field
34
- try {
35
- const packageJson = await fs.readJson(path.join(localPath, 'package.json'))
36
- return !!packageJson.version
37
- } catch {
38
- return false
39
- }
40
- }
41
- }
@@ -1,303 +0,0 @@
1
- import * as path from 'path'
2
- import * as fs from 'fs/promises'
3
-
4
- import chalk from 'chalk'
5
- import { execa } from 'execa'
6
- import fsExtra from 'fs-extra'
7
- import { readPluginMetadata } from '@launch77/plugin-runtime'
8
-
9
- import { downloadNpmPackage } from '../../../infrastructure/npm-package.js'
10
- import * as npm from '../../../infrastructure/npm.js'
11
- import { validateWorkspaceContext } from '../../../utils/launch77-validation.js'
12
- import { validateAppName } from '../../../utils/validation.js'
13
- import { PluginInstallationError, InvalidPluginContextError, createInvalidContextError, MissingPluginTargetsError, createInvalidTargetError, NpmInstallationError, PluginResolutionError, PluginDirectoryNotFoundError, InvalidPluginNameError } from '../errors/plugin-errors.js'
14
- import { PluginResolver } from '../lib/plugin-resolver.js'
15
-
16
- import type { Launch77Context, Launch77LocationType, Launch77PackageManifest, InstalledPluginMetadata, PluginMetadata } from '@launch77/plugin-runtime'
17
- import type { InstallPluginRequest, InstallPluginResult, DeletePluginRequest, DeletePluginResult } from '../types/plugin-types.js'
18
-
19
- /**
20
- * Map location type to target string
21
- */
22
- function locationTypeToTarget(locationType: Launch77LocationType): string | null {
23
- switch (locationType) {
24
- case 'workspace-app':
25
- return 'app'
26
- case 'workspace-library':
27
- return 'library'
28
- case 'workspace-plugin':
29
- return 'plugin'
30
- case 'workspace-app-template':
31
- return 'app-template'
32
- default:
33
- return null
34
- }
35
- }
36
-
37
- export class PluginService {
38
- private pluginResolver: PluginResolver
39
-
40
- constructor() {
41
- this.pluginResolver = new PluginResolver()
42
- }
43
-
44
- /**
45
- * Validate that we're in a valid package directory and return the target type
46
- */
47
- private validateContext(context: Launch77Context): string {
48
- const currentTarget = locationTypeToTarget(context.locationType)
49
- if (!currentTarget) throw createInvalidContextError(context.locationType)
50
- if (!context.appName) throw new InvalidPluginContextError('Could not determine package name. This is a bug. Please report it.')
51
- return currentTarget
52
- }
53
-
54
- /**
55
- * Validate plugin name, resolve its location, and download if needed
56
- */
57
- private async validateAndResolvePlugin(pluginName: string, workspaceRoot: string, logger: (message: string) => void): Promise<{ pluginPath: string; source: 'local' | 'npm'; npmPackage?: string; version: string }> {
58
- logger(chalk.blue(`\n🔍 Resolving plugin "${pluginName}"...`))
59
- logger(` ├─ Validating plugin name...`)
60
-
61
- const validation = this.pluginResolver.validateInput(pluginName)
62
- if (!validation.isValid) {
63
- throw new PluginResolutionError(pluginName, validation.error || 'Invalid plugin name')
64
- }
65
- logger(` │ └─ ${chalk.green('✓')} Valid plugin name`)
66
-
67
- logger(` ├─ Checking local workspace: ${chalk.dim(`plugins/${pluginName}`)}`)
68
- const resolution = await this.pluginResolver.resolveLocation(pluginName, workspaceRoot)
69
-
70
- let pluginPath: string
71
- let version: string
72
-
73
- if (resolution.source === 'local') {
74
- logger(` │ └─ ${chalk.green('✓')} Found local plugin`)
75
- pluginPath = resolution.localPath!
76
- version = resolution.version! // Local plugins always have version after verification
77
- } else {
78
- logger(` │ └─ ${chalk.dim('Not found locally')}`)
79
- logger(` ├─ Resolving to npm package: ${chalk.cyan(resolution.npmPackage)}`)
80
-
81
- pluginPath = await this.downloadNpmPlugin(resolution.npmPackage!, workspaceRoot, logger)
82
-
83
- // Read version from downloaded package
84
- const packageJsonPath = path.join(pluginPath, 'package.json')
85
- const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')
86
- const packageJson = JSON.parse(packageJsonContent)
87
- version = packageJson.version
88
- }
89
-
90
- logger(` └─ ${chalk.green('✓')} Plugin resolved\n`)
91
-
92
- return {
93
- pluginPath,
94
- source: resolution.source,
95
- npmPackage: resolution.npmPackage,
96
- version,
97
- }
98
- }
99
-
100
- /**
101
- * Read plugin metadata and validate it supports the current target
102
- */
103
- private async validatePluginTargets(pluginPath: string, pluginName: string, currentTarget: string): Promise<PluginMetadata> {
104
- const metadata = await readPluginMetadata(pluginPath)
105
-
106
- if (!metadata.targets || metadata.targets.length === 0) {
107
- throw new MissingPluginTargetsError(pluginName)
108
- }
109
-
110
- if (!metadata.targets.includes(currentTarget)) {
111
- throw createInvalidTargetError(pluginName, currentTarget, metadata.targets)
112
- }
113
-
114
- return metadata
115
- }
116
-
117
- /**
118
- * Check if plugin is already installed and return early-exit result if so
119
- */
120
- private async checkExistingInstallation(pluginName: string, packagePath: string, logger: (message: string) => void): Promise<InstallPluginResult | null> {
121
- const existingInstallation = await this.isPluginInstalled(pluginName, packagePath)
122
- if (existingInstallation) {
123
- logger(chalk.yellow(`\nℹ️ Plugin '${pluginName}' is already installed in this package.\n`))
124
- logger(`Package: ${chalk.cyan(existingInstallation.package)} (${existingInstallation.source})`)
125
- logger(`Version: ${existingInstallation.version}`)
126
- logger(`Installed: ${existingInstallation.installedAt}\n`)
127
- logger(chalk.gray('To reinstall: Remove from package.json launch77.installedPlugins'))
128
- logger(chalk.gray('(plugin:remove command coming soon)\n'))
129
- return {
130
- pluginName,
131
- filesInstalled: false,
132
- packageJsonUpdated: false,
133
- dependenciesInstalled: false,
134
- }
135
- }
136
- return null
137
- }
138
-
139
- /**
140
- * Install a plugin to the current package
141
- */
142
- async installPlugin(request: InstallPluginRequest, context: Launch77Context, logger: (message: string) => void = console.log): Promise<InstallPluginResult> {
143
- const { pluginName } = request
144
-
145
- const currentTarget = this.validateContext(context)
146
- const { pluginPath, source, npmPackage, version } = await this.validateAndResolvePlugin(pluginName, context.workspaceRoot, logger)
147
- const metadata = await this.validatePluginTargets(pluginPath, pluginName, currentTarget)
148
-
149
- const packagePath = this.getPackagePath(context)
150
- const earlyExit = await this.checkExistingInstallation(pluginName, packagePath, logger)
151
- if (earlyExit) return earlyExit
152
-
153
- await this.runGenerator(pluginPath, packagePath, context)
154
-
155
- const packageName = source === 'npm' ? npmPackage! : pluginName
156
- await this.writePluginManifest(packagePath, {
157
- pluginName,
158
- packageName,
159
- version,
160
- source,
161
- })
162
-
163
- return {
164
- pluginName,
165
- filesInstalled: true,
166
- packageJsonUpdated: true,
167
- dependenciesInstalled: true,
168
- }
169
- }
170
-
171
- /**
172
- * Download and install an npm plugin package
173
- */
174
- private async downloadNpmPlugin(npmPackage: string, workspaceRoot: string, logger: (message: string) => void): Promise<string> {
175
- logger(` └─ Installing from npm: ${chalk.cyan(npmPackage)}...`)
176
-
177
- const result = await downloadNpmPackage({
178
- packageName: npmPackage,
179
- workspaceRoot,
180
- })
181
-
182
- return result.packagePath
183
- }
184
-
185
- private getPackagePath(context: Launch77Context): string {
186
- // Determine the base directory based on location type
187
- switch (context.locationType) {
188
- case 'workspace-app':
189
- return path.join(context.appsDir, context.appName!)
190
- case 'workspace-library':
191
- return path.join(context.workspaceRoot, 'libraries', context.appName!)
192
- case 'workspace-plugin':
193
- return path.join(context.workspaceRoot, 'plugins', context.appName!)
194
- case 'workspace-app-template':
195
- return path.join(context.workspaceRoot, 'app-templates', context.appName!)
196
- default:
197
- throw new InvalidPluginContextError(`Cannot install plugin from ${context.locationType}`)
198
- }
199
- }
200
-
201
- private async runGenerator(pluginPath: string, appPath: string, context: Launch77Context): Promise<void> {
202
- try {
203
- const generatorPath = path.join(pluginPath, 'dist/generator.js')
204
-
205
- const args = [generatorPath, `--appPath=${appPath}`, `--appName=${context.appName}`, `--workspaceName=${context.workspaceName}`, `--pluginPath=${pluginPath}`]
206
-
207
- await execa('node', args, {
208
- cwd: pluginPath,
209
- stdio: 'inherit',
210
- })
211
- } catch (error) {
212
- throw new PluginInstallationError(`Generator failed: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined)
213
- }
214
- }
215
-
216
- private async isPluginInstalled(pluginName: string, packagePath: string): Promise<InstalledPluginMetadata | null> {
217
- try {
218
- const packageJsonPath = path.join(packagePath, 'package.json')
219
- const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')
220
- const packageJson = JSON.parse(packageJsonContent)
221
-
222
- const manifest = packageJson.launch77 as Launch77PackageManifest | undefined
223
-
224
- if (manifest?.installedPlugins?.[pluginName]) {
225
- return manifest.installedPlugins[pluginName]
226
- }
227
-
228
- return null
229
- } catch (error) {
230
- // If package.json doesn't exist or can't be read, assume not installed
231
- return null
232
- }
233
- }
234
-
235
- /**
236
- * Write plugin installation metadata to the target package's package.json
237
- */
238
- private async writePluginManifest(packagePath: string, installationInfo: { pluginName: string; packageName: string; version: string; source: 'local' | 'npm' }): Promise<void> {
239
- try {
240
- const packageJsonPath = path.join(packagePath, 'package.json')
241
- const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')
242
- const packageJson = JSON.parse(packageJsonContent)
243
-
244
- if (!packageJson.launch77) {
245
- packageJson.launch77 = {}
246
- }
247
-
248
- if (!packageJson.launch77.installedPlugins) {
249
- packageJson.launch77.installedPlugins = {}
250
- }
251
-
252
- const manifest = packageJson.launch77 as Launch77PackageManifest
253
-
254
- manifest.installedPlugins![installationInfo.pluginName] = {
255
- package: installationInfo.packageName,
256
- version: installationInfo.version,
257
- installedAt: new Date().toISOString(),
258
- source: installationInfo.source,
259
- }
260
-
261
- await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8')
262
- } catch (error) {
263
- throw new PluginInstallationError(`Failed to write plugin manifest: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined)
264
- }
265
- }
266
-
267
- /**
268
- * Delete a plugin from the workspace
269
- */
270
- async deletePlugin(request: DeletePluginRequest, context: Launch77Context): Promise<DeletePluginResult> {
271
- const { pluginName } = request
272
-
273
- // 1. Validate plugin name (reusing app name validation since format is the same)
274
- const nameValidation = validateAppName(pluginName)
275
- if (!nameValidation.valid) {
276
- throw new InvalidPluginNameError(nameValidation.errorMessage || 'Invalid plugin name')
277
- }
278
-
279
- // 2. Validate workspace context
280
- const contextValidation = validateWorkspaceContext(context)
281
- if (!contextValidation.valid) {
282
- throw new Error(contextValidation.errorMessage || 'Invalid workspace context')
283
- }
284
-
285
- // 3. Determine plugin path
286
- const pluginPath = path.join(context.workspaceRoot, 'plugins', pluginName)
287
-
288
- // 4. Check if plugin exists
289
- if (!(await fsExtra.pathExists(pluginPath))) {
290
- throw new PluginDirectoryNotFoundError(pluginName, pluginPath)
291
- }
292
-
293
- // 5. Delete the plugin directory
294
- await fsExtra.remove(pluginPath)
295
-
296
- // 6. Update dependencies
297
- await npm.install(context.workspaceRoot)
298
-
299
- return {
300
- pluginName,
301
- }
302
- }
303
- }
@@ -1,29 +0,0 @@
1
- export interface InstallPluginRequest {
2
- pluginName: string
3
- }
4
-
5
- export interface InstallPluginResult {
6
- pluginName: string
7
- filesInstalled: boolean
8
- packageJsonUpdated: boolean
9
- dependenciesInstalled: boolean
10
- }
11
-
12
- export interface PluginMetadata {
13
- dependencies?: Record<string, string>
14
- devDependencies?: Record<string, string>
15
- scripts?: Record<string, string>
16
- }
17
-
18
- export interface HookResult {
19
- templateVariables?: Record<string, string>
20
- hookContext?: Record<string, string>
21
- }
22
-
23
- export interface DeletePluginRequest {
24
- pluginName: string
25
- }
26
-
27
- export interface DeletePluginResult {
28
- pluginName: string
29
- }