@launch77/cli 1.7.2 → 1.7.4

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 (145) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/infrastructure/package-resolver.test.js +9 -7
  3. package/dist/infrastructure/package-resolver.test.js.map +1 -1
  4. package/dist/modules/app/commands/create-app.d.ts.map +1 -1
  5. package/dist/modules/app/commands/create-app.js +1 -1
  6. package/dist/modules/app/commands/create-app.js.map +1 -1
  7. package/dist/modules/app/commands/delete-app.d.ts.map +1 -1
  8. package/dist/modules/app/commands/delete-app.js +1 -1
  9. package/dist/modules/app/commands/delete-app.js.map +1 -1
  10. package/dist/modules/app/lib/app-template-resolver.d.ts +1 -1
  11. package/dist/modules/app/lib/app-template-resolver.d.ts.map +1 -1
  12. package/dist/modules/app/lib/app-template-resolver.js +1 -1
  13. package/dist/modules/app/lib/app-template-resolver.js.map +1 -1
  14. package/dist/modules/app/services/app-svc.d.ts +1 -0
  15. package/dist/modules/app/services/app-svc.d.ts.map +1 -1
  16. package/dist/modules/app/services/app-svc.js +4 -4
  17. package/dist/modules/app/services/app-svc.js.map +1 -1
  18. package/dist/modules/app/services/manifest-svc.d.ts +1 -1
  19. package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
  20. package/dist/modules/catalog/commands/scan.d.ts.map +1 -1
  21. package/dist/modules/catalog/commands/scan.js +1 -1
  22. package/dist/modules/catalog/commands/scan.js.map +1 -1
  23. package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -1
  24. package/dist/modules/deploy/commands/deploy-init-action.js +1 -1
  25. package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -1
  26. package/dist/modules/deploy/commands/deploy-logs-action.js +1 -1
  27. package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -1
  28. package/dist/modules/deploy/commands/deploy-status-action.js +1 -1
  29. package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -1
  30. package/dist/modules/git/commands/git-connect.d.ts.map +1 -1
  31. package/dist/modules/git/commands/git-connect.js +3 -3
  32. package/dist/modules/git/commands/git-connect.js.map +1 -1
  33. package/dist/modules/library/commands/create-library.d.ts.map +1 -1
  34. package/dist/modules/library/commands/create-library.js +1 -1
  35. package/dist/modules/library/commands/create-library.js.map +1 -1
  36. package/dist/modules/library/commands/delete-library.d.ts.map +1 -1
  37. package/dist/modules/library/commands/delete-library.js +1 -1
  38. package/dist/modules/library/commands/delete-library.js.map +1 -1
  39. package/dist/modules/library/services/library-create-svc.js +1 -1
  40. package/dist/modules/library/services/library-create-svc.js.map +1 -1
  41. package/dist/modules/library/services/library-svc.d.ts +1 -0
  42. package/dist/modules/library/services/library-svc.d.ts.map +1 -1
  43. package/dist/modules/library/services/library-svc.js +3 -2
  44. package/dist/modules/library/services/library-svc.js.map +1 -1
  45. package/dist/modules/plugin/commands/delete-plugin.d.ts.map +1 -1
  46. package/dist/modules/plugin/commands/delete-plugin.js +1 -2
  47. package/dist/modules/plugin/commands/delete-plugin.js.map +1 -1
  48. package/dist/modules/plugin/commands/plugin-create.d.ts.map +1 -1
  49. package/dist/modules/plugin/commands/plugin-create.js +1 -1
  50. package/dist/modules/plugin/commands/plugin-create.js.map +1 -1
  51. package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -1
  52. package/dist/modules/plugin/commands/plugin-install.js +1 -2
  53. package/dist/modules/plugin/commands/plugin-install.js.map +1 -1
  54. package/dist/modules/plugin/index.d.ts +3 -3
  55. package/dist/modules/plugin/index.d.ts.map +1 -1
  56. package/dist/modules/plugin/index.js +2 -3
  57. package/dist/modules/plugin/index.js.map +1 -1
  58. package/dist/modules/plugin/lib/plugin-resolver.test.js +10 -6
  59. package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -1
  60. package/dist/modules/plugin/services/plugin-create-service.d.ts +1 -0
  61. package/dist/modules/plugin/services/plugin-create-service.d.ts.map +1 -1
  62. package/dist/modules/plugin/services/plugin-create-service.js +4 -3
  63. package/dist/modules/plugin/services/plugin-create-service.js.map +1 -1
  64. package/dist/modules/plugin/services/plugin-svc.test.js +34 -30
  65. package/dist/modules/plugin/services/plugin-svc.test.js.map +1 -1
  66. package/dist/modules/release/commands/release-init.d.ts.map +1 -1
  67. package/dist/modules/release/commands/release-init.js +2 -2
  68. package/dist/modules/release/commands/release-init.js.map +1 -1
  69. package/dist/modules/release/services/release-service.d.ts.map +1 -1
  70. package/dist/modules/release/services/release-service.js +3 -3
  71. package/dist/modules/release/services/release-service.js.map +1 -1
  72. package/dist/modules/workspace/services/workspace-service.d.ts +1 -0
  73. package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -1
  74. package/dist/modules/workspace/services/workspace-service.js +3 -2
  75. package/dist/modules/workspace/services/workspace-service.js.map +1 -1
  76. package/dist/templates/plugin/README.md.hbs +10 -0
  77. package/dist/templates/plugin/package.json.hbs +1 -1
  78. package/dist/templates/plugin/scripts/validate-plugin-json.js +13 -2
  79. package/dist/templates/workspace/.eslintignore +2 -1
  80. package/package.json +3 -3
  81. package/src/infrastructure/package-resolver.test.ts +9 -8
  82. package/src/modules/app/commands/create-app.ts +1 -1
  83. package/src/modules/app/commands/delete-app.ts +1 -1
  84. package/src/modules/app/lib/app-template-resolver.ts +1 -2
  85. package/src/modules/app/services/app-svc.ts +6 -7
  86. package/src/modules/app/services/manifest-svc.ts +1 -1
  87. package/src/modules/catalog/commands/scan.ts +1 -2
  88. package/src/modules/catalog/services/catalog-svc.ts +1 -1
  89. package/src/modules/deploy/commands/deploy-init-action.ts +1 -1
  90. package/src/modules/deploy/commands/deploy-logs-action.ts +1 -1
  91. package/src/modules/deploy/commands/deploy-status-action.ts +1 -1
  92. package/src/modules/git/commands/git-connect.ts +3 -3
  93. package/src/modules/library/commands/create-library.ts +1 -1
  94. package/src/modules/library/commands/delete-library.ts +1 -1
  95. package/src/modules/library/services/library-create-svc.ts +1 -1
  96. package/src/modules/library/services/library-svc.ts +3 -2
  97. package/src/modules/plugin/commands/delete-plugin.ts +1 -3
  98. package/src/modules/plugin/commands/plugin-create.ts +1 -1
  99. package/src/modules/plugin/commands/plugin-install.ts +1 -3
  100. package/src/modules/plugin/index.ts +4 -6
  101. package/src/modules/plugin/lib/plugin-resolver.test.ts +10 -7
  102. package/src/modules/plugin/services/plugin-create-service.ts +4 -3
  103. package/src/modules/plugin/services/plugin-svc.test.ts +52 -32
  104. package/src/modules/release/commands/release-init.ts +2 -2
  105. package/src/modules/release/services/release-service.ts +4 -3
  106. package/src/modules/workspace/services/workspace-service.ts +3 -2
  107. package/templates/plugin/README.md.hbs +10 -0
  108. package/templates/plugin/package.json.hbs +1 -1
  109. package/templates/plugin/scripts/validate-plugin-json.js +13 -2
  110. package/templates/workspace/.eslintignore +2 -1
  111. package/dist/infrastructure/npm-package.d.ts +0 -42
  112. package/dist/infrastructure/npm-package.d.ts.map +0 -1
  113. package/dist/infrastructure/npm-package.js +0 -46
  114. package/dist/infrastructure/npm-package.js.map +0 -1
  115. package/dist/infrastructure/npm.d.ts +0 -9
  116. package/dist/infrastructure/npm.d.ts.map +0 -1
  117. package/dist/infrastructure/npm.js +0 -17
  118. package/dist/infrastructure/npm.js.map +0 -1
  119. package/dist/infrastructure/package-resolver.d.ts +0 -117
  120. package/dist/infrastructure/package-resolver.d.ts.map +0 -1
  121. package/dist/infrastructure/package-resolver.js +0 -170
  122. package/dist/infrastructure/package-resolver.js.map +0 -1
  123. package/dist/modules/plugin/errors/plugin-errors.d.ts +0 -51
  124. package/dist/modules/plugin/errors/plugin-errors.d.ts.map +0 -1
  125. package/dist/modules/plugin/errors/plugin-errors.js +0 -130
  126. package/dist/modules/plugin/errors/plugin-errors.js.map +0 -1
  127. package/dist/modules/plugin/lib/plugin-resolver.d.ts +0 -14
  128. package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +0 -1
  129. package/dist/modules/plugin/lib/plugin-resolver.js +0 -36
  130. package/dist/modules/plugin/lib/plugin-resolver.js.map +0 -1
  131. package/dist/modules/plugin/services/plugin-svc.d.ts +0 -42
  132. package/dist/modules/plugin/services/plugin-svc.d.ts.map +0 -1
  133. package/dist/modules/plugin/services/plugin-svc.js +0 -257
  134. package/dist/modules/plugin/services/plugin-svc.js.map +0 -1
  135. package/dist/modules/plugin/types/plugin-types.d.ts +0 -25
  136. package/dist/modules/plugin/types/plugin-types.d.ts.map +0 -1
  137. package/dist/modules/plugin/types/plugin-types.js +0 -2
  138. package/dist/modules/plugin/types/plugin-types.js.map +0 -1
  139. package/src/infrastructure/npm-package.ts +0 -73
  140. package/src/infrastructure/npm.ts +0 -18
  141. package/src/infrastructure/package-resolver.ts +0 -223
  142. package/src/modules/plugin/errors/plugin-errors.ts +0 -145
  143. package/src/modules/plugin/lib/plugin-resolver.ts +0 -41
  144. package/src/modules/plugin/services/plugin-svc.ts +0 -303
  145. package/src/modules/plugin/types/plugin-types.ts +0 -29
