Socket
Socket
Sign inDemoInstall

bpmnlint

Package Overview
Dependencies
Maintainers
3
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bpmnlint - npm Package Compare versions

Comparing version 3.3.1 to 4.0.0

184

bin/bpmnlint.js

@@ -5,3 +5,3 @@ #!/usr/bin/env node

const path = require('path');
const { red, yellow, underline } = require('chalk');
const { red, yellow, underline, bold } = require('chalk');

@@ -17,4 +17,16 @@ const { promisify } = require('util');

const Table = require('cli-table');
const pluralize = require('pluralize');
const moddle = new BpmnModdle();
function boldRed(str) {
return bold(red(str));
}
function boldYellow(str) {
return bold(yellow(str));
}
/**

@@ -24,5 +36,5 @@ * Reads XML form path and return moddle object

*/
function getModdleFromXML(source) {
function parseDiagram(diagramXML) {
return new Promise((resolve, reject) => {
moddle.fromXML(source, (err, root) => {
moddle.fromXML(diagramXML, (err, moddleElement) => {
if (err) {

@@ -32,3 +44,3 @@ return reject(new Error('failed to parse XML', err));

return resolve(root);
return resolve(moddleElement);
});

@@ -38,25 +50,51 @@ });

/**
* Logs formatted warning message
*/
const logWarning = warning =>
console.log(
`${yellow('warning:')} ${underline(warning.id)} ${warning.message}`
);
const categoryMap = {
warn: 'warning'
};
/**
* Logs a formatted error message
* Logs a formatted message
*/
const logError = error =>
console.log(`${red('error:')} ${underline(error.id)} ${error.message}`);
function tableEntry(report) {
const category = report.category;
const logRule = ruleName =>
console.log(`\nrule: ${ruleName}`);
const color = category === 'error' ? red : yellow;
return [ report.id, color(categoryMap[category] || category), report.message, report.name ];
}
function createTable() {
return new Table({
chars: {
'top': '',
'top-mid': '',
'top-left': '',
'top-right': '',
'bottom': '',
'bottom-mid': '',
'bottom-left': '',
'bottom-right': '',
'left': ' ',
'left-mid': '',
'mid': '',
'mid-mid': '',
'right': '',
'right-mid': '',
'middle': ' '
},
style: {
'padding-left': 0,
'padding-right': 0
}
});
}
/**
* Logs errors and warnings properly in the console
* @param {*} errors
* @param {*} warnings
* Prints lint results to the console
*
* @param {String} filePath
* @param {Object} results
*/
const logReports = (results) => {
function printReports(filePath, results) {

@@ -66,15 +104,25 @@ let errorCount = 0;

Object.entries(results).forEach(function([ ruleName, reports ]) {
const table = createTable();
logRule(ruleName);
Object.entries(results).forEach(function([ name, reports ]) {
reports.forEach(function(report) {
if (report.category === 'error') {
const {
category,
id,
message
} = report;
table.push(tableEntry({
category,
id,
message,
name
}));
if (category === 'error') {
errorCount++;
logError(report);
} else {
warningCount++;
logWarning(report);
}

@@ -84,13 +132,20 @@ });

console.log(`\nfound ${errorCount} errors and ${warningCount} warnings`);
const problemCount = warningCount + errorCount;
if (errorCount > 0) {
process.exit(1);
if (problemCount) {
console.log();
console.log(underline(path.resolve(filePath)));
console.log(table.toString());
}
};
return {
errorCount,
warningCount
};
}
const cli = meow(
`
Usage
$ bpmnlint <file.bpmn>
$ bpmnlint diagram.bpmn

@@ -115,3 +170,3 @@ Options

if (cli.input.length !== 1) {
if (cli.input.length === 0) {
console.log('Error: bpmn file path missing.');

@@ -127,21 +182,14 @@ process.exit(1);

async function handleConfig(configString) {
let config;
async function lintDiagram(diagramPath, config) {
try {
config = JSON.parse(configString);
} catch (e) {
return logAndExit('Error: Could not parse configuration file', e);
}
let diagramXML;
try {
diagramXML = await readFile(path.resolve(cli.input[0]), 'utf-8');
diagramXML = await readFile(path.resolve(diagramPath), 'utf-8');
} catch (e) {
return logAndExit(`Error: Failed to read ${cli.input[0]}`, e);
return logAndExit(`Error: Failed to read ${diagramPath}`, e);
}
try {
const moddleRoot = await getModdleFromXML(diagramXML);
const moddleRoot = await parseDiagram(diagramXML);

@@ -155,3 +203,3 @@ const linter = new Linter({

logReports(lintResults);
return printReports(diagramPath, lintResults);
} catch (e) {

@@ -162,2 +210,48 @@ return logAndExit(e);

async function handleConfig(configString) {
console.log();
let config;
try {
config = JSON.parse(configString);
} catch (e) {
return logAndExit('Error: Could not parse configuration file', e);
}
let errorCount = 0;
let warningCount = 0;
for (let i = 0; i < cli.input.length; i++) {
let results = await lintDiagram(cli.input[i], config);
errorCount += results.errorCount;
warningCount += results.warningCount;
}
const problemCount = errorCount + warningCount;
let color;
if (warningCount) {
color = boldYellow;
}
if (errorCount) {
color = boldRed;
}
if (problemCount) {
console.log();
console.log(color(
`✖ ${problemCount} ${pluralize('problem', problemCount)} (${errorCount} ${pluralize('error', errorCount)}, ${warningCount} ${pluralize('warning', warningCount)})`
));
}
if (errorCount) {
process.exit(1);
}
}
const { config } = cli.flags;

@@ -164,0 +258,0 @@

@@ -9,2 +9,9 @@ # Changelog

## 4.0.0
* `FEAT`: add ability to batch lint multiple files via CLI
* `FEAT`: don't resolve disabled rules ([`6c45f3f9`](https://github.com/bpmn-io/bpmnlint/commit/6c45f3f952a412dda05deb5c57861a1c76af23bb))
* `CHORE`: unify names of built-in rules
* `CHORE`: adopt cli output to eslint styling
## 3.3.1

@@ -11,0 +18,0 @@

const testRule = require('./test-rule');
const utils = require('./utils');
const flagMap = {
const categoryMap = {
0: 'off',

@@ -35,15 +35,9 @@ 1: 'warn',

*
* @param {ModdleElement} options.moddleRoot
* @param {String} options.ruleFlag
* @param {Rule} options.rule
* @param {Object} options.ruleConfig
* @param {ModdleElement} moddleRoot
* @param {Rule} rule
* @param {Object} ruleConfig
*
* @return {Array<ValidationErrors>} lint results
*/
Linter.prototype.applyRule = function applyRule({ moddleRoot, ruleFlag, rule, ruleConfig }) {
if (ruleFlag === 'off') {
return [];
}
Linter.prototype.applyRule = function applyRule(moddleRoot, rule, ruleConfig) {
return testRule({ moddleRoot, rule, ruleConfig });

@@ -110,3 +104,3 @@ };

*
* @param {Object} rulesConfig
* @param {Object} config
*

@@ -118,14 +112,36 @@ * @return {Array<RuleDefinition>}

return this.resolveConfiguredRules(config).then((rulesConfig) => {
return Promise.all(
Object.entries(rulesConfig).map(([ name, value ]) => {
return this.resolveRule(name).then(function(rule) {
return {
value,
name,
rule
};
});
})
);
// parse rule values
const parsedRules = Object.entries(rulesConfig).map(([ name, value ]) => {
const {
category,
config
} = this.parseRuleValue(value);
return {
name,
category,
config
};
});
// filter only for enabled rules
const enabledRules = parsedRules.filter(definition => definition.category !== 'off');
// load enabled rules
const loaders = enabledRules.map((definition) => {
const {
name
} = definition;
return this.resolveRule(name).then(function(rule) {
return {
...definition,
rule
};
});
});
return Promise.all(loaders);
});

@@ -186,10 +202,12 @@ };

ruleDefinitions.forEach(({ rule, name, value }) => {
ruleDefinitions.forEach((ruleDefinition) => {
const {
ruleFlag,
ruleConfig
} = this.parseRuleValue(value);
rule,
name,
config,
category
} = ruleDefinition;
const reports = this.applyRule({ moddleRoot, ruleFlag, rule, ruleConfig });
const reports = this.applyRule(moddleRoot, rule, config);

@@ -203,3 +221,3 @@ if (reports.length === 0) {

...report,
category: ruleFlag
category
};

@@ -218,11 +236,11 @@ });

let ruleFlag;
let ruleConfig;
let category;
let config;
if (Array.isArray(value)) {
ruleFlag = value[0];
ruleConfig = value[1];
category = value[0];
config = value[1];
} else {
ruleFlag = value;
ruleConfig = {};
category = value;
config = {};
}

@@ -232,11 +250,11 @@

// may be upper case or a number at this point
if (typeof ruleFlag === 'string') {
ruleFlag = ruleFlag.toLowerCase();
if (typeof category === 'string') {
category = category.toLowerCase();
}
ruleFlag = flagMap[ruleFlag] || ruleFlag;
category = categoryMap[category] || category;
return {
ruleConfig,
ruleFlag
config,
category
};

@@ -243,0 +261,0 @@ };

@@ -25,5 +25,5 @@ const Linter = require('../linter');

const { ruleFlag } = linter.parseRuleValue(value);
const { category } = linter.parseRuleValue(value);
if (ruleFlag !== 'off') {
if (category !== 'off') {
enabledRules[key] = value;

@@ -30,0 +30,0 @@ }

{
"name": "bpmnlint",
"version": "3.3.1",
"version": "4.0.0",
"main": "lib/index.js",

@@ -37,3 +37,5 @@ "keywords": [

"chalk": "^2.4.1",
"meow": "^5.0.0"
"cli-table": "^0.3.1",
"meow": "^5.0.0",
"pluralize": "^7.0.0"
},

@@ -40,0 +42,0 @@ "devDependencies": {

@@ -12,17 +12,14 @@ # bpmnlint

```zsh
> bpmnlint diagram.bpmn
```sh
> bpmnlint invoice.bpmn
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
/Projects/process-application/resources/invoice.bpmn
Flow_1 error Sequence flow is missing condition conditional-flows
Process error Process is missing end event end-event-required
Task_13 warning Element is missing label/name label-required
Event_12 warning Element is missing label/name label-required
Event_27 warning Element is missing label/name label-required
Process error Process is missing start event start-event-required
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
✖ 6 problems (6 errors, 0 warnings)
```

@@ -52,3 +49,3 @@

"rules": {
"bpmnlint/label-required": "off"
"label-required": "off"
}

@@ -55,0 +52,0 @@ }

@@ -22,3 +22,3 @@ // verify that sequence flows outgoing from a

if (missingCondition) {
reporter.report(flow.id, 'is missing condition');
reporter.report(flow.id, 'Sequence flow is missing condition');
}

@@ -25,0 +25,0 @@ });

@@ -28,3 +28,5 @@ /**

if (!hasEndEvent(node)) {
reporter.report(node.id, 'is missing an end event');
const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
reporter.report(node.id, type + ' is missing end event');
}

@@ -31,0 +33,0 @@ }

@@ -25,3 +25,3 @@ // verify that start events inside an event sub-process

if (eventDefinitions.length === 0) {
reporter.report(flowElement.id, 'is missing event definition');
reporter.report(flowElement.id, 'Start event is missing event definition');
}

@@ -28,0 +28,0 @@ });

@@ -23,3 +23,3 @@ // verify that no fake join is modeled by attempting

if (incoming.length > 1) {
reporter.report(node.id, 'does not join');
reporter.report(node.id, 'Incoming flows do not join');
}

@@ -26,0 +26,0 @@ }

@@ -18,3 +18,3 @@

if (is(node, type)) {
reporter.report(node.id, 'has disallowed type <' + type + '>');
reporter.report(node.id, 'Element has disallowed type <' + type + '>');
}

@@ -21,0 +21,0 @@ }

@@ -50,3 +50,3 @@ /**

if (name.length === 0) {
reporter.report(node.id, 'is missing label/name');
reporter.report(node.id, 'Element is missing label/name');
}

@@ -53,0 +53,0 @@ }

@@ -24,3 +24,3 @@ // verify that there exists no disconnected

if (!incoming.length && !outgoing.length) {
reporter.report(node.id, 'is not connected');
reporter.report(node.id, 'Element is not connected');
}

@@ -27,0 +27,0 @@ }

@@ -18,3 +18,3 @@ // verify that a gateway does not fork and join

if (incoming.length > 1 && outgoing.length > 1) {
reporter.report(node.id, 'forks and joins');
reporter.report(node.id, 'Gateway forks and joins');
}

@@ -21,0 +21,0 @@ }

@@ -27,3 +27,3 @@ // verify that no implicit split is modeled

if (outgoingWithoutCondition.length > 1) {
reporter.report(node.id, 'must not split implicitly');
reporter.report(node.id, 'Flow splits implicitly');
}

@@ -30,0 +30,0 @@ }

@@ -28,3 +28,5 @@ // verify that there exists only a single blank

if (blankStartEvents.length > 1) {
reporter.report(node.id, 'contains multiple blank start events');
const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
reporter.report(node.id, type + ' has multiple blank start events');
}

@@ -31,0 +33,0 @@ }

@@ -17,3 +17,3 @@ // verify that an event contains maximum one

if (eventDefinitions.length > 1) {
reporter.report(node.id, 'contains multiple event definitions');
reporter.report(node.id, 'Event has multiple event definitions');
}

@@ -20,0 +20,0 @@ }

@@ -28,3 +28,5 @@ /**

if (!hasStartEvent(node)) {
reporter.report(node.id, 'is missing a start event');
const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
reporter.report(node.id, type + ' is missing start event');
}

@@ -31,0 +33,0 @@ }

@@ -25,3 +25,3 @@ // verify that start events inside a normal sub-process

if (eventDefinitions.length > 0) {
reporter.report(flowElement.id, 'must not contain event definition');
reporter.report(flowElement.id, 'Start event must be blank');
}

@@ -28,0 +28,0 @@ });

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc