simplemdg-dev-cli 1.5.1 → 2.4.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.
- package/README.md +65 -243
- package/USER_GUIDE.md +55 -249
- package/dist/commands/cds.command.js +69 -60
- package/dist/commands/cds.command.js.map +1 -1
- package/dist/commands/cf-db.command.d.ts +2 -0
- package/dist/commands/cf-db.command.js +606 -0
- package/dist/commands/cf-db.command.js.map +1 -0
- package/dist/commands/cf.command.js +1625 -198
- package/dist/commands/cf.command.js.map +1 -1
- package/dist/commands/gitlab.command.d.ts +2 -0
- package/dist/commands/gitlab.command.js +351 -0
- package/dist/commands/gitlab.command.js.map +1 -0
- package/dist/commands/npmrc.command.js +50 -44
- package/dist/commands/npmrc.command.js.map +1 -1
- package/dist/core/cache.d.ts +1 -1
- package/dist/core/cache.js +58 -31
- package/dist/core/cache.js.map +1 -1
- package/dist/core/cds.js +32 -22
- package/dist/core/cds.js.map +1 -1
- package/dist/core/cf-env-parser.d.ts +1 -1
- package/dist/core/cf-env-parser.js +4 -1
- package/dist/core/cf-env-parser.js.map +1 -1
- package/dist/core/cf.d.ts +1 -1
- package/dist/core/cf.js +46 -31
- package/dist/core/cf.js.map +1 -1
- package/dist/core/db/db-btp.d.ts +48 -0
- package/dist/core/db/db-btp.js +162 -0
- package/dist/core/db/db-btp.js.map +1 -0
- package/dist/core/db/db-cache.d.ts +35 -0
- package/dist/core/db/db-cache.js +164 -0
- package/dist/core/db/db-cache.js.map +1 -0
- package/dist/core/db/db-connection.d.ts +22 -0
- package/dist/core/db/db-connection.js +73 -0
- package/dist/core/db/db-connection.js.map +1 -0
- package/dist/core/db/db-crypto.d.ts +3 -0
- package/dist/core/db/db-crypto.js +54 -0
- package/dist/core/db/db-crypto.js.map +1 -0
- package/dist/core/db/db-hana-adapter.d.ts +32 -0
- package/dist/core/db/db-hana-adapter.js +243 -0
- package/dist/core/db/db-hana-adapter.js.map +1 -0
- package/dist/core/db/db-metadata.d.ts +25 -0
- package/dist/core/db/db-metadata.js +150 -0
- package/dist/core/db/db-metadata.js.map +1 -0
- package/dist/core/db/db-postgres-adapter.d.ts +30 -0
- package/dist/core/db/db-postgres-adapter.js +245 -0
- package/dist/core/db/db-postgres-adapter.js.map +1 -0
- package/dist/core/db/db-query-files.d.ts +20 -0
- package/dist/core/db/db-query-files.js +106 -0
- package/dist/core/db/db-query-files.js.map +1 -0
- package/dist/core/db/db-query-history.d.ts +5 -0
- package/dist/core/db/db-query-history.js +49 -0
- package/dist/core/db/db-query-history.js.map +1 -0
- package/dist/core/db/db-row.d.ts +22 -0
- package/dist/core/db/db-row.js +70 -0
- package/dist/core/db/db-row.js.map +1 -0
- package/dist/core/db/db-studio-html.d.ts +4 -0
- package/dist/core/db/db-studio-html.js +437 -0
- package/dist/core/db/db-studio-html.js.map +1 -0
- package/dist/core/db/db-studio-server.d.ts +11 -0
- package/dist/core/db/db-studio-server.js +465 -0
- package/dist/core/db/db-studio-server.js.map +1 -0
- package/dist/core/db/db-types.d.ts +174 -0
- package/dist/core/db/db-types.js +3 -0
- package/dist/core/db/db-types.js.map +1 -0
- package/dist/core/db/db-vcap-parser.d.ts +7 -0
- package/dist/core/db/db-vcap-parser.js +137 -0
- package/dist/core/db/db-vcap-parser.js.map +1 -0
- package/dist/core/doctor.d.ts +1 -1
- package/dist/core/doctor.js +14 -8
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/guide.js +31 -26
- package/dist/core/guide.js.map +1 -1
- package/dist/core/install.d.ts +1 -1
- package/dist/core/install.js +17 -11
- package/dist/core/install.js.map +1 -1
- package/dist/core/navigator.d.ts +17 -0
- package/dist/core/navigator.js +140 -0
- package/dist/core/navigator.js.map +1 -0
- package/dist/core/npmrc.js +29 -16
- package/dist/core/npmrc.js.map +1 -1
- package/dist/core/process.js +11 -6
- package/dist/core/process.js.map +1 -1
- package/dist/core/prompts.js +16 -8
- package/dist/core/prompts.js.map +1 -1
- package/dist/core/repository.d.ts +1 -1
- package/dist/core/repository.js +16 -9
- package/dist/core/repository.js.map +1 -1
- package/dist/core/scanner.d.ts +1 -1
- package/dist/core/scanner.js +13 -7
- package/dist/core/scanner.js.map +1 -1
- package/dist/core/tooling.d.ts +28 -0
- package/dist/core/tooling.js +168 -0
- package/dist/core/tooling.js.map +1 -0
- package/dist/core/types.js +2 -1
- package/dist/core/version-conflict.d.ts +2 -2
- package/dist/core/version-conflict.js +11 -6
- package/dist/core/version-conflict.js.map +1 -1
- package/dist/index.js +65 -48
- package/dist/index.js.map +1 -1
- package/dist/types-local.js +2 -1
- package/package.json +12 -6
- package/src/commands/cds.command.ts +529 -0
- package/src/commands/cf-db.command.ts +636 -0
- package/src/commands/cf.command.ts +3345 -0
- package/src/commands/gitlab.command.ts +373 -0
- package/src/commands/npmrc.command.ts +581 -0
- package/src/core/cache.ts +332 -0
- package/src/core/cds.ts +278 -0
- package/src/core/cf-env-parser.ts +131 -0
- package/src/core/cf.ts +271 -0
- package/src/core/db/db-btp.ts +207 -0
- package/src/core/db/db-cache.ts +215 -0
- package/src/core/db/db-connection.ts +79 -0
- package/src/core/db/db-crypto.ts +53 -0
- package/src/core/db/db-hana-adapter.ts +294 -0
- package/src/core/db/db-metadata.ts +174 -0
- package/src/core/db/db-postgres-adapter.ts +275 -0
- package/src/core/db/db-query-files.ts +130 -0
- package/src/core/db/db-query-history.ts +53 -0
- package/src/core/db/db-row.ts +93 -0
- package/src/core/db/db-studio-html.ts +439 -0
- package/src/core/db/db-studio-server.ts +559 -0
- package/src/core/db/db-types.ts +195 -0
- package/src/core/db/db-vcap-parser.ts +182 -0
- package/src/core/doctor.ts +70 -0
- package/src/core/guide.ts +261 -0
- package/src/core/install.ts +91 -0
- package/src/core/navigator.ts +164 -0
- package/src/core/npmrc.ts +171 -0
- package/src/core/process.ts +75 -0
- package/src/core/prompts.ts +225 -0
- package/src/core/repository.ts +36 -0
- package/src/core/scanner.ts +41 -0
- package/src/core/tooling.ts +207 -0
- package/src/core/types.ts +152 -0
- package/src/core/version-conflict.ts +46 -0
- package/src/index.ts +460 -0
- package/src/types/external.d.ts +3 -0
- package/src/types-local.ts +11 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import {
|
|
6
|
+
buildDefaultCompileOutputFileNames,
|
|
7
|
+
buildDefaultEdmxOutputFileName,
|
|
8
|
+
resolveDefaultCdsModel,
|
|
9
|
+
scanCapProfiles,
|
|
10
|
+
scanCapServices,
|
|
11
|
+
type TCdsServiceDefinition,
|
|
12
|
+
} from "../core/cds";
|
|
13
|
+
import {
|
|
14
|
+
readCache,
|
|
15
|
+
rememberCdsEdmxOutputFileName,
|
|
16
|
+
rememberCdsModel,
|
|
17
|
+
rememberCdsPort,
|
|
18
|
+
rememberCdsProfile,
|
|
19
|
+
rememberCdsService,
|
|
20
|
+
} from "../core/cache";
|
|
21
|
+
import { runCommand, runCommandInherit } from "../core/process";
|
|
22
|
+
import { resolveRepositoryPath } from "../core/repository";
|
|
23
|
+
import { searchableSelectChoice, searchableSelectOrInput } from "../core/prompts";
|
|
24
|
+
import { ensureExternalTool } from "../core/tooling";
|
|
25
|
+
|
|
26
|
+
const NO_PROFILE_VALUE = "__SMDG_NO_PROFILE__";
|
|
27
|
+
const NO_PORT_VALUE = "__SMDG_NO_PORT__";
|
|
28
|
+
const DEFAULT_PORTS = ["4004", "4005", "4010", "4002", "4003", "4006"];
|
|
29
|
+
const DEFAULT_CDS_COMPILE_FORMATS = ["edmx", "edm", "csn", "json", "sql", "yaml"];
|
|
30
|
+
|
|
31
|
+
type TCdsWatchCommandOptions = {
|
|
32
|
+
cwd?: string;
|
|
33
|
+
profile?: string;
|
|
34
|
+
port?: string;
|
|
35
|
+
skipProfile?: boolean;
|
|
36
|
+
skipPort?: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type TCdsProfilesCommandOptions = {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type TCdsServicesCommandOptions = {
|
|
44
|
+
cwd?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type TCdsCompileCommandOptions = {
|
|
48
|
+
cwd?: string;
|
|
49
|
+
service?: string;
|
|
50
|
+
model?: string;
|
|
51
|
+
out?: string;
|
|
52
|
+
profile?: string;
|
|
53
|
+
all?: boolean;
|
|
54
|
+
print?: boolean;
|
|
55
|
+
to?: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function uniqueValues(values: Array<string | undefined>): string[] {
|
|
59
|
+
return [...new Set(values.map((value) => value?.trim() ?? "").filter(Boolean))];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function validateRequired(value: string): true | string {
|
|
63
|
+
return value.trim() ? true : "Value is required";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validatePort(value: string): true | string {
|
|
67
|
+
if (!value.trim()) {
|
|
68
|
+
return "Port is required";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const port = Number(value);
|
|
72
|
+
|
|
73
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
74
|
+
return "Port must be a number from 1 to 65535";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isServiceOptionRequired(to: string): boolean {
|
|
81
|
+
return ["edmx", "edm"].includes(to);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeCompileFormat(value: string | undefined): string {
|
|
85
|
+
return (value?.trim() || "edmx").replace(/^--to\s+/, "");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function resolveProfile(options: {
|
|
89
|
+
repositoryPath: string;
|
|
90
|
+
profile?: string;
|
|
91
|
+
skipProfile?: boolean;
|
|
92
|
+
}): Promise<string | undefined> {
|
|
93
|
+
if (options.skipProfile) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (options.profile?.trim()) {
|
|
98
|
+
await rememberCdsProfile(options.profile.trim());
|
|
99
|
+
return options.profile.trim();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const cache = await readCache();
|
|
103
|
+
const scannedProfiles = await scanCapProfiles(options.repositoryPath);
|
|
104
|
+
const profiles = uniqueValues([...cache.cds.profiles, ...scannedProfiles]);
|
|
105
|
+
|
|
106
|
+
const selectedProfile = await searchableSelectChoice({
|
|
107
|
+
message: "Select CAP profile",
|
|
108
|
+
choices: [
|
|
109
|
+
...profiles.map((profile) => ({ title: profile, value: profile })),
|
|
110
|
+
{ title: "Run without --profile", value: NO_PROFILE_VALUE },
|
|
111
|
+
],
|
|
112
|
+
validateCustomValue: validateRequired,
|
|
113
|
+
customValueTitle: (value) => `Use typed CAP profile: ${value}`,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (selectedProfile === NO_PROFILE_VALUE) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await rememberCdsProfile(selectedProfile);
|
|
121
|
+
return selectedProfile;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function resolvePort(options: {
|
|
125
|
+
port?: string;
|
|
126
|
+
skipPort?: boolean;
|
|
127
|
+
}): Promise<string | undefined> {
|
|
128
|
+
if (options.skipPort) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options.port?.trim()) {
|
|
133
|
+
const validationResult = validatePort(options.port.trim());
|
|
134
|
+
|
|
135
|
+
if (validationResult !== true) {
|
|
136
|
+
throw new Error(validationResult);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await rememberCdsPort(options.port.trim());
|
|
140
|
+
return options.port.trim();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const cache = await readCache();
|
|
144
|
+
const ports = uniqueValues([...cache.cds.ports, ...DEFAULT_PORTS]);
|
|
145
|
+
|
|
146
|
+
const selectedPort = await searchableSelectChoice({
|
|
147
|
+
message: "Select CAP port",
|
|
148
|
+
choices: [
|
|
149
|
+
...ports.map((port) => ({ title: port, value: port })),
|
|
150
|
+
{ title: "Run without --port", value: NO_PORT_VALUE },
|
|
151
|
+
],
|
|
152
|
+
validateCustomValue: validatePort,
|
|
153
|
+
customValueTitle: (value) => `Use typed port: ${value}`,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (selectedPort === NO_PORT_VALUE) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await rememberCdsPort(selectedPort);
|
|
161
|
+
return selectedPort;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function resolveCompileFormat(options: { to?: string }): Promise<string> {
|
|
165
|
+
if (options.to?.trim()) {
|
|
166
|
+
return normalizeCompileFormat(options.to);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return searchableSelectChoice({
|
|
170
|
+
message: "Select cds compile --to format",
|
|
171
|
+
choices: DEFAULT_CDS_COMPILE_FORMATS.map((format) => ({ title: format, value: format })),
|
|
172
|
+
validateCustomValue: validateRequired,
|
|
173
|
+
customValueTitle: (value) => `Use typed --to format: ${value}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function resolveCompileService(options: {
|
|
178
|
+
repositoryPath: string;
|
|
179
|
+
service?: string;
|
|
180
|
+
required: boolean;
|
|
181
|
+
}): Promise<{ serviceName?: string; serviceDefinition?: TCdsServiceDefinition }> {
|
|
182
|
+
const scannedServices = await scanCapServices(options.repositoryPath);
|
|
183
|
+
|
|
184
|
+
if (options.service?.trim()) {
|
|
185
|
+
const inputServiceName = options.service.trim();
|
|
186
|
+
const serviceDefinition = scannedServices.find((item) => {
|
|
187
|
+
return item.serviceName === inputServiceName || item.fullServiceName === inputServiceName;
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await rememberCdsService(inputServiceName);
|
|
191
|
+
return {
|
|
192
|
+
serviceName: serviceDefinition?.fullServiceName ?? inputServiceName,
|
|
193
|
+
serviceDefinition,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!scannedServices.length) {
|
|
198
|
+
if (options.required) {
|
|
199
|
+
throw new Error("No CAP service was found in srv/app/db. Please pass --service manually.");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const choices = scannedServices.map((serviceDefinition) => {
|
|
206
|
+
const titleServiceName = serviceDefinition.fullServiceName === serviceDefinition.serviceName
|
|
207
|
+
? serviceDefinition.serviceName
|
|
208
|
+
: `${serviceDefinition.fullServiceName} (${serviceDefinition.serviceName})`;
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
title: `${titleServiceName} (${serviceDefinition.relativeFilePath})`,
|
|
212
|
+
value: serviceDefinition.fullServiceName,
|
|
213
|
+
description: serviceDefinition.relativeFilePath,
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const selectedServiceName = await searchableSelectChoice({
|
|
218
|
+
message: "Select CAP service",
|
|
219
|
+
choices,
|
|
220
|
+
validateCustomValue: validateRequired,
|
|
221
|
+
customValueTitle: (value) => `Use typed service: ${value}`,
|
|
222
|
+
limit: 20,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const serviceDefinition = scannedServices.find((item) => item.fullServiceName === selectedServiceName || item.serviceName === selectedServiceName);
|
|
226
|
+
const serviceName = serviceDefinition?.fullServiceName ?? selectedServiceName;
|
|
227
|
+
|
|
228
|
+
await rememberCdsService(serviceName);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
serviceName,
|
|
232
|
+
serviceDefinition,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function resolveCompileModel(options: {
|
|
237
|
+
repositoryPath: string;
|
|
238
|
+
model?: string;
|
|
239
|
+
serviceDefinition?: TCdsServiceDefinition;
|
|
240
|
+
}): Promise<string> {
|
|
241
|
+
if (options.model?.trim()) {
|
|
242
|
+
await rememberCdsModel(options.model.trim());
|
|
243
|
+
return options.model.trim();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const cache = await readCache();
|
|
247
|
+
const defaultModel = options.serviceDefinition?.relativeFilePath ?? await resolveDefaultCdsModel(options.repositoryPath);
|
|
248
|
+
const fallbackModel = await resolveDefaultCdsModel(options.repositoryPath);
|
|
249
|
+
const model = await searchableSelectOrInput({
|
|
250
|
+
message: "Select CDS model/path",
|
|
251
|
+
values: uniqueValues([defaultModel, fallbackModel, ".", ...cache.cds.models]),
|
|
252
|
+
initialValue: defaultModel,
|
|
253
|
+
validate: validateRequired,
|
|
254
|
+
customValueTitle: (value) => `Use typed model/path: ${value}`,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await rememberCdsModel(model);
|
|
258
|
+
return model;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function resolveCompileOutputFile(options: {
|
|
262
|
+
repositoryPath: string;
|
|
263
|
+
serviceName?: string;
|
|
264
|
+
to: string;
|
|
265
|
+
out?: string;
|
|
266
|
+
}): Promise<string> {
|
|
267
|
+
if (options.out?.trim()) {
|
|
268
|
+
await rememberCdsEdmxOutputFileName(options.out.trim());
|
|
269
|
+
return options.out.trim();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const cache = await readCache();
|
|
273
|
+
const defaultOutputFiles = await buildDefaultCompileOutputFileNames({
|
|
274
|
+
repositoryPath: options.repositoryPath,
|
|
275
|
+
serviceName: options.serviceName,
|
|
276
|
+
to: options.to,
|
|
277
|
+
});
|
|
278
|
+
const outputFile = await searchableSelectOrInput({
|
|
279
|
+
message: "Select output file",
|
|
280
|
+
values: uniqueValues([...defaultOutputFiles, ...cache.cds.edmxOutputFileNames]),
|
|
281
|
+
initialValue: defaultOutputFiles[0] ?? "metadata.xml",
|
|
282
|
+
validate: validateRequired,
|
|
283
|
+
customValueTitle: (value) => `Use typed output file: ${value}`,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await rememberCdsEdmxOutputFileName(outputFile);
|
|
287
|
+
return outputFile;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function compileCds(options: {
|
|
291
|
+
repositoryPath: string;
|
|
292
|
+
model: string;
|
|
293
|
+
to: string;
|
|
294
|
+
serviceName?: string;
|
|
295
|
+
outputFile?: string;
|
|
296
|
+
profile?: string;
|
|
297
|
+
print?: boolean;
|
|
298
|
+
}): Promise<void> {
|
|
299
|
+
const args = ["compile", options.model, "--to", options.to];
|
|
300
|
+
|
|
301
|
+
if (options.serviceName) {
|
|
302
|
+
args.push("--service", options.serviceName);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (options.profile) {
|
|
306
|
+
args.push("--profile", options.profile);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log(chalk.gray(`Running: cds ${args.join(" ")}`));
|
|
310
|
+
console.log(chalk.gray(`CWD: ${options.repositoryPath}`));
|
|
311
|
+
|
|
312
|
+
if (options.outputFile && options.to === "edmx") {
|
|
313
|
+
console.log(chalk.gray(`Equivalent PowerShell: cds ${args.join(" ")} | Out-File -Encoding utf8 ${options.outputFile}`));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const result = await runCommand("cds", args, { cwd: options.repositoryPath, reject: false });
|
|
317
|
+
|
|
318
|
+
if (result.stderr.trim()) {
|
|
319
|
+
console.error(result.stderr);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (result.exitCode !== 0) {
|
|
323
|
+
if (options.serviceName) {
|
|
324
|
+
const services = await scanCapServices(options.repositoryPath);
|
|
325
|
+
const serviceList = services.map((service) => `- ${service.fullServiceName} (${service.relativeFilePath})`).join("\n");
|
|
326
|
+
|
|
327
|
+
if (serviceList) {
|
|
328
|
+
console.log(chalk.yellow("Available services detected in this repository:"));
|
|
329
|
+
console.log(serviceList);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
throw new Error(`cds compile failed with exit code ${result.exitCode}`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (options.print) {
|
|
337
|
+
console.log(result.stdout);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!options.outputFile) {
|
|
342
|
+
throw new Error("Output file is required");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const outputPath = path.isAbsolute(options.outputFile)
|
|
346
|
+
? options.outputFile
|
|
347
|
+
: path.join(options.repositoryPath, options.outputFile);
|
|
348
|
+
|
|
349
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
350
|
+
await fs.writeFile(outputPath, result.stdout, { encoding: "utf8" });
|
|
351
|
+
console.log(chalk.green(`Metadata exported with utf8 encoding: ${outputPath}`));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function runCdsWatchCommand(extraArguments: string[], options: TCdsWatchCommandOptions): Promise<void> {
|
|
355
|
+
await ensureExternalTool("cds");
|
|
356
|
+
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
357
|
+
const profile = await resolveProfile({
|
|
358
|
+
repositoryPath,
|
|
359
|
+
profile: options.profile,
|
|
360
|
+
skipProfile: options.skipProfile,
|
|
361
|
+
});
|
|
362
|
+
const port = await resolvePort({
|
|
363
|
+
port: options.port,
|
|
364
|
+
skipPort: options.skipPort,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const args = ["watch"];
|
|
368
|
+
|
|
369
|
+
if (profile) {
|
|
370
|
+
args.push("--profile", profile);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (port) {
|
|
374
|
+
args.push("--port", port);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
args.push(...extraArguments);
|
|
378
|
+
|
|
379
|
+
console.log(chalk.gray(`Running: cds ${args.join(" ")}`));
|
|
380
|
+
console.log(chalk.gray(`CWD: ${repositoryPath}`));
|
|
381
|
+
console.log("");
|
|
382
|
+
|
|
383
|
+
const exitCode = await runCommandInherit("cds", args, { cwd: repositoryPath });
|
|
384
|
+
process.exitCode = exitCode;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function runCdsProfilesCommand(options: TCdsProfilesCommandOptions): Promise<void> {
|
|
388
|
+
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
389
|
+
const profiles = await scanCapProfiles(repositoryPath);
|
|
390
|
+
|
|
391
|
+
for (const profile of profiles) {
|
|
392
|
+
console.log(profile);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function runCdsServicesCommand(options: TCdsServicesCommandOptions): Promise<void> {
|
|
397
|
+
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
398
|
+
const services = await scanCapServices(repositoryPath);
|
|
399
|
+
|
|
400
|
+
if (!services.length) {
|
|
401
|
+
console.log(chalk.yellow("No CAP services found."));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for (const service of services) {
|
|
406
|
+
console.log(`${service.fullServiceName}\t${service.relativeFilePath}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function runCdsCompileCommand(options: TCdsCompileCommandOptions): Promise<void> {
|
|
411
|
+
await ensureExternalTool("cds");
|
|
412
|
+
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
413
|
+
const to = await resolveCompileFormat({ to: options.to });
|
|
414
|
+
const service = await resolveCompileService({
|
|
415
|
+
repositoryPath,
|
|
416
|
+
service: options.service,
|
|
417
|
+
required: isServiceOptionRequired(to),
|
|
418
|
+
});
|
|
419
|
+
const model = await resolveCompileModel({
|
|
420
|
+
repositoryPath,
|
|
421
|
+
model: options.model,
|
|
422
|
+
serviceDefinition: service.serviceDefinition,
|
|
423
|
+
});
|
|
424
|
+
const profile = options.profile?.trim();
|
|
425
|
+
|
|
426
|
+
if (profile) {
|
|
427
|
+
await rememberCdsProfile(profile);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (options.all) {
|
|
431
|
+
const services = await scanCapServices(repositoryPath);
|
|
432
|
+
|
|
433
|
+
if (!services.length) {
|
|
434
|
+
throw new Error("No CAP services found to export");
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const outputDirectory = options.out?.trim() || "metadata";
|
|
438
|
+
await rememberCdsEdmxOutputFileName(outputDirectory);
|
|
439
|
+
|
|
440
|
+
for (const serviceDefinition of services) {
|
|
441
|
+
const outputFile = path.join(outputDirectory, buildDefaultEdmxOutputFileName(serviceDefinition.fullServiceName));
|
|
442
|
+
await rememberCdsService(serviceDefinition.fullServiceName);
|
|
443
|
+
await compileCds({
|
|
444
|
+
repositoryPath,
|
|
445
|
+
model: serviceDefinition.relativeFilePath,
|
|
446
|
+
to,
|
|
447
|
+
serviceName: serviceDefinition.fullServiceName,
|
|
448
|
+
outputFile,
|
|
449
|
+
profile,
|
|
450
|
+
print: false,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const outputFile = options.print
|
|
458
|
+
? undefined
|
|
459
|
+
: await resolveCompileOutputFile({
|
|
460
|
+
repositoryPath,
|
|
461
|
+
serviceName: service.serviceName,
|
|
462
|
+
to,
|
|
463
|
+
out: options.out,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
await compileCds({
|
|
467
|
+
repositoryPath,
|
|
468
|
+
model,
|
|
469
|
+
to,
|
|
470
|
+
serviceName: service.serviceName,
|
|
471
|
+
outputFile,
|
|
472
|
+
profile,
|
|
473
|
+
print: options.print,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function registerCdsCommands(program: Command): void {
|
|
478
|
+
const cdsCommand = program.command("cds").description("SAP CAP helper commands for SimpleMDG");
|
|
479
|
+
|
|
480
|
+
cdsCommand
|
|
481
|
+
.command("watch [extraArguments...]")
|
|
482
|
+
.description("Run cds watch with searchable profile and optional port")
|
|
483
|
+
.option("--cwd <path>", "Repository path", process.cwd())
|
|
484
|
+
.option("--profile <profile>", "CAP profile, for example hybrid")
|
|
485
|
+
.option("--port <port>", "Port, for example 4005")
|
|
486
|
+
.option("--skip-profile", "Run without --profile")
|
|
487
|
+
.option("--skip-port", "Run without --port")
|
|
488
|
+
.allowUnknownOption(true)
|
|
489
|
+
.action(runCdsWatchCommand);
|
|
490
|
+
|
|
491
|
+
cdsCommand
|
|
492
|
+
.command("profiles")
|
|
493
|
+
.description("Scan available CAP profiles in the current repository")
|
|
494
|
+
.option("--cwd <path>", "Repository path", process.cwd())
|
|
495
|
+
.action(runCdsProfilesCommand);
|
|
496
|
+
|
|
497
|
+
cdsCommand
|
|
498
|
+
.command("services")
|
|
499
|
+
.description("Scan available CAP services in the current repository")
|
|
500
|
+
.option("--cwd <path>", "Repository path", process.cwd())
|
|
501
|
+
.action(runCdsServicesCommand);
|
|
502
|
+
|
|
503
|
+
cdsCommand
|
|
504
|
+
.command("compline")
|
|
505
|
+
.alias("compile")
|
|
506
|
+
.description("Run cds compile and export result with utf8 encoding")
|
|
507
|
+
.option("--cwd <path>", "Repository path", process.cwd())
|
|
508
|
+
.option("--model <model>", "CDS model/path, for example srv, . or srv/main-service.cds")
|
|
509
|
+
.option("--service <service>", "CAP service name to export")
|
|
510
|
+
.option("--to <format>", "cds compile output format, default edmx", "edmx")
|
|
511
|
+
.option("--out <fileOrDirectory>", "Output file, or output directory when --all is used")
|
|
512
|
+
.option("--profile <profile>", "CAP profile used by cds compile")
|
|
513
|
+
.option("--all", "Export all scanned services to a directory")
|
|
514
|
+
.option("--print", "Print output to terminal instead of writing a file")
|
|
515
|
+
.action(runCdsCompileCommand);
|
|
516
|
+
|
|
517
|
+
cdsCommand
|
|
518
|
+
.command("edmx")
|
|
519
|
+
.alias("metadata")
|
|
520
|
+
.description("Export CAP service metadata EDMX XML. Alias of smdg cds compline --to edmx")
|
|
521
|
+
.option("--cwd <path>", "Repository path", process.cwd())
|
|
522
|
+
.option("--model <model>", "CDS model/path, for example srv, . or srv/main-service.cds")
|
|
523
|
+
.option("--service <service>", "CAP service name to export")
|
|
524
|
+
.option("--out <fileOrDirectory>", "Output XML file, or output directory when --all is used")
|
|
525
|
+
.option("--profile <profile>", "CAP profile used by cds compile")
|
|
526
|
+
.option("--all", "Export all scanned services to a directory")
|
|
527
|
+
.option("--print", "Print EDMX to terminal instead of writing a file")
|
|
528
|
+
.action((options: Omit<TCdsCompileCommandOptions, "to">) => runCdsCompileCommand({ ...options, to: "edmx" }));
|
|
529
|
+
}
|