@hahnpro/flow-cli 2.17.20 → 2025.10.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/main.js +1020 -0
- package/package.json +7 -42
- package/views/index.css +1033 -0
- package/LICENSE +0 -21
- package/README.md +0 -66
- package/lib/auth.mjs +0 -152
- package/lib/cli.mjs +0 -792
- package/lib/utils.mjs +0 -217
- package/lib/views/index.css +0 -1
- /package/{lib/views → views}/index.ejs +0 -0
package/lib/cli.mjs
DELETED
|
@@ -1,792 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-check
|
|
3
|
-
import 'reflect-metadata';
|
|
4
|
-
import 'dotenv/config';
|
|
5
|
-
|
|
6
|
-
import archiver from 'archiver';
|
|
7
|
-
import Axios from 'axios';
|
|
8
|
-
import { Command } from 'commander';
|
|
9
|
-
import copyfiles from 'copyfiles';
|
|
10
|
-
import { execa } from 'execa';
|
|
11
|
-
import FormData from 'form-data';
|
|
12
|
-
import { glob } from 'glob';
|
|
13
|
-
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import { createRequire } from 'node:module';
|
|
16
|
-
import path from 'node:path';
|
|
17
|
-
import ora from 'ora';
|
|
18
|
-
import url from 'node:url';
|
|
19
|
-
|
|
20
|
-
import { getAccessToken, login, logout } from './auth.mjs';
|
|
21
|
-
import { handleApiError, handleConvertedOutput, logger, prepareTsFile } from './utils.mjs';
|
|
22
|
-
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
24
|
-
// @ts-ignore
|
|
25
|
-
const require = createRequire(import.meta.url);
|
|
26
|
-
const BASE_URL = process.env.BASE_URL || process.env.PLATFORM_URL;
|
|
27
|
-
const BUILD_DIR = process.env.BUILD_DIR || 'dist';
|
|
28
|
-
|
|
29
|
-
const __filename = url.fileURLToPath(import.meta.url);
|
|
30
|
-
const __dirname = path.dirname(__filename);
|
|
31
|
-
|
|
32
|
-
let axios = Axios.create();
|
|
33
|
-
if (process.env.https_proxy || process.env.http_proxy) {
|
|
34
|
-
const httpsAgent = new HttpsProxyAgent(process.env.https_proxy || process.env.http_proxy);
|
|
35
|
-
axios = Axios.create({ httpsAgent, proxy: false });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let apiToken;
|
|
39
|
-
let projectsRoot = 'modules';
|
|
40
|
-
|
|
41
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString());
|
|
42
|
-
|
|
43
|
-
const CMD = {
|
|
44
|
-
AUDIT: 'audit',
|
|
45
|
-
BUILD: 'build',
|
|
46
|
-
FORMAT: 'format',
|
|
47
|
-
INSTALL: 'install',
|
|
48
|
-
LINT: 'lint',
|
|
49
|
-
RUN: 'run',
|
|
50
|
-
TEST: 'test',
|
|
51
|
-
WATCH: 'watch',
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const program = new Command();
|
|
55
|
-
|
|
56
|
-
program
|
|
57
|
-
.version(packageJson.version, '-v, --version')
|
|
58
|
-
.usage('[command] [options]')
|
|
59
|
-
.description('Flow Module Management Tool')
|
|
60
|
-
.on('--help', () => {});
|
|
61
|
-
|
|
62
|
-
program
|
|
63
|
-
.command('build [moduleNames]')
|
|
64
|
-
.description('Build specified Module(s)')
|
|
65
|
-
.action(async (moduleNames) => {
|
|
66
|
-
try {
|
|
67
|
-
const projects = await selectProjects(moduleNames);
|
|
68
|
-
for (const project of projects) {
|
|
69
|
-
await exec(CMD.INSTALL, project);
|
|
70
|
-
await exec(CMD.BUILD, project);
|
|
71
|
-
await copyProjectFiles(project);
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
if (error) logger.log(error);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
program
|
|
80
|
-
.command('install [moduleNames]')
|
|
81
|
-
.description('Install the dependencies of the specified Module(s)')
|
|
82
|
-
.action(async (moduleNames) => {
|
|
83
|
-
try {
|
|
84
|
-
const projects = await selectProjects(moduleNames);
|
|
85
|
-
for (const project of projects) {
|
|
86
|
-
await exec(CMD.INSTALL, project);
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (error) logger.log(error);
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
program
|
|
95
|
-
.command('audit [moduleNames]')
|
|
96
|
-
.description('Audit dependencies for the specified Module(s)')
|
|
97
|
-
.action(async (moduleNames) => {
|
|
98
|
-
try {
|
|
99
|
-
const projects = await selectProjects(moduleNames);
|
|
100
|
-
for (const project of projects) {
|
|
101
|
-
await exec(CMD.AUDIT, project);
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
if (error) logger.log(error);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
program
|
|
110
|
-
.command('lint [moduleNames]')
|
|
111
|
-
.description('Lint source files for the specified Module(s)')
|
|
112
|
-
.action(async (moduleNames) => {
|
|
113
|
-
try {
|
|
114
|
-
const projects = await selectProjects(moduleNames);
|
|
115
|
-
for (const project of projects) {
|
|
116
|
-
await exec(CMD.LINT, project);
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
if (error) logger.log(error);
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// if BASE_URL is not given and --url is not specified this correctly throws an error
|
|
125
|
-
program
|
|
126
|
-
.command('login')
|
|
127
|
-
.requiredOption('--url <url>', 'URL of target platform', process.env.BASE_URL)
|
|
128
|
-
.requiredOption('-r, --realm <realm>', 'Auth realm of target platform', process.env.REALM)
|
|
129
|
-
.description('Authenticate against platform')
|
|
130
|
-
.action(async (options) => {
|
|
131
|
-
try {
|
|
132
|
-
await login(options.url, options.realm);
|
|
133
|
-
process.exit(0);
|
|
134
|
-
} catch (error) {
|
|
135
|
-
if (error) logger.log(error);
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
program
|
|
141
|
-
.command('logout')
|
|
142
|
-
.requiredOption('--url <url>', 'URL of target platform', process.env.BASE_URL)
|
|
143
|
-
.description('Remove authentication data')
|
|
144
|
-
.action(async (options) => {
|
|
145
|
-
try {
|
|
146
|
-
await logout(options.url);
|
|
147
|
-
process.exit(0);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
if (error) logger.log(error);
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
program
|
|
155
|
-
.command('format')
|
|
156
|
-
.description('Format all typescript files according to prettier configuration')
|
|
157
|
-
.action(async () => {
|
|
158
|
-
try {
|
|
159
|
-
await exec(CMD.FORMAT, { name: 'all' });
|
|
160
|
-
} catch (error) {
|
|
161
|
-
if (error) logger.log(error);
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
program
|
|
167
|
-
.command('package [moduleNames]')
|
|
168
|
-
.description('Build specified Module(s) and package as .zip file for manual upload to the platform')
|
|
169
|
-
.action(async (moduleNames) => {
|
|
170
|
-
try {
|
|
171
|
-
const projects = await selectProjects(moduleNames);
|
|
172
|
-
await clean(BUILD_DIR);
|
|
173
|
-
for (const project of projects) {
|
|
174
|
-
await exec(CMD.INSTALL, project);
|
|
175
|
-
await exec(CMD.BUILD, project);
|
|
176
|
-
await copyProjectFiles(project);
|
|
177
|
-
await validateModule(project);
|
|
178
|
-
await packageModule(project);
|
|
179
|
-
}
|
|
180
|
-
} catch (error) {
|
|
181
|
-
if (error) logger.log(error);
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
program
|
|
187
|
-
.command('publish-module [moduleNames]')
|
|
188
|
-
.requiredOption('--url <url>', 'URL of target platform', process.env.BASE_URL)
|
|
189
|
-
.requiredOption('-r, --realm <realm>', 'Auth realm of target platform', process.env.REALM)
|
|
190
|
-
.option('-f, --functions', 'publish flow functions')
|
|
191
|
-
.option('-u, --update', 'update existing flow functions')
|
|
192
|
-
.option('-s, --skip', 'skip modules that already exists with the current version')
|
|
193
|
-
.description('Publish specified Module(s) to Cloud Platform')
|
|
194
|
-
.action(async (moduleNames, options) => {
|
|
195
|
-
try {
|
|
196
|
-
const projects = await selectProjects(moduleNames);
|
|
197
|
-
|
|
198
|
-
apiToken = await getAccessToken(options.url, options.realm);
|
|
199
|
-
logger.ok('Got Access Token');
|
|
200
|
-
await clean(BUILD_DIR);
|
|
201
|
-
for (const project of projects) {
|
|
202
|
-
await exec(CMD.INSTALL, project);
|
|
203
|
-
await exec(CMD.BUILD, project);
|
|
204
|
-
await copyProjectFiles(project);
|
|
205
|
-
await validateModule(project);
|
|
206
|
-
try {
|
|
207
|
-
await publishModule(project, options.url);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
if (
|
|
210
|
-
options.skip &&
|
|
211
|
-
error &&
|
|
212
|
-
error.response &&
|
|
213
|
-
error.response.data &&
|
|
214
|
-
error.response.data.message === 'New module version must greater than latest version'
|
|
215
|
-
) {
|
|
216
|
-
logger.ok(`Module "${project.name}" is up to date. Skipping.`);
|
|
217
|
-
} else {
|
|
218
|
-
logger.error(`Publishing Module "${project.name}" failed.`);
|
|
219
|
-
handleApiError(error);
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (options.functions) {
|
|
224
|
-
await publishFunctions(project, options.update, options.url);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
} catch (error) {
|
|
228
|
-
if (error) logger.log(error);
|
|
229
|
-
process.exit(1);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
program
|
|
234
|
-
.command('publish-functions [moduleNames]')
|
|
235
|
-
.requiredOption('--url <url>', 'URL of target platform', process.env.BASE_URL)
|
|
236
|
-
.requiredOption('-r, --realm <realm>', 'Auth realm of target platform', process.env.REALM)
|
|
237
|
-
.option('-u, --update', 'update existing flow functions')
|
|
238
|
-
.description('Publish all Flow Functions inside specified Module(s) to Cloud Platform')
|
|
239
|
-
.action(async (moduleNames, options) => {
|
|
240
|
-
try {
|
|
241
|
-
const projects = await selectProjects(moduleNames);
|
|
242
|
-
|
|
243
|
-
apiToken = await getAccessToken(options.url, options.realm);
|
|
244
|
-
logger.ok('Got Access Token');
|
|
245
|
-
for (const project of projects) {
|
|
246
|
-
await publishFunctions(project, options.update, options.url);
|
|
247
|
-
}
|
|
248
|
-
} catch (error) {
|
|
249
|
-
if (error) logger.log(error);
|
|
250
|
-
process.exit(1);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
program
|
|
255
|
-
.command('serve [projectName]')
|
|
256
|
-
.description('Builds and serves your project. Rebuilding on file changes')
|
|
257
|
-
.action(async (projectName) => {
|
|
258
|
-
try {
|
|
259
|
-
if (checkIfAll(projectName)) process.exit(1);
|
|
260
|
-
const project = await findProject(projectName);
|
|
261
|
-
await exec(CMD.INSTALL, project);
|
|
262
|
-
await exec(CMD.BUILD, project);
|
|
263
|
-
await copyProjectFiles(project);
|
|
264
|
-
await exec(CMD.WATCH, project);
|
|
265
|
-
} catch (error) {
|
|
266
|
-
if (error) logger.log(error);
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
program
|
|
272
|
-
.command('start [projectName]')
|
|
273
|
-
.description('Runs your project')
|
|
274
|
-
.action(async (projectName) => {
|
|
275
|
-
try {
|
|
276
|
-
if (checkIfAll(projectName)) process.exit(1);
|
|
277
|
-
const project = await findProject(projectName);
|
|
278
|
-
await exec(CMD.RUN, project);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
if (error) logger.log(error);
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
program
|
|
286
|
-
.command('test [moduleNames]')
|
|
287
|
-
.description('Runs tests for specified Module(s)')
|
|
288
|
-
.action(async (moduleNames) => {
|
|
289
|
-
try {
|
|
290
|
-
// check if it is running in CI environment
|
|
291
|
-
let projects = await selectProjects(moduleNames);
|
|
292
|
-
if (process.env.CI) {
|
|
293
|
-
// only run tests that can be run in CI
|
|
294
|
-
projects = projects.filter((project) => !project['excludeTestsInCI']);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
for (const project of projects) {
|
|
298
|
-
await exec(CMD.TEST, project);
|
|
299
|
-
}
|
|
300
|
-
} catch (error) {
|
|
301
|
-
if (error) logger.log(error);
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
program
|
|
307
|
-
.command('generate-schemas [projectName]')
|
|
308
|
-
.description('Generates Input, Output and Properties-Schemas for the project')
|
|
309
|
-
.option('-h, --hide', 'hide warnings')
|
|
310
|
-
.option('-v, --verbose', 'get more output info')
|
|
311
|
-
.action(async (projectName) => {
|
|
312
|
-
try {
|
|
313
|
-
const project = await findProject(projectName);
|
|
314
|
-
const globOptions = {
|
|
315
|
-
cwd: project.location,
|
|
316
|
-
ignore: ['node_modules/**/*', '**/package*.json', '**/tsconfig*.json'],
|
|
317
|
-
};
|
|
318
|
-
const files = await glob('**/*.*', globOptions);
|
|
319
|
-
const filtered = files.filter((file) => !file.endsWith('.spec.ts'));
|
|
320
|
-
const tsJsonMap = filtered.reduce((accumulator, current, index, array) => {
|
|
321
|
-
if (current.endsWith('.ts')) {
|
|
322
|
-
// get json file for current function
|
|
323
|
-
const json = array.find((v) => v === `${current.split('.')[0]}.json`);
|
|
324
|
-
if (json) {
|
|
325
|
-
accumulator.push({
|
|
326
|
-
ts: path.join(globOptions.cwd, current),
|
|
327
|
-
json: path.join(globOptions.cwd, json),
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return accumulator;
|
|
332
|
-
}, []);
|
|
333
|
-
for (let entry of tsJsonMap) {
|
|
334
|
-
await generateSchemasForFile(entry.ts, entry.json);
|
|
335
|
-
}
|
|
336
|
-
} catch (error) {
|
|
337
|
-
if (error) logger.log(error);
|
|
338
|
-
process.exit(1);
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
343
|
-
program.parse(process.argv);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
async function generateSchemasForFile(tsPath, jsonPath) {
|
|
347
|
-
// get schema
|
|
348
|
-
const fileContent = await fs.promises.readFile(path.join(process.cwd(), jsonPath));
|
|
349
|
-
let json = JSON.parse(fileContent.toString());
|
|
350
|
-
|
|
351
|
-
const filePath = path.join(process.cwd(), tsPath);
|
|
352
|
-
const tsFile = await fs.promises.readFile(filePath);
|
|
353
|
-
const directory = path.dirname(filePath);
|
|
354
|
-
|
|
355
|
-
const result = await execa('ts-node', ['-T', '--dir', directory], { input: prepareTsFile(tsFile.toString()), preferLocal: true });
|
|
356
|
-
json = await handleConvertedOutput(result.stdout, jsonPath, json);
|
|
357
|
-
await fs.promises.writeFile(path.join(process.cwd(), jsonPath), JSON.stringify(json, null, 2) + '\n');
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function clean(buildFolder) {
|
|
361
|
-
return new Promise((resolve, reject) => {
|
|
362
|
-
const spinner = getSpinner('Cleaning').start();
|
|
363
|
-
fs.rm(buildFolder, { recursive: true, force: true }, (error) => {
|
|
364
|
-
if (error) {
|
|
365
|
-
spinner.stop();
|
|
366
|
-
logger.error('Cleaning failed');
|
|
367
|
-
logger.error(error);
|
|
368
|
-
return reject(error);
|
|
369
|
-
} else {
|
|
370
|
-
spinner.stop();
|
|
371
|
-
logger.ok('Cleaning successful');
|
|
372
|
-
return resolve();
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function exec(cmd, project) {
|
|
379
|
-
return new Promise((resolve, reject) => {
|
|
380
|
-
if (!project) {
|
|
381
|
-
return reject();
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const options = { ...getProcessOptions(cmd, project), env: process.env };
|
|
385
|
-
if (cmd === CMD.RUN || cmd === CMD.WATCH) {
|
|
386
|
-
logger.ok(`\n${getLabel(cmd)} ${project.name}:\n`);
|
|
387
|
-
execa(getProcess(cmd), getProcessArguments(cmd, project), options).stdout.pipe(process.stdout);
|
|
388
|
-
} else {
|
|
389
|
-
const spinner = getSpinner(`${getLabel(cmd)} ${project.name}`);
|
|
390
|
-
spinner.start();
|
|
391
|
-
execa(getProcess(cmd), getProcessArguments(cmd, project), options)
|
|
392
|
-
.then((result) => {
|
|
393
|
-
spinner.stop();
|
|
394
|
-
logger.log(result.stdout);
|
|
395
|
-
logger.ok(`${getLabel(cmd)} Succeeded`);
|
|
396
|
-
return resolve();
|
|
397
|
-
})
|
|
398
|
-
.catch((error) => {
|
|
399
|
-
spinner.stop();
|
|
400
|
-
if (error.stderr) logger.error(error.stderr);
|
|
401
|
-
else logger.error(error);
|
|
402
|
-
if (error.stdout) logger.log(error.stdout);
|
|
403
|
-
logger.error(`${getLabel(cmd)} Failed`);
|
|
404
|
-
return reject();
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function isDirectory(p) {
|
|
411
|
-
return new Promise((resolve) => {
|
|
412
|
-
fs.lstat(p, (error, stats) => {
|
|
413
|
-
if (!error && stats) {
|
|
414
|
-
resolve(stats.isDirectory());
|
|
415
|
-
} else {
|
|
416
|
-
resolve(false);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
async function findProjects() {
|
|
423
|
-
const isProject = (directory) =>
|
|
424
|
-
new Promise((resolve) => {
|
|
425
|
-
fs.access(path.join(directory, 'package.json'), (error) => {
|
|
426
|
-
if (error) {
|
|
427
|
-
resolve(false);
|
|
428
|
-
} else {
|
|
429
|
-
resolve(true);
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
const rootPackage = await readJson(path.join(process.cwd(), 'package.json'));
|
|
435
|
-
|
|
436
|
-
const projects = [];
|
|
437
|
-
const files = await fs.promises.readdir(projectsRoot);
|
|
438
|
-
if (files) {
|
|
439
|
-
for (const file of files) {
|
|
440
|
-
if (file && (await isDirectory(path.join(projectsRoot, file)))) {
|
|
441
|
-
const projectPath = path.join(projectsRoot, file, 'package.json');
|
|
442
|
-
if (await isProject(path.join(projectsRoot, file))) {
|
|
443
|
-
try {
|
|
444
|
-
const package_ = await readJson(path.join(path.dirname(projectPath), 'package.json'));
|
|
445
|
-
package_.location = path.posix.join(projectsRoot, file);
|
|
446
|
-
package_.dist = path.posix.join(process.cwd(), BUILD_DIR, file);
|
|
447
|
-
if (rootPackage) {
|
|
448
|
-
package_.dependencies = { ...package_.dependencies, ...rootPackage.dependencies };
|
|
449
|
-
package_.repository = rootPackage.repository;
|
|
450
|
-
}
|
|
451
|
-
projects.push(package_);
|
|
452
|
-
} catch (error) {
|
|
453
|
-
if (error) logger.log(error);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return projects;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function findProject(projectName) {
|
|
464
|
-
return new Promise(async (resolve, reject) => {
|
|
465
|
-
if (!projectName) {
|
|
466
|
-
logger.error('No project specified');
|
|
467
|
-
return reject();
|
|
468
|
-
}
|
|
469
|
-
if (projectName === 'all') {
|
|
470
|
-
const project = {
|
|
471
|
-
name: 'all',
|
|
472
|
-
version: '0.0.0',
|
|
473
|
-
location: '.',
|
|
474
|
-
};
|
|
475
|
-
return resolve(project);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const projects = await findProjects();
|
|
479
|
-
for (const project of projects) {
|
|
480
|
-
const location = path.parse(project.location);
|
|
481
|
-
const directoryName = location.name + location.ext;
|
|
482
|
-
if (project.name === projectName || directoryName === projectName) {
|
|
483
|
-
return resolve(project);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
logger.error(`Cloud not find ${projectName} Module.`);
|
|
488
|
-
reject();
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function selectProjects(value) {
|
|
493
|
-
return new Promise(async (resolve, reject) => {
|
|
494
|
-
if (!value) {
|
|
495
|
-
logger.error('No Module specified');
|
|
496
|
-
return reject();
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const projectNames = value.split(',').map((v) => v.trim());
|
|
500
|
-
const allProjects = await findProjects();
|
|
501
|
-
if (value === 'all') {
|
|
502
|
-
return resolve(allProjects);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const projects = [];
|
|
506
|
-
for (const project of allProjects) {
|
|
507
|
-
const location = path.parse(project.location);
|
|
508
|
-
const directoryName = location.name + location.ext;
|
|
509
|
-
if (projectNames.includes(project.name) || projectNames.includes(directoryName)) {
|
|
510
|
-
projects.push(project);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (projects.length === 0) {
|
|
515
|
-
logger.error(`Cloud not find any Modules for ${JSON.stringify(projectNames)}.`);
|
|
516
|
-
reject();
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return resolve(projects);
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
async function packageModule(project) {
|
|
524
|
-
const { dist, ...package_ } = project;
|
|
525
|
-
const file = path.posix.join(dist, '..', `${project.name}.zip`);
|
|
526
|
-
await writeJson(path.join(dist, 'package.json'), package_);
|
|
527
|
-
await zipDirectory(dist, file);
|
|
528
|
-
return file;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
async function publishModule(project, baseUrl = BASE_URL) {
|
|
532
|
-
return new Promise(async (resolve, reject) => {
|
|
533
|
-
const file = await packageModule(project);
|
|
534
|
-
|
|
535
|
-
const form = new FormData();
|
|
536
|
-
form.append('file', fs.createReadStream(file));
|
|
537
|
-
form.append('name', project.name);
|
|
538
|
-
form.append('description', project.description || '');
|
|
539
|
-
form.append('version', project.version || '');
|
|
540
|
-
|
|
541
|
-
try {
|
|
542
|
-
await axios.post(`${baseUrl}/api/flow/modules`, form, {
|
|
543
|
-
headers: {
|
|
544
|
-
...form.getHeaders(),
|
|
545
|
-
Authorization: `Bearer ${apiToken}`,
|
|
546
|
-
},
|
|
547
|
-
maxBodyLength: Number.POSITIVE_INFINITY,
|
|
548
|
-
maxContentLength: Number.POSITIVE_INFINITY,
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
logger.ok(`Module "${project.name}" published!`);
|
|
552
|
-
return resolve();
|
|
553
|
-
} catch (error) {
|
|
554
|
-
return reject(error);
|
|
555
|
-
} finally {
|
|
556
|
-
deleteFile(file);
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
async function validateModule(project) {
|
|
562
|
-
const module = require(project.dist);
|
|
563
|
-
const moduleName = Reflect.getMetadata('module:name', module.default);
|
|
564
|
-
const moduleDeclarations = Reflect.getMetadata('module:declarations', module.default);
|
|
565
|
-
|
|
566
|
-
const functionFqns = [];
|
|
567
|
-
for (const declaration of moduleDeclarations) {
|
|
568
|
-
const fqn = Reflect.getMetadata('element:functionFqn', declaration);
|
|
569
|
-
if (!fqn) {
|
|
570
|
-
throw new Error(`FlowFunction (${declaration.name}) metadata is missing or invalid.`);
|
|
571
|
-
}
|
|
572
|
-
functionFqns.push(fqn);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (moduleName) {
|
|
576
|
-
project.name = moduleName;
|
|
577
|
-
project.functions = functionFqns;
|
|
578
|
-
} else {
|
|
579
|
-
throw new Error('Could not validate module name');
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
async function publishFunctions(project, update, baseUrl = BASE_URL) {
|
|
584
|
-
return new Promise(async (resolve, reject) => {
|
|
585
|
-
const globOptions = {
|
|
586
|
-
cwd: project.location,
|
|
587
|
-
ignore: ['node_modules/**/*', '**/package*.json', '**/tsconfig*.json'],
|
|
588
|
-
};
|
|
589
|
-
const files = await glob('**/*.json', globOptions).catch((error) => reject(error));
|
|
590
|
-
const headers = { Authorization: `Bearer ${apiToken}` };
|
|
591
|
-
|
|
592
|
-
for (const file of files || []) {
|
|
593
|
-
try {
|
|
594
|
-
const data = await fs.promises.readFile(path.join(globOptions.cwd, file));
|
|
595
|
-
const json = JSON.parse(data.toString());
|
|
596
|
-
if (json.fqn && json.category) {
|
|
597
|
-
if (update) {
|
|
598
|
-
try {
|
|
599
|
-
await axios.put(`${baseUrl}/api/flow/functions/${json.fqn}`, json, { headers });
|
|
600
|
-
logger.ok(`Flow Function "${json.fqn}" has been updated`);
|
|
601
|
-
} catch (error) {
|
|
602
|
-
logger.error(`Flow Function "${json.fqn}" could not be updated`);
|
|
603
|
-
handleApiError(error);
|
|
604
|
-
}
|
|
605
|
-
} else {
|
|
606
|
-
try {
|
|
607
|
-
await axios.post(`${baseUrl}/api/flow/functions`, json, { headers });
|
|
608
|
-
logger.ok(`Flow Function "${json.fqn}" has been created`);
|
|
609
|
-
} catch (error) {
|
|
610
|
-
logger.error(`Flow Function "${json.fqn}" could not be created`);
|
|
611
|
-
handleApiError(error);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
} catch (error) {
|
|
616
|
-
logger.error(error);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
return resolve();
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
function zipDirectory(source, out) {
|
|
624
|
-
const archive = archiver('zip', { zlib: { level: 8 } });
|
|
625
|
-
const stream = fs.createWriteStream(out);
|
|
626
|
-
|
|
627
|
-
return new Promise((resolve, reject) => {
|
|
628
|
-
archive
|
|
629
|
-
.directory(source, false)
|
|
630
|
-
.on('error', (error) => reject(error))
|
|
631
|
-
.pipe(stream);
|
|
632
|
-
|
|
633
|
-
stream.on('close', () => resolve());
|
|
634
|
-
archive.finalize();
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
function deleteFile(filePath) {
|
|
639
|
-
return new Promise((resolve, reject) => {
|
|
640
|
-
fs.unlink(filePath, (error) => {
|
|
641
|
-
if (error) return reject(error);
|
|
642
|
-
return resolve();
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function getProcess(cmd) {
|
|
648
|
-
switch (cmd) {
|
|
649
|
-
case CMD.BUILD: {
|
|
650
|
-
return 'tsc';
|
|
651
|
-
}
|
|
652
|
-
case CMD.FORMAT: {
|
|
653
|
-
return 'prettier';
|
|
654
|
-
}
|
|
655
|
-
case CMD.INSTALL:
|
|
656
|
-
case CMD.AUDIT: {
|
|
657
|
-
return 'npm';
|
|
658
|
-
}
|
|
659
|
-
case CMD.LINT: {
|
|
660
|
-
return 'eslint';
|
|
661
|
-
}
|
|
662
|
-
case CMD.RUN: {
|
|
663
|
-
return 'node';
|
|
664
|
-
}
|
|
665
|
-
case CMD.TEST: {
|
|
666
|
-
return 'jest';
|
|
667
|
-
}
|
|
668
|
-
case CMD.WATCH: {
|
|
669
|
-
return 'nodemon';
|
|
670
|
-
}
|
|
671
|
-
default: {
|
|
672
|
-
return '';
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
function copyProjectFiles(project) {
|
|
678
|
-
return new Promise((resolve, reject) => {
|
|
679
|
-
copyfiles(
|
|
680
|
-
[`${project.location}/**`, `${BUILD_DIR}/`],
|
|
681
|
-
{
|
|
682
|
-
exclude: [`${project.location}/*.json`, `${project.location}/**/*.ts`, `${project.location}/**/test/**`],
|
|
683
|
-
up: 1,
|
|
684
|
-
},
|
|
685
|
-
(error) => {
|
|
686
|
-
if (error) {
|
|
687
|
-
return reject(error);
|
|
688
|
-
}
|
|
689
|
-
return resolve();
|
|
690
|
-
},
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
function getProcessArguments(cmd, project) {
|
|
696
|
-
switch (cmd) {
|
|
697
|
-
case CMD.AUDIT: {
|
|
698
|
-
return ['audit', '--audit-level=moderate'];
|
|
699
|
-
}
|
|
700
|
-
case CMD.BUILD: {
|
|
701
|
-
const filename = path.join(project.location, 'tsconfig.module.json');
|
|
702
|
-
const configFile = fs.existsSync(filename) ? filename : project.location;
|
|
703
|
-
return ['-p', configFile];
|
|
704
|
-
}
|
|
705
|
-
case CMD.FORMAT: {
|
|
706
|
-
return ['--write', '**/*.ts'];
|
|
707
|
-
}
|
|
708
|
-
case CMD.INSTALL: {
|
|
709
|
-
return ['install', '--no-package-lock'];
|
|
710
|
-
}
|
|
711
|
-
case CMD.LINT: {
|
|
712
|
-
return [project.location + '/**/*.{js,ts}', '--fix'];
|
|
713
|
-
}
|
|
714
|
-
case CMD.RUN: {
|
|
715
|
-
return [project.location];
|
|
716
|
-
}
|
|
717
|
-
case CMD.TEST: {
|
|
718
|
-
return project.name === 'all'
|
|
719
|
-
? ['--runInBand', '--coverage', '--forceExit', '--verbose', '--passWithNoTests']
|
|
720
|
-
: ['roots', project.location, '--forceExit', '--verbose', '--passWithNoTests'];
|
|
721
|
-
}
|
|
722
|
-
case CMD.WATCH: {
|
|
723
|
-
return ['--inspect', project.location];
|
|
724
|
-
}
|
|
725
|
-
default: {
|
|
726
|
-
return [];
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
function getProcessOptions(cmd, project) {
|
|
732
|
-
switch (cmd) {
|
|
733
|
-
case CMD.INSTALL: {
|
|
734
|
-
return { cwd: project.location };
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
function getLabel(cmd) {
|
|
740
|
-
switch (cmd) {
|
|
741
|
-
case CMD.RUN: {
|
|
742
|
-
return 'Running';
|
|
743
|
-
}
|
|
744
|
-
default: {
|
|
745
|
-
return `${cmd.charAt(0).toUpperCase()}${cmd.slice(1)}ing`;
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
function getSpinner(message) {
|
|
751
|
-
return ora({
|
|
752
|
-
color: 'magenta',
|
|
753
|
-
spinner: 'dots',
|
|
754
|
-
text: message,
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
function checkIfAll(projectName) {
|
|
759
|
-
if (projectName === 'all') {
|
|
760
|
-
logger.error(`Please specify a project. Command can't be run for all.`);
|
|
761
|
-
return true;
|
|
762
|
-
}
|
|
763
|
-
return false;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function readJson(filePath) {
|
|
767
|
-
return new Promise((resolve, reject) => {
|
|
768
|
-
fs.readFile(filePath, { encoding: 'utf8' }, (error, data) => {
|
|
769
|
-
if (error) return reject(error);
|
|
770
|
-
try {
|
|
771
|
-
return resolve(JSON.parse(data));
|
|
772
|
-
} catch (error_) {
|
|
773
|
-
return reject(error_);
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function writeJson(filePath, data) {
|
|
780
|
-
return new Promise((resolve, reject) => {
|
|
781
|
-
let dataString;
|
|
782
|
-
try {
|
|
783
|
-
dataString = JSON.stringify(data, null, 2) + '\n';
|
|
784
|
-
} catch (error) {
|
|
785
|
-
return reject(error);
|
|
786
|
-
}
|
|
787
|
-
fs.writeFile(filePath, dataString, (error) => {
|
|
788
|
-
if (error) return reject(error);
|
|
789
|
-
return resolve();
|
|
790
|
-
});
|
|
791
|
-
});
|
|
792
|
-
}
|