Comparing version 1.0.0-alpha6 to 1.0.0
@@ -47,2 +47,5 @@ #!/usr/bin/env node | ||
const logRule = ruleName => | ||
console.log(`\nrule: ${ruleName}`); | ||
/** | ||
@@ -53,5 +56,29 @@ * Logs errors and warnings properly in the console | ||
*/ | ||
const logReports = ({ errors, warnings }) => { | ||
errors.forEach(logError); | ||
warnings.forEach(logWarning); | ||
const logReports = (results) => { | ||
let errorCount = 0; | ||
let warningCount = 0; | ||
Object.entries(results).forEach(function([ ruleName, reports ]) { | ||
logRule(ruleName); | ||
reports.forEach(function(report) { | ||
if (report.category === 'error') { | ||
errorCount++; | ||
logError(report); | ||
} else { | ||
warningCount++; | ||
logWarning(report); | ||
} | ||
}); | ||
}); | ||
console.log(`\nfound ${errorCount} errors and ${warningCount} warnings`); | ||
if (errorCount > 0) { | ||
process.exit(1); | ||
} | ||
}; | ||
@@ -58,0 +85,0 @@ |
@@ -9,2 +9,13 @@ # Changelog | ||
## 1.0.0 | ||
* `FEAT`: add numerous new rules ([#5](https://github.com/bpmn-io/bpmnlint/issues/5)) | ||
* `FEAT`: add `bpmnlint:all` configuration | ||
* `FEAT`: improve `label-required` rule ([#11](https://github.com/bpmn-io/bpmnlint/issues/11)) | ||
* `FEAT`: group lint results by rule names | ||
* `FEAT`: exit cli with code=1 on lint errors | ||
* `FEAT`: add `isAny(node, [ ... types ])` method to `utils` | ||
* `CHORE`: improve / test cover existing rules | ||
* `CHORE`: include new rules in `bpmnlint:recommended` configuration | ||
## 1.0.0-alpha6 | ||
@@ -11,0 +22,0 @@ |
module.exports = { | ||
rules: { | ||
'label-required': 'warn', | ||
'conditional-flows': 'error', | ||
'end-event-required': 'error', | ||
'event-sub-process-typed-start-event': 'error', | ||
'fake-join': 'warn', | ||
'label-required': 'error', | ||
'no-complex-gateway': 'error', | ||
'no-disconnected': 'error', | ||
'no-gateway-join-fork': 'error', | ||
'no-implicit-split': 'error', | ||
'no-inclusive-gateway': 'error', | ||
'single-blank-start-event': 'error', | ||
'single-event-definition': 'error', | ||
'start-event-required': 'error', | ||
'end-event-required': 'error' | ||
'sub-process-blank-start-event': 'error' | ||
} | ||
}; |
const testRule = require('./testRule'); | ||
const utils = require('./utils'); | ||
const flagsMap = { | ||
1: 'warnings', | ||
2: 'errors', | ||
warn: 'warnings', | ||
error: 'errors' | ||
const flagMap = { | ||
0: 'off', | ||
1: 'warn', | ||
2: 'error' | ||
}; | ||
@@ -35,22 +34,15 @@ | ||
* @param {ModdleElement} options.moddleRoot | ||
* @param {String|Number} options.ruleFlag | ||
* @param {String} options.ruleFlag | ||
* @param {Rule} options.rule | ||
* @param {Object} options.ruleConfig | ||
* | ||
* @return {Object} lint results, keyed by category name | ||
* @return {Array<ValidationErrors>} lint results | ||
*/ | ||
Linter.prototype.applyRule = function applyRule({ moddleRoot, ruleFlag, rule, ruleConfig }) { | ||
if (typeof ruleFlag === 'string') { | ||
ruleFlag = ruleFlag.toLowerCase(); | ||
if (ruleFlag === 'off') { | ||
return []; | ||
} | ||
const flagName = flagsMap[ruleFlag]; | ||
if (!flagName) { | ||
return {}; | ||
} | ||
const reports = testRule({ moddleRoot, rule, ruleConfig }); | ||
return { [flagName]: reports }; | ||
return testRule({ moddleRoot, rule, ruleConfig }); | ||
}; | ||
@@ -179,7 +171,16 @@ | ||
const ruleResults = this.applyRule({ moddleRoot, ruleFlag, rule, ruleConfig }); | ||
const reports = this.applyRule({ moddleRoot, ruleFlag, rule, ruleConfig }); | ||
Object.entries(ruleResults).forEach(([category, reports]) => { | ||
finalReport[category] = (finalReport[category] || []).concat(reports); | ||
if (reports.length === 0) { | ||
return; | ||
} | ||
const categorizedReports = reports.map(function(report) { | ||
return { | ||
...report, | ||
category: ruleFlag | ||
}; | ||
}); | ||
finalReport[name] = categorizedReports; | ||
}); | ||
@@ -205,2 +206,10 @@ | ||
// normalize rule flag to <error> and <warn> which | ||
// may be upper case or a number at this point | ||
if (typeof ruleFlag === 'string') { | ||
ruleFlag = ruleFlag.toLowerCase(); | ||
} | ||
ruleFlag = flagMap[ruleFlag] || ruleFlag; | ||
return { | ||
@@ -207,0 +216,0 @@ ruleConfig, |
@@ -45,3 +45,3 @@ /* global it, describe, beforeEach */ | ||
.then(lintResults => { | ||
expectEqual(lintResults.errors, []); | ||
expectEqual(lintResults, {}); | ||
}) | ||
@@ -61,2 +61,8 @@ ); | ||
it(`test case #${idx}`, function() { | ||
const expectedResult = { | ||
...report, | ||
category: 'error' | ||
}; | ||
return ( | ||
@@ -68,3 +74,5 @@ Promise.resolve(moddleElement) | ||
.then(lintResults => { | ||
expectEqual(lintResults.errors, [report]); | ||
expectEqual(lintResults, { | ||
[ruleName]: [ expectedResult ] | ||
}); | ||
}) | ||
@@ -71,0 +79,0 @@ ); |
@@ -22,4 +22,20 @@ /** | ||
/** | ||
* Checks whether node has any of the specified types. | ||
* | ||
* @param {ModdleElement} node | ||
* @param {Array<String>} types | ||
* | ||
* @return {Boolean} | ||
*/ | ||
function isAny(node, types) { | ||
return types.some(function(type) { | ||
return is(node, type); | ||
}); | ||
} | ||
module.exports = { | ||
is | ||
is, | ||
isAny | ||
}; |
{ | ||
"name": "bpmnlint", | ||
"version": "1.0.0-alpha6", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -12,7 +12,17 @@ # bpmnlint | ||
```bash | ||
```zsh | ||
> bpmnlint diagram.bpmn | ||
error: Process_1 is missing a Start Event | ||
error: Process_1 is missing an End Event | ||
rule: bpmnlint/label-required | ||
error: sid-E391B624-F6E8-428B-9C3E-7026F85C4F24 is missing label/name | ||
error: sid-3E1FA189-AC8C-4CF1-9057-3D2EF8C6D3AF is missing label/name | ||
error: sid-DD0BC4E1-4AA3-4835-A477-373EA263A593 is missing label/name | ||
error: sid-B8B18E3A-EF8D-4D19-B5CD-C666D39E2E0D is missing label/name | ||
error: sid-994BB7B0-64D8-4DC4-B549-0758628F5A16 is missing label/name | ||
rule: bpmnlint/no-gateway-join-fork | ||
warn: sid-545B3227-D12A-43A8-B746-55E8C75F3A8A forks and joins | ||
warn: sid-AB73793C-D47A-4738-B34F-A82C6219A92C forks and joins | ||
found 10 errors and 2 warnings | ||
``` | ||
@@ -19,0 +29,0 @@ |
@@ -5,11 +5,12 @@ /** | ||
module.exports = utils => { | ||
const { is } = utils; | ||
module.exports = function(utils) { | ||
const ERROR = 'is missing an End Event'; | ||
const is = utils.is; | ||
const isAny = utils.isAny; | ||
function hasEndEvent(node) { | ||
const flowElements = node.flowElements || []; | ||
return ( | ||
(node.flowElements || []).filter(node => is(node, 'bpmn:EndEvent')) | ||
.length > 0 | ||
flowElements.some(node => is(node, 'bpmn:EndEvent')) | ||
); | ||
@@ -19,7 +20,13 @@ } | ||
function check(node, reporter) { | ||
if (is(node, 'Process')) { | ||
if (!hasEndEvent(node)) { | ||
reporter.report(node.id, ERROR); | ||
} | ||
if (!isAny(node, [ | ||
'bpmn:Process', | ||
'bpmn:SubProcess' | ||
])) { | ||
return; | ||
} | ||
if (!hasEndEvent(node)) { | ||
reporter.report(node.id, 'is missing an end event'); | ||
} | ||
} | ||
@@ -26,0 +33,0 @@ |
@@ -5,15 +5,50 @@ /** | ||
module.exports = utils => { | ||
const { is } = utils; | ||
const ERROR = 'Element is missing a label/name.'; | ||
module.exports = function(utils) { | ||
const is = utils.is; | ||
const isAny = utils.isAny; | ||
function check(node, reporter) { | ||
if (is(node, 'bpmn:ParallelGateway')) { | ||
if (isAny(node, [ | ||
'bpmn:ParallelGateway', | ||
'bpmn:EventBasedGateway' | ||
])) { | ||
return; | ||
} | ||
if (is(node, 'bpmn:FlowNode') && !(node.name || '').trim().length) { | ||
reporter.report(node.id, ERROR); | ||
// ignore joining gateways | ||
if (is(node, 'bpmn:Gateway') && !isForking(node)) { | ||
return; | ||
} | ||
if (is(node, 'bpmn:BoundaryEvent')) { | ||
return; | ||
} | ||
// ignore sub-processes | ||
if (is(node, 'bpmn:SubProcess')) { | ||
// TODO(nikku): better ignore expanded sub-processes only | ||
return; | ||
} | ||
// ignore sequence flow without condition | ||
if (is(node, 'bpmn:SequenceFlow') && !hasCondition(node)) { | ||
return; | ||
} | ||
// ignore data objects and artifacts for now | ||
if (isAny(node, [ | ||
'bpmn:FlowNode', | ||
'bpmn:SequenceFlow', | ||
'bpmn:Participant', | ||
'bpmn:Lane' | ||
])) { | ||
const name = (node.name || '').trim(); | ||
if (name.length === 0) { | ||
reporter.report(node.id, 'is missing label/name'); | ||
} | ||
} | ||
} | ||
@@ -23,1 +58,14 @@ | ||
}; | ||
// helpers //////////////////////// | ||
function isForking(node) { | ||
const outgoing = node.outgoing || []; | ||
return outgoing.length > 1; | ||
} | ||
function hasCondition(node) { | ||
return node.conditionExpression; | ||
} |
@@ -5,12 +5,12 @@ /** | ||
module.exports = utils => { | ||
const { is } = utils; | ||
module.exports = function(utils) { | ||
const ERROR = 'is missing a Start Event'; | ||
const is = utils.is; | ||
const isAny = utils.isAny; | ||
function hasStartEvent(node) { | ||
const flowElements = node.flowElements || []; | ||
return ( | ||
(node.flowElements || []).filter( | ||
node => node.$type !== 'String' && is(node, 'bpmn:StartEvent') | ||
).length > 0 | ||
flowElements.some(node => is(node, 'bpmn:StartEvent')) | ||
); | ||
@@ -20,7 +20,13 @@ } | ||
function check(node, reporter) { | ||
if (is(node, 'bpmn:Process')) { | ||
if (!hasStartEvent(node)) { | ||
reporter.report(node.id, ERROR); | ||
} | ||
if (!isAny(node, [ | ||
'bpmn:Process', | ||
'bpmn:SubProcess' | ||
])) { | ||
return; | ||
} | ||
if (!hasStartEvent(node)) { | ||
reporter.report(node.id, 'is missing a start event'); | ||
} | ||
} | ||
@@ -27,0 +33,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
29548
29
928
1
65