@@ -1,223 +0,0 @@
1
- import * as path from 'path'
2
-
3
- import { parsePluginName, isValidNpmPackageName } from '@launch77/plugin-runtime'
4
- import fs from 'fs-extra'
5
-
6
- import type { ValidationResult } from '@launch77/plugin-runtime'
7
-
8
- /**
9
- * Base interface for package resolution results
10
- */
11
- export interface PackageResolution {
12
- /** The source of the package */
13
- source: 'local' | 'npm'
14
- /** The resolved name/package to use */
15
- resolvedName: string
16
- /** The local path if source is 'local' */
17
- localPath?: string
18
- /** The npm package name if source is 'npm' */
19
- npmPackage?: string
20
- /** The version from package.json (required for local packages, undefined for npm until installed) */
21
- version?: string
22
- }
23
-
24
- /**
25
- * Abstract base class for resolving Launch77 packages
26
- *
27
- * Provides generic resolution logic for finding packages in:
28
- * 1. Local workspace directory (e.g., plugins/, app-templates/)
29
- * 2. npm packages with configured prefix (e.g., @launch77-shared/plugin-*, @launch77-shared/app-template-*)
30
- *
31
- * Concrete implementations must provide:
32
- * - Folder name for local resolution
33
- * - Package prefix for npm resolution
34
- * - Verification logic to validate local packages
35
- */
36
- export abstract class PackageResolver {
37
- /**
38
- * Get the local folder name where packages of this type are stored
39
- * @example 'plugins' or 'app-templates'
40
- */
41
- protected abstract getFolderName(): string
42
-
43
- /**
44
- * Get the npm package prefix for unscoped packages
45
- * @example '@launch77-shared/plugin-' or '@launch77-shared/app-template-'
46
- */
47
- protected abstract getPackagePrefix(): string
48
-
49
- /**
50
- * Verify that a local package is valid and complete
51
- * @param localPath - The local directory path to verify
52
- * @returns true if the package is valid, false otherwise
53
- */
54
- protected abstract verify(localPath: string): Promise<boolean>
55
-
56
- /**
57
- * Validate package input name
58
- *
59
- * Accepts:
60
- * - Unscoped names (e.g., "release", "my-package")
61
- * - Scoped npm packages (e.g., "@ibm/package-name")
62
- *
63
- * Rejects:
64
- * - Invalid formats
65
- * - Empty strings
66
- * - Names with invalid characters
67
- *
68
- * @param name - The package name to validate
69
- * @returns ValidationResult with isValid and optional error message
70
- *
71
- * @example
72
- * validateInput('release') // { isValid: true }
73
- * validateInput('@ibm/analytics') // { isValid: true }
74
- * validateInput('@invalid') // { isValid: false, error: '...' }
75
- */
76
- validateInput(name: string): ValidationResult {
77
- if (!name || name.trim().length === 0) {
78
- return {
79
- isValid: false,
80
- error: 'Package name cannot be empty',
81
- }
82
- }
83
-
84
- const trimmedName = name.trim()
85
-
86
- // Parse the name to determine type and validate
87
- //TODO: move/rename parsePluginName() to parsePackageName()
88
- const parsed = parsePluginName(trimmedName)
89
-
90
- if (!parsed.isValid) {
91
- return {
92
- isValid: false,
93
- error: parsed.error,
94
- }
95
- }
96
-
97
- // If it's scoped, it must be a valid npm package name
98
- if (parsed.type === 'scoped') {
99
- return isValidNpmPackageName(trimmedName)
100
- }
101
-
102
- // Unscoped names are valid for both local and npm
103
- return { isValid: true }
104
- }
105
-
106
- /**
107
- * Convert an unscoped package name to an npm package name
108
- *
109
- * Rules:
110
- * - Unscoped names: prefix with configured package prefix
111
- * - Scoped names: use as-is
112
- *
113
- * @param name - The package name (must be validated first)
114
- * @returns The npm package name
115
- *
116
- * @example
117
- * toNpmPackageName('release') // '@launch77-shared/plugin-release' (for PluginResolver)
118
- * toNpmPackageName('@ibm/analytics') // '@ibm/analytics'
119
- */
120
- toNpmPackageName(name: string): string {
121
- const trimmedName = name.trim()
122
-
123
- // If already scoped, use as-is
124
- if (trimmedName.startsWith('@')) {
125
- return trimmedName
126
- }
127
-
128
- // Otherwise, convert using configured prefix
129
- return `${this.getPackagePrefix()}${trimmedName}`
130
- }
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
-
156
- /**
157
- * Resolve package location from name
158
- *
159
- * Resolution order:
160
- * 1. Check local workspace directory (configured by getFolderName())
161
- * 2. Verify local package is valid (using verify())
162
- * 3. Fall back to npm package name (with configured prefix)
163
- * 4. Read version from package.json (if available)
164
- *
165
- * @param name - The package name to resolve
166
- * @param workspaceRoot - The workspace root directory
167
- * @returns PackageResolution with source, resolved location, and version
168
- *
169
- * @example
170
- * // Local package found
171
- * await resolveLocation('my-package', '/workspace')
172
- * // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package', version: '1.0.0' }
173
- *
174
- * // Not found locally, resolve to npm
175
- * await resolveLocation('release', '/workspace')
176
- * // { source: 'npm', resolvedName: 'release', npmPackage: '@launch77-shared/plugin-release' }
177
- *
178
- * // Scoped package always resolves to npm
179
- * await resolveLocation('@ibm/analytics', '/workspace')
180
- * // { source: 'npm', resolvedName: '@ibm/analytics', npmPackage: '@ibm/analytics' }
181
- */
182
- async resolveLocation(name: string, workspaceRoot: string): Promise<PackageResolution> {
183
- const trimmedName = name.trim()
184
- const parsed = parsePluginName(trimmedName)
185
-
186
- // If scoped, always use npm (local packages are never scoped)
187
- if (parsed.type === 'scoped') {
188
- return {
189
- source: 'npm',
190
- resolvedName: trimmedName,
191
- npmPackage: trimmedName,
192
- }
193
- }
194
-
195
- // Check local workspace directory
196
- const localPath = path.join(workspaceRoot, this.getFolderName(), trimmedName)
197
- const localExists = await fs.pathExists(localPath)
198
-
199
- if (localExists) {
200
- // Verify it's a valid package
201
- const isValid = await this.verify(localPath)
202
-
203
- if (isValid) {
204
- const version = await this.readVersion(localPath)
205
- return {
206
- source: 'local',
207
- resolvedName: trimmedName,
208
- localPath,
209
- version,
210
- }
211
- }
212
- }
213
-
214
- // Not found locally or invalid, resolve to npm package
215
- const npmPackage = this.toNpmPackageName(trimmedName)
216
-
217
- return {
218
- source: 'npm',
219
- resolvedName: trimmedName,
220
- npmPackage,
221
- }
222
- }
223
- }
@@ -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
- }