bpmnlint-plugin-camunda-compat 0.2.0 → 0.5.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/CHANGELOG.md +20 -0
- package/README.md +1 -1
- package/index.js +31 -7
- package/package.json +13 -5
- package/rules/camunda-cloud-1-0-checks.js +80 -37
- package/rules/camunda-cloud-1-0.js +1 -1
- package/rules/camunda-cloud-1-1-checks.js +17 -18
- package/rules/camunda-cloud-1-1.js +1 -1
- package/rules/camunda-cloud-1-2-checks.js +43 -15
- package/rules/camunda-cloud-1-2.js +1 -1
- package/rules/camunda-cloud-1-3-checks.js +18 -1
- package/rules/camunda-cloud-1-3.js +1 -1
- package/rules/camunda-cloud-8-0-checks.js +5 -0
- package/rules/camunda-cloud-8-0.js +5 -0
- package/rules/camunda-platform-7-15.js +3 -0
- package/rules/camunda-platform-7-16.js +3 -0
- package/rules/camunda-platform-7-17.js +3 -0
- package/rules/utils/cloud/element.js +111 -0
- package/rules/utils/element.js +635 -0
- package/rules/utils/engine-profile.js +1 -1
- package/rules/utils/rule.js +156 -94
@@ -0,0 +1,635 @@
|
|
1
|
+
const {
|
2
|
+
isArray,
|
3
|
+
isObject,
|
4
|
+
isString,
|
5
|
+
some
|
6
|
+
} = require('min-dash');
|
7
|
+
|
8
|
+
const {
|
9
|
+
is,
|
10
|
+
isAny
|
11
|
+
} = require('bpmnlint-utils');
|
12
|
+
|
13
|
+
const { getPath } = require('@philippfromme/moddle-helpers');
|
14
|
+
|
15
|
+
const ERROR_TYPES = Object.freeze({
|
16
|
+
ELEMENT_TYPE: 'elementType',
|
17
|
+
EXTENSION_ELEMENT_REQUIRED: 'extensionElementRequired',
|
18
|
+
PROPERTY_DEPENDEND_REQUIRED: 'propertyDependendRequired',
|
19
|
+
PROPERTY_REQUIRED: 'propertyRequired',
|
20
|
+
PROPERTY_TYPE: 'propertyType'
|
21
|
+
});
|
22
|
+
|
23
|
+
module.exports.ERROR_TYPES = ERROR_TYPES;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* @param {ModdleElement} node
|
27
|
+
*
|
28
|
+
* @returns {boolean|string}
|
29
|
+
*/
|
30
|
+
function hasNoEventDefinition(node) {
|
31
|
+
const eventDefinitions = node.get('eventDefinitions');
|
32
|
+
|
33
|
+
return !eventDefinitions
|
34
|
+
|| !eventDefinitions.length
|
35
|
+
|| getElementNotSupportedError(node.$type, eventDefinitions[ 0 ].$type, [ 'eventDefinitions', 0 ]);
|
36
|
+
}
|
37
|
+
|
38
|
+
module.exports.hasNoEventDefinition = hasNoEventDefinition;
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @param {ModdleElement} node
|
42
|
+
* @param {string[]} types
|
43
|
+
*
|
44
|
+
* @returns {boolean|string}
|
45
|
+
*/
|
46
|
+
function hasEventDefinitionOfType(node, types) {
|
47
|
+
if (!isArray(types)) {
|
48
|
+
types = [ types ];
|
49
|
+
}
|
50
|
+
|
51
|
+
const eventDefinitions = node.get('eventDefinitions');
|
52
|
+
|
53
|
+
if (!eventDefinitions || eventDefinitions.length !== 1) {
|
54
|
+
return getElementNotSupportedError(node.$type, null);
|
55
|
+
}
|
56
|
+
|
57
|
+
const eventDefinition = eventDefinitions[ 0 ];
|
58
|
+
|
59
|
+
return isAny(eventDefinition, types)
|
60
|
+
|| getElementNotSupportedError(node.$type, eventDefinition.$type, [ 'eventDefinitions', 0 ]);
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @param {string[]} types
|
65
|
+
*
|
66
|
+
* @returns {Function}
|
67
|
+
*/
|
68
|
+
module.exports.hasEventDefinitionOfType = function(types) {
|
69
|
+
return function(node) {
|
70
|
+
return hasEventDefinitionOfType(node, types);
|
71
|
+
};
|
72
|
+
};
|
73
|
+
|
74
|
+
/**
|
75
|
+
* @param {string[]} types
|
76
|
+
*
|
77
|
+
* @returns {Function}
|
78
|
+
*/
|
79
|
+
module.exports.hasEventDefinitionOfTypeOrNone = function(types) {
|
80
|
+
return function(node) {
|
81
|
+
const results = [
|
82
|
+
hasNoEventDefinition(node),
|
83
|
+
hasEventDefinitionOfType(node, types)
|
84
|
+
];
|
85
|
+
|
86
|
+
return some(results, result => result === true)
|
87
|
+
|| results.find(result => isArray(result) || isObject(result) || isString(result));
|
88
|
+
};
|
89
|
+
};
|
90
|
+
|
91
|
+
module.exports.hasLoopCharacteristics = function(node) {
|
92
|
+
return !!node.get('loopCharacteristics');
|
93
|
+
};
|
94
|
+
|
95
|
+
/**
|
96
|
+
* @param {string[]} types
|
97
|
+
*
|
98
|
+
* @returns {boolean|string}
|
99
|
+
*/
|
100
|
+
module.exports.hasLoopCharacteristicsOfTypeOrNone = function(type) {
|
101
|
+
return function(node) {
|
102
|
+
const loopCharacteristics = node.get('loopCharacteristics');
|
103
|
+
|
104
|
+
if (!loopCharacteristics) {
|
105
|
+
return true;
|
106
|
+
}
|
107
|
+
|
108
|
+
return is(loopCharacteristics, type)
|
109
|
+
|| getElementNotSupportedError(node.$type, loopCharacteristics.$type, [ 'loopCharacteristics' ]);
|
110
|
+
};
|
111
|
+
};
|
112
|
+
|
113
|
+
/**
|
114
|
+
* @param {ModdleElement} node
|
115
|
+
*
|
116
|
+
* @returns {boolean|Object|Object[]}
|
117
|
+
*/
|
118
|
+
module.exports.hasMultiInstanceLoopCharacteristics = function(node) {
|
119
|
+
const results = checkProperties(node, {
|
120
|
+
loopCharacteristics: {
|
121
|
+
type: 'bpmn:MultiInstanceLoopCharacteristics'
|
122
|
+
}
|
123
|
+
}, node);
|
124
|
+
|
125
|
+
if (results.length === 1) {
|
126
|
+
return results[ 0 ];
|
127
|
+
} else if (results.length > 1) {
|
128
|
+
return results;
|
129
|
+
}
|
130
|
+
|
131
|
+
return true;
|
132
|
+
};
|
133
|
+
|
134
|
+
module.exports.hasNoLanes = function(node) {
|
135
|
+
const laneSets = node.get('laneSets');
|
136
|
+
|
137
|
+
return !laneSets
|
138
|
+
|| !laneSets.length
|
139
|
+
|| getElementNotSupportedError(node.$type, 'bpmn:LaneSet', [ 'laneSets' ]);
|
140
|
+
};
|
141
|
+
|
142
|
+
module.exports.isNotBpmn = function(node) {
|
143
|
+
return !is(node, 'bpmn:BaseElement');
|
144
|
+
};
|
145
|
+
|
146
|
+
function findExtensionElement(node, types) {
|
147
|
+
const extensionElements = findExtensionElements(node, types);
|
148
|
+
|
149
|
+
if (extensionElements && extensionElements.length) {
|
150
|
+
return extensionElements[ 0 ];
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
function findExtensionElements(node, types) {
|
155
|
+
const extensionElements = node.get('extensionElements');
|
156
|
+
|
157
|
+
if (!extensionElements) {
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
|
161
|
+
const values = extensionElements.get('values');
|
162
|
+
|
163
|
+
if (!values || !values.length) {
|
164
|
+
return;
|
165
|
+
}
|
166
|
+
|
167
|
+
if (!isArray(types)) {
|
168
|
+
types = [ types ];
|
169
|
+
}
|
170
|
+
|
171
|
+
return values.filter(value => isAny(value, types));
|
172
|
+
}
|
173
|
+
|
174
|
+
module.exports.findExtensionElement = findExtensionElement;
|
175
|
+
|
176
|
+
function getElementNotSupportedError(type, propertyType, path = null) {
|
177
|
+
if (propertyType) {
|
178
|
+
return {
|
179
|
+
message: `Element of type <${ type }> (<${ propertyType }>) not supported by {{ executionPlatform }} {{ executionPlatformVersion }}`,
|
180
|
+
path,
|
181
|
+
error: {
|
182
|
+
type: ERROR_TYPES.ELEMENT_TYPE,
|
183
|
+
elementType: type,
|
184
|
+
propertyType
|
185
|
+
}
|
186
|
+
};
|
187
|
+
}
|
188
|
+
|
189
|
+
return {
|
190
|
+
message: `Element of type <${ type }> not supported by {{ executionPlatform }} {{ executionPlatformVersion }}`,
|
191
|
+
path,
|
192
|
+
error: {
|
193
|
+
type: ERROR_TYPES.ELEMENT_TYPE,
|
194
|
+
elementType: type
|
195
|
+
}
|
196
|
+
};
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* @param {string|Object} type
|
201
|
+
*
|
202
|
+
* @returns {string}
|
203
|
+
*/
|
204
|
+
function getType(type) {
|
205
|
+
if (isObject(type)) {
|
206
|
+
return type.type;
|
207
|
+
}
|
208
|
+
|
209
|
+
return type;
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* @param {string|Object} type
|
214
|
+
*
|
215
|
+
* @returns {string|Object}
|
216
|
+
*/
|
217
|
+
function getProperties(type) {
|
218
|
+
if (isObject(type)) {
|
219
|
+
return type.properties;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* @param {Function} check
|
225
|
+
* @param {Function} ifFn
|
226
|
+
* @param {*} [elseReturnValue]
|
227
|
+
*
|
228
|
+
* @returns {Function}
|
229
|
+
*/
|
230
|
+
module.exports.checkIf = function(check, ifFn, elseReturnValue = true) {
|
231
|
+
return function(node) {
|
232
|
+
if (ifFn(node) === true) {
|
233
|
+
return check(node);
|
234
|
+
}
|
235
|
+
|
236
|
+
return elseReturnValue;
|
237
|
+
};
|
238
|
+
};
|
239
|
+
|
240
|
+
function formatTypes(types, exclusive = false) {
|
241
|
+
return types.reduce((string, type, index) => {
|
242
|
+
type = getType(type);
|
243
|
+
|
244
|
+
// first
|
245
|
+
if (index === 0) {
|
246
|
+
return `<${ type }>`;
|
247
|
+
}
|
248
|
+
|
249
|
+
// last
|
250
|
+
if (index === types.length - 1) {
|
251
|
+
return `${ string } ${ exclusive ? 'or' : 'and' } <${ type }>`;
|
252
|
+
}
|
253
|
+
|
254
|
+
return `${ string }, <${ type }>`;
|
255
|
+
}, '');
|
256
|
+
}
|
257
|
+
|
258
|
+
module.exports.formatTypes = formatTypes;
|
259
|
+
|
260
|
+
function checkProperties(node, properties, parentNode) {
|
261
|
+
return Object.entries(properties).reduce((results, property) => {
|
262
|
+
const [ propertyName, propertyChecks ] = property;
|
263
|
+
|
264
|
+
const path = getPath(node, parentNode);
|
265
|
+
|
266
|
+
const propertyValue = node.get(propertyName);
|
267
|
+
|
268
|
+
if (propertyChecks.required && !propertyValue) {
|
269
|
+
return [
|
270
|
+
...results,
|
271
|
+
{
|
272
|
+
message: `Element of type <${ node.$type }> must have property <${ propertyName }>`,
|
273
|
+
path: path
|
274
|
+
? [ ...path, propertyName ]
|
275
|
+
: [ propertyName ],
|
276
|
+
error: {
|
277
|
+
type: ERROR_TYPES.PROPERTY_REQUIRED,
|
278
|
+
requiredProperty: propertyName
|
279
|
+
}
|
280
|
+
}
|
281
|
+
];
|
282
|
+
}
|
283
|
+
|
284
|
+
if (propertyChecks.dependendRequired) {
|
285
|
+
const dependency = node.get(propertyChecks.dependendRequired);
|
286
|
+
|
287
|
+
if (dependency && !propertyValue) {
|
288
|
+
return [
|
289
|
+
...results,
|
290
|
+
{
|
291
|
+
message: `Element of type <${ node.$type }> must have property <${ propertyName }> if property <${ propertyChecks.dependendRequired }> is set`,
|
292
|
+
path: path
|
293
|
+
? [ ...path, propertyName ]
|
294
|
+
: [ propertyName ],
|
295
|
+
error: {
|
296
|
+
type: ERROR_TYPES.PROPERTY_DEPENDEND_REQUIRED,
|
297
|
+
dependendRequiredProperty: propertyName
|
298
|
+
}
|
299
|
+
}
|
300
|
+
];
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
if (propertyChecks.type && propertyValue && (!propertyValue.$instanceOf || !propertyValue.$instanceOf(propertyChecks.type))) {
|
305
|
+
return [
|
306
|
+
...results,
|
307
|
+
{
|
308
|
+
message: `Element of type <${ node.$type }> must have property <${ propertyName }> of type <${ propertyChecks.type }>`,
|
309
|
+
path: path
|
310
|
+
? [ ...path, propertyName ]
|
311
|
+
: [ propertyName ],
|
312
|
+
error: {
|
313
|
+
type: ERROR_TYPES.PROPERTY_TYPE,
|
314
|
+
propertyType: propertyChecks.type
|
315
|
+
}
|
316
|
+
}
|
317
|
+
];
|
318
|
+
}
|
319
|
+
|
320
|
+
return results;
|
321
|
+
}, []);
|
322
|
+
}
|
323
|
+
|
324
|
+
module.exports.checkProperties = checkProperties;
|
325
|
+
|
326
|
+
/**
|
327
|
+
* @example
|
328
|
+
*
|
329
|
+
* const check = hasExtensionElementsOfTypes([ 'zeebe:CalledDecision', 'zeebe:TaskDefinition' ]);
|
330
|
+
*
|
331
|
+
* check(node, parentNode);
|
332
|
+
*
|
333
|
+
* @example
|
334
|
+
*
|
335
|
+
* const check = hasExtensionElementsOfTypes([
|
336
|
+
* {
|
337
|
+
* type: 'zeebe:CalledDecision',
|
338
|
+
* properties: {
|
339
|
+
* decisionId: { required: true },
|
340
|
+
* resultVariable: { required: true }
|
341
|
+
* }
|
342
|
+
* },
|
343
|
+
* {
|
344
|
+
* type: 'zeebe:TaskDefinition',
|
345
|
+
* properties: {
|
346
|
+
* type: { required: true },
|
347
|
+
* retries: { required: true }
|
348
|
+
* }
|
349
|
+
* }
|
350
|
+
* ]);
|
351
|
+
*
|
352
|
+
* check(node, parentNode);
|
353
|
+
*
|
354
|
+
* @param {string[]|Object[]} types
|
355
|
+
*
|
356
|
+
* @returns {Function}
|
357
|
+
*/
|
358
|
+
module.exports.hasExtensionElementsOfTypes = function(types, exclusive = false) {
|
359
|
+
return function(node, parentNode) {
|
360
|
+
const extensionElements = findExtensionElements(node, types.map(type => getType(type)));
|
361
|
+
|
362
|
+
if (!extensionElements || !extensionElements.length) {
|
363
|
+
return {
|
364
|
+
message: `Element of type <${ node.$type }> must have have at least one ${ formatTypes(types, true) } extension element`,
|
365
|
+
path: getPath(node, parentNode),
|
366
|
+
error: {
|
367
|
+
type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
|
368
|
+
requiredExtensionElement: getType(types[ 0 ])
|
369
|
+
}
|
370
|
+
};
|
371
|
+
}
|
372
|
+
|
373
|
+
if (exclusive && extensionElements.length > 1) {
|
374
|
+
return {
|
375
|
+
message: `Element of type <${ node.$type }> must have have either one ${ formatTypes(types, true) } extension element`,
|
376
|
+
path: getPath(node, parentNode)
|
377
|
+
};
|
378
|
+
}
|
379
|
+
|
380
|
+
const results = extensionElements.reduce((errors, extensionElement) => {
|
381
|
+
const type = types.find(type => is(extensionElement, getType(type)));
|
382
|
+
|
383
|
+
const properties = getProperties(type);
|
384
|
+
|
385
|
+
if (properties) {
|
386
|
+
return [
|
387
|
+
...errors,
|
388
|
+
...checkProperties(extensionElement, properties, parentNode)
|
389
|
+
];
|
390
|
+
}
|
391
|
+
|
392
|
+
return errors;
|
393
|
+
}, []);
|
394
|
+
|
395
|
+
if (results.length === 1) {
|
396
|
+
return results[ 0 ];
|
397
|
+
} else if (results.length > 1) {
|
398
|
+
return results;
|
399
|
+
}
|
400
|
+
|
401
|
+
return true;
|
402
|
+
};
|
403
|
+
};
|
404
|
+
|
405
|
+
/**
|
406
|
+
* @example
|
407
|
+
*
|
408
|
+
* const check = hasExtensionElementOfType('zeebe:TaskDefinition');
|
409
|
+
*
|
410
|
+
* check(node, parentNode);
|
411
|
+
*
|
412
|
+
* @example
|
413
|
+
*
|
414
|
+
* const check = hasExtensionElementOfType(
|
415
|
+
* {
|
416
|
+
* type: 'zeebe:TaskDefinition',
|
417
|
+
* properties: {
|
418
|
+
* type: { required: true },
|
419
|
+
* retries: { required: true }
|
420
|
+
* }
|
421
|
+
* }
|
422
|
+
* );
|
423
|
+
*
|
424
|
+
* check(node, parentNode);
|
425
|
+
*
|
426
|
+
* @param {string[]|Object[]} types
|
427
|
+
*
|
428
|
+
* @returns {Function}
|
429
|
+
*/
|
430
|
+
module.exports.hasExtensionElementOfType = function(type) {
|
431
|
+
return function(node, parentNode) {
|
432
|
+
const extensionElement = findExtensionElement(node, getType(type));
|
433
|
+
|
434
|
+
if (!extensionElement) {
|
435
|
+
return {
|
436
|
+
message: `Element of type <${ node.$type }> must have <${getType(
|
437
|
+
type
|
438
|
+
)}> extension element`,
|
439
|
+
path: getPath(node, parentNode),
|
440
|
+
error: {
|
441
|
+
type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
|
442
|
+
requiredExtensionElement: getType(type)
|
443
|
+
}
|
444
|
+
};
|
445
|
+
}
|
446
|
+
|
447
|
+
const properties = getProperties(type);
|
448
|
+
|
449
|
+
if (properties) {
|
450
|
+
const results = checkProperties(extensionElement, properties, parentNode);
|
451
|
+
|
452
|
+
if (results.length === 1) {
|
453
|
+
return results[ 0 ];
|
454
|
+
} else if (results.length > 1) {
|
455
|
+
return results;
|
456
|
+
}
|
457
|
+
}
|
458
|
+
|
459
|
+
return true;
|
460
|
+
};
|
461
|
+
};
|
462
|
+
|
463
|
+
/**
|
464
|
+
* @example
|
465
|
+
*
|
466
|
+
* const check = checkError((error) => { ... });
|
467
|
+
*
|
468
|
+
* check(errorEventDefinition);
|
469
|
+
*
|
470
|
+
* @param {Function} check
|
471
|
+
*
|
472
|
+
* @returns {Function}
|
473
|
+
*/
|
474
|
+
function checkError(check) {
|
475
|
+
return function(node, parentNode) {
|
476
|
+
const results = checkProperties(node, {
|
477
|
+
errorRef: {
|
478
|
+
required: true
|
479
|
+
}
|
480
|
+
}, parentNode);
|
481
|
+
|
482
|
+
if (results.length === 1) {
|
483
|
+
return results[ 0 ];
|
484
|
+
} else if (results.length > 1) {
|
485
|
+
return results;
|
486
|
+
}
|
487
|
+
|
488
|
+
const error = node.get('errorRef');
|
489
|
+
|
490
|
+
return check(error);
|
491
|
+
};
|
492
|
+
}
|
493
|
+
|
494
|
+
module.exports.checkError = checkError;
|
495
|
+
|
496
|
+
/**
|
497
|
+
* @example
|
498
|
+
*
|
499
|
+
* const check = checkEventDefinition((eventDefinition, event) => { ... });
|
500
|
+
*
|
501
|
+
* check(startEvent);
|
502
|
+
*
|
503
|
+
* @param {Function} check
|
504
|
+
*
|
505
|
+
* @returns {Function}
|
506
|
+
*/
|
507
|
+
module.exports.checkEventDefinition = function(check) {
|
508
|
+
return function(node) {
|
509
|
+
const results = checkProperties(node, {
|
510
|
+
eventDefinitions: {
|
511
|
+
required: true
|
512
|
+
}
|
513
|
+
});
|
514
|
+
|
515
|
+
if (results.length === 1) {
|
516
|
+
return results[ 0 ];
|
517
|
+
} else if (results.length > 1) {
|
518
|
+
return results;
|
519
|
+
}
|
520
|
+
|
521
|
+
const eventDefinitions = node.get('eventDefinitions');
|
522
|
+
|
523
|
+
return check(eventDefinitions[ 0 ], node);
|
524
|
+
};
|
525
|
+
};
|
526
|
+
|
527
|
+
/**
|
528
|
+
* @example
|
529
|
+
*
|
530
|
+
* const check = checkFlowNode((node, parentNode) => { ... });
|
531
|
+
*
|
532
|
+
* check(serviceTask);
|
533
|
+
*
|
534
|
+
* @param {Function} check
|
535
|
+
*
|
536
|
+
* @returns {Function}
|
537
|
+
*/
|
538
|
+
module.exports.checkFlowNode = function(check) {
|
539
|
+
return function(node) {
|
540
|
+
return check(node, node);
|
541
|
+
};
|
542
|
+
};
|
543
|
+
|
544
|
+
/**
|
545
|
+
* @example
|
546
|
+
*
|
547
|
+
* const check = checkMessage((message) => { ... });
|
548
|
+
*
|
549
|
+
* check(messageEventDefinition);
|
550
|
+
*
|
551
|
+
* @param {Function} check
|
552
|
+
*
|
553
|
+
* @returns {Function}
|
554
|
+
*/
|
555
|
+
module.exports.checkMessage = function(check) {
|
556
|
+
return function(node, parentNode) {
|
557
|
+
const results = checkProperties(node, {
|
558
|
+
messageRef: {
|
559
|
+
required: true
|
560
|
+
}
|
561
|
+
}, parentNode);
|
562
|
+
|
563
|
+
if (results.length === 1) {
|
564
|
+
return results[ 0 ];
|
565
|
+
} else if (results.length > 1) {
|
566
|
+
return results;
|
567
|
+
}
|
568
|
+
|
569
|
+
const message = node.get('messageRef');
|
570
|
+
|
571
|
+
return check(message);
|
572
|
+
};
|
573
|
+
};
|
574
|
+
|
575
|
+
/**
|
576
|
+
* @example
|
577
|
+
*
|
578
|
+
* const check = checkLoopCharacteristics((loopCharacteristics, activity) => { ... });
|
579
|
+
*
|
580
|
+
* check(serviceTask);
|
581
|
+
*
|
582
|
+
* @param {Function} check
|
583
|
+
*
|
584
|
+
* @returns {Function}
|
585
|
+
*/
|
586
|
+
module.exports.checkLoopCharacteristics = function(check) {
|
587
|
+
return function(node) {
|
588
|
+
const results = checkProperties(node, {
|
589
|
+
loopCharacteristics: {
|
590
|
+
required: true
|
591
|
+
}
|
592
|
+
});
|
593
|
+
|
594
|
+
if (results.length === 1) {
|
595
|
+
return results[ 0 ];
|
596
|
+
} else if (results.length > 1) {
|
597
|
+
return results;
|
598
|
+
}
|
599
|
+
|
600
|
+
const loopCharacteristics = node.get('loopCharacteristics');
|
601
|
+
|
602
|
+
return check(loopCharacteristics, node);
|
603
|
+
};
|
604
|
+
};
|
605
|
+
|
606
|
+
module.exports.hasErrorReference = checkError(() => true);
|
607
|
+
|
608
|
+
function translate(result, translations) {
|
609
|
+
if (isString(result)) {
|
610
|
+
return translations[result] || result;
|
611
|
+
}
|
612
|
+
|
613
|
+
const { message } = result;
|
614
|
+
|
615
|
+
return {
|
616
|
+
...result,
|
617
|
+
message: translations[message] || message
|
618
|
+
};
|
619
|
+
}
|
620
|
+
|
621
|
+
module.exports.withTranslations = function(check, translations) {
|
622
|
+
return function(node) {
|
623
|
+
const results = check(node);
|
624
|
+
|
625
|
+
if (isObject(results) || isString(results)) {
|
626
|
+
return translate(results, translations);
|
627
|
+
}
|
628
|
+
|
629
|
+
if (isArray(results)) {
|
630
|
+
return results.map((result) => translate(result, translations));
|
631
|
+
}
|
632
|
+
|
633
|
+
return results;
|
634
|
+
};
|
635
|
+
};
|