bpmn-io / bpmnlint

Validate BPMN diagrams based on configurable lint rules.
MIT License
121 stars 36 forks source link

Implement <enter> and <leave> hooks as well as sub-tree skipping #54

Closed nikku closed 3 years ago

nikku commented 3 years ago

A rule can now be explicit about when to check a given node, on enter, on leave or during both occasions:

module.exports = function someRule() {

  return {
    check: {
      enter: function(node, reporter) { ... },
      leave: function(node, reporter) { ... }
    }
  };
};

A rule may also return false from the enter hook to stop traversing deeper into the elements children.


Closes https://github.com/bpmn-io/bpmnlint/issues/52 Closes https://github.com/bpmn-io/bpmnlint/issues/53

adroste commented 3 years ago

tl;dr it's fine 👍


Use case 1

Search for matching types & perform checks after every element was visited.

Works by doing sth. like this:

function leave(node, reporter) {
    if (!is(node, 'bpmn:Definitions'))
        return;

   // calc & write reports
}

function enter(node, reporter) {
    // collect relevant elements
}

return { check: { enter, leave } };

Use case 2

Traversing/searching for inner element.

Works, example:

let startNode = null;
let innerElements = [];

function leave(node, reporter) {
    if (node !== startNode) 
        return;

    // do the checks

    startNode = null;
    innerElements = [];
}

function enter(node, reporter) {
    if (startNode) { 
        if (is(node, 'my:innerType')) {
            innerElements.push(node);
        }
    } 
    else if (is(node, 'my:outerType')) {
        startNode = node;
    }
}

return { check: { enter, leave } };

Use case 3

Early exit.

Works somewhat by short circuiting every enter. Example:

let finished = false;

function enter(node, reporter) {
    if (finished)
        return false;

    if (is(node, 'my:type')) {

        // do stuff...

        finished = true;
        return false;
    }
}

return { check: { enter } };

Conclusion

Works for me. Seems to be a good compromise between versatility and the need to write boilerplate code for certain use cases.

My tests did not encounter any unwanted behaviour.

As it does not break existing implementations, I think a merge should be safe.