@hahnpro/flow-cli 2.8.0 → 2.12.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/LICENSE +1 -1
- package/README.md +1 -1
- package/lib/cli.mjs +802 -0
- package/lib/utils.mjs +197 -0
- package/package.json +46 -33
- package/lib/cli.js +0 -954
package/lib/utils.mjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const defaultLogger = {
|
|
5
|
+
/* eslint-disable no-console */
|
|
6
|
+
log: console.log,
|
|
7
|
+
error: console.error,
|
|
8
|
+
ok: console.info,
|
|
9
|
+
/* eslint-enable no-console */
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function checkTypes(definedTypes, propertiesSchema, jsonPath, logger = defaultLogger) {
|
|
13
|
+
const knownTypes = new Set([
|
|
14
|
+
...definedTypes,
|
|
15
|
+
'string',
|
|
16
|
+
'undefined',
|
|
17
|
+
'number',
|
|
18
|
+
'boolean',
|
|
19
|
+
'any',
|
|
20
|
+
'object',
|
|
21
|
+
'array',
|
|
22
|
+
'integer',
|
|
23
|
+
'Asset',
|
|
24
|
+
'AssetType',
|
|
25
|
+
'Flow',
|
|
26
|
+
'Secret',
|
|
27
|
+
'TimeSeries',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// check if all types are known
|
|
31
|
+
const properties = propertiesSchema.properties || {};
|
|
32
|
+
for (const property of Object.keys(properties)) {
|
|
33
|
+
if (properties[property].type && !knownTypes.has(properties[property].type)) {
|
|
34
|
+
logger.error(
|
|
35
|
+
`ERROR: unknown type ${properties[property].type}.
|
|
36
|
+
Please add a schema for this type in ${jsonPath}
|
|
37
|
+
for more info check the documentation`,
|
|
38
|
+
);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function getTypes(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
const json = JSON.parse(await fs.readFile(path.join(process.cwd(), filePath)));
|
|
48
|
+
return json.definitions ? Object.keys(json.definitions) : [];
|
|
49
|
+
} catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function handleConvertedOutput(result, jsonPath, json, logger = defaultLogger) {
|
|
55
|
+
let schema;
|
|
56
|
+
try {
|
|
57
|
+
schema = JSON.parse(result);
|
|
58
|
+
} catch {
|
|
59
|
+
logger.error(result);
|
|
60
|
+
return json;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const values = [
|
|
64
|
+
['propertiesSchema', 'Properties'],
|
|
65
|
+
['inputStreams', 'InputProperties'],
|
|
66
|
+
['outputStreams', 'OutputProperties'],
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const value of values) {
|
|
70
|
+
const propertiesSchema = schema[value[1]] || {};
|
|
71
|
+
for (const requestProperty of propertiesSchema.required || []) {
|
|
72
|
+
propertiesSchema.properties[requestProperty] = { ...propertiesSchema.properties[requestProperty], required: true };
|
|
73
|
+
}
|
|
74
|
+
// remove required field
|
|
75
|
+
delete propertiesSchema.required;
|
|
76
|
+
|
|
77
|
+
const types = await getTypes(jsonPath);
|
|
78
|
+
checkTypes(types, propertiesSchema, jsonPath);
|
|
79
|
+
|
|
80
|
+
const completeSchema = {
|
|
81
|
+
schema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
...propertiesSchema.properties,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (value[0] === 'propertiesSchema') {
|
|
90
|
+
if (!json['propertiesSchema']) {
|
|
91
|
+
json['propertiesSchema'] = completeSchema;
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
// check if config for default input/output stream exists
|
|
95
|
+
if (!json[value[0]].some((v) => v.name === 'default') && propertiesSchema) {
|
|
96
|
+
json[value[0]].push({
|
|
97
|
+
name: 'default',
|
|
98
|
+
...completeSchema,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// add definitions
|
|
105
|
+
if (Object.keys(schema).some((key) => !['Properties', 'InputProperties', 'OutputProperties'].includes(key))) {
|
|
106
|
+
const typeDefinitions = Object.keys(schema).filter((key) => !['Properties', 'InputProperties', 'OutputProperties'].includes(key));
|
|
107
|
+
json.definitions = typeDefinitions.reduce((previousValue, currentValue) => {
|
|
108
|
+
const additionalSchema = schema[currentValue];
|
|
109
|
+
for (const requestProperty of additionalSchema.required || []) {
|
|
110
|
+
additionalSchema.properties[requestProperty] = { ...additionalSchema.properties[requestProperty], required: true };
|
|
111
|
+
}
|
|
112
|
+
delete additionalSchema.required;
|
|
113
|
+
previousValue[currentValue] = additionalSchema;
|
|
114
|
+
return previousValue;
|
|
115
|
+
}, {});
|
|
116
|
+
}
|
|
117
|
+
return json;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function prepareTsFile(file) {
|
|
121
|
+
// if a class extends another and does not have its own fields no metadata is generated and so no schema can be generated
|
|
122
|
+
// in this case replace empty block with the block it inherits from
|
|
123
|
+
let codeBlocks = getCodeBlocks(file);
|
|
124
|
+
const emptyExtendsBlock = codeBlocks.find((block) => blockDefinitionIncludes(block, 'extends') && isBlockEmpty(block));
|
|
125
|
+
if (emptyExtendsBlock) {
|
|
126
|
+
// replace block and remove extends
|
|
127
|
+
let replBlock = `${emptyExtendsBlock}`;
|
|
128
|
+
if (replBlock.replace(/\s\s+/g, ' ').trim().startsWith('class OutputProperties')) {
|
|
129
|
+
// remove extends
|
|
130
|
+
replBlock = replBlock.replace('extends InputProperties', '');
|
|
131
|
+
// replace block with InputProperties block
|
|
132
|
+
const inputPropertiesBlock = codeBlocks.find(
|
|
133
|
+
(v) => blockDefinitionIncludes(v, 'InputProperties') && !blockDefinitionIncludes(v, 'OutputProperties'),
|
|
134
|
+
);
|
|
135
|
+
replBlock = replBlock.replace(getBlockContent(replBlock), getBlockContent(inputPropertiesBlock));
|
|
136
|
+
|
|
137
|
+
file = file.replace(emptyExtendsBlock, replBlock);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return (
|
|
141
|
+
`import { validationMetadatasToSchemas as v } from 'class-validator-jsonschema';\n` +
|
|
142
|
+
`import { defaultMetadataStorage as classTransformerDefaultMetadataStorage } from 'class-transformer/cjs/storage';\n` +
|
|
143
|
+
`${file}\n` +
|
|
144
|
+
`const s = v({\n
|
|
145
|
+
additionalConverters: {\n
|
|
146
|
+
UnitArgsValidator: (meta) => {\n
|
|
147
|
+
return {\n
|
|
148
|
+
measure: meta.constraints[0],\n
|
|
149
|
+
unit: meta.constraints[1],\n
|
|
150
|
+
type: 'number',\n
|
|
151
|
+
};\n
|
|
152
|
+
},\n
|
|
153
|
+
},\n
|
|
154
|
+
classTransformerMetadataStorage\n
|
|
155
|
+
});\n` +
|
|
156
|
+
`console.log(JSON.stringify(s));`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function getCodeBlocks(string_) {
|
|
161
|
+
const blocks = [];
|
|
162
|
+
let counter = 0;
|
|
163
|
+
let start = 0;
|
|
164
|
+
let lastNewline = 0;
|
|
165
|
+
for (const [index, char] of [...string_].entries()) {
|
|
166
|
+
if (char === '\n') {
|
|
167
|
+
lastNewline = index;
|
|
168
|
+
}
|
|
169
|
+
if (char === '{') {
|
|
170
|
+
if (counter === 0) {
|
|
171
|
+
// first bracket of block
|
|
172
|
+
start = lastNewline;
|
|
173
|
+
}
|
|
174
|
+
counter++;
|
|
175
|
+
} else if (char === '}') {
|
|
176
|
+
counter--;
|
|
177
|
+
if (counter === 0) {
|
|
178
|
+
// last bracket of block
|
|
179
|
+
blocks.push(string_.slice(start, index + 1));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return blocks;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function blockDefinitionIncludes(block, value) {
|
|
187
|
+
return block.trim().split('\n', 1)[0].includes(value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getBlockContent(block) {
|
|
191
|
+
return block.slice(block.indexOf('{'), block.lastIndexOf('}') + 1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function isBlockEmpty(block) {
|
|
195
|
+
const blockContent = block.slice(block.indexOf('{') + 1, block.lastIndexOf('}'));
|
|
196
|
+
return !blockContent.trim();
|
|
197
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/flow-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "CLI for managing Flow Modules",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"author": {
|
|
7
8
|
"name": "Hahn Projects GmbH",
|
|
8
9
|
"url": "https://hahnpro.com"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
12
|
-
"url": "git@
|
|
13
|
+
"url": "git@github.com:hahnprojects/flow.git"
|
|
13
14
|
},
|
|
14
15
|
"bin": {
|
|
15
|
-
"flow": "lib/cli.
|
|
16
|
+
"flow": "lib/cli.mjs"
|
|
16
17
|
},
|
|
17
18
|
"directories": {
|
|
18
19
|
"lib": "lib",
|
|
@@ -24,42 +25,54 @@
|
|
|
24
25
|
"publishConfig": {
|
|
25
26
|
"access": "public"
|
|
26
27
|
},
|
|
27
|
-
"scripts": {},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"class-transformer": "0.3.1",
|
|
37
|
-
"class-validator": "~0.12.2",
|
|
38
|
-
"class-validator-jsonschema": "^2.2.0",
|
|
39
|
-
"commander": "^7.1.0",
|
|
29
|
+
"archiver": "^5.3.0",
|
|
30
|
+
"axios": "^0.24.0",
|
|
31
|
+
"chalk": "^5.0.0",
|
|
32
|
+
"class-transformer": "0.5.1",
|
|
33
|
+
"class-validator": "~0.13.2",
|
|
34
|
+
"class-validator-jsonschema": "^3.1.0",
|
|
35
|
+
"commander": "^8.3.0",
|
|
40
36
|
"copyfiles": "^2.4.1",
|
|
41
37
|
"dotenv": "^10.0.0",
|
|
42
|
-
"execa": "^
|
|
38
|
+
"execa": "^6.0.0",
|
|
43
39
|
"form-data": "^4.0.0",
|
|
44
|
-
"glob": "^7.
|
|
40
|
+
"glob": "^7.2.0",
|
|
45
41
|
"https-proxy-agent": "^5.0.0",
|
|
46
|
-
"
|
|
47
|
-
"latest-semver": "^3.0.0",
|
|
48
|
-
"nodemon": "^2.0.7",
|
|
49
|
-
"ora": "^5.4.1",
|
|
50
|
-
"prettier": "^2.3.1",
|
|
51
|
-
"querystring": "^0.2.1",
|
|
52
|
-
"read-pkg": "^5.2.0",
|
|
42
|
+
"ora": "^6.0.1",
|
|
53
43
|
"reflect-metadata": "^0.1.13",
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
44
|
+
"ts-node": "^10.4.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/jest": "^27.0.3",
|
|
48
|
+
"@types/node": "^16.11.12",
|
|
49
|
+
"eslint": "^8.4.1",
|
|
50
|
+
"eslint-plugin-unicorn": "^39.0.0",
|
|
51
|
+
"jest": "^27.4.4",
|
|
52
|
+
"prettier": "^2.5.1",
|
|
53
|
+
"typescript": "^4.5.3"
|
|
61
54
|
},
|
|
62
55
|
"engines": {
|
|
63
|
-
"node": ">=
|
|
56
|
+
"node": "^14.13.1 || >=16.0.0"
|
|
57
|
+
},
|
|
58
|
+
"eslintConfig": {
|
|
59
|
+
"extends": [
|
|
60
|
+
"eslint:recommended",
|
|
61
|
+
"prettier",
|
|
62
|
+
"plugin:prettier/recommended",
|
|
63
|
+
"plugin:unicorn/all"
|
|
64
|
+
],
|
|
65
|
+
"rules": {
|
|
66
|
+
"unicorn/no-array-reduce": "off",
|
|
67
|
+
"unicorn/no-null": "off",
|
|
68
|
+
"unicorn/prefer-object-from-entries": "off",
|
|
69
|
+
"no-async-promise-executor": "off",
|
|
70
|
+
"no-console": "error"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"format": "prettier --write .",
|
|
75
|
+
"lint": "eslint '*/**/*.{js,mjs}'",
|
|
76
|
+
"test": "jest"
|
|
64
77
|
}
|
|
65
|
-
}
|
|
78
|
+
}
|