bpmnlint 10.2.3 → 10.3.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
@@ -3,6 +3,7 @@ const allRules = [
3
3
  'end-event-required',
4
4
  'event-sub-process-typed-start-event',
5
5
  'fake-join',
6
+ 'global',
6
7
  'label-required',
7
8
  'link-event',
8
9
  'no-bpmndi',
@@ -4,6 +4,7 @@ module.exports = {
4
4
  'end-event-required': 'error',
5
5
  'event-sub-process-typed-start-event': 'error',
6
6
  'fake-join': 'warn',
7
+ 'global': 'warn',
7
8
  'label-required': 'error',
8
9
  'link-event': 'error',
9
10
  'no-bpmndi': 'error',
@@ -0,0 +1,37 @@
1
+ export type TestFn = (...any) => any;
2
+
3
+ export type ModdleElement = any;
4
+
5
+ export type ValidRuleTest = {
6
+ config?: any,
7
+ it?: TestFn,
8
+ moddleElement: ModdleElement,
9
+ name?: string
10
+ };
11
+
12
+ export type InvalidReport = {
13
+ id: string,
14
+ message: string
15
+ };
16
+
17
+ export type InvalidRuleTest = {
18
+ config?: any,
19
+ it?: TestFn,
20
+ moddleElement: ModdleElement,
21
+ name?: string,
22
+ report: InvalidReport | InvalidReport[]
23
+ };
24
+
25
+ export type RuleTests = {
26
+ valid?: ValidRuleTest[],
27
+ invalid?: InvalidRuleTest[]
28
+ };
29
+
30
+ /**
31
+ * Verify a rule.
32
+ *
33
+ * @param ruleName
34
+ * @param rule the rule implementation
35
+ * @param tests the tests to verify
36
+ */
37
+ export function verify(ruleName: string, rule: any, tests: RuleTests): void;
package/lib/traverse.js CHANGED
@@ -1,9 +1,14 @@
1
+ /**
2
+ * @typedef { import('./types.js').EnterFn } EnterFn
3
+ * @typedef { import('./types.js').LeaveFn } LeaveFn
4
+ */
5
+
1
6
  /**
2
7
  * Traverse a moddle tree, depth first from top to bottom
3
8
  * and call the passed visitor fn.
4
9
  *
5
10
  * @param {ModdleElement} element
6
- * @param {{ enter?: Function; leave?: Function }} options
11
+ * @param {{ enter?: EnterFn; leave?: LeaveFn }} options
7
12
  */
8
13
  module.exports = function traverse(element, options) {
9
14
 
package/lib/types.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type ModdleElement = any;
2
+
3
+ export type EnterFn = (element: ModdleElement) => boolean | null;
4
+
5
+ export type LeaveFn = (element: ModdleElement) => void;
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.2.3",
4
+ "version": "10.3.0",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [
7
7
  "bpmnlint",
@@ -40,11 +40,11 @@
40
40
  "dependencies": {
41
41
  "@bpmn-io/moddle-utils": "^0.2.1",
42
42
  "ansi-colors": "^4.1.3",
43
- "bpmn-moddle": "^8.0.1",
43
+ "bpmn-moddle": "^8.1.0",
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.1.1",
47
+ "min-dash": "^4.2.1",
48
48
  "mri": "^1.2.0",
49
49
  "pluralize": "^7.0.0",
50
50
  "tiny-glob": "^0.2.9"
@@ -52,12 +52,12 @@
52
52
  "devDependencies": {
53
53
  "@bpmn-io/bpmnlint-generate-docs-images": "^0.4.0",
54
54
  "bpmnlint": ".",
55
- "chai": "^4.3.8",
56
- "eslint": "^8.49.0",
55
+ "chai": "^4.4.1",
56
+ "eslint": "^8.57.0",
57
57
  "eslint-plugin-bpmn-io": "^1.0.0",
58
58
  "execa": "^8.0.1",
59
59
  "install-local": "^3.0.1",
60
- "mocha": "^10.2.0",
60
+ "mocha": "^10.4.0",
61
61
  "npm-run-all": "^4.1.5",
62
62
  "nyc": "^15.1.0",
63
63
  "strip-indent": "^3.0.0"
@@ -0,0 +1,117 @@
1
+ const {
2
+ isAny, is
3
+ } = require('bpmnlint-utils');
4
+
5
+
6
+ /**
7
+ * A rule that verifies that global elements are properly used.
8
+ *
9
+ * Currently recognized global elements are:
10
+ *
11
+ * * `bpmn:Error`
12
+ * * `bpmn:Escalation`
13
+ * * `bpmn:Signal`
14
+ * * `bpmn:Message`
15
+ *
16
+ * For each of these elements proper usage implies:
17
+ *
18
+ * * element must have a name
19
+ * * element is used (referenced) from event definitions
20
+ * * there exists only a single element per type with a given name
21
+ */
22
+ module.exports = function() {
23
+
24
+ function check(node, reporter) {
25
+
26
+ if (!is(node, 'bpmn:Definitions')) {
27
+ return false;
28
+ }
29
+
30
+ const events = getEvents(node);
31
+ const eventDefinitions = getEventDefinitions(node);
32
+
33
+ events.forEach(event => {
34
+ if (!hasName(event)) {
35
+ reporter.report(event.id, 'Element is missing name');
36
+ }
37
+
38
+ if (!isReferenced(event, eventDefinitions)) {
39
+ reporter.report(event.id, 'Element is unused');
40
+ }
41
+
42
+ if (!isUnique(event, events)) {
43
+ reporter.report(event.id, 'Element name is not unique');
44
+ }
45
+ });
46
+
47
+ }
48
+
49
+ return {
50
+ check
51
+ };
52
+
53
+ // helpers /////////////////////////////
54
+
55
+ function getEvents(definition) {
56
+ return definition.rootElements.filter(node => isAny(node, [ 'bpmn:Error', 'bpmn:Escalation', 'bpmn:Message', 'bpmn:Signal' ]));
57
+ }
58
+
59
+ function getEventDefinitions(definition) {
60
+ const eventDefinitions = [];
61
+
62
+ function traverse(element) {
63
+ if (element.rootElements) {
64
+ element.rootElements.forEach(traverse);
65
+ }
66
+
67
+ if (element.flowElements) {
68
+ element.flowElements.forEach(traverse);
69
+ }
70
+
71
+ if (element.eventDefinitions) {
72
+ element.eventDefinitions.forEach(eventDefinition => eventDefinitions.push(eventDefinition));
73
+ }
74
+ }
75
+
76
+ traverse(definition);
77
+ return eventDefinitions;
78
+ }
79
+
80
+ function hasName(event) {
81
+ return (
82
+ event.name?.trim() !== ''
83
+ );
84
+ }
85
+
86
+ function isReferenced(event, eventDefinitions) {
87
+ if (is(event, 'bpmn:Error')) {
88
+ return (
89
+ eventDefinitions.some(node => is(node, 'bpmn:ErrorEventDefinition') && event.id === node.errorRef?.id)
90
+ );
91
+ }
92
+
93
+ if (is(event, 'bpmn:Escalation')) {
94
+ return (
95
+ eventDefinitions.some(node => is(node, 'bpmn:EscalationEventDefinition') && event.id === node.escalationRef?.id)
96
+ );
97
+ }
98
+
99
+ if (is(event, 'bpmn:Message')) {
100
+ return (
101
+ eventDefinitions.some(node => is(node, 'bpmn:MessageEventDefinition') && event.id === node.messageRef?.id)
102
+ );
103
+ }
104
+
105
+ if (is(event, 'bpmn:Signal')) {
106
+ return (
107
+ eventDefinitions.some(node => is(node, 'bpmn:SignalEventDefinition') && event.id === node.signalRef?.id)
108
+ );
109
+ }
110
+ }
111
+
112
+ function isUnique(event, events) {
113
+ return (
114
+ events.filter(node => is(node, event.$type) && event.name === node.name).length === 1
115
+ );
116
+ }
117
+ };