@launch77/cli 1.4.4 → 1.5.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 +1 -4
- package/dist/cli.js.map +1 -1
- package/dist/infrastructure/package-resolver.d.ts +12 -2
- package/dist/infrastructure/package-resolver.d.ts.map +1 -1
- package/dist/infrastructure/package-resolver.js +29 -2
- package/dist/infrastructure/package-resolver.js.map +1 -1
- package/dist/infrastructure/package-resolver.test.js +3 -3
- package/dist/infrastructure/package-resolver.test.js.map +1 -1
- package/dist/infrastructure/template.d.ts +0 -1
- package/dist/infrastructure/template.d.ts.map +1 -1
- package/dist/infrastructure/template.js.map +1 -1
- package/dist/modules/app/commands/create-app.d.ts.map +1 -1
- package/dist/modules/app/commands/create-app.js +8 -13
- 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/commands/validate-manifest.d.ts.map +1 -1
- package/dist/modules/app/commands/validate-manifest.js +0 -1
- package/dist/modules/app/commands/validate-manifest.js.map +1 -1
- package/dist/modules/app/index.d.ts +1 -2
- package/dist/modules/app/index.d.ts.map +1 -1
- package/dist/modules/app/index.js +0 -1
- package/dist/modules/app/index.js.map +1 -1
- package/dist/modules/app/lib/app-template-resolver.d.ts.map +1 -1
- package/dist/modules/app/lib/app-template-resolver.js +15 -0
- package/dist/modules/app/lib/app-template-resolver.js.map +1 -1
- package/dist/modules/app/lib/manifest-schema.d.ts +0 -7
- package/dist/modules/app/lib/manifest-schema.d.ts.map +1 -1
- package/dist/modules/app/lib/manifest-schema.js +0 -2
- package/dist/modules/app/lib/manifest-schema.js.map +1 -1
- package/dist/modules/app/services/app-svc.d.ts.map +1 -1
- package/dist/modules/app/services/app-svc.js +36 -6
- package/dist/modules/app/services/app-svc.js.map +1 -1
- package/dist/modules/app/services/manifest-svc.d.ts +2 -2
- package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
- package/dist/modules/app/services/manifest-svc.js +9 -50
- package/dist/modules/app/services/manifest-svc.js.map +1 -1
- package/dist/modules/app/types/app-types.d.ts +2 -5
- package/dist/modules/app/types/app-types.d.ts.map +1 -1
- package/dist/modules/app/types/app-types.js +1 -4
- package/dist/modules/app/types/app-types.js.map +1 -1
- package/dist/modules/catalog/commands/scan.js +3 -3
- package/dist/modules/catalog/commands/scan.js.map +1 -1
- package/dist/modules/catalog/services/catalog-svc.d.ts +2 -2
- package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -1
- package/dist/modules/catalog/services/catalog-svc.js +2 -2
- package/dist/modules/catalog/services/catalog-svc.js.map +1 -1
- package/dist/modules/deploy/commands/deploy-init-action.d.ts.map +1 -1
- package/dist/modules/deploy/commands/deploy-init-action.js +21 -20
- package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -1
- package/dist/modules/deploy/commands/deploy-logs-action.d.ts.map +1 -1
- package/dist/modules/deploy/commands/deploy-logs-action.js +16 -13
- package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -1
- package/dist/modules/deploy/commands/deploy-status-action.d.ts.map +1 -1
- package/dist/modules/deploy/commands/deploy-status-action.js +16 -13
- package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -1
- package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +1 -1
- package/dist/modules/plugin/lib/plugin-resolver.js +13 -2
- package/dist/modules/plugin/lib/plugin-resolver.js.map +1 -1
- package/dist/modules/plugin/lib/plugin-resolver.test.js +2 -0
- package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -1
- package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -1
- package/dist/modules/plugin/services/plugin-svc.js +10 -2
- package/dist/modules/plugin/services/plugin-svc.js.map +1 -1
- package/dist/modules/plugin/services/plugin-svc.test.js +12 -6
- package/dist/modules/plugin/services/plugin-svc.test.js.map +1 -1
- package/dist/templates/plugin/package.json.hbs +1 -1
- package/dist/templates/plugin/plugin.json.hbs +1 -3
- package/dist/utils/validation.d.ts +3 -7
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +7 -31
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +1 -4
- package/src/infrastructure/package-resolver.test.ts +3 -3
- package/src/infrastructure/package-resolver.ts +31 -2
- package/src/infrastructure/template.ts +0 -1
- package/src/modules/app/commands/create-app.ts +8 -16
- package/src/modules/app/commands/delete-app.ts +1 -1
- package/src/modules/app/commands/validate-manifest.ts +0 -1
- package/src/modules/app/index.ts +1 -2
- package/src/modules/app/lib/app-template-resolver.ts +16 -0
- package/src/modules/app/lib/manifest-schema.ts +0 -5
- package/src/modules/app/services/app-svc.ts +46 -7
- package/src/modules/app/services/manifest-svc.ts +8 -57
- package/src/modules/app/types/app-types.ts +2 -9
- package/src/modules/catalog/commands/scan.ts +3 -3
- package/src/modules/catalog/services/catalog-svc.ts +4 -4
- package/src/modules/deploy/commands/deploy-init-action.ts +23 -20
- package/src/modules/deploy/commands/deploy-logs-action.ts +19 -13
- package/src/modules/deploy/commands/deploy-status-action.ts +19 -13
- package/src/modules/plugin/lib/plugin-resolver.test.ts +2 -0
- package/src/modules/plugin/lib/plugin-resolver.ts +13 -2
- package/src/modules/plugin/services/plugin-svc.test.ts +12 -6
- package/src/modules/plugin/services/plugin-svc.ts +12 -3
- package/src/utils/validation.ts +10 -38
- package/templates/plugin/package.json.hbs +1 -1
- package/templates/plugin/plugin.json.hbs +1 -3
- package/dist/modules/app/commands/generate-manifest.d.ts +0 -3
- package/dist/modules/app/commands/generate-manifest.d.ts.map +0 -1
- package/dist/modules/app/commands/generate-manifest.js +0 -62
- package/dist/modules/app/commands/generate-manifest.js.map +0 -1
- package/dist/modules/startup/commands/create-startup.d.ts +0 -3
- package/dist/modules/startup/commands/create-startup.d.ts.map +0 -1
- package/dist/modules/startup/commands/create-startup.js +0 -43
- package/dist/modules/startup/commands/create-startup.js.map +0 -1
- package/dist/modules/startup/errors/startup-errors.d.ts +0 -13
- package/dist/modules/startup/errors/startup-errors.d.ts.map +0 -1
- package/dist/modules/startup/errors/startup-errors.js +0 -25
- package/dist/modules/startup/errors/startup-errors.js.map +0 -1
- package/dist/modules/startup/index.d.ts +0 -5
- package/dist/modules/startup/index.d.ts.map +0 -1
- package/dist/modules/startup/index.js +0 -7
- package/dist/modules/startup/index.js.map +0 -1
- package/dist/modules/startup/services/startup-service.d.ts +0 -7
- package/dist/modules/startup/services/startup-service.d.ts.map +0 -1
- package/dist/modules/startup/services/startup-service.js +0 -43
- package/dist/modules/startup/services/startup-service.js.map +0 -1
- package/dist/modules/startup/types/startup-types.d.ts +0 -8
- package/dist/modules/startup/types/startup-types.d.ts.map +0 -1
- package/dist/modules/startup/types/startup-types.js +0 -2
- package/dist/modules/startup/types/startup-types.js.map +0 -1
- package/dist/modules/startup/utils/startup-validators.d.ts +0 -8
- package/dist/modules/startup/utils/startup-validators.d.ts.map +0 -1
- package/dist/modules/startup/utils/startup-validators.js +0 -17
- package/dist/modules/startup/utils/startup-validators.js.map +0 -1
- package/dist/templates/startup/apps/.gitkeep +0 -8
- package/dist/utils/monorepo.d.ts +0 -19
- package/dist/utils/monorepo.d.ts.map +0 -1
- package/dist/utils/monorepo.js +0 -100
- package/dist/utils/monorepo.js.map +0 -1
- package/src/modules/app/commands/generate-manifest.ts +0 -75
- package/src/modules/startup/commands/create-startup.ts +0 -53
- package/src/modules/startup/errors/startup-errors.ts +0 -23
- package/src/modules/startup/index.ts +0 -11
- package/src/modules/startup/services/startup-service.ts +0 -57
- package/src/modules/startup/types/startup-types.ts +0 -8
- package/src/modules/startup/utils/startup-validators.ts +0 -19
- package/src/utils/monorepo.ts +0 -137
- package/templates/startup/apps/.gitkeep +0 -8
|
@@ -17,6 +17,8 @@ export interface PackageResolution {
|
|
|
17
17
|
localPath?: string
|
|
18
18
|
/** The npm package name if source is 'npm' */
|
|
19
19
|
npmPackage?: string
|
|
20
|
+
/** The version from package.json (required for local packages, undefined for npm until installed) */
|
|
21
|
+
version?: string
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -127,6 +129,30 @@ export abstract class PackageResolver {
|
|
|
127
129
|
return `${this.getPackagePrefix()}${trimmedName}`
|
|
128
130
|
}
|
|
129
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Read version from package.json
|
|
134
|
+
* @param packagePath - The path to the package directory
|
|
135
|
+
* @returns The version string from package.json
|
|
136
|
+
* @throws If package.json doesn't exist, can't be read, or is missing the version field
|
|
137
|
+
*/
|
|
138
|
+
private async readVersion(packagePath: string): Promise<string> {
|
|
139
|
+
const packageJsonPath = path.join(packagePath, 'package.json')
|
|
140
|
+
try {
|
|
141
|
+
const packageJson = await fs.readJson(packageJsonPath)
|
|
142
|
+
if (!packageJson.version) {
|
|
143
|
+
throw new Error(`Invalid package structure: package.json at ${packagePath} is missing required version field. ` + `All Launch77 packages must include a valid package.json with a version field.`)
|
|
144
|
+
}
|
|
145
|
+
return packageJson.version
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// Re-throw our own error messages
|
|
148
|
+
if (error instanceof Error && error.message.includes('Invalid package structure')) {
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
151
|
+
// File not found or invalid JSON
|
|
152
|
+
throw new Error(`Invalid package structure: package.json not found or invalid at ${packagePath}. ` + `All Launch77 packages must include a valid package.json with a version field.`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
130
156
|
/**
|
|
131
157
|
* Resolve package location from name
|
|
132
158
|
*
|
|
@@ -134,15 +160,16 @@ export abstract class PackageResolver {
|
|
|
134
160
|
* 1. Check local workspace directory (configured by getFolderName())
|
|
135
161
|
* 2. Verify local package is valid (using verify())
|
|
136
162
|
* 3. Fall back to npm package name (with configured prefix)
|
|
163
|
+
* 4. Read version from package.json (if available)
|
|
137
164
|
*
|
|
138
165
|
* @param name - The package name to resolve
|
|
139
166
|
* @param workspaceRoot - The workspace root directory
|
|
140
|
-
* @returns PackageResolution with source
|
|
167
|
+
* @returns PackageResolution with source, resolved location, and version
|
|
141
168
|
*
|
|
142
169
|
* @example
|
|
143
170
|
* // Local package found
|
|
144
171
|
* await resolveLocation('my-package', '/workspace')
|
|
145
|
-
* // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package' }
|
|
172
|
+
* // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package', version: '1.0.0' }
|
|
146
173
|
*
|
|
147
174
|
* // Not found locally, resolve to npm
|
|
148
175
|
* await resolveLocation('release', '/workspace')
|
|
@@ -174,10 +201,12 @@ export abstract class PackageResolver {
|
|
|
174
201
|
const isValid = await this.verify(localPath)
|
|
175
202
|
|
|
176
203
|
if (isValid) {
|
|
204
|
+
const version = await this.readVersion(localPath)
|
|
177
205
|
return {
|
|
178
206
|
source: 'local',
|
|
179
207
|
resolvedName: trimmedName,
|
|
180
208
|
localPath,
|
|
209
|
+
version,
|
|
181
210
|
}
|
|
182
211
|
}
|
|
183
212
|
}
|
|
@@ -7,27 +7,19 @@ import ora from 'ora'
|
|
|
7
7
|
|
|
8
8
|
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
9
9
|
import { AppService } from '../services/app-svc.js'
|
|
10
|
-
import { APP_TYPES, APP_TYPES_LIST } from '../types/app-types.js'
|
|
11
|
-
|
|
12
|
-
import type { AppType } from '../types/app-types.js'
|
|
13
10
|
|
|
14
11
|
export function createAppCommand(): Command {
|
|
15
12
|
const command = new Command('app:create')
|
|
16
|
-
.argument('<
|
|
13
|
+
.argument('<template>', 'App template name (e.g., webapp, api, marketing-site)')
|
|
17
14
|
.argument('<app-name>', 'Name of the application')
|
|
18
|
-
.description('Create a new application')
|
|
19
|
-
.option('-p, --port <port>', 'Default port for the app')
|
|
20
|
-
.action(async (
|
|
15
|
+
.description('Create a new application from a template')
|
|
16
|
+
.option('-p, --port <port>', 'Default port for the app (default: 3000)')
|
|
17
|
+
.action(async (template: string, appName: string, options) => {
|
|
21
18
|
try {
|
|
22
|
-
|
|
23
|
-
if (!APP_TYPES.includes(type as AppType)) {
|
|
24
|
-
throw new Error(`Invalid app type: ${type}\n\nSupported types: ${APP_TYPES_LIST}`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
console.log(chalk.blue(`\n🚀 Creating ${type} app: ${appName}\n`))
|
|
19
|
+
console.log(chalk.blue(`\n🚀 Creating app: ${appName} from template: ${template}\n`))
|
|
28
20
|
|
|
29
|
-
// Set default port
|
|
30
|
-
const port = options.port ||
|
|
21
|
+
// Set default port
|
|
22
|
+
const port = options.port || '3000'
|
|
31
23
|
|
|
32
24
|
// Detect context
|
|
33
25
|
const context = await detectLaunch77Context(process.cwd())
|
|
@@ -39,7 +31,7 @@ export function createAppCommand(): Command {
|
|
|
39
31
|
const templateSpinner = ora('Generating app structure...').start()
|
|
40
32
|
let result
|
|
41
33
|
try {
|
|
42
|
-
result = await appService.createApp({ type:
|
|
34
|
+
result = await appService.createApp({ type: template, appName, port }, context)
|
|
43
35
|
templateSpinner.succeed('App structure generated')
|
|
44
36
|
} catch (error) {
|
|
45
37
|
templateSpinner.fail('Failed to generate app structure')
|
|
@@ -12,7 +12,7 @@ import { AppService } from '../services/app-svc.js'
|
|
|
12
12
|
export function deleteAppCommand(): Command {
|
|
13
13
|
const command = new Command('app:delete')
|
|
14
14
|
.argument('<app-name>', 'Name of the app to delete')
|
|
15
|
-
.description('Delete an app from the
|
|
15
|
+
.description('Delete an app from the workspace (requires confirmation)')
|
|
16
16
|
.action(async (appName: string) => {
|
|
17
17
|
try {
|
|
18
18
|
console.log(chalk.yellow(`\n⚠️ WARNING: You are about to delete the app '${appName}'\n`))
|
|
@@ -25,7 +25,6 @@ export function validateManifestCommand(): Command {
|
|
|
25
25
|
console.log()
|
|
26
26
|
console.log(chalk.cyan('Manifest details:'))
|
|
27
27
|
console.log(chalk.gray(' Schema version:'), manifest.schemaVersion)
|
|
28
|
-
console.log(chalk.gray(' App type:'), manifest.type)
|
|
29
28
|
console.log(chalk.gray(' Package:'), manifest.package)
|
|
30
29
|
|
|
31
30
|
if ('name' in manifest && manifest.name) {
|
package/src/modules/app/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { AppService } from './services/app-svc.js'
|
|
|
3
3
|
export { ManifestService } from './services/manifest-svc.js'
|
|
4
4
|
|
|
5
5
|
// Types
|
|
6
|
-
export type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult
|
|
6
|
+
export type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult } from './types/app-types.js'
|
|
7
7
|
export type { AppManifest, DeploymentConfig } from './lib/manifest-schema.js'
|
|
8
8
|
|
|
9
9
|
// Errors
|
|
@@ -12,5 +12,4 @@ export { AppAlreadyExistsError, AppNotFoundError, InvalidAppNameError, TemplateN
|
|
|
12
12
|
// Commands
|
|
13
13
|
export { createAppCommand } from './commands/create-app.js'
|
|
14
14
|
export { deleteAppCommand } from './commands/delete-app.js'
|
|
15
|
-
export { generateManifestCommand } from './commands/generate-manifest.js'
|
|
16
15
|
export { validateManifestCommand } from './commands/validate-manifest.js'
|
|
@@ -21,6 +21,22 @@ export class AppTemplateResolver extends PackageResolver {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
protected async verify(localPath: string): Promise<boolean> {
|
|
24
|
+
// All Launch77 packages must have package.json with version
|
|
25
|
+
const hasPackageJson = await fs.pathExists(path.join(localPath, 'package.json'))
|
|
26
|
+
if (!hasPackageJson) {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Verify package.json has a version field
|
|
31
|
+
try {
|
|
32
|
+
const packageJson = await fs.readJson(path.join(localPath, 'package.json'))
|
|
33
|
+
if (!packageJson.version) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
// Check for either:
|
|
25
41
|
// 1. A 'template' subdirectory (preferred structure for npm packages)
|
|
26
42
|
// 2. Any files at the root (simple local templates)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
-
import type { AppType } from '../types/app-types.js'
|
|
4
|
-
|
|
5
3
|
// Deployment configuration
|
|
6
4
|
export type DeploymentConfig = {
|
|
7
5
|
platform: 'vercel'
|
|
@@ -14,7 +12,6 @@ export type DeploymentConfig = {
|
|
|
14
12
|
export type AppManifest = {
|
|
15
13
|
schemaVersion: 1
|
|
16
14
|
name: string
|
|
17
|
-
type: AppType | 'marketing' // Support both 'marketing' and 'marketing-site'
|
|
18
15
|
package: string
|
|
19
16
|
deployment?: DeploymentConfig
|
|
20
17
|
}
|
|
@@ -28,12 +25,10 @@ const deploymentConfigZ = z.object({
|
|
|
28
25
|
})
|
|
29
26
|
|
|
30
27
|
// Main manifest schema - strict mode to reject extra fields
|
|
31
|
-
// Note: Manifests store 'marketing' but we accept 'marketing-site' in commands
|
|
32
28
|
export const manifestZ = z
|
|
33
29
|
.object({
|
|
34
30
|
schemaVersion: z.literal(1),
|
|
35
31
|
name: z.string().min(1),
|
|
36
|
-
type: z.enum(['marketing', 'webapp', 'api']),
|
|
37
32
|
package: z.string().min(1),
|
|
38
33
|
deployment: deploymentConfigZ.optional(),
|
|
39
34
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from 'path'
|
|
2
2
|
|
|
3
3
|
import fs from 'fs-extra'
|
|
4
|
+
import { readTemplateMetadata } from '@launch77/plugin-runtime'
|
|
4
5
|
|
|
5
6
|
import { ManifestService } from './manifest-svc.js'
|
|
6
7
|
import * as filesystem from '../../../infrastructure/filesystem.js'
|
|
@@ -12,7 +13,7 @@ import { validateAppName } from '../../../utils/validation.js'
|
|
|
12
13
|
import { AppTemplateResolver } from '../lib/app-template-resolver.js'
|
|
13
14
|
|
|
14
15
|
import type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult } from '../types/app-types.js'
|
|
15
|
-
import type { Launch77Context } from '@launch77/plugin-runtime'
|
|
16
|
+
import type { Launch77Context, TemplateReference, Launch77PackageManifest } from '@launch77/plugin-runtime'
|
|
16
17
|
|
|
17
18
|
export class AppService {
|
|
18
19
|
private manifestService: ManifestService
|
|
@@ -51,9 +52,14 @@ export class AppService {
|
|
|
51
52
|
const templateResolution = await this.appTemplateResolver.resolveLocation(type, context.workspaceRoot)
|
|
52
53
|
|
|
53
54
|
let templatePath: string
|
|
55
|
+
let templateVersion: string
|
|
56
|
+
let packageRootPath: string
|
|
57
|
+
|
|
54
58
|
if (templateResolution.source === 'local') {
|
|
55
59
|
// Use local template
|
|
56
|
-
|
|
60
|
+
packageRootPath = templateResolution.localPath!
|
|
61
|
+
templatePath = packageRootPath
|
|
62
|
+
templateVersion = templateResolution.version! // Local templates always have version after verification
|
|
57
63
|
} else {
|
|
58
64
|
// Download npm template
|
|
59
65
|
const downloadResult = await downloadNpmPackage({
|
|
@@ -61,13 +67,20 @@ export class AppService {
|
|
|
61
67
|
workspaceRoot: context.workspaceRoot,
|
|
62
68
|
})
|
|
63
69
|
|
|
70
|
+
packageRootPath = downloadResult.packagePath
|
|
71
|
+
|
|
64
72
|
// Check if template has a 'template' subdirectory, otherwise use package root
|
|
65
|
-
const templateSubdir = path.join(
|
|
73
|
+
const templateSubdir = path.join(packageRootPath, 'template')
|
|
66
74
|
if (await fs.pathExists(templateSubdir)) {
|
|
67
75
|
templatePath = templateSubdir
|
|
68
76
|
} else {
|
|
69
|
-
templatePath =
|
|
77
|
+
templatePath = packageRootPath
|
|
70
78
|
}
|
|
79
|
+
|
|
80
|
+
// Read version from downloaded package
|
|
81
|
+
const packageJsonPath = path.join(packageRootPath, 'package.json')
|
|
82
|
+
const packageJson = await fs.readJson(packageJsonPath)
|
|
83
|
+
templateVersion = packageJson.version
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
// 5. Determine app path
|
|
@@ -91,14 +104,40 @@ export class AppService {
|
|
|
91
104
|
port,
|
|
92
105
|
})
|
|
93
106
|
|
|
94
|
-
// 9.
|
|
95
|
-
const
|
|
107
|
+
// 9. Read template metadata (currently empty, placeholder for future config)
|
|
108
|
+
//const templateMetadata = await readTemplateMetadata(templatePath)
|
|
109
|
+
|
|
110
|
+
// 10. Write template reference to app's package.json
|
|
111
|
+
const appPackageJsonPath = path.join(appPath, 'package.json')
|
|
112
|
+
const appPackageJson = await fs.readJson(appPackageJsonPath)
|
|
113
|
+
|
|
114
|
+
// Build template reference - version read from package.json
|
|
115
|
+
const templatePackage = templateResolution.source === 'npm' ? templateResolution.npmPackage! : templateResolution.resolvedName
|
|
116
|
+
const templateRef: TemplateReference = {
|
|
117
|
+
package: templatePackage,
|
|
118
|
+
version: templateVersion,
|
|
119
|
+
source: templateResolution.source,
|
|
120
|
+
createdAt: new Date().toISOString(),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Initialize launch77 manifest structure
|
|
124
|
+
if (!appPackageJson.launch77) {
|
|
125
|
+
appPackageJson.launch77 = {}
|
|
126
|
+
}
|
|
127
|
+
const launch77Manifest = appPackageJson.launch77 as Launch77PackageManifest
|
|
128
|
+
launch77Manifest.template = templateRef
|
|
129
|
+
|
|
130
|
+
// Write updated package.json
|
|
131
|
+
await fs.writeJson(appPackageJsonPath, appPackageJson, { spaces: 2 })
|
|
132
|
+
|
|
133
|
+
// 11. Generate and write app manifest
|
|
134
|
+
const manifest = this.manifestService.generateManifest(appName, context, { port })
|
|
96
135
|
const launchDir = path.join(appPath, '.launch')
|
|
97
136
|
await filesystem.ensureDir(launchDir)
|
|
98
137
|
const manifestPath = path.join(launchDir, 'app.json')
|
|
99
138
|
await filesystem.writeJSON(manifestPath, manifest)
|
|
100
139
|
|
|
101
|
-
//
|
|
140
|
+
// 12. Install dependencies
|
|
102
141
|
await npm.install(context.workspaceRoot)
|
|
103
142
|
|
|
104
143
|
return {
|
|
@@ -8,7 +8,7 @@ import type { AppManifest } from '../lib/manifest-schema.js'
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Convert kebab-case to Title Case
|
|
11
|
-
* e.g., "my-
|
|
11
|
+
* e.g., "my-workspace-name" -> "My Workspace Name"
|
|
12
12
|
*/
|
|
13
13
|
function toTitleCase(str: string): string {
|
|
14
14
|
return str
|
|
@@ -17,66 +17,17 @@ function toTitleCase(str: string): string {
|
|
|
17
17
|
.join(' ')
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Generate a manifest for a marketing app
|
|
22
|
-
*/
|
|
23
|
-
function generateMarketingManifest(appName: string, context: Launch77Context): AppManifest {
|
|
24
|
-
const workspaceName = context.workspaceName
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
schemaVersion: 1,
|
|
28
|
-
name: `${toTitleCase(workspaceName)} Marketing`,
|
|
29
|
-
type: 'marketing',
|
|
30
|
-
package: `@${workspaceName}/${appName}`,
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Generate a manifest for a webapp
|
|
36
|
-
*/
|
|
37
|
-
function generateWebappManifest(appName: string, context: Launch77Context): AppManifest {
|
|
38
|
-
const workspaceName = context.workspaceName
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
schemaVersion: 1,
|
|
42
|
-
name: `${toTitleCase(workspaceName)} Web`,
|
|
43
|
-
type: 'webapp',
|
|
44
|
-
package: `@${workspaceName}/${appName}`,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Generate a manifest for an API app
|
|
50
|
-
*/
|
|
51
|
-
function generateApiManifest(appName: string, context: Launch77Context): AppManifest {
|
|
52
|
-
const workspaceName = context.workspaceName
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
schemaVersion: 1,
|
|
56
|
-
name: `${toTitleCase(workspaceName)} API`,
|
|
57
|
-
type: 'api',
|
|
58
|
-
package: `@${workspaceName}/${appName}`,
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
20
|
export class ManifestService {
|
|
63
21
|
/**
|
|
64
|
-
* Generate an app manifest
|
|
22
|
+
* Generate an app manifest
|
|
65
23
|
*/
|
|
66
|
-
generateManifest(
|
|
67
|
-
|
|
68
|
-
case 'marketing-site':
|
|
69
|
-
case 'marketing':
|
|
70
|
-
return generateMarketingManifest(appName, context)
|
|
71
|
-
|
|
72
|
-
case 'webapp':
|
|
73
|
-
return generateWebappManifest(appName, context)
|
|
74
|
-
|
|
75
|
-
case 'api':
|
|
76
|
-
return generateApiManifest(appName, context)
|
|
24
|
+
generateManifest(appName: string, context: Launch77Context, _options: { port?: string }): AppManifest {
|
|
25
|
+
const workspaceName = context.workspaceName
|
|
77
26
|
|
|
78
|
-
|
|
79
|
-
|
|
27
|
+
return {
|
|
28
|
+
schemaVersion: 1,
|
|
29
|
+
name: toTitleCase(workspaceName),
|
|
30
|
+
package: `@${workspaceName}/${appName}`,
|
|
80
31
|
}
|
|
81
32
|
}
|
|
82
33
|
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
// Supported app types
|
|
2
|
-
export const APP_TYPES = ['api', 'webapp', 'marketing-site'] as const
|
|
3
|
-
export type AppType = (typeof APP_TYPES)[number]
|
|
4
|
-
|
|
5
|
-
// Display list for error messages
|
|
6
|
-
export const APP_TYPES_LIST = APP_TYPES.join(', ')
|
|
7
|
-
|
|
8
1
|
export interface CreateAppRequest {
|
|
9
|
-
type:
|
|
2
|
+
type: string
|
|
10
3
|
appName: string
|
|
11
4
|
port: string
|
|
12
5
|
}
|
|
@@ -14,7 +7,7 @@ export interface CreateAppRequest {
|
|
|
14
7
|
export interface CreateAppResult {
|
|
15
8
|
appPath: string
|
|
16
9
|
appName: string
|
|
17
|
-
type:
|
|
10
|
+
type: string
|
|
18
11
|
}
|
|
19
12
|
|
|
20
13
|
export interface DeleteAppRequest {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import { Command } from 'commander'
|
|
3
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
3
4
|
|
|
4
|
-
import { detectMonorepoContext } from '../../../utils/monorepo.js'
|
|
5
5
|
import { CatalogService } from '../services/catalog-svc.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -17,10 +17,10 @@ export function scanCommand(): Command {
|
|
|
17
17
|
.option('--quiet', 'Suppress quality report output')
|
|
18
18
|
.action(async (options) => {
|
|
19
19
|
try {
|
|
20
|
-
const context = await
|
|
20
|
+
const context = await detectLaunch77Context(process.cwd())
|
|
21
21
|
|
|
22
22
|
if (!context.isValid) {
|
|
23
|
-
console.error(chalk.red('\n✖ Must be run from within a Launch77
|
|
23
|
+
console.error(chalk.red('\n✖ Must be run from within a Launch77 workspace\n'))
|
|
24
24
|
process.exit(1)
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -9,7 +9,7 @@ import { scanUILibrary } from '../scanners/ui-component-scanner.js'
|
|
|
9
9
|
import { generateExamplesFiles } from '../utils/examples-generator.js'
|
|
10
10
|
import { generateQualityReport, printQualityReport } from '../utils/quality-reporter.js'
|
|
11
11
|
|
|
12
|
-
import type {
|
|
12
|
+
import type { Launch77Context } from '@launch77/plugin-runtime'
|
|
13
13
|
import type { ValidationResult, UiComponent } from '../types/catalog-types.js'
|
|
14
14
|
|
|
15
15
|
export interface ScanCatalogOptions {
|
|
@@ -32,13 +32,13 @@ export class CatalogService {
|
|
|
32
32
|
/**
|
|
33
33
|
* Scan Launch77 codebase and generate catalog.json
|
|
34
34
|
*/
|
|
35
|
-
async scanCatalog(context:
|
|
35
|
+
async scanCatalog(context: Launch77Context, options: ScanCatalogOptions = {}): Promise<ScanCatalogResult> {
|
|
36
36
|
const strict = options.strict !== false // Default to true (strict mode)
|
|
37
37
|
const quiet = options.quiet || false
|
|
38
38
|
|
|
39
39
|
console.log(chalk.blue('\n🔍 Scanning Launch77 artifacts...\n'))
|
|
40
40
|
|
|
41
|
-
const launch77Root = path.join(context.
|
|
41
|
+
const launch77Root = path.join(context.workspaceRoot, 'launch77')
|
|
42
42
|
|
|
43
43
|
const spinner = ora('Scanning codebase...').start()
|
|
44
44
|
|
|
@@ -109,7 +109,7 @@ export class CatalogService {
|
|
|
109
109
|
console.log(chalk.green(` ✓ Saved to ${path.relative(process.cwd(), catalogPath)}`))
|
|
110
110
|
|
|
111
111
|
// Sync catalog to Launch77 marketing site
|
|
112
|
-
const marketingSiteCatalogPath = path.join(context.
|
|
112
|
+
const marketingSiteCatalogPath = path.join(context.workspaceRoot, 'apps/marketing/src/catalog.json')
|
|
113
113
|
|
|
114
114
|
if (await fs.pathExists(path.dirname(marketingSiteCatalogPath))) {
|
|
115
115
|
await fs.writeFile(marketingSiteCatalogPath, formatted)
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
import * as path from 'path'
|
|
7
7
|
|
|
8
8
|
import chalk from 'chalk'
|
|
9
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
9
10
|
import fs from 'fs-extra'
|
|
10
11
|
import ora from 'ora'
|
|
11
12
|
|
|
12
|
-
import { detectMonorepoContext } from '../../../utils/monorepo.js'
|
|
13
13
|
import { DeployService } from '../services/deploy-svc.js'
|
|
14
14
|
import { connectGitRepository, getPackageName, getVercelInstallInstructions, isVercelCliInstalled, loadVercelProject, runVercelCommand, updateProjectRootDirectory, verifyVercelProjectExists } from '../utils/vercel-extended.js'
|
|
15
15
|
|
|
@@ -23,16 +23,15 @@ interface DeployInitOptions {
|
|
|
23
23
|
export async function deployInit(options: DeployInitOptions = {}) {
|
|
24
24
|
const deployService = new DeployService()
|
|
25
25
|
|
|
26
|
-
// Detect
|
|
27
|
-
const context = await
|
|
26
|
+
// Detect Launch77 context
|
|
27
|
+
const context = await detectLaunch77Context(process.cwd())
|
|
28
28
|
|
|
29
|
-
if (context.
|
|
30
|
-
throw new Error('Must be run from within an app directory (e.g.,
|
|
29
|
+
if (context.locationType !== 'workspace-app') {
|
|
30
|
+
throw new Error('Must be run from within an app directory (e.g., apps/myapp)')
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const appPath = process.cwd()
|
|
34
34
|
const appName = context.appName!
|
|
35
|
-
const startupName = context.startupName!
|
|
36
35
|
|
|
37
36
|
console.log(chalk.blue(`\n🚀 Initializing Vercel deployment for ${appName}...\n`))
|
|
38
37
|
|
|
@@ -116,23 +115,27 @@ export async function deployInit(options: DeployInitOptions = {}) {
|
|
|
116
115
|
throw new Error('Could not read package name from package.json')
|
|
117
116
|
}
|
|
118
117
|
|
|
118
|
+
// Check template type from app's package.json
|
|
119
|
+
const appPackageJsonPath = path.join(appPath, 'package.json')
|
|
120
|
+
const appPackageJson = await fs.readJson(appPackageJsonPath)
|
|
121
|
+
const templatePackage = appPackageJson.launch77?.template?.package
|
|
122
|
+
|
|
119
123
|
console.log(chalk.gray(`App details:`))
|
|
120
124
|
console.log(chalk.gray(` Name: ${manifest.name}`))
|
|
121
|
-
console.log(chalk.gray(` Type: ${manifest.type}`))
|
|
122
125
|
console.log(chalk.gray(` Package: ${packageName}`))
|
|
126
|
+
if (templatePackage) {
|
|
127
|
+
console.log(chalk.gray(` Template: ${templatePackage}`))
|
|
128
|
+
}
|
|
123
129
|
console.log()
|
|
124
130
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.log(chalk.
|
|
130
|
-
console.log(chalk.
|
|
131
|
-
console.log(chalk.white(' • Fly.io'), chalk.gray('- Global edge deployment'))
|
|
132
|
-
console.log(chalk.white(' • Render.com'), chalk.gray('- Managed cloud hosting'))
|
|
131
|
+
// Only allow webapp apps to deploy to Vercel
|
|
132
|
+
const isWebapp = templatePackage === 'webapp' || templatePackage === '@launch77-shared/app-template-webapp'
|
|
133
|
+
|
|
134
|
+
if (!isWebapp) {
|
|
135
|
+
console.log(chalk.yellow('⚠️ Vercel deployment only supports webapp apps'))
|
|
136
|
+
console.log(chalk.gray('Other app types coming soon'))
|
|
133
137
|
console.log()
|
|
134
|
-
|
|
135
|
-
process.exit(0)
|
|
138
|
+
process.exit(1)
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
// Check if vercel.json exists
|
|
@@ -160,7 +163,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
|
|
|
160
163
|
console.log()
|
|
161
164
|
|
|
162
165
|
try {
|
|
163
|
-
const suggestedProjectName =
|
|
166
|
+
const suggestedProjectName = appName
|
|
164
167
|
await runVercelCommand(['link', '--project', suggestedProjectName, '--yes'], appPath)
|
|
165
168
|
|
|
166
169
|
console.log()
|
|
@@ -172,7 +175,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
|
|
|
172
175
|
|
|
173
176
|
// Read the created .vercel/project.json to get project info
|
|
174
177
|
const vercelProject = await loadVercelProject(appPath)
|
|
175
|
-
const projectName = vercelProject?.projectName ||
|
|
178
|
+
const projectName = vercelProject?.projectName || appName
|
|
176
179
|
|
|
177
180
|
// Update manifest with deployment configuration
|
|
178
181
|
manifest.deployment = {
|
|
@@ -187,7 +190,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
|
|
|
187
190
|
|
|
188
191
|
// Set Root Directory for monorepo deployments
|
|
189
192
|
console.log()
|
|
190
|
-
const rootDirectory = `
|
|
193
|
+
const rootDirectory = `apps/${appName}`
|
|
191
194
|
await updateProjectRootDirectory(projectName, rootDirectory)
|
|
192
195
|
|
|
193
196
|
// Automatically connect to Git repository
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* eslint-disable no-console */
|
|
6
|
+
import * as path from 'path'
|
|
7
|
+
|
|
6
8
|
import chalk from 'chalk'
|
|
9
|
+
import { detectLaunch77Context } from '@launch77/plugin-runtime'
|
|
10
|
+
import fs from 'fs-extra'
|
|
7
11
|
|
|
8
|
-
import { detectMonorepoContext } from '../../../utils/monorepo.js'
|
|
9
12
|
import { DeployService } from '../services/deploy-svc.js'
|
|
10
13
|
import { getVercelInstallInstructions, isVercelCliInstalled, runVercelCommand } from '../utils/vercel-extended.js'
|
|
11
14
|
|
|
@@ -20,11 +23,11 @@ export interface DeployLogsOptions {
|
|
|
20
23
|
export async function deployLogs(options: DeployLogsOptions) {
|
|
21
24
|
const deployService = new DeployService()
|
|
22
25
|
|
|
23
|
-
// Detect
|
|
24
|
-
const context = await
|
|
26
|
+
// Detect Launch77 context
|
|
27
|
+
const context = await detectLaunch77Context(process.cwd())
|
|
25
28
|
|
|
26
|
-
if (context.
|
|
27
|
-
throw new Error('Must be run from within an app directory (e.g.,
|
|
29
|
+
if (context.locationType !== 'workspace-app') {
|
|
30
|
+
throw new Error('Must be run from within an app directory (e.g., apps/myapp)')
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
const appPath = process.cwd()
|
|
@@ -34,14 +37,17 @@ export async function deployLogs(options: DeployLogsOptions) {
|
|
|
34
37
|
// Load manifest
|
|
35
38
|
const manifest = await deployService.loadManifest()
|
|
36
39
|
|
|
37
|
-
// Check
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
// Check template type from app's package.json
|
|
41
|
+
const appPackageJsonPath = path.join(appPath, 'package.json')
|
|
42
|
+
const appPackageJson = await fs.readJson(appPackageJsonPath)
|
|
43
|
+
const templatePackage = appPackageJson.launch77?.template?.package
|
|
44
|
+
|
|
45
|
+
// Only allow webapp apps to deploy to Vercel
|
|
46
|
+
const isWebapp = templatePackage === 'webapp' || templatePackage === '@launch77-shared/app-template-webapp'
|
|
47
|
+
|
|
48
|
+
if (!isWebapp) {
|
|
49
|
+
console.log(chalk.yellow('⚠️ Vercel deployment only supports webapp apps'))
|
|
50
|
+
console.log(chalk.gray('Other app types coming soon'))
|
|
45
51
|
console.log()
|
|
46
52
|
return
|
|
47
53
|
}
|