@camunda/linting 0.4.1 → 0.5.0-alpha.2

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/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ All notable changes to [@camunda/linting](https://github.com/camunda/linting) ar
6
6
 
7
7
  ___Note:__ Yet to be released changes appear here._
8
8
 
9
+ ## 0.5.0-alpha.2
10
+
11
+ * `FIX`: publish assets
12
+
13
+ ## 0.5.0-alpha.1
14
+
15
+ * `FIX`: publish modeler.js
16
+
17
+ ## 0.5.0-alpha.0
18
+
19
+ * `FEAT`: add bpmn-js plugin for canvas and properties panel errors ([#11](https://github.com/camunda/linting/pull/11))
20
+ * `FEAT`: add no-zeebe-properties rule ([#43](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/43))
21
+
9
22
  ## 0.4.1
10
23
 
11
24
  * `DEPS`: broaden supported versions range of `bpmn-js-properties-panel`
package/README.md CHANGED
@@ -8,12 +8,24 @@ The BPMN linter used by the Camunda Desktop and Web Modeler. Batteries included.
8
8
  * configures linter based on `modeler:executionPlatform` and `modeler:executionPlatformVersion`
9
9
  * creates error messages to be shown in desktop and web modeler
10
10
  * creates errors to be shown in properties panel
11
+ * creates error overlays to be shown on canvas
11
12
 
12
13
  # Usage
13
14
 
14
15
  ```javascript
16
+ import Modeler from 'bpmn-js/lib/Modeler';
17
+
15
18
  import { Linter } from '@camunda/linting';
16
- import { getErrors } from '@camunda/linting/properties-panel';
19
+
20
+ import lintingModule from '@camunda/linting/modeler';
21
+
22
+ import '@camunda/linting/assets/linting.css';
23
+
24
+ const modeler = new Modeler({
25
+ additionalModules: [
26
+ lintingModule
27
+ ]
28
+ });
17
29
 
18
30
  // configure to be used with desktop or web modeler
19
31
  const linter = new Linter({
@@ -28,10 +40,27 @@ const reports = await linter.lint(xml);
28
40
 
29
41
  ...
30
42
 
31
- const errors = getErrors(reports, element);
43
+ // update errors on canvas and in properties panel (requires bpmn-js-properties-panel >= 1.3.0)
44
+ modeler.get('linting').setErrors(reports);
45
+
46
+ // show error by selecting element and properties panel entry
47
+ modeler.get('linting').showError(report);
48
+ ```
49
+
50
+ # Development
51
+
52
+ ```sh
53
+ # install
54
+ npm i
55
+
56
+ # run tests
57
+ npm t
58
+
59
+ # run tests in watch mode
60
+ npm run test:watch
32
61
 
33
- // bpmn-js-properties-panel >=1.3.0
34
- eventBus.fire('propertiesPanel.setErrors', { errors });
62
+ # run example
63
+ npm start
35
64
  ```
36
65
 
37
66
  # License
@@ -0,0 +1,33 @@
1
+ :root {
2
+ --color-red-360-100-45: hsl(360, 100%, 45%);
3
+ --color-white: hsl(0, 0%, 100%);
4
+
5
+ --linting-annotation-background-color: var(--color-red-360-100-45);
6
+ --linting-annotation-fill-color: var(--color-white);
7
+ }
8
+
9
+ .djs-overlays:not(.hover) .bjs-linting-annotation {
10
+ opacity: 0.5;
11
+ }
12
+
13
+ .djs-overlays.hover .bjs-linting-annotation,
14
+ .djs-overlays .bjs-linting-annotation:hover {
15
+ opacity: 1;
16
+ }
17
+
18
+ .bjs-linting-annotation {
19
+ display: flex;
20
+ flex-direction: row;
21
+ align-items: center;
22
+ padding: 1px;
23
+ border-radius: 2px;
24
+ background-color: var(--linting-annotation-background-color);
25
+ cursor: default;
26
+ z-index: 100000;
27
+ }
28
+
29
+ .bjs-linting-annotation > svg {
30
+ fill: var(--linting-annotation-fill-color);
31
+ width: 18px;
32
+ height: 18px;
33
+ }
@@ -0,0 +1,105 @@
1
+ import { getErrors } from '../utils/properties-panel';
2
+
3
+ export default class Linting {
4
+ constructor(canvas, elementRegistry, eventBus, lintingAnnotations, selection) {
5
+ this._canvas = canvas;
6
+ this._elementRegistry = elementRegistry;
7
+ this._eventBus = eventBus;
8
+ this._lintingAnnotations = lintingAnnotations;
9
+ this._selection = selection;
10
+
11
+ this._reports = [];
12
+
13
+ eventBus.on('selection.changed', () => this._update());
14
+
15
+ eventBus.on('lintingAnnotations.click', ({ report }) => this.showError(report));
16
+ }
17
+
18
+ showError(report) {
19
+ const {
20
+ id,
21
+ propertiesPanel = {}
22
+ } = report;
23
+
24
+ const element = this._elementRegistry.get(id);
25
+
26
+ if (element !== this._canvas.getRootElement() && needsScrollToElement(this._canvas, element)) {
27
+ this._canvas.scrollToElement(element);
28
+ }
29
+
30
+ this._selection.select(element);
31
+
32
+ const { entryId } = propertiesPanel;
33
+
34
+ this._eventBus.fire('propertiesPanel.showEntry', {
35
+ id: entryId
36
+ });
37
+ }
38
+
39
+ setErrors(reports) {
40
+ this._reports = reports;
41
+
42
+ this._update();
43
+ }
44
+
45
+ activate() {
46
+ this._update();
47
+ }
48
+
49
+ deactivate() {
50
+ this._update([]);
51
+ }
52
+
53
+ _update(reports) {
54
+ if (!reports) {
55
+ reports = this._reports;
56
+ }
57
+
58
+ // set annotations
59
+ this._lintingAnnotations.setErrors(reports);
60
+
61
+ // set properties panel errors
62
+ const selectedElement = this._getSelectedElement();
63
+
64
+ this._eventBus.fire('propertiesPanel.setErrors', {
65
+ errors: getErrors(this._reports, selectedElement)
66
+ });
67
+ }
68
+
69
+ _getSelectedElement() {
70
+ const selection = this._selection.get();
71
+
72
+ if (!selection || !selection.length) {
73
+ return this._canvas.getRootElement();
74
+ }
75
+
76
+ const selectedElement = selection[ 0 ];
77
+
78
+ if (isLabel(selectedElement)) {
79
+ return selectedElement.labelTarget;
80
+ }
81
+
82
+ return selectedElement;
83
+ }
84
+ }
85
+
86
+ Linting.$inject = [
87
+ 'canvas',
88
+ 'elementRegistry',
89
+ 'eventBus',
90
+ 'lintingAnnotations',
91
+ 'selection'
92
+ ];
93
+
94
+ function isLabel(element) {
95
+ return !!element.labelTarget;
96
+ }
97
+
98
+ function needsScrollToElement(canvas, element) {
99
+ const viewbox = canvas.viewbox();
100
+
101
+ return viewbox.x > element.x
102
+ || viewbox.y > element.y
103
+ || viewbox.x + viewbox.width < element.x + element.width
104
+ || viewbox.y + viewbox.height < element.y + element.height;
105
+ }
@@ -0,0 +1,72 @@
1
+ import { groupBy } from 'min-dash';
2
+
3
+ import { domify } from 'min-dom';
4
+
5
+ const errorSvg = `
6
+ <svg viewBox="2 2 20 20">
7
+ <path d="M12,5 C15.8659932,5 19,8.13400675 19,12 C19,15.8659932 15.8659932,19 12,19 C8.13400675,19 5,15.8659932 5,12 C5,8.13400675 8.13400675,5 12,5 Z M9.33333333,8 L8,9.33333333 L10.667,12 L8,14.6666667 L9.33333333,16 L12,13.333 L14.6666667,16 L16,14.6666667 L13.333,12 L16,9.33333333 L14.6666667,8 L12,10.666 L9.33333333,8 Z"></path>
8
+ </svg>
9
+ `;
10
+
11
+ export default class LintingAnnotations {
12
+ constructor(elementRegistry, eventBus, overlays) {
13
+ this._elementRegistry = elementRegistry;
14
+ this._eventBus = eventBus;
15
+ this._overlays = overlays;
16
+
17
+ this._reportsByElement = {};
18
+
19
+ this._overlayIds = {};
20
+ }
21
+
22
+ setErrors(reports) {
23
+ this._reportsByElement = groupBy(reports, 'id');
24
+
25
+ this._update();
26
+ }
27
+
28
+ _update(reportsByElement) {
29
+ if (!reportsByElement) {
30
+ reportsByElement = this._reportsByElement;
31
+ }
32
+
33
+ this._overlays.remove({ type: 'linting' });
34
+
35
+ Object.entries(reportsByElement).forEach(([ id, reports ]) => {
36
+ const element = this._elementRegistry.get(id);
37
+
38
+ if (!element) {
39
+ return;
40
+ }
41
+
42
+ const overlay = domify(`
43
+ <div class="bjs-linting-annotation" title="Click to show">
44
+ ${ errorSvg }
45
+ </div>
46
+ `);
47
+
48
+ overlay.addEventListener('click', () => {
49
+ this._eventBus.fire('lintingAnnotations.click', { report: reports[ 0 ] });
50
+ });
51
+
52
+ const overlayId = this._overlays.add(element, 'linting', {
53
+ position: {
54
+ bottom: -5,
55
+ left: 0
56
+ },
57
+ html: overlay,
58
+ show: {
59
+ minZoom: 0.5
60
+ }
61
+ });
62
+
63
+ this._overlayIds[ id ] = overlayId;
64
+ });
65
+ }
66
+ }
67
+
68
+ LintingAnnotations.$inject = [
69
+ 'elementRegistry',
70
+ 'eventBus',
71
+ 'overlays'
72
+ ];
@@ -0,0 +1,11 @@
1
+ import Linting from './Linting';
2
+ import LintingAnnotations from './LintingAnnotations';
3
+
4
+ export default {
5
+ __init__: [
6
+ 'linting',
7
+ 'lintingAnnotations'
8
+ ],
9
+ linting: [ 'type', Linting ],
10
+ lintingAnnotations: [ 'type', LintingAnnotations ]
11
+ };
@@ -108,13 +108,20 @@ function getExtensionElementNotAllowedErrorMessage(report, executionPlatformLabe
108
108
 
109
109
  const {
110
110
  node,
111
+ parentNode,
111
112
  extensionElement
112
113
  } = error;
113
114
 
115
+ const typeString = getTypeString(parentNode || node);
116
+
114
117
  if (is(node, 'bpmn:BusinessRuleTask') && is(extensionElement, 'zeebe:CalledDecision')) {
115
118
  return `A <Business Rule Task> with <Implementation: DMN decision> is not supported by ${ executionPlatformLabel }`;
116
119
  }
117
120
 
121
+ if (is(extensionElement, 'zeebe:Properties')) {
122
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Extension properties> is not supported by ${ executionPlatformLabel }`;
123
+ }
124
+
118
125
  return message;
119
126
  }
120
127
 
@@ -122,6 +122,14 @@ export function getEntryIds(report) {
122
122
  });
123
123
  }
124
124
 
125
+ if (isExtensionElementNotAllowedError(error, 'zeebe:Properties')) {
126
+ const { extensionElement } = error;
127
+
128
+ return extensionElement.get('zeebe:properties').map((zeebeProperty, index) => {
129
+ return `${ id }-extensionProperty-${ index }-name`;
130
+ });
131
+ }
132
+
125
133
  return [];
126
134
  }
127
135
 
@@ -189,6 +197,16 @@ export function getErrorMessage(id) {
189
197
  if (/^.+-header-[0-9]+-key$/.test(id)) {
190
198
  return 'Must be unique.';
191
199
  }
200
+
201
+ if (/^.+-extensionProperty-[0-9]+-name$/.test(id)) {
202
+ return 'Not supported.';
203
+ }
204
+ }
205
+
206
+ function isExtensionElementNotAllowedError(error, extensionElement, type) {
207
+ return error.type === ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED
208
+ && is(error.extensionElement, extensionElement)
209
+ && (!type || is(error.node, type));
192
210
  }
193
211
 
194
212
  function isExtensionElementRequiredError(error, requiredExtensionElement, type) {
package/modeler.js ADDED
@@ -0,0 +1 @@
1
+ export { default } from './lib/modeler';
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@camunda/linting",
3
- "version": "0.4.1",
3
+ "version": "0.5.0-alpha.2",
4
4
  "description": "Linting for Camunda Platform",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "all": "npm run lint && npm test",
8
8
  "lint": "eslint .",
9
- "test": "mocha -r esm --reporter=spec --recursive test/spec",
10
- "test:watch": "npm run test -- --watch"
9
+ "start": "cross-env SINGLE_START=modeler npm run test:watch",
10
+ "test": "karma start",
11
+ "test:watch": "npm test -- --auto-watch --no-single-run"
11
12
  },
12
13
  "keywords": [
13
14
  "bpmnlint",
@@ -25,18 +26,32 @@
25
26
  "dependencies": {
26
27
  "bpmn-moddle": "^7.1.2",
27
28
  "bpmnlint": "^7.8.0",
28
- "bpmnlint-plugin-camunda-compat": "^0.10.0",
29
+ "bpmnlint-plugin-camunda-compat": "^0.11.0",
29
30
  "bpmnlint-utils": "^1.0.2",
31
+ "min-dash": "^3.8.1",
32
+ "min-dom": "^3.2.1",
30
33
  "modeler-moddle": "^0.2.0",
31
- "zeebe-bpmn-moddle": "^0.12.1"
34
+ "zeebe-bpmn-moddle": "^0.14.0"
32
35
  },
33
36
  "devDependencies": {
37
+ "bpmn-js": "^9.4.0",
34
38
  "chai": "^4.3.6",
39
+ "cross-env": "^7.0.3",
35
40
  "eslint": "^4.11.0",
36
41
  "eslint-plugin-bpmn-io": "^0.4.1",
37
- "esm": "^3.2.25",
38
- "min-dash": "^3.8.1",
39
- "mocha": "^4.0.1"
42
+ "karma": "^6.4.0",
43
+ "karma-chrome-launcher": "^3.1.1",
44
+ "karma-debug-launcher": "0.0.5",
45
+ "karma-env-preprocessor": "^0.1.1",
46
+ "karma-mocha": "^2.0.1",
47
+ "karma-sinon-chai": "^2.0.2",
48
+ "karma-webpack": "^5.0.0",
49
+ "mocha": "^4.0.1",
50
+ "mocha-test-container-support": "^0.2.0",
51
+ "puppeteer": "^16.2.0",
52
+ "sinon": "^14.0.0",
53
+ "sinon-chai": "^3.7.0",
54
+ "webpack": "^5.74.0"
40
55
  },
41
56
  "peerDependencies": {
42
57
  "bpmn-js-properties-panel": "^1.3.0"
@@ -47,8 +62,9 @@
47
62
  }
48
63
  },
49
64
  "files": [
65
+ "assets",
50
66
  "lib",
51
- "properties-panel.js"
67
+ "modeler.js"
52
68
  ],
53
69
  "publishConfig": {
54
70
  "access": "public"
@@ -1 +0,0 @@
1
- export * from './lib/utils/properties-panel';