@kosdev-code/kos-ui-cli 2.0.43 → 2.0.45
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/README.md +481 -4
- package/package.json +5 -3
- package/src/lib/cli.mjs +59 -467
- package/src/lib/generators/api/compare.mjs +139 -0
- package/src/lib/generators/api/generate.mjs +105 -0
- package/src/lib/generators/api/lib/compare-api.mjs +748 -0
- package/src/lib/generators/api/lib/generate-api.mjs +452 -0
- package/src/lib/generators/dev/index.mjs +437 -0
- package/src/lib/generators/env/index.mjs +1 -0
- package/src/lib/generators/kab/index.mjs +82 -0
- package/src/lib/generators/metadata.json +71 -2
- package/src/lib/generators/model/add-future.mjs +21 -5
- package/src/lib/generators/model/companion.mjs +24 -5
- package/src/lib/generators/model/container.mjs +24 -5
- package/src/lib/generators/model/context.mjs +22 -5
- package/src/lib/generators/model/hook.mjs +22 -5
- package/src/lib/generators/model/model.mjs +3 -1
- package/src/lib/generators/plugin/index.mjs +30 -3
- package/src/lib/generators/serve/index.mjs +74 -0
- package/src/lib/generators/version/index.mjs +182 -0
- package/src/lib/generators/workspace/index.mjs +13 -3
- package/src/lib/plopfile.mjs +12 -0
- package/src/lib/utils/cli-help-display.mjs +84 -0
- package/src/lib/utils/cli-help-utils.mjs +261 -0
- package/src/lib/utils/command-builder.mjs +94 -0
- package/src/lib/utils/dev-config.mjs +150 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for CLI help generation
|
|
3
|
+
*
|
|
4
|
+
* Provides intelligent help generation for KOS CLI commands including:
|
|
5
|
+
* - Example value generation based on argument types and names
|
|
6
|
+
* - Relevant argument filtering for concise examples
|
|
7
|
+
* - Command-line example generation with proper formatting
|
|
8
|
+
* - Named argument documentation display
|
|
9
|
+
*
|
|
10
|
+
* @module cli-help-utils
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generate example values for command-line arguments based on argument names.
|
|
15
|
+
*
|
|
16
|
+
* Uses heuristics to determine appropriate example values based on the argument
|
|
17
|
+
* name and command context. Provides realistic examples for common patterns like
|
|
18
|
+
* names, projects, and boolean flags.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} argName - The argument name (e.g., 'name', 'project', 'singleton')
|
|
21
|
+
* @param {string} command - The command name for context (e.g., 'model', 'api:generate')
|
|
22
|
+
* @returns {string|boolean} Example value appropriate for the argument type
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* generateExampleValue('name', 'model') // Returns 'MyComponent'
|
|
26
|
+
* generateExampleValue('singleton', 'model') // Returns true
|
|
27
|
+
* generateExampleValue('project', 'api:generate') // Returns 'my-ui-lib'
|
|
28
|
+
*/
|
|
29
|
+
export function generateExampleValue(argName, command) {
|
|
30
|
+
switch (argName) {
|
|
31
|
+
case "name":
|
|
32
|
+
case "componentName":
|
|
33
|
+
return command.includes("plugin") ? "MyPlugin" : "MyComponent";
|
|
34
|
+
case "modelName":
|
|
35
|
+
return "my-model";
|
|
36
|
+
case "workspaceName":
|
|
37
|
+
return "my-workspace";
|
|
38
|
+
case "project":
|
|
39
|
+
case "componentProject":
|
|
40
|
+
case "modelProject":
|
|
41
|
+
return "my-ui-lib";
|
|
42
|
+
case "registrationProject":
|
|
43
|
+
return "my-lib";
|
|
44
|
+
case "companionParent":
|
|
45
|
+
return "parent-model";
|
|
46
|
+
case "extensionPoint":
|
|
47
|
+
return "utility";
|
|
48
|
+
case "group":
|
|
49
|
+
return "appearance";
|
|
50
|
+
case "locale":
|
|
51
|
+
return "en";
|
|
52
|
+
case "container":
|
|
53
|
+
case "singleton":
|
|
54
|
+
case "parentAware":
|
|
55
|
+
case "dataServices":
|
|
56
|
+
return true;
|
|
57
|
+
case "dryRun":
|
|
58
|
+
return true;
|
|
59
|
+
default:
|
|
60
|
+
return `my-${argName}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Filter and prioritize arguments for concise help examples.
|
|
66
|
+
*
|
|
67
|
+
* Selects the most relevant arguments to display in help examples by prioritizing:
|
|
68
|
+
* 1. Name-type arguments (name, componentName, modelName, workspaceName)
|
|
69
|
+
* 2. Project-type arguments (project, componentProject, modelProject)
|
|
70
|
+
* 3. Other non-boolean arguments (up to 2 additional)
|
|
71
|
+
*
|
|
72
|
+
* Boolean flags are excluded from this list and handled separately.
|
|
73
|
+
*
|
|
74
|
+
* @param {string[]} args - All available argument names from the generator
|
|
75
|
+
* @param {string} command - The command name for context
|
|
76
|
+
* @returns {string[]} Filtered list of most relevant arguments to show (typically 2-4 items)
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* getRelevantArgs(['name', 'project', 'singleton', 'dryRun'], 'model')
|
|
80
|
+
* // Returns ['name', 'project']
|
|
81
|
+
*/
|
|
82
|
+
export function getRelevantArgs(args, command) {
|
|
83
|
+
const relevantArgs = [];
|
|
84
|
+
|
|
85
|
+
const booleanArgs = [
|
|
86
|
+
"container",
|
|
87
|
+
"singleton",
|
|
88
|
+
"parentAware",
|
|
89
|
+
"dataServices",
|
|
90
|
+
"dryRun",
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Always prefer name-type arguments first
|
|
94
|
+
const nameArgs = [
|
|
95
|
+
"name",
|
|
96
|
+
"componentName",
|
|
97
|
+
"modelName",
|
|
98
|
+
"workspaceName",
|
|
99
|
+
].filter((arg) => args.includes(arg));
|
|
100
|
+
if (nameArgs.length > 0) {
|
|
101
|
+
relevantArgs.push(nameArgs[0]); // Take the first name argument
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Then add project-type arguments
|
|
105
|
+
const projectArgs = [
|
|
106
|
+
"project",
|
|
107
|
+
"componentProject",
|
|
108
|
+
"modelProject",
|
|
109
|
+
"registrationProject",
|
|
110
|
+
].filter((arg) => args.includes(arg));
|
|
111
|
+
if (projectArgs.length > 0) {
|
|
112
|
+
relevantArgs.push(projectArgs[0]); // Take the first project argument
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Add other specific arguments
|
|
116
|
+
const otherArgs = args.filter(
|
|
117
|
+
(arg) =>
|
|
118
|
+
!nameArgs.includes(arg) &&
|
|
119
|
+
!projectArgs.includes(arg) &&
|
|
120
|
+
!booleanArgs.includes(arg) &&
|
|
121
|
+
arg !== "interactive"
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Add up to 2 more relevant arguments
|
|
125
|
+
relevantArgs.push(...otherArgs.slice(0, 2));
|
|
126
|
+
|
|
127
|
+
return relevantArgs;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate command-line examples for a generator with proper formatting.
|
|
132
|
+
*
|
|
133
|
+
* Creates multiple example commands showing:
|
|
134
|
+
* - Basic usage with most relevant arguments
|
|
135
|
+
* - Advanced usage with boolean flags
|
|
136
|
+
* - Interactive mode invocation (both long and short form)
|
|
137
|
+
*
|
|
138
|
+
* Examples are formatted ready for terminal display with proper indentation.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} command - The command name (e.g., 'model', 'api:generate')
|
|
141
|
+
* @param {Object} namedArguments - Mapping of CLI argument names to prompt names
|
|
142
|
+
* @returns {string[]} Array of formatted example command strings with leading spaces
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* generateCommandExamples('api:generate', { project: 'project', host: 'host' })
|
|
146
|
+
* // Returns:
|
|
147
|
+
* // [
|
|
148
|
+
* // ' kosui api:generate --project my-ui-lib --host http://localhost',
|
|
149
|
+
* // ' kosui api:generate --interactive # Force interactive mode',
|
|
150
|
+
* // ' kosui api:generate -i # Force interactive mode (short form)'
|
|
151
|
+
* // ]
|
|
152
|
+
*/
|
|
153
|
+
export function generateCommandExamples(command, namedArguments) {
|
|
154
|
+
if (!namedArguments) {
|
|
155
|
+
return [` kosui ${command} [options]`];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const args = Object.keys(namedArguments);
|
|
159
|
+
const examples = [];
|
|
160
|
+
|
|
161
|
+
const booleanArgs = [
|
|
162
|
+
"container",
|
|
163
|
+
"singleton",
|
|
164
|
+
"parentAware",
|
|
165
|
+
"dataServices",
|
|
166
|
+
"dryRun",
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const relevantArgs = getRelevantArgs(args, command);
|
|
170
|
+
|
|
171
|
+
// Build basic example with most important arguments
|
|
172
|
+
const basicArgs = [];
|
|
173
|
+
relevantArgs.forEach((argName) => {
|
|
174
|
+
const value = generateExampleValue(argName, command);
|
|
175
|
+
basicArgs.push(`--${argName} ${value}`);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (basicArgs.length > 0) {
|
|
179
|
+
examples.push(` kosui ${command} ${basicArgs.join(" ")}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Create advanced example with boolean flags
|
|
183
|
+
const advancedBooleanArgs = [];
|
|
184
|
+
booleanArgs.forEach((argName) => {
|
|
185
|
+
if (args.includes(argName)) {
|
|
186
|
+
advancedBooleanArgs.push(`--${argName}`);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Show advanced example with boolean flags if any exist
|
|
191
|
+
if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
|
|
192
|
+
examples.push(
|
|
193
|
+
` kosui ${command} ${basicArgs
|
|
194
|
+
.slice(0, 2)
|
|
195
|
+
.join(" ")} ${advancedBooleanArgs.join(" ")}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// If no basic args, show a minimal example
|
|
200
|
+
if (basicArgs.length === 0) {
|
|
201
|
+
examples.push(` kosui ${command} [options]`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Always show interactive mode example
|
|
205
|
+
examples.push(` kosui ${command} --interactive # Force interactive mode`);
|
|
206
|
+
examples.push(
|
|
207
|
+
` kosui ${command} -i # Force interactive mode (short form)`
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return examples;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Display the named arguments help section to the console.
|
|
215
|
+
*
|
|
216
|
+
* Outputs a formatted table showing the mapping between CLI argument names
|
|
217
|
+
* and their corresponding prompt names in the generator schema. This helps
|
|
218
|
+
* users understand which CLI arguments map to which interactive prompts.
|
|
219
|
+
*
|
|
220
|
+
* @param {Object} namedArguments - Mapping of CLI argument names to prompt names
|
|
221
|
+
* (e.g., { 'project': 'project', 'host': 'host' })
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* displayNamedArguments({ project: 'project', host: 'host' })
|
|
225
|
+
* // Outputs:
|
|
226
|
+
* // Named Arguments:
|
|
227
|
+
* // --project Maps to prompt: project
|
|
228
|
+
* // --host Maps to prompt: host
|
|
229
|
+
*/
|
|
230
|
+
export function displayNamedArguments(namedArguments) {
|
|
231
|
+
if (!namedArguments) return;
|
|
232
|
+
|
|
233
|
+
console.log("\nNamed Arguments:");
|
|
234
|
+
Object.entries(namedArguments).forEach(([cliArg, promptName]) => {
|
|
235
|
+
console.log(` --${cliArg} Maps to prompt: ${promptName}`);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Display the examples help section to the console.
|
|
241
|
+
*
|
|
242
|
+
* Outputs formatted command-line examples showing various ways to invoke the
|
|
243
|
+
* command, including basic usage, advanced usage with boolean flags, and
|
|
244
|
+
* interactive mode shortcuts.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} command - The command name (e.g., 'model', 'api:generate')
|
|
247
|
+
* @param {Object} namedArguments - Mapping of CLI argument names to prompt names
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* displayExamples('api:generate', { project: 'project', host: 'host' })
|
|
251
|
+
* // Outputs:
|
|
252
|
+
* // Examples:
|
|
253
|
+
* // kosui api:generate --project my-ui-lib --host http://localhost
|
|
254
|
+
* // kosui api:generate --interactive # Force interactive mode
|
|
255
|
+
* // kosui api:generate -i # Force interactive mode (short form)
|
|
256
|
+
*/
|
|
257
|
+
export function displayExamples(command, namedArguments) {
|
|
258
|
+
console.log("\nExamples:");
|
|
259
|
+
const examples = generateCommandExamples(command, namedArguments);
|
|
260
|
+
examples.forEach((example) => console.log(example));
|
|
261
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a non-interactive command string from interactive session answers
|
|
3
|
+
* This helps users understand how to run the same command without prompts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts interactive prompt answers back to CLI command arguments
|
|
8
|
+
* @param {string} command - The generator command name
|
|
9
|
+
* @param {Object} answers - The answers collected from prompts
|
|
10
|
+
* @param {Object} meta - Generator metadata including namedArguments mapping
|
|
11
|
+
* @returns {string|null} The equivalent CLI command string, or null if cannot be built
|
|
12
|
+
*/
|
|
13
|
+
export function buildNonInteractiveCommand(command, answers, meta) {
|
|
14
|
+
if (!meta?.namedArguments) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const args = [`kosui ${command}`];
|
|
19
|
+
const reverseMap = {};
|
|
20
|
+
|
|
21
|
+
// Create reverse mapping from prompt names to CLI arguments
|
|
22
|
+
Object.entries(meta.namedArguments).forEach(([cliArg, promptName]) => {
|
|
23
|
+
// Skip special flags that don't map to prompts
|
|
24
|
+
if (cliArg !== "interactive" && cliArg !== "dryRun") {
|
|
25
|
+
reverseMap[promptName] = cliArg;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Build command arguments from answers
|
|
30
|
+
Object.entries(answers).forEach(([promptName, value]) => {
|
|
31
|
+
const cliArg = reverseMap[promptName];
|
|
32
|
+
if (cliArg && value !== undefined && value !== null && value !== "") {
|
|
33
|
+
args.push(formatArgument(cliArg, value));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Add dryRun if it was provided
|
|
38
|
+
if (answers.dryRun) {
|
|
39
|
+
args.push("--dryRun");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return args.join(" ");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Formats a single CLI argument based on its value type
|
|
47
|
+
* @param {string} argName - The CLI argument name
|
|
48
|
+
* @param {any} value - The value for the argument
|
|
49
|
+
* @returns {string} The formatted CLI argument string
|
|
50
|
+
*/
|
|
51
|
+
function formatArgument(argName, value) {
|
|
52
|
+
// Handle boolean values
|
|
53
|
+
if (typeof value === "boolean") {
|
|
54
|
+
if (value) {
|
|
55
|
+
return `--${argName}`;
|
|
56
|
+
} else {
|
|
57
|
+
// For false booleans, explicitly set to false
|
|
58
|
+
// This ensures the command can be replicated exactly
|
|
59
|
+
return `--${argName}=false`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle string/number values
|
|
64
|
+
const stringValue = String(value);
|
|
65
|
+
|
|
66
|
+
// Check if value needs escaping
|
|
67
|
+
if (needsEscaping(stringValue)) {
|
|
68
|
+
// Use double quotes and escape internal quotes
|
|
69
|
+
return `--${argName}="${stringValue.replace(/"/g, '\\"')}"`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return `--${argName}=${stringValue}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Determines if a string value needs shell escaping
|
|
77
|
+
* @param {string} value - The value to check
|
|
78
|
+
* @returns {boolean} True if the value needs escaping
|
|
79
|
+
*/
|
|
80
|
+
function needsEscaping(value) {
|
|
81
|
+
// Check for characters that need escaping in shell
|
|
82
|
+
return /[\s'"$`\\!]/.test(value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Determines if the CLI is running in interactive mode
|
|
87
|
+
* @param {Object} parsedArgs - Parsed command line arguments
|
|
88
|
+
* @param {boolean} hasAnyAnswers - Whether any answers were provided via CLI
|
|
89
|
+
* @returns {boolean} True if running in interactive mode
|
|
90
|
+
*/
|
|
91
|
+
export function isRunningInteractively(parsedArgs, hasAnyAnswers) {
|
|
92
|
+
const forceInteractive = parsedArgs.interactive || parsedArgs.i;
|
|
93
|
+
return forceInteractive || (!hasAnyAnswers && !parsedArgs.help);
|
|
94
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// utils/dev-config.mjs
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Read the development configuration from root .kos.json
|
|
7
|
+
*
|
|
8
|
+
* @returns {Object|null} Development config or null if not found
|
|
9
|
+
*/
|
|
10
|
+
export function getDevConfig() {
|
|
11
|
+
const workspaceRoot = process.cwd();
|
|
12
|
+
const kosJsonPath = path.join(workspaceRoot, '.kos.json');
|
|
13
|
+
|
|
14
|
+
if (!existsSync(kosJsonPath)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const content = readFileSync(kosJsonPath, 'utf-8');
|
|
20
|
+
const config = JSON.parse(content);
|
|
21
|
+
|
|
22
|
+
if (config.type !== 'root') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!config.development || !config.development.hosts) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return config.development;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(`[kos-cli] Error reading .kos.json: ${error.message}`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Find a host configuration by name
|
|
39
|
+
*
|
|
40
|
+
* @param {string} hostName - Name of the host to find
|
|
41
|
+
* @returns {Object|null} Host config or null if not found
|
|
42
|
+
*/
|
|
43
|
+
export function getHost(hostName) {
|
|
44
|
+
const devConfig = getDevConfig();
|
|
45
|
+
|
|
46
|
+
if (!devConfig) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return devConfig.hosts.find(h => h.name === hostName) || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get all available hosts
|
|
55
|
+
*
|
|
56
|
+
* @returns {Array} Array of host configurations
|
|
57
|
+
*/
|
|
58
|
+
export function getAllHosts() {
|
|
59
|
+
const devConfig = getDevConfig();
|
|
60
|
+
|
|
61
|
+
if (!devConfig) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return devConfig.hosts || [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Filter plugins based on options
|
|
70
|
+
*
|
|
71
|
+
* @param {Array} hostPlugins - Array of plugin configs from host
|
|
72
|
+
* @param {Object} options - Filter options
|
|
73
|
+
* @param {Array} options.plugins - Specific plugin aliases to include
|
|
74
|
+
* @param {Array} options.disable - Plugin aliases to exclude
|
|
75
|
+
* @returns {Array} Filtered array of plugin configurations
|
|
76
|
+
*/
|
|
77
|
+
export function filterPlugins(hostPlugins, options = {}) {
|
|
78
|
+
let plugins = hostPlugins.filter(p => p.enabled);
|
|
79
|
+
|
|
80
|
+
// If specific plugins requested, override enabled flag
|
|
81
|
+
if (options.plugins && options.plugins.length > 0) {
|
|
82
|
+
plugins = hostPlugins.filter(p => options.plugins.includes(p.alias));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Apply disable list
|
|
86
|
+
if (options.disable && options.disable.length > 0) {
|
|
87
|
+
plugins = plugins.filter(p => !options.disable.includes(p.alias));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return plugins;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if a project exists in the workspace
|
|
95
|
+
*
|
|
96
|
+
* @param {string} projectName - Name of the project
|
|
97
|
+
* @returns {boolean} True if project exists
|
|
98
|
+
*/
|
|
99
|
+
export function checkProjectExists(projectName) {
|
|
100
|
+
const workspaceRoot = process.cwd();
|
|
101
|
+
|
|
102
|
+
// Check common project locations
|
|
103
|
+
const appPath = path.join(workspaceRoot, 'apps', projectName, 'project.json');
|
|
104
|
+
const libPath = path.join(workspaceRoot, 'libs', projectName, 'project.json');
|
|
105
|
+
const pluginPath = path.join(workspaceRoot, 'plugins', projectName, 'project.json');
|
|
106
|
+
const packagesPath = path.join(workspaceRoot, 'packages', projectName, 'project.json');
|
|
107
|
+
|
|
108
|
+
return existsSync(appPath) ||
|
|
109
|
+
existsSync(libPath) ||
|
|
110
|
+
existsSync(pluginPath) ||
|
|
111
|
+
existsSync(packagesPath);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Validate development configuration
|
|
116
|
+
*
|
|
117
|
+
* @returns {Object} Validation result with isValid and errors
|
|
118
|
+
*/
|
|
119
|
+
export function validateDevConfig() {
|
|
120
|
+
const devConfig = getDevConfig();
|
|
121
|
+
const errors = [];
|
|
122
|
+
|
|
123
|
+
if (!devConfig) {
|
|
124
|
+
errors.push('.kos.json missing or does not contain development configuration');
|
|
125
|
+
return { isValid: false, errors };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!Array.isArray(devConfig.hosts) || devConfig.hosts.length === 0) {
|
|
129
|
+
errors.push('No hosts defined in development configuration');
|
|
130
|
+
return { isValid: false, errors };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Validate each host
|
|
134
|
+
devConfig.hosts.forEach((host, idx) => {
|
|
135
|
+
if (!host.name) {
|
|
136
|
+
errors.push(`Host at index ${idx} missing required 'name' field`);
|
|
137
|
+
}
|
|
138
|
+
if (!host.project) {
|
|
139
|
+
errors.push(`Host at index ${idx} missing required 'project' field`);
|
|
140
|
+
}
|
|
141
|
+
if (!host.port) {
|
|
142
|
+
errors.push(`Host at index ${idx} missing required 'port' field`);
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(host.plugins)) {
|
|
145
|
+
errors.push(`Host '${host.name}' missing 'plugins' array`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return { isValid: errors.length === 0, errors };
|
|
150
|
+
}
|