bpmnlint 10.0.0 → 10.2.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
@@ -4,13 +4,14 @@ const allRules = [
4
4
  'event-sub-process-typed-start-event',
5
5
  'fake-join',
6
6
  'label-required',
7
+ 'link-event',
7
8
  'no-bpmndi',
8
9
  'no-complex-gateway',
9
10
  'no-disconnected',
10
11
  'no-duplicate-sequence-flows',
11
12
  'no-gateway-join-fork',
12
- 'no-implicit-split',
13
13
  'no-implicit-end',
14
+ 'no-implicit-split',
14
15
  'no-implicit-start',
15
16
  'no-inclusive-gateway',
16
17
  'no-overlapping-elements',
@@ -18,7 +19,8 @@ const allRules = [
18
19
  'single-event-definition',
19
20
  'start-event-required',
20
21
  'sub-process-blank-start-event',
21
- 'superfluous-gateway'
22
+ 'superfluous-gateway',
23
+ 'superfluous-termination'
22
24
  ];
23
25
 
24
26
 
@@ -5,6 +5,7 @@ module.exports = {
5
5
  'event-sub-process-typed-start-event': 'error',
6
6
  'fake-join': 'warn',
7
7
  'label-required': 'error',
8
+ 'link-event': 'error',
8
9
  'no-bpmndi': 'error',
9
10
  'no-complex-gateway': 'error',
10
11
  'no-disconnected': 'error',
@@ -19,6 +20,7 @@ module.exports = {
19
20
  'single-event-definition': 'error',
20
21
  'start-event-required': 'error',
21
22
  'sub-process-blank-start-event': 'error',
22
- 'superfluous-gateway': 'warn'
23
+ 'superfluous-gateway': 'warn',
24
+ 'superfluous-termination': 'warn'
23
25
  }
24
26
  };
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": "10.0.0",
4
+ "version": "10.2.0",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [
7
7
  "bpmnlint",
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "scripts": {
30
30
  "all": "run-s lint test-coverage",
31
- "lint": "eslint .",
31
+ "lint": "eslint . --ext js,mjs",
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",
@@ -0,0 +1,100 @@
1
+ const {
2
+ groupBy
3
+ } = require('min-dash');
4
+
5
+ const {
6
+ is
7
+ } = require('bpmnlint-utils');
8
+
9
+
10
+ /**
11
+ * A rule that verifies that link events are properly used.
12
+ *
13
+ * This implies:
14
+ *
15
+ * * for every link throw there exists a link catch within
16
+ * the same scope, and vice versa
17
+ * * there exists only a single pair of [ throw, catch ] links
18
+ * with a given name, per scope
19
+ * * link events have a name
20
+ *
21
+ */
22
+ module.exports = function() {
23
+
24
+ function check(node, reporter) {
25
+
26
+ if (!is(node, 'bpmn:FlowElementsContainer')) {
27
+ return;
28
+ }
29
+
30
+ const links = (node.flowElements || []).filter(isLinkEvent);
31
+
32
+ for (const link of links) {
33
+ if (!link.name) {
34
+ reporter.report(link.id, 'Link event is missing name');
35
+ }
36
+ }
37
+
38
+ const names = groupBy(links, (link) => link.name);
39
+
40
+ for (const [ name, events ] of Object.entries(names)) {
41
+
42
+ // ignore unnamed (validated earlier)
43
+ if (!name) {
44
+ continue;
45
+ }
46
+
47
+ // missing catch or throw event
48
+ if (events.length === 1) {
49
+ const event = events[0];
50
+
51
+ reporter.report(event.id, `Link ${isThrowEvent(event) ? 'catch' : 'throw' } event with name <${ name }> missing in scope`);
52
+ }
53
+
54
+ const throwEvents = events.filter(isThrowEvent);
55
+
56
+ if (throwEvents.length > 1) {
57
+ for (const event of throwEvents) {
58
+ reporter.report(event.id, `Duplicate link throw event with name <${name}> in scope`);
59
+ }
60
+ }
61
+
62
+ const catchEvents = events.filter(isCatchEvent);
63
+
64
+ if (catchEvents.length > 1) {
65
+ for (const event of catchEvents) {
66
+ reporter.report(event.id, `Duplicate link catch event with name <${name}> in scope`);
67
+ }
68
+ }
69
+ }
70
+
71
+ }
72
+
73
+ return {
74
+ check
75
+ };
76
+ };
77
+
78
+
79
+ // helpers /////////////////
80
+
81
+ function isLinkEvent(node) {
82
+
83
+ var eventDefinitions = node.eventDefinitions || [];
84
+
85
+ if (!is(node, 'bpmn:Event')) {
86
+ return false;
87
+ }
88
+
89
+ return eventDefinitions.some(
90
+ definition => is(definition, 'bpmn:LinkEventDefinition')
91
+ );
92
+ }
93
+
94
+ function isThrowEvent(node) {
95
+ return is(node, 'bpmn:ThrowEvent');
96
+ }
97
+
98
+ function isCatchEvent(node) {
99
+ return is(node, 'bpmn:CatchEvent');
100
+ }
@@ -0,0 +1,63 @@
1
+ const {
2
+ is, isAny
3
+ } = require('bpmnlint-utils');
4
+
5
+ /**
6
+ * A rule that checks, whether a gateway has only one source and target.
7
+ *
8
+ * Those gateways are superfluous since they don't do anything.
9
+ */
10
+ module.exports = function() {
11
+
12
+ function check(node, reporter) {
13
+
14
+ if (!isAny(node, [ 'bpmn:Process', 'bpmn:SubProcess' ])) {
15
+ return;
16
+ }
17
+
18
+ const flowElements = node.flowElements || [];
19
+
20
+ const ends = flowElements.filter(
21
+ element => is(element, 'bpmn:FlowNode') && (element.outgoing || []).length === 0
22
+ );
23
+
24
+ const terminateEnds = ends.filter(isTerminateEnd);
25
+
26
+ if (terminateEnds.length !== 1) {
27
+
28
+ // TODO(nikku): only detect basic cases, do not
29
+ // do any kinds of elaborate flow analysis
30
+ return;
31
+ }
32
+
33
+ const superfluous = ends.every(
34
+ (end) => isInterruptingEventSub(end) || isTerminateEnd(end)
35
+ );
36
+
37
+ if (superfluous) {
38
+
39
+ for (const node of terminateEnds) {
40
+ reporter.report(node.id, 'Termination is superfluous.');
41
+ }
42
+ }
43
+ }
44
+
45
+ return {
46
+ check
47
+ };
48
+
49
+ };
50
+
51
+ function isTerminateEnd(element) {
52
+ return is(element, 'bpmn:EndEvent') && (element.eventDefinitions || []).some(
53
+ eventDefinition => is(eventDefinition, 'bpmn:TerminateEventDefinition')
54
+ );
55
+ }
56
+
57
+ function isInterruptingEventSub(element) {
58
+ const isEventSub = is(element, 'bpmn:SubProcess') && element.triggeredByEvent;
59
+
60
+ return isEventSub && (element.flowElements || []).some(
61
+ element => is(element, 'bpmn:StartEvent') && element.isInterrupting
62
+ );
63
+ }