bpmnlint 10.3.0 → 11.0.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/README.md +2 -0
- package/package.json +11 -12
- package/rules/global.js +66 -43
- package/rules/helper.js +31 -1
- package/rules/label-required.js +0 -4
- package/rules/no-bpmndi.js +2 -2
- package/rules/no-implicit-end.js +40 -1
- package/rules/no-overlapping-elements.js +9 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bpmnlint",
|
|
3
3
|
"description": "Validate your BPMN diagrams based on configurable lint rules",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "11.0.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bpmnlint",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
}
|
|
24
24
|
],
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">= 20"
|
|
27
27
|
},
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"scripts": {
|
|
30
30
|
"all": "run-s lint test-coverage",
|
|
31
|
-
"lint": "eslint .
|
|
31
|
+
"lint": "eslint .",
|
|
32
32
|
"dev": "npm test -- --watch",
|
|
33
33
|
"test": "mocha --exclude 'test/integration/*/**' 'test/**/*.*js'",
|
|
34
34
|
"test-coverage": "nyc --reporter=lcov --reporter=html npm test",
|
|
@@ -40,26 +40,25 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@bpmn-io/moddle-utils": "^0.2.1",
|
|
42
42
|
"ansi-colors": "^4.1.3",
|
|
43
|
-
"bpmn-moddle": "^
|
|
43
|
+
"bpmn-moddle": "^9.0.1",
|
|
44
44
|
"bpmnlint-utils": "^1.1.1",
|
|
45
45
|
"cli-table": "^0.3.11",
|
|
46
46
|
"color-support": "^1.1.3",
|
|
47
|
-
"min-dash": "^4.2.
|
|
47
|
+
"min-dash": "^4.2.2",
|
|
48
48
|
"mri": "^1.2.0",
|
|
49
49
|
"pluralize": "^7.0.0",
|
|
50
50
|
"tiny-glob": "^0.2.9"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@bpmn-io/bpmnlint-generate-docs-images": "^0.4.0",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"eslint": "^
|
|
57
|
-
"
|
|
58
|
-
"execa": "^8.0.1",
|
|
54
|
+
"chai": "^4.5.0",
|
|
55
|
+
"eslint": "^9.17.0",
|
|
56
|
+
"eslint-plugin-bpmn-io": "^2.0.2",
|
|
57
|
+
"execa": "^9.5.2",
|
|
59
58
|
"install-local": "^3.0.1",
|
|
60
|
-
"mocha": "^
|
|
59
|
+
"mocha": "^11.0.1",
|
|
61
60
|
"npm-run-all": "^4.1.5",
|
|
62
|
-
"nyc": "^
|
|
61
|
+
"nyc": "^17.1.0",
|
|
63
62
|
"strip-indent": "^3.0.0"
|
|
64
63
|
},
|
|
65
64
|
"files": [
|
package/rules/global.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const {
|
|
2
|
-
|
|
2
|
+
is,
|
|
3
|
+
isAny
|
|
3
4
|
} = require('bpmnlint-utils');
|
|
4
5
|
|
|
5
6
|
|
|
@@ -10,13 +11,13 @@ const {
|
|
|
10
11
|
*
|
|
11
12
|
* * `bpmn:Error`
|
|
12
13
|
* * `bpmn:Escalation`
|
|
13
|
-
* * `bpmn:Signal`
|
|
14
14
|
* * `bpmn:Message`
|
|
15
|
+
* * `bpmn:Signal`
|
|
15
16
|
*
|
|
16
17
|
* For each of these elements proper usage implies:
|
|
17
18
|
*
|
|
18
19
|
* * element must have a name
|
|
19
|
-
* * element is
|
|
20
|
+
* * element is referenced by at least one element
|
|
20
21
|
* * there exists only a single element per type with a given name
|
|
21
22
|
*/
|
|
22
23
|
module.exports = function() {
|
|
@@ -27,20 +28,21 @@ module.exports = function() {
|
|
|
27
28
|
return false;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const
|
|
31
|
-
const eventDefinitions = getEventDefinitions(node);
|
|
31
|
+
const rootElements = getRootElements(node);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const referencingElements = getReferencingElements(node);
|
|
34
|
+
|
|
35
|
+
rootElements.forEach(rootElement => {
|
|
36
|
+
if (!hasName(rootElement)) {
|
|
37
|
+
reporter.report(rootElement.id, 'Element is missing name');
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
if (!isReferenced(
|
|
39
|
-
reporter.report(
|
|
40
|
+
if (!isReferenced(rootElement, referencingElements)) {
|
|
41
|
+
reporter.report(rootElement.id, 'Element is unused');
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
if (!isUnique(
|
|
43
|
-
reporter.report(
|
|
44
|
+
if (!isUnique(rootElement, rootElements)) {
|
|
45
|
+
reporter.report(rootElement.id, 'Element name is not unique');
|
|
44
46
|
}
|
|
45
47
|
});
|
|
46
48
|
|
|
@@ -52,29 +54,42 @@ module.exports = function() {
|
|
|
52
54
|
|
|
53
55
|
// helpers /////////////////////////////
|
|
54
56
|
|
|
55
|
-
function
|
|
56
|
-
return
|
|
57
|
+
function getRootElements(definitions) {
|
|
58
|
+
return definitions.rootElements.filter(node => isAny(node, [ 'bpmn:Error', 'bpmn:Escalation', 'bpmn:Message', 'bpmn:Signal' ]));
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
function
|
|
60
|
-
const
|
|
61
|
+
function getReferencingElements(definitions) {
|
|
62
|
+
const referencingElements = [];
|
|
61
63
|
|
|
62
64
|
function traverse(element) {
|
|
63
|
-
if (element.rootElements) {
|
|
64
|
-
element.rootElements.forEach(traverse);
|
|
65
|
+
if (is(element, 'bpmn:Definitions') && element.get('rootElements').length) {
|
|
66
|
+
element.get('rootElements').forEach(traverse);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (is(element, 'bpmn:FlowElementsContainer') && element.get('flowElements').length) {
|
|
70
|
+
element.get('flowElements').forEach(traverse);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (is(element, 'bpmn:Event') && element.get('eventDefinitions').length) {
|
|
74
|
+
element.get('eventDefinitions').forEach(eventDefinition => referencingElements.push(eventDefinition));
|
|
65
75
|
}
|
|
66
76
|
|
|
67
|
-
if (element.
|
|
68
|
-
element.
|
|
77
|
+
if (is(element, 'bpmn:Collaboration') && element.get('messageFlows').length) {
|
|
78
|
+
element.get('messageFlows').forEach(traverse);
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
if (element
|
|
72
|
-
|
|
81
|
+
if (isAny(element, [
|
|
82
|
+
'bpmn:MessageFlow',
|
|
83
|
+
'bpmn:ReceiveTask',
|
|
84
|
+
'bpmn:SendTask'
|
|
85
|
+
])) {
|
|
86
|
+
referencingElements.push(element);
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
traverse(
|
|
77
|
-
|
|
90
|
+
traverse(definitions);
|
|
91
|
+
|
|
92
|
+
return referencingElements;
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
function hasName(event) {
|
|
@@ -83,35 +98,43 @@ module.exports = function() {
|
|
|
83
98
|
);
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
function isReferenced(
|
|
87
|
-
if (is(
|
|
88
|
-
return (
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
function isReferenced(rootElement, referencingElements) {
|
|
102
|
+
if (is(rootElement, 'bpmn:Error')) {
|
|
103
|
+
return referencingElements.some(referencingElement => {
|
|
104
|
+
return is(referencingElement, 'bpmn:ErrorEventDefinition')
|
|
105
|
+
&& rootElement.get('id') === referencingElement.get('errorRef')?.get('id');
|
|
106
|
+
});
|
|
91
107
|
}
|
|
92
108
|
|
|
93
|
-
if (is(
|
|
94
|
-
return (
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
if (is(rootElement, 'bpmn:Escalation')) {
|
|
110
|
+
return referencingElements.some(referencingElement => {
|
|
111
|
+
return is(referencingElement, 'bpmn:EscalationEventDefinition')
|
|
112
|
+
&& rootElement.get('id') === referencingElement.get('escalationRef')?.get('id');
|
|
113
|
+
});
|
|
97
114
|
}
|
|
98
115
|
|
|
99
|
-
if (is(
|
|
100
|
-
return (
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
if (is(rootElement, 'bpmn:Message')) {
|
|
117
|
+
return referencingElements.some(referencingElement => {
|
|
118
|
+
return isAny(referencingElement, [
|
|
119
|
+
'bpmn:MessageEventDefinition',
|
|
120
|
+
'bpmn:MessageFlow',
|
|
121
|
+
'bpmn:ReceiveTask',
|
|
122
|
+
'bpmn:SendTask'
|
|
123
|
+
]) && rootElement.get('id') === referencingElement.get('messageRef')?.get('id');
|
|
124
|
+
});
|
|
103
125
|
}
|
|
104
126
|
|
|
105
|
-
if (is(
|
|
106
|
-
return (
|
|
107
|
-
|
|
108
|
-
|
|
127
|
+
if (is(rootElement, 'bpmn:Signal')) {
|
|
128
|
+
return referencingElements.some(referencingElement => {
|
|
129
|
+
return is(referencingElement, 'bpmn:SignalEventDefinition')
|
|
130
|
+
&& rootElement.get('id') === referencingElement.get('signalRef')?.get('id');
|
|
131
|
+
});
|
|
109
132
|
}
|
|
110
133
|
}
|
|
111
134
|
|
|
112
|
-
function isUnique(
|
|
135
|
+
function isUnique(rootElement, rootElements) {
|
|
113
136
|
return (
|
|
114
|
-
|
|
137
|
+
rootElements.filter(otherRootElement => is(otherRootElement, rootElement.$type) && rootElement.name === otherRootElement.name).length === 1
|
|
115
138
|
);
|
|
116
139
|
}
|
|
117
140
|
};
|
package/rules/helper.js
CHANGED
|
@@ -28,4 +28,34 @@ function disallowNodeType(type) {
|
|
|
28
28
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
module.exports.disallowNodeType = disallowNodeType;
|
|
31
|
+
module.exports.disallowNodeType = disallowNodeType;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Find a parent for the given element
|
|
35
|
+
*
|
|
36
|
+
* @param {ModdleElement} node
|
|
37
|
+
*
|
|
38
|
+
* @param {String} type
|
|
39
|
+
*
|
|
40
|
+
* @return {ModdleElement} element
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
function findParent(node, type) {
|
|
44
|
+
if (!node) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const parent = node.$parent;
|
|
49
|
+
|
|
50
|
+
if (!parent) {
|
|
51
|
+
return node;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (is(parent, type)) {
|
|
55
|
+
return parent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return findParent(parent, type);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports.findParent = findParent;
|
package/rules/label-required.js
CHANGED
package/rules/no-bpmndi.js
CHANGED
|
@@ -103,13 +103,13 @@ function getAllBpmnElements(rootElements) {
|
|
|
103
103
|
*/
|
|
104
104
|
function getAllDiBpmnReferences(definitionsNode) {
|
|
105
105
|
return flatten(
|
|
106
|
-
definitionsNode.diagrams.map((diagram) => {
|
|
106
|
+
definitionsNode.get('diagrams').map((diagram) => {
|
|
107
107
|
|
|
108
108
|
const diElements = diagram.plane.planeElement || [];
|
|
109
109
|
|
|
110
110
|
return diElements.map((element) => {
|
|
111
111
|
|
|
112
|
-
return element.bpmnElement
|
|
112
|
+
return element.bpmnElement?.id;
|
|
113
113
|
});
|
|
114
114
|
})
|
|
115
115
|
);
|
package/rules/no-implicit-end.js
CHANGED
|
@@ -3,6 +3,9 @@ const {
|
|
|
3
3
|
isAny
|
|
4
4
|
} = require('bpmnlint-utils');
|
|
5
5
|
|
|
6
|
+
const {
|
|
7
|
+
findParent
|
|
8
|
+
} = require('./helper');
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* A rule that checks that an element is not an implicit end (token sink).
|
|
@@ -17,6 +20,34 @@ module.exports = function() {
|
|
|
17
20
|
);
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
function isCompensationEvent(node) {
|
|
24
|
+
const eventDefinitions = node.eventDefinitions || [];
|
|
25
|
+
|
|
26
|
+
return eventDefinitions.length && eventDefinitions.every(
|
|
27
|
+
definition => is(definition, 'bpmn:CompensateEventDefinition')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasCompensationActivity(node) {
|
|
32
|
+
const parent = findParent(node, 'bpmn:Process');
|
|
33
|
+
|
|
34
|
+
const artifacts = parent.artifacts || [];
|
|
35
|
+
|
|
36
|
+
return artifacts.some((element) => {
|
|
37
|
+
if (!is(element, 'bpmn:Association')) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const source = element.sourceRef;
|
|
42
|
+
|
|
43
|
+
return source.id === node.id;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isForCompensation(node) {
|
|
48
|
+
return node.isForCompensation;
|
|
49
|
+
}
|
|
50
|
+
|
|
20
51
|
function isImplicitEnd(node) {
|
|
21
52
|
const outgoing = node.outgoing || [];
|
|
22
53
|
|
|
@@ -32,6 +63,14 @@ module.exports = function() {
|
|
|
32
63
|
return false;
|
|
33
64
|
}
|
|
34
65
|
|
|
66
|
+
if (is(node, 'bpmn:BoundaryEvent') && isCompensationEvent(node) && hasCompensationActivity(node)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (is(node, 'bpmn:Task') && isForCompensation(node)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
35
74
|
return outgoing.length === 0;
|
|
36
75
|
}
|
|
37
76
|
|
|
@@ -47,4 +86,4 @@ module.exports = function() {
|
|
|
47
86
|
}
|
|
48
87
|
|
|
49
88
|
return { check };
|
|
50
|
-
};
|
|
89
|
+
};
|
|
@@ -108,7 +108,15 @@ function checkElementsArray(elements, elementsToReport, diObjects) {
|
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
const bounds1 = diObjects.get(element)?.bounds;
|
|
112
|
+
const bounds2 = diObjects.get(element2)?.bounds;
|
|
113
|
+
|
|
114
|
+
// ignore if an element doesn't have bounds
|
|
115
|
+
if (!bounds1 || !bounds2) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (isCollision(bounds1, bounds2)) {
|
|
112
120
|
elementsToReport.add(element);
|
|
113
121
|
elementsToReport.add(element2);
|
|
114
122
|
}
|