bpmnlint 11.12.0 → 11.13.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/config/all.js CHANGED
@@ -21,6 +21,7 @@ const allRules = [
21
21
  'no-overlapping-elements',
22
22
  'single-blank-start-event',
23
23
  'single-event-definition',
24
+ 'standard-size',
24
25
  'start-event-required',
25
26
  'sub-process-blank-start-event',
26
27
  'superfluous-gateway',
@@ -17,7 +17,7 @@ module.exports = {
17
17
  'no-implicit-split': 'error',
18
18
  'no-implicit-end': 'error',
19
19
  'no-implicit-start': 'error',
20
- 'no-inclusive-gateway': 'error',
20
+ 'no-inclusive-gateway': 'warn',
21
21
  'no-overlapping-elements': 'warn',
22
22
  'single-blank-start-event': 'error',
23
23
  'single-event-definition': 'error',
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.12.0",
4
+ "version": "11.13.0",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [
7
7
  "bpmnlint",
@@ -41,11 +41,11 @@
41
41
  "dependencies": {
42
42
  "@bpmn-io/moddle-utils": "^0.3.0",
43
43
  "ansi-colors": "^4.1.3",
44
- "bpmn-moddle": "^10.0.0",
44
+ "bpmn-moddle": "^10.1.0",
45
45
  "bpmnlint-utils": "^1.1.1",
46
46
  "cli-table": "^0.3.11",
47
47
  "color-support": "^1.1.3",
48
- "min-dash": "^5.0.0",
48
+ "min-dash": "^5.1.0",
49
49
  "mri": "^1.2.0",
50
50
  "pluralize": "^8.0.0",
51
51
  "tiny-glob": "^0.2.9"
@@ -53,14 +53,15 @@
53
53
  "devDependencies": {
54
54
  "@bpmn-io/bpmnlint-generate-docs-images": "^1.2.0",
55
55
  "@types/mocha": "^10.0.10",
56
+ "@types/node": "^24.13.3",
56
57
  "chai": "^6.2.2",
57
- "eslint": "^9.39.2",
58
- "eslint-plugin-bpmn-io": "^2.2.0",
58
+ "eslint": "^9.39.5",
59
+ "eslint-plugin-bpmn-io": "^2.3.1",
59
60
  "execa": "^9.6.1",
60
61
  "install-local": "^4.1.0",
61
- "mocha": "^11.7.5",
62
- "npm-run-all2": "^8.0.4",
63
- "nyc": "^17.1.0",
62
+ "mocha": "^11.8.0",
63
+ "npm-run-all2": "^9.0.3",
64
+ "nyc": "^18.0.0",
64
65
  "strip-indent": "^4.1.1",
65
66
  "typescript": "^5.9.3"
66
67
  },
@@ -0,0 +1,235 @@
1
+ const {
2
+ is
3
+ } = require('bpmnlint-utils');
4
+
5
+ const {
6
+ annotateRule
7
+ } = require('./helper');
8
+
9
+
10
+ /**
11
+ * Default element sizes.
12
+ *
13
+ * NOTE: Kept in sync with `bpmn-js`
14
+ * (`lib/util/ElementSizeUtil.js`). We intentionally copy the values
15
+ * here to avoid a (heavy) dependency on `bpmn-js`.
16
+ *
17
+ * @type { Record<string, { width: number, height: number }> }
18
+ */
19
+ const DEFAULT_SIZES = {
20
+ 'bpmn:Task': { width: 100, height: 80 },
21
+ 'bpmn:CallActivity': { width: 100, height: 80 },
22
+ 'bpmn:SubProcess': { width: 100, height: 80 }, // collapsed
23
+ 'bpmn:Gateway': { width: 50, height: 50 },
24
+ 'bpmn:Event': { width: 36, height: 36 },
25
+ 'bpmn:DataObjectReference': { width: 36, height: 50 },
26
+ 'bpmn:DataStoreReference': { width: 50, height: 50 },
27
+ 'bpmn:Participant': { width: 60, height: 60 } // collapsed breadth (empty pool)
28
+ };
29
+
30
+ /**
31
+ * Element types resolved through the generic size lookup, most specific first.
32
+ *
33
+ * NOTE: `bpmn:Participant` is handled separately (empty pools only).
34
+ *
35
+ * @type { string[] }
36
+ */
37
+ const CHECKED_TYPES = [
38
+ 'bpmn:SubProcess',
39
+ 'bpmn:CallActivity',
40
+ 'bpmn:Task',
41
+ 'bpmn:Gateway',
42
+ 'bpmn:Event',
43
+ 'bpmn:DataObjectReference',
44
+ 'bpmn:DataStoreReference'
45
+ ];
46
+
47
+
48
+ /**
49
+ * A rule that checks that elements have a standard (default) size.
50
+ *
51
+ * Resizable containers, whose size follows their content, are excluded:
52
+ *
53
+ * * expanded sub-processes
54
+ * * participants (pools) with content
55
+ * * lanes
56
+ *
57
+ * Empty pools (no `processRef`) are only checked for their fixed dimension,
58
+ * i.e. the default height for horizontally modeled and the default width for
59
+ * vertically modeled diagrams.
60
+ *
61
+ * Standard sizes are configurable per element type:
62
+ *
63
+ * ```json
64
+ * {
65
+ * "rules": {
66
+ * "standard-size": [ "warn", {
67
+ * "bpmn:Task": { "width": 120, "height": 80 }
68
+ * } ]
69
+ * }
70
+ * }
71
+ * ```
72
+ *
73
+ * @type { import('../lib/types.js').RuleFactory }
74
+ */
75
+ module.exports = function(config) {
76
+
77
+ const sizes = {
78
+ ...DEFAULT_SIZES,
79
+ ...(config || {})
80
+ };
81
+
82
+ function check(node, reporter) {
83
+
84
+ // check each shape as we traverse the DI, avoiding a separate pass;
85
+ // each element is validated independently based on its own bounds
86
+ if (!is(node, 'bpmndi:BPMNShape')) {
87
+ return;
88
+ }
89
+
90
+ const element = node.bpmnElement;
91
+ const bounds = node.bounds;
92
+
93
+ // only check shapes with a linked element and proper bounds
94
+ if (!element || !isValidBounds(bounds)) {
95
+ return;
96
+ }
97
+
98
+ const expected = getExpectedSize(element, node, sizes);
99
+
100
+ // ignore elements without an expected size or with a malformed
101
+ // (non-object) size configuration
102
+ if (!expected || typeof expected !== 'object') {
103
+ return;
104
+ }
105
+
106
+ const widthMismatch = typeof expected.width === 'number' && bounds.width !== expected.width;
107
+ const heightMismatch = typeof expected.height === 'number' && bounds.height !== expected.height;
108
+
109
+ if (!widthMismatch && !heightMismatch) {
110
+ return;
111
+ }
112
+
113
+ reporter.report(element.id, getMessage(expected));
114
+ }
115
+
116
+ return annotateRule('standard-size', {
117
+ check
118
+ });
119
+ };
120
+
121
+
122
+ // helpers ////////////////////////
123
+
124
+ /**
125
+ * Determine the expected (standard) size of an element or `null` if the
126
+ * element must not be checked.
127
+ *
128
+ * The returned object may contain only `width` or only `height`, in which case
129
+ * just that dimension is checked (empty pools).
130
+ *
131
+ * @param { Object } element
132
+ * @param { Object } di
133
+ * @param { Record<string, { width: number, height: number }> } sizes
134
+ *
135
+ * @return { { width?: number, height?: number } | null }
136
+ */
137
+ function getExpectedSize(element, di, sizes) {
138
+
139
+ // participants (pools)
140
+ if (is(element, 'bpmn:Participant')) {
141
+
142
+ // pools with content follow their content
143
+ if (element.processRef) {
144
+ return null;
145
+ }
146
+
147
+ const horizontal = isHorizontal(di);
148
+
149
+ // empty pools are only checked for their fixed dimension:
150
+ // the height for horizontal, the width for vertical pools
151
+ const participantSize = sizes['bpmn:Participant'] || {};
152
+ const value = horizontal ? participantSize.height : participantSize.width;
153
+
154
+ // ignore missing / malformed configuration
155
+ if (typeof value !== 'number') {
156
+ return null;
157
+ }
158
+
159
+ return horizontal
160
+ ? { height: value }
161
+ : { width: value };
162
+ }
163
+
164
+ // lanes follow their content
165
+ if (is(element, 'bpmn:Lane')) {
166
+ return null;
167
+ }
168
+
169
+ // expanded sub-processes follow their content
170
+ if (is(element, 'bpmn:SubProcess') && isExpanded(di)) {
171
+ return null;
172
+ }
173
+
174
+ const type = CHECKED_TYPES.find(type => is(element, type));
175
+
176
+ if (!type) {
177
+ return null;
178
+ }
179
+
180
+ return sizes[type];
181
+ }
182
+
183
+ /**
184
+ * @param { { width?: number, height?: number } } expected
185
+ *
186
+ * @return { string }
187
+ */
188
+ function getMessage(expected) {
189
+ if (typeof expected.height !== 'number') {
190
+ return `Element has a non-standard width; expected width of ${expected.width}`;
191
+ }
192
+
193
+ if (typeof expected.width !== 'number') {
194
+ return `Element has a non-standard height; expected height of ${expected.height}`;
195
+ }
196
+
197
+ return `Element has a non-standard size; expected ${expected.width}x${expected.height}`;
198
+ }
199
+
200
+ /**
201
+ * Check whether a sub-process DI is expanded.
202
+ *
203
+ * NOTE: Adapted from `bpmn-js` (`lib/util/DiUtil.js`).
204
+ *
205
+ * @param { Object } di
206
+ *
207
+ * @return { boolean }
208
+ */
209
+ function isExpanded(di) {
210
+ return !!di && !!di.isExpanded;
211
+ }
212
+
213
+ /**
214
+ * Check whether a participant / pool DI is modeled horizontally.
215
+ *
216
+ * NOTE: Adapted from `bpmn-js` (`lib/util/DiUtil.js`).
217
+ *
218
+ * @param { Object } di
219
+ *
220
+ * @return { boolean }
221
+ */
222
+ function isHorizontal(di) {
223
+ return !di || di.isHorizontal === undefined || di.isHorizontal === true;
224
+ }
225
+
226
+ /**
227
+ * @param { Object } bounds
228
+ *
229
+ * @return { boolean }
230
+ */
231
+ function isValidBounds(bounds) {
232
+ return !!bounds && is(bounds, 'dc:Bounds') &&
233
+ typeof bounds.width === 'number' &&
234
+ typeof bounds.height === 'number';
235
+ }