@launch77/cli 1.3.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.
- package/CHANGELOG.md +11 -0
- package/dist/cli.js +6 -3
- package/dist/cli.js.map +1 -1
- package/dist/infrastructure/template-generator.d.ts +1 -1
- package/dist/infrastructure/template-generator.d.ts.map +1 -1
- package/dist/infrastructure/template.d.ts +5 -0
- package/dist/infrastructure/template.d.ts.map +1 -1
- package/dist/infrastructure/template.js +11 -0
- package/dist/infrastructure/template.js.map +1 -1
- package/dist/modules/app/commands/create-app.js +1 -1
- package/dist/modules/app/commands/create-app.js.map +1 -1
- package/dist/modules/app/commands/delete-app.js +1 -1
- package/dist/modules/app/commands/delete-app.js.map +1 -1
- package/dist/modules/app/services/app-svc.d.ts +1 -1
- package/dist/modules/app/services/app-svc.d.ts.map +1 -1
- package/dist/modules/app/services/manifest-svc.d.ts +1 -1
- package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
- package/dist/modules/catalog/config/catalog-config.test.js +1 -1
- package/dist/modules/catalog/config/catalog-config.test.js.map +1 -1
- package/dist/modules/git/commands/git-connect.js +2 -2
- package/dist/modules/git/commands/git-connect.js.map +1 -1
- package/dist/modules/git/errors/git-errors.d.ts +3 -0
- package/dist/modules/git/errors/git-errors.d.ts.map +1 -1
- package/dist/modules/git/errors/git-errors.js +6 -0
- package/dist/modules/git/errors/git-errors.js.map +1 -1
- package/dist/modules/git/index.d.ts +3 -1
- package/dist/modules/git/index.d.ts.map +1 -1
- package/dist/modules/git/index.js +6 -1
- package/dist/modules/git/index.js.map +1 -1
- package/dist/modules/git/services/git-service.d.ts +5 -0
- package/dist/modules/git/services/git-service.d.ts.map +1 -1
- package/dist/modules/git/services/git-service.js +11 -1
- package/dist/modules/git/services/git-service.js.map +1 -1
- package/dist/modules/plugin/commands/plugin-create.d.ts +3 -0
- package/dist/modules/plugin/commands/plugin-create.d.ts.map +1 -0
- package/dist/modules/plugin/commands/plugin-create.js +59 -0
- package/dist/modules/plugin/commands/plugin-create.js.map +1 -0
- package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -1
- package/dist/modules/plugin/commands/plugin-install.js +9 -24
- package/dist/modules/plugin/commands/plugin-install.js.map +1 -1
- package/dist/modules/plugin/errors/plugin-errors.d.ts +24 -1
- package/dist/modules/plugin/errors/plugin-errors.d.ts.map +1 -1
- package/dist/modules/plugin/errors/plugin-errors.js +79 -6
- package/dist/modules/plugin/errors/plugin-errors.js.map +1 -1
- package/dist/modules/plugin/index.d.ts +4 -2
- package/dist/modules/plugin/index.d.ts.map +1 -1
- package/dist/modules/plugin/index.js +4 -2
- package/dist/modules/plugin/index.js.map +1 -1
- package/dist/modules/plugin/lib/plugin-registry.d.ts +6 -12
- package/dist/modules/plugin/lib/plugin-registry.d.ts.map +1 -1
- package/dist/modules/plugin/lib/plugin-registry.js +13 -30
- package/dist/modules/plugin/lib/plugin-registry.js.map +1 -1
- package/dist/modules/plugin/lib/plugin-resolver.d.ts +76 -0
- package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +1 -0
- package/dist/modules/plugin/lib/plugin-resolver.js +128 -0
- package/dist/modules/plugin/lib/plugin-resolver.js.map +1 -0
- package/dist/modules/plugin/lib/plugin-resolver.test.d.ts +2 -0
- package/dist/modules/plugin/lib/plugin-resolver.test.d.ts.map +1 -0
- package/dist/modules/plugin/lib/plugin-resolver.test.js +175 -0
- package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -0
- package/dist/modules/plugin/services/plugin-create-service.d.ts +16 -0
- package/dist/modules/plugin/services/plugin-create-service.d.ts.map +1 -0
- package/dist/modules/plugin/services/plugin-create-service.js +47 -0
- package/dist/modules/plugin/services/plugin-create-service.js.map +1 -0
- package/dist/modules/plugin/services/plugin-svc.d.ts +8 -3
- package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -1
- package/dist/modules/plugin/services/plugin-svc.js +96 -15
- package/dist/modules/plugin/services/plugin-svc.js.map +1 -1
- package/dist/modules/release/commands/release-init.d.ts +3 -0
- package/dist/modules/release/commands/release-init.d.ts.map +1 -0
- package/dist/modules/release/commands/release-init.js +92 -0
- package/dist/modules/release/commands/release-init.js.map +1 -0
- package/dist/modules/release/errors/release-errors.d.ts +7 -0
- package/dist/modules/release/errors/release-errors.d.ts.map +1 -0
- package/dist/modules/release/errors/release-errors.js +13 -0
- package/dist/modules/release/errors/release-errors.js.map +1 -0
- package/dist/modules/release/index.d.ts +4 -0
- package/dist/modules/release/index.d.ts.map +1 -0
- package/dist/modules/release/index.js +7 -0
- package/dist/modules/release/index.js.map +1 -0
- package/dist/modules/release/services/release-service.d.ts +34 -0
- package/dist/modules/release/services/release-service.d.ts.map +1 -0
- package/dist/modules/release/services/release-service.js +154 -0
- package/dist/modules/release/services/release-service.js.map +1 -0
- package/dist/templates/plugin/README.md.hbs +39 -0
- package/dist/templates/plugin/package.json.hbs +34 -0
- package/dist/templates/plugin/plugin.json.hbs +7 -0
- package/dist/templates/plugin/src/generator.ts.hbs +64 -0
- package/dist/templates/plugin/templates/src/.gitkeep +0 -0
- package/dist/templates/plugin/tsconfig.json +10 -0
- package/dist/templates/plugin/tsup.config.ts +9 -0
- package/dist/templates/workspace/.github/workflows/ci.yml +8 -5
- package/dist/templates/workspace/package.json +1 -0
- package/dist/templates/workspace/turbo.json +5 -0
- package/dist/utils/launch77-context.d.ts +1 -1
- package/dist/utils/launch77-context.d.ts.map +1 -1
- package/dist/utils/launch77-context.js +25 -2
- package/dist/utils/launch77-context.js.map +1 -1
- package/dist/utils/launch77-validation.d.ts +1 -1
- package/dist/utils/launch77-validation.d.ts.map +1 -1
- package/dist/utils/string.d.ts +13 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +18 -0
- package/dist/utils/string.js.map +1 -0
- package/package.json +6 -9
- package/src/cli.ts +7 -3
- package/src/infrastructure/template-generator.ts +1 -1
- package/src/infrastructure/template.ts +14 -0
- package/src/modules/app/commands/create-app.ts +1 -1
- package/src/modules/app/commands/delete-app.ts +1 -1
- package/src/modules/app/services/app-svc.ts +1 -1
- package/src/modules/app/services/manifest-svc.ts +1 -1
- package/src/modules/catalog/config/catalog-config.test.ts +1 -1
- package/src/modules/git/commands/git-connect.ts +2 -2
- package/src/modules/git/errors/git-errors.ts +7 -0
- package/src/modules/git/index.ts +8 -1
- package/src/modules/git/services/git-service.ts +12 -1
- package/src/modules/plugin/commands/plugin-create.ts +68 -0
- package/src/modules/plugin/commands/plugin-install.ts +9 -26
- package/src/modules/plugin/errors/plugin-errors.ts +87 -6
- package/src/modules/plugin/index.ts +4 -2
- package/src/modules/plugin/lib/plugin-registry.ts +14 -37
- package/src/modules/plugin/lib/plugin-resolver.test.ts +215 -0
- package/src/modules/plugin/lib/plugin-resolver.ts +160 -0
- package/src/modules/plugin/services/plugin-create-service.ts +69 -0
- package/src/modules/plugin/services/plugin-svc.ts +108 -15
- package/src/modules/release/commands/release-init.ts +102 -0
- package/src/modules/release/errors/release-errors.ts +13 -0
- package/src/modules/release/index.ts +8 -0
- package/src/modules/release/services/release-service.ts +170 -0
- package/src/utils/launch77-context.ts +29 -3
- package/src/utils/launch77-validation.ts +1 -1
- package/src/utils/string.ts +17 -0
- package/templates/plugin/README.md.hbs +39 -0
- package/templates/plugin/package.json.hbs +34 -0
- package/templates/plugin/plugin.json.hbs +7 -0
- package/templates/plugin/src/generator.ts.hbs +64 -0
- package/templates/plugin/templates/src/.gitkeep +0 -0
- package/templates/plugin/tsconfig.json +10 -0
- package/templates/plugin/tsup.config.ts +9 -0
- package/templates/workspace/.github/workflows/ci.yml +8 -5
- package/templates/workspace/package.json +1 -0
- package/templates/workspace/turbo.json +5 -0
- package/tests/integration/cli.test.ts +25 -0
- package/tests/integration/setup.ts +20 -0
- package/vitest.config.ts +9 -0
- package/vitest.integration.config.ts +9 -0
- package/dist/modules/git/commands/git-setup-releases.d.ts +0 -3
- package/dist/modules/git/commands/git-setup-releases.d.ts.map +0 -1
- package/dist/modules/git/commands/git-setup-releases.js +0 -128
- package/dist/modules/git/commands/git-setup-releases.js.map +0 -1
- package/launch77-cli-1.2.0.tgz +0 -0
- package/src/modules/git/commands/git-setup-releases.ts +0 -148
- package/src/modules/plugin/lib/launch77-workspace.code-workspace +0 -14
|
@@ -57,6 +57,20 @@ export function getTemplatePath(templateName: string): string {
|
|
|
57
57
|
return path.join(baseDir, templateName)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Get the path to a plugin template directory
|
|
62
|
+
* Plugin templates are in dist/templates/ (copied during build)
|
|
63
|
+
*/
|
|
64
|
+
export function getPluginTemplatePath(templateName: string): string {
|
|
65
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
66
|
+
const __dirname = path.dirname(__filename)
|
|
67
|
+
|
|
68
|
+
// From dist/infrastructure/ to dist/templates/
|
|
69
|
+
const baseDir = path.join(__dirname, '../templates')
|
|
70
|
+
|
|
71
|
+
return path.join(baseDir, templateName)
|
|
72
|
+
}
|
|
73
|
+
|
|
60
74
|
/**
|
|
61
75
|
* Check if a template exists
|
|
62
76
|
*/
|
|
@@ -5,7 +5,7 @@ import chalk from 'chalk'
|
|
|
5
5
|
import { Command } from 'commander'
|
|
6
6
|
import ora from 'ora'
|
|
7
7
|
|
|
8
|
-
import { detectLaunch77Context } from '
|
|
8
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
9
9
|
import { AppService } from '../services/app-svc.js'
|
|
10
10
|
import { APP_TYPES, APP_TYPES_LIST } from '../types/app-types.js'
|
|
11
11
|
|
|
@@ -6,7 +6,7 @@ import { Command } from 'commander'
|
|
|
6
6
|
import inquirer from 'inquirer'
|
|
7
7
|
import ora from 'ora'
|
|
8
8
|
|
|
9
|
-
import { detectLaunch77Context } from '
|
|
9
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
10
10
|
import { AppService } from '../services/app-svc.js'
|
|
11
11
|
|
|
12
12
|
export function deleteAppCommand(): Command {
|
|
@@ -10,7 +10,7 @@ import { templateExists } from '../../../infrastructure/template.js'
|
|
|
10
10
|
import { validateWorkspaceContext } from '../../../utils/launch77-validation.js'
|
|
11
11
|
import { validateAppName } from '../../../utils/validation.js'
|
|
12
12
|
|
|
13
|
-
import type { Launch77Context } from '
|
|
13
|
+
import type { Launch77Context } from '@launch77/plugin-runtime'
|
|
14
14
|
import type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult } from '../types/app-types.js'
|
|
15
15
|
|
|
16
16
|
export class AppService {
|
|
@@ -3,7 +3,7 @@ import * as path from 'path'
|
|
|
3
3
|
|
|
4
4
|
import { parseManifest } from '../lib/manifest-schema.js'
|
|
5
5
|
|
|
6
|
-
import type { Launch77Context } from '
|
|
6
|
+
import type { Launch77Context } from '@launch77/plugin-runtime'
|
|
7
7
|
import type { AppManifest } from '../lib/manifest-schema.js'
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -159,7 +159,7 @@ describe('validateConfig', () => {
|
|
|
159
159
|
type: 'server-functions',
|
|
160
160
|
packageName: '@launch77/ui',
|
|
161
161
|
}
|
|
162
|
-
expect(() => validateConfig(config)).toThrow(
|
|
162
|
+
expect(() => validateConfig(config)).toThrow(/expected "ui-components"/)
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
test('type is number', () => {
|
|
@@ -3,7 +3,7 @@ import { Command } from 'commander'
|
|
|
3
3
|
import { select, input, confirm } from '@inquirer/prompts'
|
|
4
4
|
import ora from 'ora'
|
|
5
5
|
|
|
6
|
-
import { detectLaunch77Context } from '
|
|
6
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
7
7
|
import { GitHubCLINotInstalledError, GitHubNotAuthenticatedError, NotInWorkspaceError, GitNotInstalledError } from '../errors/git-errors.js'
|
|
8
8
|
import { GitHubService } from '../services/github-service.js'
|
|
9
9
|
import { GitService } from '../services/git-service.js'
|
|
@@ -139,7 +139,7 @@ export function gitConnectCommand(): Command {
|
|
|
139
139
|
// Success message
|
|
140
140
|
console.log(chalk.green(`\n✅ Workspace connected to GitHub!\n`))
|
|
141
141
|
console.log(chalk.white(` Repository: ${chalk.cyan(repoUrl)}\n`))
|
|
142
|
-
console.log(chalk.gray(`Next steps:\n` + ` - Your code has been pushed to GitHub\n` + ` - GitHub Actions will run automatically\n` + ` - View your repository: ${chalk.cyan('gh repo view --web')}\n` + `\n` + ` ${chalk.yellow('📦 Enable automatic releases (recommended):')}\n` + ` - Run: ${chalk.cyan('launch77
|
|
142
|
+
console.log(chalk.gray(`Next steps:\n` + ` - Your code has been pushed to GitHub\n` + ` - GitHub Actions will run automatically\n` + ` - View your repository: ${chalk.cyan('gh repo view --web')}\n` + `\n` + ` ${chalk.yellow('📦 Enable automatic releases (recommended):')}\n` + ` - Run: ${chalk.cyan('launch77 release:init')}\n` + ` - This sets up complete release workflow (GitHub tokens, changesets, npm)\n`))
|
|
143
143
|
} catch (error) {
|
|
144
144
|
createSpinner.fail('Failed to create repository')
|
|
145
145
|
|
|
@@ -35,3 +35,10 @@ export class RepositoryAlreadyExistsError extends Error {
|
|
|
35
35
|
this.name = 'RepositoryAlreadyExistsError'
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
export class GitHubNotConnectedError extends Error {
|
|
40
|
+
constructor() {
|
|
41
|
+
super('This workspace is not connected to a GitHub repository')
|
|
42
|
+
this.name = 'GitHubNotConnectedError'
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/modules/git/index.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
// Services (for other modules to use)
|
|
2
|
+
export { GitService } from './services/git-service.js'
|
|
3
|
+
export { GitHubService } from './services/github-service.js'
|
|
4
|
+
|
|
5
|
+
// Errors (for proper error handling)
|
|
6
|
+
export { GitHubCLINotInstalledError, GitHubNotAuthenticatedError, NotInWorkspaceError, GitNotInstalledError, RepositoryAlreadyExistsError, GitHubNotConnectedError } from './errors/git-errors.js'
|
|
7
|
+
|
|
8
|
+
// Commands
|
|
1
9
|
export { gitConnectCommand } from './commands/git-connect.js'
|
|
2
|
-
export { gitSetupReleasesCommand } from './commands/git-setup-releases.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as git from '../../../infrastructure/git.js'
|
|
2
|
-
import { GitNotInstalledError } from '../errors/git-errors.js'
|
|
2
|
+
import { GitNotInstalledError, GitHubNotConnectedError } from '../errors/git-errors.js'
|
|
3
3
|
|
|
4
4
|
export class GitService {
|
|
5
5
|
/**
|
|
@@ -49,4 +49,15 @@ export class GitService {
|
|
|
49
49
|
async getRemoteUrl(cwd: string, remoteName: string = 'origin'): Promise<string> {
|
|
50
50
|
return git.getRemoteUrl(cwd, remoteName)
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ensure repository is connected to GitHub (has a remote)
|
|
55
|
+
* Throws GitHubNotConnectedError if not connected
|
|
56
|
+
*/
|
|
57
|
+
async ensureConnectedToGitHub(cwd: string, remoteName: string = 'origin'): Promise<void> {
|
|
58
|
+
const hasOrigin = await this.hasRemote(cwd, remoteName)
|
|
59
|
+
if (!hasOrigin) {
|
|
60
|
+
throw new GitHubNotConnectedError()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
52
63
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { Command } from 'commander'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
|
|
8
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
9
|
+
import { PluginCreateService } from '../services/plugin-create-service.js'
|
|
10
|
+
|
|
11
|
+
export function pluginCreateCommand(): Command {
|
|
12
|
+
const command = new Command('plugin:create')
|
|
13
|
+
.argument('<plugin-name>', 'Name of the plugin to create (e.g., analytics)')
|
|
14
|
+
.option('-d, --description <description>', 'Plugin description')
|
|
15
|
+
.description('Create a new plugin from template')
|
|
16
|
+
.action(async (pluginName: string, options) => {
|
|
17
|
+
try {
|
|
18
|
+
console.log(chalk.blue(`\n🔌 Creating plugin: ${pluginName}\n`))
|
|
19
|
+
|
|
20
|
+
// Detect context
|
|
21
|
+
const context = await detectLaunch77Context(process.cwd())
|
|
22
|
+
|
|
23
|
+
// Create plugin service
|
|
24
|
+
const pluginCreateService = new PluginCreateService()
|
|
25
|
+
|
|
26
|
+
// Create plugin
|
|
27
|
+
const spinner = ora('Generating plugin structure...').start()
|
|
28
|
+
let result
|
|
29
|
+
try {
|
|
30
|
+
result = await pluginCreateService.createPlugin(
|
|
31
|
+
{
|
|
32
|
+
pluginName,
|
|
33
|
+
description: options.description,
|
|
34
|
+
},
|
|
35
|
+
context
|
|
36
|
+
)
|
|
37
|
+
spinner.succeed('Plugin structure generated')
|
|
38
|
+
} catch (error) {
|
|
39
|
+
spinner.fail('Failed to generate plugin structure')
|
|
40
|
+
throw error
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Success message
|
|
44
|
+
const relativePath = path.relative(process.cwd(), result.pluginPath)
|
|
45
|
+
const displayPath = relativePath.startsWith('..') ? result.pluginPath : relativePath
|
|
46
|
+
console.log(chalk.green(`\n✅ Plugin created successfully at ${displayPath}`))
|
|
47
|
+
|
|
48
|
+
// Next steps
|
|
49
|
+
console.log(chalk.white('\n' + '─'.repeat(60) + '\n'))
|
|
50
|
+
console.log(chalk.cyan('📋 Next Steps:\n'))
|
|
51
|
+
const cdPath = path.relative(process.cwd(), result.pluginPath)
|
|
52
|
+
console.log(chalk.gray('1. Navigate to your plugin directory:'))
|
|
53
|
+
console.log(chalk.cyan(` cd ${cdPath}\n`))
|
|
54
|
+
console.log(chalk.gray('2. Implement your plugin logic in src/generator.ts\n'))
|
|
55
|
+
console.log(chalk.gray('3. Add any template files to templates/\n'))
|
|
56
|
+
console.log(chalk.gray('4. Build your plugin:'))
|
|
57
|
+
console.log(chalk.cyan(' npm run build\n'))
|
|
58
|
+
console.log(chalk.gray('5. Test your plugin:'))
|
|
59
|
+
console.log(chalk.cyan(` launch77 plugin:install ${pluginName}\n`))
|
|
60
|
+
} catch (error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
62
|
+
console.error(chalk.red('Error:'), message)
|
|
63
|
+
process.exit(1)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return command
|
|
68
|
+
}
|
|
@@ -1,48 +1,31 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import { Command } from 'commander'
|
|
4
|
-
import ora from 'ora'
|
|
5
4
|
|
|
6
|
-
import { detectLaunch77Context } from '
|
|
5
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
7
6
|
import { PluginService } from '../services/plugin-svc.js'
|
|
8
7
|
|
|
9
8
|
export function pluginInstallCommand(): Command {
|
|
10
9
|
const command = new Command('plugin:install')
|
|
11
|
-
.argument('<plugin-name>', 'Name of the plugin to install (e.g.,
|
|
12
|
-
.description('Install a plugin to the current
|
|
10
|
+
.argument('<plugin-name>', 'Name of the plugin to install (e.g., release, @org/plugin-name)')
|
|
11
|
+
.description('Install a plugin to the current package')
|
|
13
12
|
.action(async (pluginName: string) => {
|
|
14
13
|
try {
|
|
15
|
-
console.log(chalk.blue(`\n🔌 Installing ${pluginName} plugin\n`))
|
|
16
|
-
|
|
17
14
|
// Detect context
|
|
18
15
|
const context = await detectLaunch77Context(process.cwd())
|
|
19
16
|
|
|
20
17
|
// Create plugin service
|
|
21
18
|
const pluginService = new PluginService()
|
|
22
19
|
|
|
23
|
-
// Install
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await pluginService.installPlugin({ pluginName }, context)
|
|
27
|
-
filesSpinner.succeed('Plugin files installed')
|
|
28
|
-
} catch (error) {
|
|
29
|
-
filesSpinner.fail('Failed to install plugin')
|
|
30
|
-
throw error
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Package.json update spinner
|
|
34
|
-
const pkgSpinner = ora('Updating package.json...').start()
|
|
35
|
-
pkgSpinner.succeed('package.json updated')
|
|
36
|
-
|
|
37
|
-
// Dependencies spinner
|
|
38
|
-
const installSpinner = ora('Installing dependencies...').start()
|
|
39
|
-
installSpinner.succeed('Dependencies installed')
|
|
20
|
+
// Install plugin with transparent logging
|
|
21
|
+
// The service handles all logging internally
|
|
22
|
+
await pluginService.installPlugin({ pluginName }, context, console.log)
|
|
40
23
|
|
|
41
|
-
// Success message
|
|
42
|
-
console.log(chalk.green(
|
|
24
|
+
// Success message
|
|
25
|
+
console.log(chalk.green(`✅ Plugin '${pluginName}' installed successfully!\n`))
|
|
43
26
|
} catch (error) {
|
|
44
27
|
const message = error instanceof Error ? error.message : String(error)
|
|
45
|
-
console.error(chalk.red('
|
|
28
|
+
console.error(chalk.red('\nError:'), message)
|
|
46
29
|
process.exit(1)
|
|
47
30
|
}
|
|
48
31
|
})
|
|
@@ -16,20 +16,71 @@ export class InvalidPluginContextError extends Error {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Factory function to create standardized InvalidPluginContextError
|
|
19
|
-
* for when plugin:install is run outside of
|
|
19
|
+
* for when plugin:install is run outside of a package directory
|
|
20
20
|
*/
|
|
21
21
|
export function createInvalidContextError(currentLocation: string): InvalidPluginContextError {
|
|
22
22
|
return new InvalidPluginContextError(
|
|
23
|
-
`plugin:install must be run from within
|
|
23
|
+
`plugin:install must be run from within a package directory.
|
|
24
24
|
|
|
25
25
|
Current location: ${currentLocation}
|
|
26
|
-
Expected: apps/<app-name>/
|
|
26
|
+
Expected: apps/<name>/, libraries/<name>/, plugins/<name>/, or app-templates/<name>/
|
|
27
27
|
|
|
28
|
-
Navigate to
|
|
28
|
+
Navigate to a package directory:
|
|
29
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(', ')}
|
|
30
81
|
|
|
31
|
-
|
|
32
|
-
|
|
82
|
+
Allowed locations:
|
|
83
|
+
${targetLocations.map((loc) => ` ${loc}`).join('\n')}`
|
|
33
84
|
)
|
|
34
85
|
}
|
|
35
86
|
|
|
@@ -42,3 +93,33 @@ export class PluginInstallationError extends Error {
|
|
|
42
93
|
this.name = 'PluginInstallationError'
|
|
43
94
|
}
|
|
44
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
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// Services
|
|
2
2
|
export { PluginService } from './services/plugin-svc.js'
|
|
3
|
+
export { PluginCreateService } from './services/plugin-create-service.js'
|
|
3
4
|
|
|
4
5
|
// Types
|
|
5
6
|
export type { InstallPluginRequest, InstallPluginResult, PluginMetadata, HookResult } from './types/plugin-types.js'
|
|
6
7
|
|
|
7
8
|
// Errors
|
|
8
|
-
export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError } from './errors/plugin-errors.js'
|
|
9
|
+
export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError, PluginResolutionError, NpmInstallationError } from './errors/plugin-errors.js'
|
|
9
10
|
|
|
10
11
|
// Commands
|
|
11
12
|
export { pluginInstallCommand } from './commands/plugin-install.js'
|
|
13
|
+
export { pluginCreateCommand } from './commands/plugin-create.js'
|
|
12
14
|
|
|
13
15
|
// Utilities
|
|
14
|
-
export {
|
|
16
|
+
export { listAvailablePlugins } from './lib/plugin-registry.js'
|
|
@@ -1,55 +1,32 @@
|
|
|
1
1
|
import * as path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
2
|
|
|
4
3
|
import fs from 'fs-extra'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* List all available plugins in the workspace
|
|
7
|
+
* Scans the workspace plugins/ directory for valid plugins
|
|
8
|
+
*
|
|
9
|
+
* @param workspaceRoot - The root directory of the Launch77 workspace
|
|
10
|
+
* @returns Array of plugin names found in the workspace
|
|
9
11
|
*/
|
|
10
|
-
export function
|
|
11
|
-
const
|
|
12
|
-
const __dirname = path.dirname(__filename)
|
|
12
|
+
export async function listAvailablePlugins(workspaceRoot: string): Promise<string[]> {
|
|
13
|
+
const pluginsDir = path.join(workspaceRoot, 'plugins')
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
const baseDir = path.join(__dirname, '../../../plugins')
|
|
16
|
-
|
|
17
|
-
return path.join(baseDir, pluginName)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if a plugin exists
|
|
22
|
-
* A plugin must have: dist/generator.js and plugin.json
|
|
23
|
-
*/
|
|
24
|
-
export async function pluginExists(pluginName: string): Promise<boolean> {
|
|
25
|
-
const pluginPath = getPluginPath(pluginName)
|
|
26
|
-
|
|
27
|
-
const hasGenerator = await fs.pathExists(path.join(pluginPath, 'dist/generator.js'))
|
|
28
|
-
const hasPluginJson = await fs.pathExists(path.join(pluginPath, 'plugin.json'))
|
|
29
|
-
|
|
30
|
-
return hasGenerator && hasPluginJson
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* List all available plugins
|
|
35
|
-
*/
|
|
36
|
-
export async function listAvailablePlugins(): Promise<string[]> {
|
|
37
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
38
|
-
const __dirname = path.dirname(__filename)
|
|
39
|
-
|
|
40
|
-
const baseDir = path.join(__dirname, '../../../plugins')
|
|
41
|
-
|
|
42
|
-
if (!(await fs.pathExists(baseDir))) {
|
|
15
|
+
if (!(await fs.pathExists(pluginsDir))) {
|
|
43
16
|
return []
|
|
44
17
|
}
|
|
45
18
|
|
|
46
|
-
const items = await fs.readdir(
|
|
19
|
+
const items = await fs.readdir(pluginsDir, { withFileTypes: true })
|
|
47
20
|
const plugins: string[] = []
|
|
48
21
|
|
|
49
22
|
for (const item of items) {
|
|
50
23
|
if (item.isDirectory()) {
|
|
51
24
|
// Verify it's a valid plugin
|
|
52
|
-
|
|
25
|
+
const pluginPath = path.join(pluginsDir, item.name)
|
|
26
|
+
const hasPluginJson = await fs.pathExists(path.join(pluginPath, 'plugin.json'))
|
|
27
|
+
const hasGenerator = await fs.pathExists(path.join(pluginPath, 'dist/generator.js'))
|
|
28
|
+
|
|
29
|
+
if (hasPluginJson && hasGenerator) {
|
|
53
30
|
plugins.push(item.name)
|
|
54
31
|
}
|
|
55
32
|
}
|