bpmnlint 11.14.0 → 11.15.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/package.json +4 -3
- package/rules/no-bpmndi.js +10 -9
- package/rules/no-overlapping-elements.js +88 -23
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": "11.
|
|
4
|
+
"version": "11.15.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bpmnlint",
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
},
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"scripts": {
|
|
30
|
-
"all": "run-s lint lint:types test-coverage",
|
|
30
|
+
"all": "run-s lint lint:types test-coverage test:perf",
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"lint:types": "tsc",
|
|
33
33
|
"dev": "npm test -- --watch",
|
|
34
|
-
"test": "mocha --exclude \"test/integration/*/**\" \"test/**/*.*js\"",
|
|
34
|
+
"test": "mocha --exclude \"test/integration/*/**\" --exclude \"test/performance/**\" \"test/**/*.*js\"",
|
|
35
|
+
"test:perf": "mocha \"test/performance/**/*.*js\"",
|
|
35
36
|
"test-coverage": "nyc --reporter=lcov --reporter=html npm test",
|
|
36
37
|
"generate-docs-images": "npx @bpmn-io/bpmnlint-generate-docs-images ."
|
|
37
38
|
},
|
package/rules/no-bpmndi.js
CHANGED
|
@@ -35,12 +35,12 @@ module.exports = function() {
|
|
|
35
35
|
// (2) Filter BPMN elements without visual representation
|
|
36
36
|
const visualBpmnElements = bpmnElements.filter(hasVisualRepresentation);
|
|
37
37
|
|
|
38
|
-
// (3) Construct
|
|
39
|
-
const
|
|
38
|
+
// (3) Construct set of BPMNDI references
|
|
39
|
+
const referencedBpmnElementIds = getReferencedBpmnElementIds(node);
|
|
40
40
|
|
|
41
41
|
// (4) Report elements without BPMNDI
|
|
42
42
|
visualBpmnElements.forEach((element) => {
|
|
43
|
-
if (
|
|
43
|
+
if (!referencedBpmnElementIds.has(element.id)) {
|
|
44
44
|
reporter.report(element.id, 'Element is missing bpmndi');
|
|
45
45
|
}
|
|
46
46
|
});
|
|
@@ -105,16 +105,17 @@ function getAllBpmnElements(rootElements) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* Get all BPMN elements
|
|
108
|
+
* Get the ids of all BPMN elements referenced by BPMNDI elements
|
|
109
|
+
* within a bpmn:Definitions node
|
|
109
110
|
*
|
|
110
111
|
* @param {ModdleElement} definitionsNode - A moddleElement representing the
|
|
111
112
|
* bpmn:Definitions element
|
|
112
113
|
*
|
|
113
|
-
* @return {string
|
|
114
|
-
* this bpmn:Definitions node
|
|
114
|
+
* @return {Set<string>} ids of all BPMN elements referenced by BPMNDI
|
|
115
|
+
* elements part of this bpmn:Definitions node
|
|
115
116
|
*/
|
|
116
|
-
function
|
|
117
|
-
return flatten(
|
|
117
|
+
function getReferencedBpmnElementIds(definitionsNode) {
|
|
118
|
+
return new Set(flatten(
|
|
118
119
|
definitionsNode.get('diagrams').map((diagram) => {
|
|
119
120
|
|
|
120
121
|
const diElements = diagram.plane.planeElement || [];
|
|
@@ -124,7 +125,7 @@ function getAllDiBpmnReferences(definitionsNode) {
|
|
|
124
125
|
return element.bpmnElement?.id;
|
|
125
126
|
});
|
|
126
127
|
})
|
|
127
|
-
);
|
|
128
|
+
));
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
/**
|
|
@@ -6,6 +6,18 @@ const {
|
|
|
6
6
|
annotateRule
|
|
7
7
|
} = require('./helper');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @typedef { import('../lib/types.js').ModdleElement } ModdleElement
|
|
11
|
+
*
|
|
12
|
+
* @typedef { {
|
|
13
|
+
* element: ModdleElement,
|
|
14
|
+
* left: number,
|
|
15
|
+
* right: number,
|
|
16
|
+
* top: number,
|
|
17
|
+
* bottom: number
|
|
18
|
+
* } } Shape
|
|
19
|
+
*/
|
|
20
|
+
|
|
9
21
|
|
|
10
22
|
/**
|
|
11
23
|
* Rule that checks if two elements overlap except:
|
|
@@ -99,36 +111,88 @@ function checkProcess(node, elementsToReport, elementsOutsideToReport, diObjects
|
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
/**
|
|
114
|
+
* Check elements for overlap.
|
|
115
|
+
*
|
|
116
|
+
* Sweeps the shapes of the elements from left to right, so that every shape
|
|
117
|
+
* is only compared with the shapes that start before it ends.
|
|
118
|
+
*
|
|
102
119
|
* @param {Array} elements
|
|
103
120
|
* @param {Set} elementsToReport
|
|
121
|
+
* @param {Map} diObjects
|
|
104
122
|
*/
|
|
105
123
|
function checkElementsArray(elements, elementsToReport, diObjects) {
|
|
106
|
-
|
|
107
|
-
const element = elements[i];
|
|
124
|
+
const shapes = getShapes(elements, diObjects).sort((a, b) => a.left - b.left);
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
const element2 = elements[j];
|
|
126
|
+
const overlapping = new Set();
|
|
111
127
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (element.attachedToRef === element2 || element2.attachedToRef === element) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
128
|
+
for (let i = 0; i < shapes.length; i++) {
|
|
129
|
+
const shape = shapes[i];
|
|
117
130
|
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
// shapes starting right of this one cannot overlap it
|
|
132
|
+
for (let j = i + 1; j < shapes.length && shapes[j].left <= shape.right; j++) {
|
|
133
|
+
const otherShape = shapes[j];
|
|
120
134
|
|
|
121
|
-
|
|
122
|
-
if (!bounds1 || !bounds2) {
|
|
135
|
+
if (!isCollision(shape, otherShape)) {
|
|
123
136
|
continue;
|
|
124
137
|
}
|
|
125
138
|
|
|
126
|
-
if
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
// ignore if Boundary events overlap their host
|
|
140
|
+
// but still check if they overlap other elements
|
|
141
|
+
if (shape.element.attachedToRef === otherShape.element || otherShape.element.attachedToRef === shape.element) {
|
|
142
|
+
continue;
|
|
129
143
|
}
|
|
144
|
+
|
|
145
|
+
overlapping.add(shape.element);
|
|
146
|
+
overlapping.add(otherShape.element);
|
|
130
147
|
}
|
|
131
148
|
}
|
|
149
|
+
|
|
150
|
+
// report in element order, independent of the order overlaps are detected
|
|
151
|
+
elements.forEach(element => {
|
|
152
|
+
if (overlapping.has(element)) {
|
|
153
|
+
elementsToReport.add(element);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the shapes of all elements with valid bounds, the only elements
|
|
160
|
+
* that can overlap.
|
|
161
|
+
*
|
|
162
|
+
* @param {Array} elements
|
|
163
|
+
* @param {Map} diObjects
|
|
164
|
+
*
|
|
165
|
+
* @return {Shape[]}
|
|
166
|
+
*/
|
|
167
|
+
function getShapes(elements, diObjects) {
|
|
168
|
+
const shapes = [];
|
|
169
|
+
|
|
170
|
+
elements.forEach(element => {
|
|
171
|
+
const bounds = diObjects.get(element)?.bounds;
|
|
172
|
+
|
|
173
|
+
if (!isValidShapeElement(bounds)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const left = bounds.x;
|
|
178
|
+
const right = bounds.x + bounds.width;
|
|
179
|
+
|
|
180
|
+
// shapes with a NaN left or right edge never collide,
|
|
181
|
+
// and a NaN left edge would break the sweep
|
|
182
|
+
if (Number.isNaN(left) || Number.isNaN(right)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
shapes.push({
|
|
187
|
+
element,
|
|
188
|
+
left,
|
|
189
|
+
right,
|
|
190
|
+
top: bounds.y,
|
|
191
|
+
bottom: bounds.y + bounds.height
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return shapes;
|
|
132
196
|
}
|
|
133
197
|
|
|
134
198
|
/**
|
|
@@ -148,14 +212,15 @@ function isOutsideParentBoundary(childBounds, parentBounds) {
|
|
|
148
212
|
|
|
149
213
|
/**
|
|
150
214
|
* Check if two rectangle shapes collides
|
|
215
|
+
*
|
|
216
|
+
* @param {Shape} firstShape
|
|
217
|
+
* @param {Shape} secondShape
|
|
218
|
+
*
|
|
219
|
+
* @return {boolean}
|
|
151
220
|
*/
|
|
152
|
-
function isCollision(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const collisionX = firstBounds.x + firstBounds.width >= secondBounds.x && secondBounds.x + secondBounds.width >= firstBounds.x;
|
|
158
|
-
const collisionY = firstBounds.y + firstBounds.height >= secondBounds.y && secondBounds.y + secondBounds.height >= firstBounds.y;
|
|
221
|
+
function isCollision(firstShape, secondShape) {
|
|
222
|
+
const collisionX = firstShape.right >= secondShape.left && secondShape.right >= firstShape.left;
|
|
223
|
+
const collisionY = firstShape.bottom >= secondShape.top && secondShape.bottom >= firstShape.top;
|
|
159
224
|
|
|
160
225
|
// collision on both axis
|
|
161
226
|
return collisionX && collisionY;
|