bpmnlint 8.2.0 → 8.3.1
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/README.md +5 -2
- package/config/all.js +2 -0
- package/config/recommended.js +2 -0
- package/lib/resolver/node-resolver.js +49 -14
- package/lib/support/compile-config.js +67 -7
- package/package.json +9 -8
- package/rules/no-implicit-end.js +30 -0
- package/rules/no-implicit-start.js +30 -0
package/README.md
CHANGED
|
@@ -105,8 +105,6 @@ const reports = linter.lint(definitions);
|
|
|
105
105
|
// }
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
For browser usage include your [linting configuration](#configuration) using your favorite bundler plug-in ([Rollup](https://www.npmjs.com/package/rollup-plugin-bpmnlint), [Webpack](https://www.npmjs.com/package/bpmnlint-loader)).
|
|
109
|
-
|
|
110
108
|
|
|
111
109
|
## Writing a Plug-in
|
|
112
110
|
|
|
@@ -119,6 +117,11 @@ npm init bpmnlint-plugin {PLUGIN_NAME}
|
|
|
119
117
|
Checkout the [bpmnlint-plugin-example](https://github.com/bpmn-io/bpmnlint-plugin-example) for details on how to define, test, and consume custom lint rules. Use the [bpmnlint playground](https://github.com/bpmn-io/bpmnlint-playground) to implement new rules with quick visual feedback.
|
|
120
118
|
|
|
121
119
|
|
|
120
|
+
## Bundling
|
|
121
|
+
|
|
122
|
+
For browser usage include your [linting configuration](#configuration) using your favorite bundler plug-in ([Rollup](https://www.npmjs.com/package/rollup-plugin-bpmnlint), [Webpack](https://www.npmjs.com/package/bpmnlint-loader)).
|
|
123
|
+
|
|
124
|
+
|
|
122
125
|
## Visual Feedback
|
|
123
126
|
|
|
124
127
|
Integrate the linter via [bpmn-js-bpmnlint](https://github.com/bpmn-io/bpmn-js-bpmnlint) into [bpmn-js](https://github.com/bpmn-io/bpmn-js) and get direct feedback during modeling.
|
package/config/all.js
CHANGED
package/config/recommended.js
CHANGED
|
@@ -11,6 +11,8 @@ module.exports = {
|
|
|
11
11
|
'no-duplicate-sequence-flows': 'error',
|
|
12
12
|
'no-gateway-join-fork': 'error',
|
|
13
13
|
'no-implicit-split': 'error',
|
|
14
|
+
'no-implicit-end': 'error',
|
|
15
|
+
'no-implicit-start': 'error',
|
|
14
16
|
'no-inclusive-gateway': 'error',
|
|
15
17
|
'single-blank-start-event': 'error',
|
|
16
18
|
'single-event-definition': 'error',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const Module = require('module');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
+
const { isString } = require('min-dash');
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* A resolver that locates rules and configurations
|
|
6
8
|
* using node module resolution.
|
|
@@ -26,15 +28,48 @@ NodeResolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
|
26
28
|
|
|
27
29
|
pkg = this.normalizePkg(pkg);
|
|
28
30
|
|
|
31
|
+
let pkgInstance;
|
|
32
|
+
|
|
33
|
+
// (1) try resolving rule via $PKG.rules[$NAME]
|
|
34
|
+
try {
|
|
35
|
+
pkgInstance = this.require(pkg);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
|
|
38
|
+
/* ignore */
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (pkgInstance) {
|
|
42
|
+
const rules = pkgInstance.rules || {};
|
|
43
|
+
|
|
44
|
+
if (ruleName in rules) {
|
|
45
|
+
const rule = rules[ruleName];
|
|
46
|
+
|
|
47
|
+
if (!isString(rule)) {
|
|
48
|
+
throw new Error('cannot resolve rule <' + ruleName + '> from <' + originalPkg + '>: illegal rule export (expected path reference)');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// local reference, resolved relative to pkg location
|
|
52
|
+
if (rule.startsWith('.')) {
|
|
53
|
+
const pkgDir = path.dirname(this.require.resolve(pkg));
|
|
54
|
+
|
|
55
|
+
return this.require(path.posix.normalize(`${ pkgDir }/${ rule }`));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// absolute reference
|
|
59
|
+
return this.require(rule);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// (2) try resolving rule via $PKG/rules/$NAME
|
|
29
64
|
try {
|
|
30
65
|
if (pkg === 'bpmnlint') {
|
|
31
66
|
return this.requireLocal(`../../rules/${ruleName}`);
|
|
32
67
|
} else {
|
|
33
68
|
return this.require(`${pkg}/rules/${ruleName}`);
|
|
34
69
|
}
|
|
35
|
-
} catch (err) {
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
} catch (err) { /* ignore */ }
|
|
71
|
+
|
|
72
|
+
throw new Error('cannot resolve rule <' + ruleName + '> from <' + originalPkg + '>');
|
|
38
73
|
};
|
|
39
74
|
|
|
40
75
|
NodeResolver.prototype.resolveConfig = function(pkg, configName) {
|
|
@@ -43,16 +78,7 @@ NodeResolver.prototype.resolveConfig = function(pkg, configName) {
|
|
|
43
78
|
|
|
44
79
|
pkg = this.normalizePkg(pkg);
|
|
45
80
|
|
|
46
|
-
//
|
|
47
|
-
try {
|
|
48
|
-
if (pkg === 'bpmnlint') {
|
|
49
|
-
return this.requireLocal(`../../config/${configName}`);
|
|
50
|
-
} else {
|
|
51
|
-
return this.require(`${pkg}/config/${configName}`);
|
|
52
|
-
}
|
|
53
|
-
} catch (err) { /* ignore */ }
|
|
54
|
-
|
|
55
|
-
// resolve config via $PKG.configs[$NAME]
|
|
81
|
+
// (1) try resolving config via $PKG.configs[$NAME]
|
|
56
82
|
try {
|
|
57
83
|
const instance = this.require(pkg);
|
|
58
84
|
|
|
@@ -66,8 +92,17 @@ NodeResolver.prototype.resolveConfig = function(pkg, configName) {
|
|
|
66
92
|
/* ignore */
|
|
67
93
|
}
|
|
68
94
|
|
|
95
|
+
// (2) try resolving config via $PKG/config/$NAME
|
|
96
|
+
try {
|
|
97
|
+
if (pkg === 'bpmnlint') {
|
|
98
|
+
return this.requireLocal(`../../config/${configName}`);
|
|
99
|
+
} else {
|
|
100
|
+
return this.require(`${pkg}/config/${configName}`);
|
|
101
|
+
}
|
|
102
|
+
} catch (err) { /* ignore */ }
|
|
103
|
+
|
|
69
104
|
throw new Error(
|
|
70
|
-
'
|
|
105
|
+
'cannot resolve config <' + configName + '> from <' + originalPkg + '>'
|
|
71
106
|
);
|
|
72
107
|
};
|
|
73
108
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const path = require('path').posix;
|
|
2
|
+
|
|
3
|
+
const { isString } = require('min-dash');
|
|
4
|
+
|
|
1
5
|
const Linter = require('../linter');
|
|
2
6
|
|
|
3
7
|
const NodeResolver = require('../resolver/node-resolver');
|
|
@@ -53,7 +57,7 @@ Resolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
|
53
57
|
const rule = cache[pkg + '/' + ruleName];
|
|
54
58
|
|
|
55
59
|
if (!rule) {
|
|
56
|
-
throw new Error('cannot resolve rule <' + pkg + '/' + ruleName + '
|
|
60
|
+
throw new Error('cannot resolve rule <' + pkg + '/' + ruleName + '>: not bundled');
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
return rule;
|
|
@@ -61,7 +65,7 @@ Resolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
|
61
65
|
|
|
62
66
|
Resolver.prototype.resolveConfig = function(pkg, configName) {
|
|
63
67
|
throw new Error(
|
|
64
|
-
'cannot resolve config <' + configName + '> in <' + pkg +'
|
|
68
|
+
'cannot resolve config <' + configName + '> in <' + pkg +'>: not bundled'
|
|
65
69
|
);
|
|
66
70
|
};
|
|
67
71
|
|
|
@@ -93,12 +97,68 @@ export default bundle;
|
|
|
93
97
|
pkg, ruleName
|
|
94
98
|
} = linter.parseRuleName(key);
|
|
95
99
|
|
|
96
|
-
return
|
|
97
|
-
import rule_${idx} from '${resolver.normalizePkg(pkg)}/rules/${ruleName}';
|
|
98
|
-
cache['${pkg}/${ruleName}'] = rule_${idx};`;
|
|
100
|
+
return createImportStatement(pkg, ruleName, idx, resolver);
|
|
99
101
|
}).filter(e => e).join('\n');
|
|
100
102
|
|
|
101
|
-
return `${preambleCode}
|
|
103
|
+
return `${preambleCode}${importCode}`;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
module.exports = compileConfig;
|
|
106
|
+
module.exports = compileConfig;
|
|
107
|
+
|
|
108
|
+
function requirePkg(resolver, pkg) {
|
|
109
|
+
try {
|
|
110
|
+
return resolver.require(pkg);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function createImportStatement(pkg, ruleName, idx, resolver) {
|
|
117
|
+
const originalPkg = pkg;
|
|
118
|
+
|
|
119
|
+
pkg = resolver.normalizePkg(pkg);
|
|
120
|
+
|
|
121
|
+
const pkgExport = requirePkg(resolver, pkg);
|
|
122
|
+
|
|
123
|
+
// (1) try resolving rule via $PKG.rules[$NAME]
|
|
124
|
+
if (pkgExport && pkgExport.rules) {
|
|
125
|
+
const rules = pkgExport.rules;
|
|
126
|
+
|
|
127
|
+
if (ruleName in rules) {
|
|
128
|
+
const rule = rules[ruleName];
|
|
129
|
+
|
|
130
|
+
if (!isString(rule)) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`failed to bundle rule <${ruleName}> from <${ pkg }>: illegal rule export (expected path reference)`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const rulePath = computeRuleImport(resolver, pkg, rule);
|
|
137
|
+
|
|
138
|
+
return `
|
|
139
|
+
import rule_${ idx } from '${rulePath}';
|
|
140
|
+
|
|
141
|
+
cache['${ originalPkg }/${ ruleName }'] = rule_${ idx };`;
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// (2) resolve rule via $PKG/rules/$NAME
|
|
147
|
+
return `
|
|
148
|
+
import rule_${ idx } from '${ pkg }/rules/${ ruleName }';
|
|
149
|
+
|
|
150
|
+
cache['${ originalPkg }/${ ruleName }'] = rule_${ idx };`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function computeRuleImport(resolver, pkg, rule) {
|
|
154
|
+
|
|
155
|
+
// local reference, resolved relative to pkg location
|
|
156
|
+
if (rule.startsWith('.')) {
|
|
157
|
+
const pkgDir = path.dirname(resolver.require.resolve(pkg));
|
|
158
|
+
|
|
159
|
+
return path.posix.normalize(`${ pkgDir }/${ rule }`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// absolute reference
|
|
163
|
+
return path.normalize(rule);
|
|
164
|
+
}
|
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": "8.
|
|
4
|
+
"version": "8.3.1",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bpmnlint",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"all": "run-s lint test-coverage",
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"dev": "npm test -- --watch",
|
|
33
|
-
"test": "mocha -r esm --exclude 'test/integration
|
|
33
|
+
"test": "mocha -r esm --exclude 'test/integration/*/**' 'test/**/*.js'",
|
|
34
34
|
"test-coverage": "nyc --reporter=lcov --reporter=html npm test",
|
|
35
35
|
"generate-docs-images": "npx @bpmn-io/bpmnlint-generate-docs-images ."
|
|
36
36
|
},
|
|
@@ -38,21 +38,22 @@
|
|
|
38
38
|
"bpmnlint": "./bin/bpmnlint.js"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@bpmn-io/moddle-utils": "^0.
|
|
41
|
+
"@bpmn-io/moddle-utils": "^0.2.0",
|
|
42
42
|
"ansi-colors": "^4.1.3",
|
|
43
|
-
"bpmn-moddle": "^
|
|
44
|
-
"bpmnlint-utils": "^1.
|
|
43
|
+
"bpmn-moddle": "^8.0.1",
|
|
44
|
+
"bpmnlint-utils": "^1.1.1",
|
|
45
45
|
"cli-table": "^0.3.11",
|
|
46
46
|
"color-support": "^1.1.3",
|
|
47
|
-
"min-dash": "^
|
|
47
|
+
"min-dash": "^4.1.1",
|
|
48
48
|
"mri": "^1.2.0",
|
|
49
49
|
"pluralize": "^7.0.0",
|
|
50
50
|
"tiny-glob": "^0.2.9"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@bpmn-io/bpmnlint-generate-docs-images": "^0.3.2",
|
|
53
54
|
"chai": "^4.3.4",
|
|
54
|
-
"eslint": "^8.
|
|
55
|
-
"eslint-plugin-bpmn-io": "^0.
|
|
55
|
+
"eslint": "^8.42.0",
|
|
56
|
+
"eslint-plugin-bpmn-io": "^1.0.0",
|
|
56
57
|
"esm": "^3.2.25",
|
|
57
58
|
"execa": "^5.1.1",
|
|
58
59
|
"install-local": "^3.0.1",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const {
|
|
2
|
+
is,
|
|
3
|
+
isAny
|
|
4
|
+
} = require('bpmnlint-utils');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A rule that checks that an element is not an implicit end (token sink).
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function() {
|
|
11
|
+
|
|
12
|
+
function isImplicitEnd(node) {
|
|
13
|
+
const outgoing = node.outgoing || [];
|
|
14
|
+
|
|
15
|
+
return !is(node, 'bpmn:EndEvent') && outgoing.length === 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function check(node, reporter) {
|
|
19
|
+
|
|
20
|
+
if (!isAny(node, [ 'bpmn:Event', 'bpmn:Activity', 'bpmn:Gateway' ])) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (isImplicitEnd(node)) {
|
|
25
|
+
reporter.report(node.id, 'Element is an implicit end');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { check };
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const {
|
|
2
|
+
is,
|
|
3
|
+
isAny
|
|
4
|
+
} = require('bpmnlint-utils');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A rule that checks that an element is not an implicit start (token spawn).
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function() {
|
|
11
|
+
|
|
12
|
+
function isImplicitStart(node) {
|
|
13
|
+
const incoming = node.incoming || [];
|
|
14
|
+
|
|
15
|
+
return !is(node, 'bpmn:StartEvent') && incoming.length === 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function check(node, reporter) {
|
|
19
|
+
|
|
20
|
+
if (!isAny(node, [ 'bpmn:Event', 'bpmn:Activity', 'bpmn:Gateway' ])) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (isImplicitStart(node)) {
|
|
25
|
+
reporter.report(node.id, 'Element is an implicit start');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { check };
|
|
30
|
+
};
|