├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ ├── FEATURE_REQUEST.md │ ├── NEW_RULE.md │ └── config.yml └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── bpmnlint.js ├── config ├── all.js ├── correctness.js └── recommended.js ├── docs └── rules │ ├── README.md │ ├── ad-hoc-sub-process.md │ ├── conditional-flows.md │ ├── end-event-required.md │ ├── event-sub-process-typed-start-event.md │ ├── examples │ ├── ad-hoc-sub-process-correct.bpmn │ ├── ad-hoc-sub-process-correct.png │ ├── ad-hoc-sub-process-incorrect.bpmn │ ├── ad-hoc-sub-process-incorrect.png │ ├── conditional-flows-correct.bpmn │ ├── conditional-flows-correct.png │ ├── conditional-flows-incorrect.bpmn │ ├── conditional-flows-incorrect.png │ ├── end-event-required-correct.bpmn │ ├── end-event-required-correct.png │ ├── end-event-required-incorrect.bpmn │ ├── end-event-required-incorrect.png │ ├── event-sub-process-typed-start-event-correct.bpmn │ ├── event-sub-process-typed-start-event-correct.png │ ├── event-sub-process-typed-start-event-incorrect.bpmn │ ├── event-sub-process-typed-start-event-incorrect.png │ ├── fake-join-correct.bpmn │ ├── fake-join-correct.png │ ├── fake-join-incorrect.bpmn │ ├── fake-join-incorrect.png │ ├── label-required-correct.bpmn │ ├── label-required-correct.png │ ├── label-required-incorrect.bpmn │ ├── label-required-incorrect.png │ ├── link-event-correct.bpmn │ ├── link-event-correct.png │ ├── link-event-incorrect.bpmn │ ├── link-event-incorrect.png │ ├── no-bpmndi-correct.bpmn │ ├── no-bpmndi-correct.png │ ├── no-bpmndi-incorrect.bpmn │ ├── no-bpmndi-incorrect.png │ ├── no-complex-gateway-correct.bpmn │ ├── no-complex-gateway-correct.png │ ├── no-complex-gateway-incorrect.bpmn │ ├── no-complex-gateway-incorrect.png │ ├── no-disconnected-correct.bpmn │ ├── no-disconnected-correct.png │ ├── no-disconnected-incorrect.bpmn │ ├── no-disconnected-incorrect.png │ ├── no-duplicate-sequence-flows-correct.bpmn │ ├── no-duplicate-sequence-flows-correct.png │ ├── no-duplicate-sequence-flows-incorrect.bpmn │ ├── no-duplicate-sequence-flows-incorrect.png │ ├── no-gateway-join-fork-correct.bpmn │ ├── no-gateway-join-fork-correct.png │ ├── no-gateway-join-fork-incorrect.bpmn │ ├── no-gateway-join-fork-incorrect.png │ ├── no-implicit-end-correct.bpmn │ ├── no-implicit-end-correct.png │ ├── no-implicit-end-incorrect.bpmn │ ├── no-implicit-end-incorrect.png │ ├── no-implicit-split-correct.bpmn │ ├── no-implicit-split-correct.png │ ├── no-implicit-split-incorrect.bpmn │ ├── no-implicit-split-incorrect.png │ ├── no-implicit-start-correct.bpmn │ ├── no-implicit-start-correct.png │ ├── no-implicit-start-incorrect.bpmn │ ├── no-implicit-start-incorrect.png │ ├── no-inclusive-gateway-correct.bpmn │ ├── no-inclusive-gateway-correct.png │ ├── no-inclusive-gateway-incorrect.bpmn │ ├── no-inclusive-gateway-incorrect.png │ ├── no-overlapping-elements-correct.bpmn │ ├── no-overlapping-elements-correct.png │ ├── no-overlapping-elements-incorrect.bpmn │ ├── no-overlapping-elements-incorrect.png │ ├── single-blank-start-event-correct.bpmn │ ├── single-blank-start-event-correct.png │ ├── single-blank-start-event-incorrect.bpmn │ ├── single-blank-start-event-incorrect.png │ ├── single-event-definition-correct.bpmn │ ├── single-event-definition-correct.png │ ├── single-event-definition-incorrect.bpmn │ ├── single-event-definition-incorrect.png │ ├── start-event-required-correct.bpmn │ ├── start-event-required-correct.png │ ├── start-event-required-incorrect.bpmn │ ├── start-event-required-incorrect.png │ ├── sub-process-blank-start-event-correct.bpmn │ ├── sub-process-blank-start-event-correct.png │ ├── sub-process-blank-start-event-incorrect.bpmn │ ├── sub-process-blank-start-event-incorrect.png │ ├── superfluous-gateway-correct.bpmn │ ├── superfluous-gateway-correct.png │ ├── superfluous-gateway-incorrect.bpmn │ ├── superfluous-gateway-incorrect.png │ ├── superfluous-termination-correct.bpmn │ ├── superfluous-termination-correct.png │ ├── superfluous-termination-incorrect.bpmn │ └── superfluous-termination-incorrect.png │ ├── fake-join.md │ ├── label-required.md │ ├── link-event.md │ ├── no-bpmndi.md │ ├── no-complex-gateway.md │ ├── no-disconnected.md │ ├── no-duplicate-sequence-flows.md │ ├── no-gateway-join-fork.md │ ├── no-implicit-end.md │ ├── no-implicit-split.md │ ├── no-implicit-start.md │ ├── no-inclusive-gateway.md │ ├── no-overlapping-elements.md │ ├── single-blank-start-event.md │ ├── single-event-definition.md │ ├── start-event-required.md │ ├── sub-process-blank-start-event.md │ ├── superfluous-gateway.md │ └── superfluous-termination.md ├── eslint.config.mjs ├── lib ├── index.js ├── linter.js ├── resolver │ ├── node-resolver.js │ └── static-resolver.js ├── support │ └── compile-config.js ├── test-rule.js ├── testers │ ├── helper.js │ ├── rule-tester.d.ts │ └── rule-tester.js ├── traverse.js └── types.d.ts ├── package-lock.json ├── package.json ├── rules ├── ad-hoc-sub-process.js ├── conditional-flows.js ├── end-event-required.js ├── event-sub-process-typed-start-event.js ├── fake-join.js ├── global.js ├── helper.js ├── label-required.js ├── link-event.js ├── no-bpmndi.js ├── no-complex-gateway.js ├── no-disconnected.js ├── no-duplicate-sequence-flows.js ├── no-gateway-join-fork.js ├── no-implicit-end.js ├── no-implicit-split.js ├── no-implicit-start.js ├── no-inclusive-gateway.js ├── no-overlapping-elements.js ├── single-blank-start-event.js ├── single-event-definition.js ├── start-event-required.js ├── sub-process-blank-start-event.js ├── superfluous-gateway.js └── superfluous-termination.js ├── test ├── helper.mjs ├── integration │ ├── bpmnlint-plugin-exported │ │ ├── package.json │ │ └── src │ │ │ ├── bar.js │ │ │ ├── foo.js │ │ │ └── index.js │ ├── bpmnlint-plugin-test-ns │ │ ├── index.js │ │ ├── package.json │ │ └── rules │ │ │ └── no-label-xxx.js │ ├── bpmnlint-plugin-test │ │ ├── index.js │ │ ├── package.json │ │ └── rules │ │ │ ├── no-label-bar.js │ │ │ └── no-label-foo.js │ ├── bpmnlint-plugin-test2 │ │ ├── index.js │ │ ├── package.json │ │ └── rules │ │ │ └── no-label-xxx.js │ ├── bundling-spec.mjs │ ├── bundling │ │ ├── .gitignore │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── .bpmnlintrc │ │ │ └── app.js │ │ ├── test │ │ │ ├── app.rollup.expected.js │ │ │ └── app.webpack.expected.js │ │ └── webpack.config.js │ ├── cli-spec.mjs │ ├── cli │ │ ├── .bpmnlintrc │ │ ├── child │ │ │ ├── .bpmnlintrc │ │ │ ├── diagram.bpmn │ │ │ └── package.json │ │ ├── complex.bpmn │ │ ├── diagram-broken.bpmn │ │ ├── diagram-import-warnings.bpmn │ │ ├── diagram-invalid.bpmn │ │ ├── diagram-warnings.bpmn │ │ ├── diagram.bpmn │ │ ├── empty │ │ │ └── .gitkeep │ │ ├── exported.json │ │ ├── extends-builtin.json │ │ ├── extends-external.json │ │ ├── glob │ │ │ ├── diagram-invalid.bpmn │ │ │ └── subfolder │ │ │ │ └── diagram-import-warnings.bpmn │ │ ├── local-rules │ │ │ ├── .bpmnlintrc │ │ │ ├── diagram.bpmn │ │ │ ├── lib │ │ │ │ └── bpmnlint-plugin │ │ │ │ │ ├── index.js │ │ │ │ │ ├── package.json │ │ │ │ │ └── rules │ │ │ │ │ └── no-label-bar.js │ │ │ └── package.json │ │ ├── ns │ │ │ ├── diagram-invalid.bpmn │ │ │ ├── diagram.bpmn │ │ │ ├── extends.json │ │ │ ├── package.json │ │ │ └── uses-rules.json │ │ ├── package.json │ │ └── test.js │ ├── compilation │ │ ├── .gitignore │ │ ├── compile.js │ │ ├── package.json │ │ └── test │ │ │ └── bpmnlintrc.expected.js │ └── compile-spec.mjs ├── rules │ ├── ad-hoc-sub-process.mjs │ ├── ad-hoc-sub-process │ │ ├── invalid-intermediate.bpmn │ │ ├── invalid-start-end.bpmn │ │ └── valid.bpmn │ ├── conditional-flows.mjs │ ├── conditional-flows │ │ ├── invalid-fork-after-exclusive-gateway-default.bpmn │ │ ├── invalid-fork-after-exclusive-gateway.bpmn │ │ ├── invalid-fork-after-task-default.bpmn │ │ ├── invalid-fork-after-task.bpmn │ │ ├── valid-conditional-fork.bpmn │ │ ├── valid-no-condition-after-merge.bpmn │ │ ├── valid-split-after-task.bpmn │ │ └── valid-split.bpmn │ ├── end-event-required.mjs │ ├── end-event-required │ │ ├── invalid-sub-process-sub-types.bpmn │ │ ├── invalid-sub-process.bpmn │ │ ├── invalid.bpmn │ │ ├── valid-sub-process-sub-types.bpmn │ │ ├── valid-sub-process.bpmn │ │ └── valid.bpmn │ ├── event-sub-process-typed-start-event.mjs │ ├── event-sub-process-typed-start-event │ │ ├── invalid.bpmn │ │ ├── valid-empty-sub-process.bpmn │ │ ├── valid-empty.bpmn │ │ ├── valid-intermediate-event.bpmn │ │ ├── valid-sub-process.bpmn │ │ └── valid.bpmn │ ├── fake-join.mjs │ ├── fake-join │ │ ├── invalid-callActivity.bpmn │ │ ├── invalid-task.bpmn │ │ ├── valid-gateway.bpmn │ │ └── valid.bpmn │ ├── global.mjs │ ├── global │ │ ├── invalid-error-duplicate-name.bpmn │ │ ├── invalid-error-missing-name.bpmn │ │ ├── invalid-error-missing-reference.bpmn │ │ ├── invalid-escalation-duplicate-name.bpmn │ │ ├── invalid-escalation-missing-name.bpmn │ │ ├── invalid-escalation-missing-reference.bpmn │ │ ├── invalid-message-duplicate-name.bpmn │ │ ├── invalid-message-missing-name.bpmn │ │ ├── invalid-message-missing-reference.bpmn │ │ ├── invalid-signal-duplicate-name.bpmn │ │ ├── invalid-signal-missing-name.bpmn │ │ ├── invalid-signal-missing-reference.bpmn │ │ ├── valid-error.bpmn │ │ ├── valid-escalation.bpmn │ │ ├── valid-message.bpmn │ │ ├── valid-no-ref.bpmn │ │ └── valid-signal.bpmn │ ├── label-required.mjs │ ├── label-required │ │ ├── invalid-boundary-event.bpmn │ │ ├── invalid-conditional-flow.bpmn │ │ ├── invalid-event.bpmn │ │ ├── invalid-gateway-split.bpmn │ │ ├── invalid-lane.bpmn │ │ ├── invalid-participant.bpmn │ │ ├── invalid-task.bpmn │ │ ├── valid-boundary-event.bpmn │ │ ├── valid-conditional-flow.bpmn │ │ ├── valid-data-objects.bpmn │ │ ├── valid-gateways.bpmn │ │ ├── valid-participant-lanes.bpmn │ │ └── valid-start-event.bpmn │ ├── link-event.mjs │ ├── link-event │ │ ├── invalid.bpmn │ │ ├── valid-collaboration.bpmn │ │ └── valid.bpmn │ ├── no-bpmndi.mjs │ ├── no-bpmndi │ │ ├── ignore-edge-without-bpmn-element.bpmn │ │ ├── invalid-catch-event.bpmn │ │ ├── invalid-collapsed-pool.bpmn │ │ ├── invalid-group.bpmn │ │ ├── invalid-lane.bpmn │ │ ├── invalid-message-flow.bpmn │ │ ├── invalid-missing-nested-lane-deep.bpmn │ │ ├── invalid-missing-nested-lane.bpmn │ │ ├── invalid-multiple-nested-levels.bpmn │ │ ├── invalid-multiple-sub-processes.bpmn │ │ ├── invalid-nested-boundary.bpmn │ │ ├── invalid-nested-lanes.bpmn │ │ ├── invalid-no-bpmn-diagram.bpmn │ │ ├── invalid-participant.bpmn │ │ ├── invalid-sequence-flow.bpmn │ │ ├── invalid-sub-processes.bpmn │ │ ├── valid-complex.bpmn │ │ ├── valid-data-object.bpmn │ │ ├── valid-empty.bpmn │ │ ├── valid-error.bpmn │ │ ├── valid-extension-elements.bpmn │ │ ├── valid-group.bpmn │ │ ├── valid-lanes.bpmn │ │ ├── valid-message-flow.bpmn │ │ ├── valid-multiple-nested-levels.bpmn │ │ ├── valid-nested-boundary.bpmn │ │ ├── valid-no-lanes.bpmn │ │ ├── valid-signavio.bpmn │ │ ├── valid-sub-processes.bpmn │ │ └── valid.bpmn │ ├── no-complex-gateway.mjs │ ├── no-complex-gateway │ │ ├── invalid.bpmn │ │ └── valid.bpmn │ ├── no-disconnected.mjs │ ├── no-disconnected │ │ ├── invalid.bpmn │ │ ├── valid-adhoc-subprocess.bpmn │ │ ├── valid-compensation.bpmn │ │ ├── valid-event-subprocess.bpmn │ │ ├── valid-text-annotation.bpmn │ │ └── valid.bpmn │ ├── no-duplicate-sequence-flows.mjs │ ├── no-duplicate-sequence-flows │ │ ├── invalid-condition.bpmn │ │ ├── invalid-multiple.bpmn │ │ ├── invalid-no-condition.bpmn │ │ └── valid.bpmn │ ├── no-gateway-join-fork.mjs │ ├── no-gateway-join-fork │ │ ├── invalid.bpmn │ │ ├── valid-fork-join-task.bpmn │ │ ├── valid-fork.bpmn │ │ └── valid-join.bpmn │ ├── no-implicit-end.mjs │ ├── no-implicit-end │ │ ├── invalid.bpmn │ │ ├── valid-collaboration.bpmn │ │ └── valid.bpmn │ ├── no-implicit-split.mjs │ ├── no-implicit-split │ │ ├── invalid-call-activity.bpmn │ │ ├── invalid-event.bpmn │ │ ├── invalid-task.bpmn │ │ ├── valid-default-conditional-flow.bpmn │ │ └── valid.bpmn │ ├── no-implicit-start.mjs │ ├── no-implicit-start │ │ ├── invalid.bpmn │ │ ├── valid-collaboration.bpmn │ │ └── valid.bpmn │ ├── no-inclusive-gateway.mjs │ ├── no-inclusive-gateway │ │ ├── invalid.bpmn │ │ └── valid.bpmn │ ├── no-overlapping-elements.mjs │ ├── no-overlapping-elements │ │ ├── ignore-missing-bounds.bpmn │ │ ├── ignore-missing-di.bpmn │ │ ├── invalid-boundary-event.bpmn │ │ ├── invalid-collaboration.bpmn │ │ ├── invalid-process.bpmn │ │ ├── invalid-subprocess-collapsed.bpmn │ │ ├── invalid-subprocess.bpmn │ │ ├── valid-boundary-event.bpmn │ │ ├── valid-collaboration-subprocess.bpmn │ │ ├── valid-collaboration.bpmn │ │ ├── valid-data-objects.bpmn │ │ ├── valid-process.bpmn │ │ ├── valid-subprocess-collapsed.bpmn │ │ └── valid-subprocess.bpmn │ ├── single-blank-start-event.mjs │ ├── single-blank-start-event │ │ ├── invalid-sub-process.bpmn │ │ ├── invalid.bpmn │ │ ├── valid-empty.bpmn │ │ ├── valid-end-event.bpmn │ │ ├── valid-scopes.bpmn │ │ ├── valid-sub-process.bpmn │ │ ├── valid-typed-sub-process.bpmn │ │ ├── valid-typed.bpmn │ │ └── valid.bpmn │ ├── single-event-definition.mjs │ ├── single-event-definition │ │ ├── invalid.bpmn │ │ ├── valid-blank.bpmn │ │ └── valid.bpmn │ ├── start-event-required.mjs │ ├── start-event-required │ │ ├── invalid-sub-process-sub-types.bpmn │ │ ├── invalid-sub-process.bpmn │ │ ├── invalid.bpmn │ │ ├── valid-sub-process-sub-types.bpmn │ │ ├── valid-sub-process.bpmn │ │ └── valid.bpmn │ ├── sub-process-blank-start-event.mjs │ ├── sub-process-blank-start-event │ │ ├── invalid-ad-hoc.bpmn │ │ ├── invalid.bpmn │ │ ├── valid-empty.bpmn │ │ ├── valid-event-sub-process.bpmn │ │ ├── valid-intermediate-event.bpmn │ │ └── valid.bpmn │ ├── superfluous-gateway.mjs │ ├── superfluous-gateway │ │ ├── invalid.bpmn │ │ ├── valid-none-gateway.bpmn │ │ └── valid.bpmn │ ├── superfluous-termination.mjs │ └── superfluous-termination │ │ ├── invalid-boundary-interrupting.bpmn │ │ ├── invalid-event-sub-process-interrupting.bpmn │ │ ├── invalid-exclusive-paths.bpmn │ │ ├── invalid-sub-process.bpmn │ │ ├── invalid.bpmn │ │ ├── valid-boundary-non-interrupting.bpmn │ │ ├── valid-event-sub-process-non-interrupting.bpmn │ │ ├── valid-implicit-end.bpmn │ │ └── valid.bpmn └── spec │ ├── config-spec.mjs │ ├── diagram.bpmn │ ├── index-spec.js │ ├── linter-spec.mjs │ ├── process-diagram.bpmn │ ├── resolver │ ├── node-resolver-spec.mjs │ └── static-resolver-spec.mjs │ ├── support │ └── compile-config-spec.mjs │ ├── test-rule-spec.mjs │ ├── testers │ └── rule-tester-spec.mjs │ └── traverse-spec.mjs └── tsconfig.json /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a problem and help us fix it. 4 | labels: "bug" 5 | --- 6 | 7 | 8 | ### Describe the Bug 9 | 10 | 11 | 12 | 13 | ### Steps to Reproduce 14 | 15 | 16 | 17 | 1. do this 18 | 2. do that 19 | 3. now this happens 20 | 21 | 22 | ### Expected Behavior 23 | 24 | 25 | 26 | 27 | ### Environment 28 | 29 | - Host (Browser/Node version), if applicable: [e.g. MS Edge 18, Chrome 69, Node 10 LTS] 30 | - OS: [e.g. Windows 7] 31 | - Library version: [e.g. 2.0.0] 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea or general improvement. 4 | labels: "enhancement" 5 | --- 6 | 7 | 8 | ### Is your feature request related to a problem? Please describe 9 | 10 | 11 | 12 | 13 | ### Describe the solution you'd like 14 | 15 | 16 | 17 | 18 | ### Describe alternatives you've considered 19 | 20 | 21 | 22 | 23 | ### Additional context 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/NEW_RULE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Propose new Rule 3 | about: Use this template to propose new modeling rules to be included in the library. 4 | labels: [ "rules" ] 5 | --- 6 | 7 | ### The rule detects the following modeling patterns 8 | 9 | 10 | 11 | * 12 | * 13 | 14 | ### How does the rule improve the BPMN diagram? 15 | 16 | 19 | 20 | ### Rule Details 21 | 22 | * Name: 23 | * Default notification level: `error|warn|off` 24 | 25 | 26 | ### What alternatives did you consider? 27 | 28 | 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://forum.bpmn.io 5 | about: Head over to our community forum to ask questions and get answers. 6 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [ push, pull_request ] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest] 10 | node-version: [ 20 ] 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Use Node.js ${{matrix.node-version}} 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: ${{matrix.node-version}} 19 | cache: 'npm' 20 | - name: Install dependencies 21 | run: npm ci 22 | - name: Build 23 | run: COVERAGE=1 npm run all 24 | - name: Upload coverage 25 | uses: codecov/codecov-action@v3 26 | 27 | build_nodes: 28 | runs-on: ${{ matrix.os }} 29 | 30 | strategy: 31 | matrix: 32 | os: [ubuntu-latest] 33 | node-version: [ 22 ] 34 | 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v4 38 | - name: Use Node.js ${{matrix.node-version}} 39 | uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{matrix.node-version}} 42 | cache: 'npm' 43 | - name: Install dependencies 44 | run: npm ci 45 | - name: Build 46 | run: npm run all 47 | build_platforms: 48 | runs-on: ${{ matrix.os }} 49 | 50 | strategy: 51 | matrix: 52 | os: [macos-latest, windows-latest] 53 | node-version: [ 20 ] 54 | 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | - name: Use Node.js ${{matrix.node-version}} 59 | uses: actions/setup-node@v4 60 | with: 61 | node-version: ${{matrix.node-version}} 62 | cache: 'npm' 63 | - name: Install dependencies 64 | run: npm ci 65 | - name: Build 66 | run: npm run all 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | node_modules 3 | coverage -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-present bpmn.io Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /config/all.js: -------------------------------------------------------------------------------- 1 | const allRules = [ 2 | 'ad-hoc-sub-process', 3 | 'conditional-flows', 4 | 'end-event-required', 5 | 'event-sub-process-typed-start-event', 6 | 'fake-join', 7 | 'global', 8 | 'label-required', 9 | 'link-event', 10 | 'no-bpmndi', 11 | 'no-complex-gateway', 12 | 'no-disconnected', 13 | 'no-duplicate-sequence-flows', 14 | 'no-gateway-join-fork', 15 | 'no-implicit-end', 16 | 'no-implicit-split', 17 | 'no-implicit-start', 18 | 'no-inclusive-gateway', 19 | 'no-overlapping-elements', 20 | 'single-blank-start-event', 21 | 'single-event-definition', 22 | 'start-event-required', 23 | 'sub-process-blank-start-event', 24 | 'superfluous-gateway', 25 | 'superfluous-termination' 26 | ]; 27 | 28 | 29 | module.exports = { 30 | rules: allRules.reduce(function(rules, ruleName) { 31 | rules[ruleName] = 'error'; 32 | 33 | return rules; 34 | }, {}) 35 | }; 36 | -------------------------------------------------------------------------------- /config/correctness.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'ad-hoc-sub-process': 'error', 4 | 'event-sub-process-typed-start-event': 'error', 5 | 'link-event': 'error', 6 | 'no-duplicate-sequence-flows': 'warn', 7 | 'sub-process-blank-start-event': 'error' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /config/recommended.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'ad-hoc-sub-process': 'error', 4 | 'conditional-flows': 'error', 5 | 'end-event-required': 'error', 6 | 'event-sub-process-typed-start-event': 'error', 7 | 'fake-join': 'warn', 8 | 'global': 'warn', 9 | 'label-required': 'error', 10 | 'link-event': 'error', 11 | 'no-bpmndi': 'error', 12 | 'no-complex-gateway': 'error', 13 | 'no-disconnected': 'error', 14 | 'no-duplicate-sequence-flows': 'error', 15 | 'no-gateway-join-fork': 'error', 16 | 'no-implicit-split': 'error', 17 | 'no-implicit-end': 'error', 18 | 'no-implicit-start': 'error', 19 | 'no-inclusive-gateway': 'error', 20 | 'no-overlapping-elements': 'warn', 21 | 'single-blank-start-event': 'error', 22 | 'single-event-definition': 'error', 23 | 'start-event-required': 'error', 24 | 'sub-process-blank-start-event': 'error', 25 | 'superfluous-gateway': 'warn', 26 | 'superfluous-termination': 'warn' 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /docs/rules/README.md: -------------------------------------------------------------------------------- 1 | # Rules 2 | 3 | This library implements the following lint rules: 4 | 5 | * [Ad-Hoc Sub-Process](./ad-hoc-sub-process.md) 6 | * [Conditional Flows](./conditional-flows.md) 7 | * [End Event Required](./end-event-required.md) 8 | * [Event Sub-Process Typed Start Event](./event-sub-process-typed-start-event.md) 9 | * [Fake Join](./fake-join.md) 10 | * [Superfluous Gateway](./superfluous-gateway.md) 11 | * [Label Required](./label-required.md) 12 | * [No Complex Gateway](./no-complex-gateway.md) 13 | * [No Disconnected](./no-disconnected.md) 14 | * [No Duplicate Sequence Flows](./no-duplicate-sequence-flows.md) 15 | * [No Gateway Join Fork](./no-gateway-join-fork.md) 16 | * [No Implicit Start](./no-implicit-start.md) 17 | * [No Implicit Split](./no-implicit-split.md) 18 | * [No Implicit End](./no-implicit-end.md) 19 | * [No Inclusive Gateway](./no-inclusive-gateway.md) 20 | * [No Overlapping Elements](./no-overlapping-elements.md) 21 | * [Single Blank Start Event](./single-blank-start-event.md) 22 | * [Single Event Definition](./single-event-definition.md) 23 | * [Start Event Required](./start-event-required.md) 24 | * [Sub-Process Blank Start Event](./sub-process-blank-start-event.md) -------------------------------------------------------------------------------- /docs/rules/ad-hoc-sub-process.md: -------------------------------------------------------------------------------- 1 | # Ad-hoc Sub-Process (ad-hoc-sub-process) 2 | 3 | Ensure that an Ad-Hoc Sub-Process is valid according to the BPMN specification: 4 | 5 | - Must not contain start or end events. 6 | - Every intermediate catch event must have an outgoing sequence flow. 7 | 8 | 9 | Example of __incorrect__ usage for this rule: 10 | 11 | ![Incorrect usage example](./examples/ad-hoc-sub-process-incorrect.png) 12 | 13 | Cf. [`ad-hoc-sub-process-incorrect.bpmn`](./examples/no-complex-gateway-incorrect.bpmn). 14 | 15 | 16 | Example of __correct__ usage for this rule: 17 | 18 | ![Correct usage example](./examples/ad-hoc-sub-process-correct.png) 19 | 20 | Cf. [`ad-hoc-sub-process-correct.bpmn`](./examples/no-complex-gateway-correct.bpmn). 21 | -------------------------------------------------------------------------------- /docs/rules/conditional-flows.md: -------------------------------------------------------------------------------- 1 | # Conditional Flows (conditional-flows) 2 | 3 | Ensures that conditions on sequence flows are consistently set. If a sequence flow outgoing from a conditional forking gateway or activity is default _or_ any sequence flow has a condition attached, all others have to have to have respective condition meta-data attached, too. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/conditional-flows-incorrect.png) 9 | 10 | ```xml 11 | ... 12 | 13 | ... 14 | ``` 15 | 16 | Cf. [`conditional-flows-incorrect.bpmn`](./examples/conditional-flows-incorrect.bpmn). 17 | 18 | 19 | Example of __correct__ usage for this rule: 20 | 21 | ![Correct usage example](./examples/conditional-flows-correct.png) 22 | 23 | ```xml 24 | ... 25 | 26 | foo 27 | 28 | ... 29 | ``` 30 | 31 | Cf. [`conditional-flows-correct.bpmn`](./examples/conditional-flows-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/end-event-required.md: -------------------------------------------------------------------------------- 1 | # End Event Required (end-event-required) 2 | 3 | Ensures that every process and sub-process has an end event. Explicitly modeling it improves the understandability of drawn process diagrams. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/end-event-required-incorrect.png) 9 | 10 | Cf. [`end-event-required-incorrect.bpmn`](./examples/end-event-required-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/end-event-required-correct.png) 16 | 17 | Cf. [`end-event-required-correct.bpmn`](./examples/end-event-required-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/event-sub-process-typed-start-event.md: -------------------------------------------------------------------------------- 1 | # Event Sub Process Typed Start Event (event-sub-process-typed-start-event) 2 | 3 | Ensures that start events inside event sub-processes are typed (have an event definition). This is required by the BPMN 2.0 standard. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/event-sub-process-typed-start-event-incorrect.png) 9 | 10 | Cf. [`event-sub-process-typed-start-event-incorrect.bpmn`](./examples/event-sub-process-typed-start-event-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/event-sub-process-typed-start-event-correct.png) 16 | 17 | Cf. [`event-sub-process-typed-start-event-correct.bpmn`](./examples/event-sub-process-typed-start-event-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/examples/ad-hoc-sub-process-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/ad-hoc-sub-process-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/ad-hoc-sub-process-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/ad-hoc-sub-process-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/conditional-flows-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/conditional-flows-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/conditional-flows-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/conditional-flows-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/end-event-required-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/end-event-required-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/end-event-required-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_09dspiw 6 | 7 | 8 | SequenceFlow_09dspiw 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/rules/examples/end-event-required-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/end-event-required-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/event-sub-process-typed-start-event-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/rules/examples/event-sub-process-typed-start-event-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/event-sub-process-typed-start-event-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/event-sub-process-typed-start-event-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/rules/examples/event-sub-process-typed-start-event-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/event-sub-process-typed-start-event-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/fake-join-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/fake-join-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/fake-join-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/fake-join-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/label-required-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/label-required-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/label-required-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/label-required-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/link-event-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/link-event-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/link-event-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/link-event-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-bpmndi-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_1 6 | 7 | 8 | 9 | SequenceFlow_1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/rules/examples/no-bpmndi-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-bpmndi-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-bpmndi-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_1 6 | 7 | 8 | 9 | SequenceFlow_1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/rules/examples/no-bpmndi-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-bpmndi-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-complex-gateway-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-complex-gateway-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-complex-gateway-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-complex-gateway-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-disconnected-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-disconnected-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-disconnected-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-disconnected-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-duplicate-sequence-flows-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-duplicate-sequence-flows-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-duplicate-sequence-flows-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-duplicate-sequence-flows-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-gateway-join-fork-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-gateway-join-fork-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-gateway-join-fork-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-gateway-join-fork-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-end-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-end-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-end-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_1 6 | 7 | 8 | SequenceFlow_1 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-end-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-end-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-split-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-split-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-split-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-split-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-start-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-start-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-start-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_09avy1r 6 | 7 | 8 | Flow_09avy1r 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/rules/examples/no-implicit-start-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-implicit-start-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-inclusive-gateway-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-inclusive-gateway-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-inclusive-gateway-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-inclusive-gateway-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/no-overlapping-elements-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_1h3ws2k 6 | 7 | 8 | Flow_1h3ws2k 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/rules/examples/no-overlapping-elements-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-overlapping-elements-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/no-overlapping-elements-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_1h3ws2k 6 | 7 | 8 | Flow_1h3ws2k 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/rules/examples/no-overlapping-elements-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/no-overlapping-elements-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/single-blank-start-event-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/single-blank-start-event-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/single-blank-start-event-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/single-blank-start-event-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/single-event-definition-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/rules/examples/single-event-definition-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/single-event-definition-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/single-event-definition-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/rules/examples/single-event-definition-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/single-event-definition-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/start-event-required-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/start-event-required-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/start-event-required-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/start-event-required-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/sub-process-blank-start-event-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/rules/examples/sub-process-blank-start-event-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/sub-process-blank-start-event-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/sub-process-blank-start-event-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/rules/examples/sub-process-blank-start-event-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/sub-process-blank-start-event-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/superfluous-gateway-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/superfluous-gateway-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/superfluous-gateway-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/superfluous-gateway-incorrect.png -------------------------------------------------------------------------------- /docs/rules/examples/superfluous-termination-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/superfluous-termination-correct.png -------------------------------------------------------------------------------- /docs/rules/examples/superfluous-termination-incorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/docs/rules/examples/superfluous-termination-incorrect.png -------------------------------------------------------------------------------- /docs/rules/fake-join.md: -------------------------------------------------------------------------------- 1 | # Fake Join (fake-join) 2 | 3 | Checks that no fake join is modeled by attempting to give a task or event join semantics. Users should model a parallel joining gateway to achieve the desired behavior. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/fake-join-incorrect.png) 9 | 10 | ```xml 11 | ... 12 | 13 | SequenceFlow_1 14 | SequenceFlow_2 15 | 16 | ... 17 | ``` 18 | 19 | Cf. [`fake-join-incorrect.bpmn`](./examples/fake-join-incorrect.bpmn). 20 | 21 | 22 | Example of __correct__ usage for this rule: 23 | 24 | ![Correct usage example](./examples/fake-join-correct.png) 25 | 26 | ```xml 27 | ... 28 | 29 | SequenceFlow_1 30 | 31 | ... 32 | ``` 33 | 34 | Cf. [`fake-join-correct.bpmn`](./examples/fake-join-correct.bpmn). 35 | -------------------------------------------------------------------------------- /docs/rules/label-required.md: -------------------------------------------------------------------------------- 1 | # Label Required (label-required) 2 | 3 | Checks the presence of a label. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/label-required-incorrect.png) 9 | 10 | ```xml 11 | ... 12 | 13 | SequenceFlow_1 14 | SequenceFlow_2 15 | 16 | ... 17 | ``` 18 | 19 | Cf. [`label-required-incorrect.bpmn`](./examples/label-required-incorrect.bpmn). 20 | 21 | 22 | Example of __correct__ usage for this rule: 23 | 24 | ![Correct usage example](./examples/label-required-correct.png) 25 | 26 | ```xml 27 | ... 28 | 29 | SequenceFlow_1 30 | SequenceFlow_2 31 | 32 | ... 33 | ``` 34 | 35 | Cf. [`label-required-correct.bpmn`](./examples/label-required-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/link-event.md: -------------------------------------------------------------------------------- 1 | # Link Event (link-event) 2 | 3 | A rule that checks that link events are used in accordance with the BPMN specification: 4 | 5 | * A link event definition must be named 6 | * For every link throw event there exists a matching catch event in the same scope 7 | 8 | 9 | Example of __incorrect__ usage for this rule: 10 | 11 | ![Incorrect usage example](./examples/link-event-incorrect.png) 12 | 13 | Cf. [`link-event-incorrect.bpmn`](./examples/link-event-incorrect.bpmn). 14 | 15 | 16 | Example of __correct__ usage for this rule: 17 | 18 | ![Correct usage example](./examples/link-event-correct.png) 19 | 20 | Cf. [`link-event-correct.bpmn`](./examples/link-event-correct.bpmn). 21 | -------------------------------------------------------------------------------- /docs/rules/no-bpmndi.md: -------------------------------------------------------------------------------- 1 | # No BPMNDI information (no-bpmndi) 2 | 3 | Checks that semantic BPMN elements with a visual representation have graphical information (BPMNDI) attached. 4 | 5 | A BPMN element is referenced by a BPMNDI element. The BPMNDI defines how to visually display / render the respective element. It might happen, that a user deletes such an BPMNDI element by accident (e.g., by working in the XML directly). This might lead to errors, since the BPMN element would still be interpreted when executing the process, but it would not be visible anymore in graphical modeling tools. This rule identifies these cases. 6 | 7 | Example of __incorrect__ usage for this rule. See `task1`: 8 | 9 | ```xml 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ``` 18 | 19 | 20 | Example of __correct__ usage for this rule: 21 | 22 | ```xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/rules/no-complex-gateway.md: -------------------------------------------------------------------------------- 1 | # No Complex Gateway (no-complex-gateway) 2 | 3 | A rule that disallows complex gateways. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-complex-gateway-incorrect.png) 9 | 10 | Cf. [`no-complex-gateway-incorrect.bpmn`](./examples/no-complex-gateway-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-complex-gateway-correct.png) 16 | 17 | Cf. [`no-complex-gateway-correct.bpmn`](./examples/no-complex-gateway-correct.bpmn). 18 | -------------------------------------------------------------------------------- /docs/rules/no-disconnected.md: -------------------------------------------------------------------------------- 1 | # No Disconnected (no-disconnected) 2 | 3 | Checks that a flow element is connected with other flow elements, either via incoming _or_ outgoing sequence flows. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-disconnected-incorrect.png) 9 | 10 | Cf. [`no-disconnected-incorrect.bpmn`](./examples/no-disconnected-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-disconnected-correct.png) 16 | 17 | Cf. [`no-disconnected-correct.bpmn`](./examples/no-disconnected-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-duplicate-sequence-flows.md: -------------------------------------------------------------------------------- 1 | # No Duplicate Sequence Flows (no-duplicate-sequence-flows) 2 | 3 | Checks that sequence flows do not duplicate, leading to an unintentional fork. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-duplicate-sequence-flows-incorrect.png) 9 | 10 | Cf. [`no-duplicate-sequence-flows-incorrect.bpmn`](./examples/no-duplicate-sequence-flows-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-duplicate-sequence-flows-correct.png) 16 | 17 | Cf. [`no-duplicate-sequence-flows-correct.bpmn`](./examples/no-duplicate-sequence-flows-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-gateway-join-fork.md: -------------------------------------------------------------------------------- 1 | # No Gateway Join Fork (no-gateway-join-fork) 2 | 3 | A rule that checks, whether a gateway forks and joins at the same time. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-gateway-join-fork-incorrect.png) 9 | 10 | ```xml 11 | ... 12 | 13 | SequenceFlow_1 14 | SequenceFlow_2 15 | SequenceFlow_3 16 | SequenceFlow_4 17 | 18 | ... 19 | ``` 20 | 21 | Cf. [`no-gateway-join-fork-incorrect.bpmn`](./examples/no-gateway-join-fork-incorrect.bpmn). 22 | 23 | 24 | Example of __correct__ usage for this rule: 25 | 26 | ![Correct usage example](./examples/no-gateway-join-fork-correct.png) 27 | 28 | ```xml 29 | ... 30 | 31 | SequenceFlow_1 32 | SequenceFlow_2 33 | SequenceFlow_3 34 | 35 | ... 36 | ``` 37 | 38 | Cf. [`no-gateway-join-fork-correct.bpmn`](./examples/no-gateway-join-fork-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-implicit-end.md: -------------------------------------------------------------------------------- 1 | # No Implicit End (no-implicit-end) 2 | 3 | Checks that no implicit ends are modeled starting on a diagram. Users should model ends (token sinks) explicitly using end events to promote readability of a diagram. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-implicit-end-incorrect.png) 9 | 10 | Cf. [`no-implicit-end-incorrect.bpmn`](./examples/no-implicit-end-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-implicit-end-correct.png) 16 | 17 | Cf. [`no-implicit-end-correct.bpmn`](./examples/no-implicit-end-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-implicit-split.md: -------------------------------------------------------------------------------- 1 | # No Implicit Split (no-implicit-split) 2 | 3 | Checks that no implicit split is modeled starting from a task. Users should model the parallel splitting gateway explicitly instead. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-implicit-split-incorrect.png) 9 | 10 | Cf. [`no-implicit-split-incorrect.bpmn`](./examples/no-implicit-split-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-implicit-split-correct.png) 16 | 17 | Cf. [`no-implicit-split-correct.bpmn`](./examples/no-implicit-split-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-implicit-start.md: -------------------------------------------------------------------------------- 1 | # No Implicit Start (no-implicit-start) 2 | 3 | Checks that no implicit starts are modeled starting on a diagram. Users should model starts (token spawns) explicitly using start events to promote readability of a diagram. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-implicit-start-incorrect.png) 9 | 10 | Cf. [`no-implicit-start-incorrect.bpmn`](./examples/no-implicit-start-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-implicit-start-correct.png) 16 | 17 | Cf. [`no-implicit-start-correct.bpmn`](./examples/no-implicit-start-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/no-inclusive-gateway.md: -------------------------------------------------------------------------------- 1 | # No Inclusive Gateway (no-inclusive-gateway) 2 | 3 | Checks for the presence of inclusive gateways. The complex split and join semantics of inclusive gateways make it hard to follow the token flow in bigger diagrams. Furthermore, the inclusive gateway is not well supported by BPMN engines, including [Camunda BPM](https://docs.camunda.org/manual/latest/reference/bpmn20/gateways/inclusive-gateway/#camunda-specific-behavior). In most cases, a combination of exclusive and parallel gateways can express inclusive gateway semantics in a clearer, albeit more verbose manner. 4 | 5 | Example of __incorrect__ usage for this rule: 6 | 7 | ![Incorrect usage example](./examples/no-inclusive-gateway-incorrect.png) 8 | 9 | Cf. [`no-inclusive-gateway-incorrect.bpmn`](./examples/no-inclusive-gateway-incorrect.bpmn). 10 | 11 | 12 | Example of __correct__ usage for this rule: 13 | 14 | ![Correct usage example](./examples/no-inclusive-gateway-correct.png) 15 | 16 | Cf. [`no-inclusive-gateway-correct.bpmn`](./examples/no-inclusive-gateway-correct.bpmn). 17 | -------------------------------------------------------------------------------- /docs/rules/no-overlapping-elements.md: -------------------------------------------------------------------------------- 1 | # No Overlapping Elements (no-overlapping-elements) 2 | 3 | Checks that elements are not overlapping with each other unintentionally. 4 | To prevent misinterpretation ensure that a diagram is clearly layouted with sufficient space between flow elements. 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/no-overlapping-elements-incorrect.png) 9 | 10 | Cf. [`no-overlapping-elements-incorrect.bpmn`](./examples/no-overlapping-elements-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/no-overlapping-elements-correct.png) 16 | 17 | Cf. [`no-overlapping-elements-correct.bpmn`](./examples/no-overlapping-elements-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/single-blank-start-event.md: -------------------------------------------------------------------------------- 1 | # Single Blank Start Event (single-blank-start-event) 2 | 3 | Checks that there is only a single blank start event per process (or sub-process). 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/single-blank-start-event-incorrect.png) 9 | 10 | Cf. [`single-blank-start-event-incorrect.bpmn`](./examples/single-blank-start-event-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/single-blank-start-event-correct.png) 16 | 17 | Cf. [`single-blank-start-event-correct.bpmn`](./examples/single-blank-start-event-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/single-event-definition.md: -------------------------------------------------------------------------------- 1 | # Single Event Definition (single-event-definition) 2 | 3 | Checks that an event contains no more than one event definition. 4 | 5 | Example of __incorrect__ usage for this rule: 6 | 7 | ![Incorrect usage example](./examples/single-event-definition-incorrect.png) 8 | 9 | ```xml 10 | ... 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ... 20 | ``` 21 | 22 | Cf. [`single-event-definition-incorrect.bpmn`](./examples/single-event-definition-incorrect.bpmn). 23 | 24 | Example of __correct__ usage for this rule: 25 | 26 | ![Correct usage example](./examples/single-event-definition-correct.png) 27 | 28 | ```xml 29 | ... 30 | 31 | 32 | 33 | 34 | 35 | 36 | ... 37 | ``` 38 | 39 | Cf. [`single-event-definition-correct.bpmn`](./examples/single-event-definition-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/start-event-required.md: -------------------------------------------------------------------------------- 1 | # Start Event Required (start-event-required) 2 | 3 | Checks for the presence of a start event in a process or sub-process. 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/start-event-required-incorrect.png) 9 | 10 | Cf. [`start-event-required-incorrect.bpmn`](./examples/start-event-required-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/start-event-required-correct.png) 16 | 17 | Cf. [`start-event-required-correct.bpmn`](./examples/start-event-required-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/sub-process-blank-start-event.md: -------------------------------------------------------------------------------- 1 | # Sub-Process Blank Start Event (sub-process-blank-start-event) 2 | 3 | Checks that start events inside a normal sub-processes are blank (do not have an event definition). 4 | 5 | 6 | Example of __incorrect__ usage for this rule: 7 | 8 | ![Incorrect usage example](./examples/sub-process-blank-start-event-incorrect.png) 9 | 10 | Cf. [`sub-process-blank-start-event-incorrect.bpmn`](./examples/sub-process-blank-start-event-incorrect.bpmn). 11 | 12 | 13 | Example of __correct__ usage for this rule: 14 | 15 | ![Correct usage example](./examples/sub-process-blank-start-event-correct.png) 16 | 17 | Cf. [`sub-process-blank-start-event-correct.bpmn`](./examples/sub-process-blank-start-event-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/superfluous-gateway.md: -------------------------------------------------------------------------------- 1 | # Redundant Gateway (superfluous-gateway) 2 | 3 | A rule that checks, whether a gateway has only one source and target. Those gateways are superfluous since they don't do anything. 4 | 5 | Example of __incorrect__ usage for this rule: 6 | 7 | ![Incorrect usage example](./examples/superfluous-gateway-incorrect.png) 8 | 9 | Cf. [`superfluous-gateway-incorrect.bpmn`](./examples/superfluous-gateway-incorrect.bpmn). 10 | 11 | 12 | Example of __correct__ usage for this rule: 13 | 14 | ![Correct usage example](./examples/superfluous-gateway-correct.png) 15 | 16 | Cf. [`superfluous-gateway.bpmn`](./examples/superfluous-gateway-correct.bpmn). -------------------------------------------------------------------------------- /docs/rules/superfluous-termination.md: -------------------------------------------------------------------------------- 1 | # Redundant Termination (superfluous-termination) 2 | 3 | A rule that checks, whether termination is used in a meaningful manner. It cancels other tokens in a running process instance, hence it is superfluous (and potentially missleading) in scenarios where no other tokens to be canceled can exist. 4 | 5 | Example of __incorrect__ usage for this rule: 6 | 7 | ![Incorrect usage example](./examples/superfluous-termination-incorrect.png) 8 | 9 | Cf. [`superfluous-termination-incorrect.bpmn`](./examples/superfluous-termination-incorrect.bpmn). 10 | 11 | 12 | Example of __correct__ usage for this rule: 13 | 14 | ![Correct usage example](./examples/superfluous-termination-correct.png) 15 | 16 | Cf. [`superfluous-termination.bpmn`](./examples/superfluous-termination-correct.bpmn). -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import bpmnIoPlugin from 'eslint-plugin-bpmn-io'; 2 | 3 | const files = { 4 | lib: [ 5 | 'lib/**/*.js', 6 | 'rules/**/*.js', 7 | 'config/**/*.js' 8 | ], 9 | test: [ 10 | 'test/**/*.js', 11 | 'test/**/*.mjs' 12 | ], 13 | ignored: [ 14 | 'coverage', 15 | '.nyc_output', 16 | 'test/integration/bundling/dist', 17 | 'test/integration/bundling/test', 18 | 'test/integration/compilation/test' 19 | ] 20 | }; 21 | 22 | export default [ 23 | { 24 | 'ignores': files.ignored 25 | }, 26 | 27 | // lib 28 | ...bpmnIoPlugin.configs.recommended.map(config => { 29 | 30 | return { 31 | ...config, 32 | files: files.lib, 33 | languageOptions: { 34 | ...config.languageOptions, 35 | sourceType: 'commonjs' 36 | } 37 | }; 38 | }), 39 | 40 | // build + test 41 | ...bpmnIoPlugin.configs.node.map(config => { 42 | 43 | return { 44 | ...config, 45 | ignores: files.lib, 46 | }; 47 | }), 48 | 49 | // test 50 | ...bpmnIoPlugin.configs.mocha.map(config => { 51 | 52 | return { 53 | ...config, 54 | files: files.test 55 | }; 56 | }) 57 | ]; 58 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const Linter = require('./linter'); 2 | 3 | module.exports = { 4 | Linter 5 | }; -------------------------------------------------------------------------------- /lib/resolver/static-resolver.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef { import('../types.js').RuleFactory } RuleFactory 3 | * @typedef { import('../types.js').Config } Config 4 | */ 5 | 6 | 7 | /** 8 | * A resolver that resolves rules and packages from a static cache. 9 | * 10 | * @param { any } cache 11 | */ 12 | function StaticResolver(cache) { 13 | this.cache = cache; 14 | } 15 | 16 | module.exports = StaticResolver; 17 | 18 | /** 19 | * @param { string } pkg 20 | * @param { string } ruleName 21 | * 22 | * @return { RuleFactory } 23 | */ 24 | StaticResolver.prototype.resolveRule = function(pkg, ruleName) { 25 | return /** @type { RuleFactory } */ (this.resolve('rule', pkg, ruleName)); 26 | }; 27 | 28 | /** 29 | * @param { string } pkg 30 | * @param { string } configName 31 | * 32 | * @return { Config } 33 | */ 34 | StaticResolver.prototype.resolveConfig = function(pkg, configName) { 35 | return /** @type { Config } */ (this.resolve('config', pkg, configName)); 36 | }; 37 | 38 | StaticResolver.prototype.resolve = function(type, pkg, name) { 39 | const id = `${pkg}/${name}`; 40 | 41 | const resolved = this.cache[`${type}:${id}`]; 42 | 43 | if (!resolved) { 44 | throw new Error(`unknown ${type} <${id}>`); 45 | } 46 | 47 | return resolved; 48 | }; -------------------------------------------------------------------------------- /lib/testers/helper.js: -------------------------------------------------------------------------------- 1 | // @ts-expect-error 'missing types' 2 | const BpmnModdle = require('bpmn-moddle'); 3 | 4 | const { readFileSync } = require('node:fs'); 5 | 6 | 7 | /** 8 | * Create moddle instance. 9 | * 10 | * @param {String} xml the XML string 11 | * 12 | * @return {Promise} 13 | */ 14 | async function createModdle(xml, elementType = 'bpmn:Definitions') { 15 | const moddle = new BpmnModdle(); 16 | 17 | const { 18 | rootElement: root, 19 | warnings = [] 20 | } = await moddle.fromXML(xml, elementType, { lax: true }); 21 | 22 | return { 23 | root, 24 | moddle, 25 | context: { 26 | warnings 27 | }, 28 | warnings 29 | }; 30 | } 31 | 32 | module.exports.createModdle = createModdle; 33 | 34 | 35 | /** 36 | * Return moddle instance, read from the given file. 37 | * 38 | * @param {String} filePath 39 | * 40 | * @return {Promise} 41 | */ 42 | function readModdle(filePath) { 43 | const contents = readFileSync(filePath, 'utf8'); 44 | 45 | return createModdle(contents); 46 | } 47 | 48 | module.exports.readModdle = readModdle; -------------------------------------------------------------------------------- /lib/testers/rule-tester.d.ts: -------------------------------------------------------------------------------- 1 | export type TestFn = (...any) => any; 2 | 3 | export type ModdleElement = any; 4 | 5 | export type ValidRuleTest = { 6 | config?: any, 7 | it?: TestFn, 8 | moddleElement: ModdleElement, 9 | name?: string 10 | }; 11 | 12 | export type InvalidReport = { 13 | id: string, 14 | message: string 15 | }; 16 | 17 | export type InvalidRuleTest = { 18 | config?: any, 19 | it?: TestFn, 20 | moddleElement: ModdleElement, 21 | name?: string, 22 | report: InvalidReport | InvalidReport[] 23 | }; 24 | 25 | export type RuleTests = { 26 | valid?: ValidRuleTest[], 27 | invalid?: InvalidRuleTest[] 28 | }; 29 | 30 | /** 31 | * Verify a rule. 32 | * 33 | * @param ruleName 34 | * @param rule the rule implementation 35 | * @param tests the tests to verify 36 | */ 37 | export function verify(ruleName: string, rule: any, tests: RuleTests): void; -------------------------------------------------------------------------------- /lib/traverse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef { import('./types.js').ModdleElement } ModdleElement 3 | * 4 | * @typedef { (element: ModdleElement) => boolean | void } EnterFn 5 | * @typedef { (element: ModdleElement) => void } LeaveFn 6 | */ 7 | 8 | 9 | /** 10 | * Traverse a moddle tree, depth first from top to bottom 11 | * and call the passed visitor fn. 12 | * 13 | * @param { ModdleElement } element 14 | * @param { { enter?: EnterFn; leave?: LeaveFn } } options 15 | */ 16 | module.exports = function traverse(element, options) { 17 | 18 | const enter = options.enter; 19 | const leave = options.leave; 20 | 21 | const enterSubTree = enter && enter(element); 22 | 23 | const descriptor = element.$descriptor; 24 | 25 | if (enterSubTree !== false && !descriptor.isGeneric) { 26 | 27 | const containedProperties = descriptor.properties.filter(p => { 28 | return !p.isAttr && !p.isReference && p.type !== 'String'; 29 | }); 30 | 31 | containedProperties.forEach(p => { 32 | if (p.name in element) { 33 | const propertyValue = element[p.name]; 34 | 35 | if (p.isMany) { 36 | propertyValue.forEach(child => { 37 | traverse(child, options); 38 | }); 39 | } else { 40 | traverse(propertyValue, options); 41 | } 42 | } 43 | }); 44 | } 45 | 46 | leave && leave(element); 47 | }; 48 | -------------------------------------------------------------------------------- /rules/ad-hoc-sub-process.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that ensures that an Ad Hoc Sub Process is valid according to the BPMN spec: 12 | * 13 | * - No start or end events 14 | * - Every intermediate event has an outgoing sequence flow 15 | * 16 | * @type { import('../lib/types.js').RuleFactory } 17 | */ 18 | module.exports = function() { 19 | 20 | function check(node, reporter) { 21 | 22 | if (!is(node, 'bpmn:AdHocSubProcess')) { 23 | return; 24 | } 25 | 26 | const flowElements = node.flowElements || []; 27 | 28 | flowElements.forEach(function(flowElement) { 29 | 30 | if (is(flowElement, 'bpmn:StartEvent')) { 31 | reporter.report(flowElement.id, 'A is not allowed in '); 32 | } 33 | 34 | if (is(flowElement, 'bpmn:EndEvent')) { 35 | reporter.report(flowElement.id, 'An is not allowed in '); 36 | } 37 | 38 | if (is(flowElement, 'bpmn:IntermediateCatchEvent')) { 39 | if (!flowElement.outgoing || flowElement.outgoing.length === 0) { 40 | reporter.report(flowElement.id, 'An intermediate catch event inside must have an outgoing sequence flow'); 41 | } 42 | } 43 | 44 | }); 45 | } 46 | 47 | return annotateRule('ad-hoc-sub-process', { 48 | check 49 | }); 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /rules/conditional-flows.js: -------------------------------------------------------------------------------- 1 | const { 2 | annotateRule 3 | } = require('./helper'); 4 | 5 | 6 | /** 7 | * A rule that checks that sequence flows outgoing from a 8 | * conditional forking gateway or activity are 9 | * either default flows _or_ have a condition attached 10 | * 11 | * @type { import('../lib/types.js').RuleFactory } 12 | */ 13 | module.exports = function() { 14 | 15 | function check(node, reporter) { 16 | 17 | if (!isConditionalForking(node)) { 18 | return; 19 | } 20 | 21 | const outgoing = node.outgoing || []; 22 | 23 | outgoing.forEach((flow) => { 24 | const missingCondition = ( 25 | !hasCondition(flow) && 26 | !isDefaultFlow(node, flow) 27 | ); 28 | 29 | if (missingCondition) { 30 | reporter.report(flow.id, 'Sequence flow is missing condition', [ 'conditionExpression' ]); 31 | } 32 | }); 33 | } 34 | 35 | return annotateRule('conditional-flows', { 36 | check 37 | }); 38 | 39 | }; 40 | 41 | 42 | // helpers ///////////////////////////// 43 | 44 | function isConditionalForking(node) { 45 | 46 | const defaultFlow = node['default']; 47 | const outgoing = node.outgoing || []; 48 | 49 | return defaultFlow || outgoing.find(hasCondition); 50 | } 51 | 52 | function hasCondition(flow) { 53 | return !!flow.conditionExpression; 54 | } 55 | 56 | function isDefaultFlow(node, flow) { 57 | return node['default'] === flow; 58 | } -------------------------------------------------------------------------------- /rules/end-event-required.js: -------------------------------------------------------------------------------- 1 | const { 2 | is, 3 | isAny 4 | } = require('bpmnlint-utils'); 5 | 6 | const { 7 | annotateRule 8 | } = require('./helper'); 9 | 10 | 11 | /** 12 | * A rule that checks the presence of an end event per scope. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function hasEndEvent(node) { 19 | const flowElements = node.flowElements || []; 20 | 21 | return ( 22 | flowElements.some(node => is(node, 'bpmn:EndEvent')) 23 | ); 24 | } 25 | 26 | function check(node, reporter) { 27 | 28 | if (!isAny(node, [ 29 | 'bpmn:Process', 30 | 'bpmn:SubProcess' 31 | ]) || is(node, 'bpmn:AdHocSubProcess')) { 32 | return; 33 | } 34 | 35 | if (!hasEndEvent(node)) { 36 | const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process'; 37 | 38 | reporter.report(node.id, type + ' is missing end event'); 39 | } 40 | } 41 | 42 | return annotateRule('end-event-required', { 43 | check 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /rules/event-sub-process-typed-start-event.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks that start events inside an event sub-process 12 | * are typed. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function check(node, reporter) { 19 | 20 | if (!is(node, 'bpmn:SubProcess') || !node.triggeredByEvent) { 21 | return; 22 | } 23 | 24 | const flowElements = node.flowElements || []; 25 | 26 | flowElements.forEach(function(flowElement) { 27 | 28 | if (!is(flowElement, 'bpmn:StartEvent')) { 29 | return false; 30 | } 31 | 32 | const eventDefinitions = flowElement.eventDefinitions || []; 33 | 34 | if (eventDefinitions.length === 0) { 35 | reporter.report(flowElement.id, 'Start event is missing event definition', [ 'eventDefinitions' ]); 36 | } 37 | }); 38 | } 39 | 40 | return annotateRule('event-sub-process-typed-start-event', { 41 | check 42 | }); 43 | 44 | }; -------------------------------------------------------------------------------- /rules/fake-join.js: -------------------------------------------------------------------------------- 1 | const { 2 | isAny 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks that no fake join is modeled by attempting 12 | * to give a task or event join semantics. 13 | * 14 | * Users should model a parallel joining gateway 15 | * to achieve the desired behavior. 16 | * 17 | * @type { import('../lib/types.js').RuleFactory } 18 | */ 19 | module.exports = function() { 20 | 21 | function check(node, reporter) { 22 | 23 | if (!isAny(node, [ 24 | 'bpmn:Activity', 25 | 'bpmn:Event' 26 | ])) { 27 | return; 28 | } 29 | 30 | const incoming = node.incoming || []; 31 | 32 | if (incoming.length > 1) { 33 | reporter.report(node.id, 'Incoming flows do not join'); 34 | } 35 | } 36 | 37 | return annotateRule('fake-join', { 38 | check 39 | }); 40 | 41 | }; -------------------------------------------------------------------------------- /rules/label-required.js: -------------------------------------------------------------------------------- 1 | const { 2 | is, 3 | isAny 4 | } = require('bpmnlint-utils'); 5 | 6 | const { 7 | annotateRule 8 | } = require('./helper'); 9 | 10 | 11 | /** 12 | * A rule that checks the presence of a label. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function check(node, reporter) { 19 | 20 | if (isAny(node, [ 21 | 'bpmn:ParallelGateway', 22 | 'bpmn:EventBasedGateway' 23 | ])) { 24 | return; 25 | } 26 | 27 | // ignore joining gateways 28 | if (is(node, 'bpmn:Gateway') && !isForking(node)) { 29 | return; 30 | } 31 | 32 | // ignore sub-processes 33 | if (is(node, 'bpmn:SubProcess')) { 34 | 35 | // TODO(nikku): better ignore expanded sub-processes only 36 | return; 37 | } 38 | 39 | // ignore sequence flow without condition 40 | if (is(node, 'bpmn:SequenceFlow') && !hasCondition(node)) { 41 | return; 42 | } 43 | 44 | // ignore data objects and artifacts for now 45 | if (isAny(node, [ 46 | 'bpmn:FlowNode', 47 | 'bpmn:SequenceFlow', 48 | 'bpmn:Participant', 49 | 'bpmn:Lane' 50 | ])) { 51 | 52 | const name = (node.name || '').trim(); 53 | 54 | if (name.length === 0) { 55 | reporter.report(node.id, 'Element is missing label/name', [ 'name' ]); 56 | } 57 | } 58 | } 59 | 60 | return annotateRule('label-required', { 61 | check 62 | }); 63 | }; 64 | 65 | 66 | // helpers //////////////////////// 67 | 68 | function isForking(node) { 69 | const outgoing = node.outgoing || []; 70 | 71 | return outgoing.length > 1; 72 | } 73 | 74 | function hasCondition(node) { 75 | return node.conditionExpression; 76 | } -------------------------------------------------------------------------------- /rules/no-complex-gateway.js: -------------------------------------------------------------------------------- 1 | const checkDiscouragedNodeType = require('./helper').checkDiscouragedNodeType; 2 | 3 | module.exports = checkDiscouragedNodeType('bpmn:ComplexGateway', 'no-complex-gateway'); -------------------------------------------------------------------------------- /rules/no-duplicate-sequence-flows.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that verifies that there are no disconnected 12 | * flow elements, i.e. elements without incoming or outgoing sequence flows. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | const keyed = {}; 19 | 20 | const outgoingReported = {}; 21 | const incomingReported = {}; 22 | 23 | function check(node, reporter) { 24 | 25 | if (!is(node, 'bpmn:SequenceFlow')) { 26 | return; 27 | } 28 | 29 | const key = flowKey(node); 30 | 31 | if (key in keyed) { 32 | reporter.report(node.id, 'SequenceFlow is a duplicate'); 33 | 34 | const sourceId = node.sourceRef.id; 35 | const targetId = node.targetRef.id; 36 | 37 | if (!outgoingReported[sourceId]) { 38 | reporter.report(sourceId, 'Duplicate outgoing sequence flows'); 39 | 40 | outgoingReported[sourceId] = true; 41 | } 42 | 43 | if (!incomingReported[targetId]) { 44 | reporter.report(targetId, 'Duplicate incoming sequence flows'); 45 | 46 | incomingReported[targetId] = true; 47 | } 48 | } else { 49 | keyed[key] = node; 50 | } 51 | } 52 | 53 | return annotateRule('no-duplicate-sequence-flows', { 54 | check 55 | }); 56 | 57 | }; 58 | 59 | 60 | // helpers ///////////////// 61 | 62 | function flowKey(flow) { 63 | const conditionExpression = flow.conditionExpression; 64 | 65 | const condition = conditionExpression ? conditionExpression.body : ''; 66 | const source = flow.sourceRef ? flow.sourceRef.id : flow.id; 67 | const target = flow.targetRef ? flow.targetRef.id : flow.id; 68 | 69 | return source + '#' + target + '#' + condition; 70 | } -------------------------------------------------------------------------------- /rules/no-gateway-join-fork.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks, whether a gateway forks and joins 12 | * at the same time. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function check(node, reporter) { 19 | 20 | if (!is(node, 'bpmn:Gateway')) { 21 | return; 22 | } 23 | 24 | const incoming = node.incoming || []; 25 | const outgoing = node.outgoing || []; 26 | 27 | if (incoming.length > 1 && outgoing.length > 1) { 28 | reporter.report(node.id, 'Gateway forks and joins'); 29 | } 30 | } 31 | 32 | return annotateRule('no-gateway-fork-join', { 33 | check 34 | }); 35 | 36 | }; -------------------------------------------------------------------------------- /rules/no-implicit-split.js: -------------------------------------------------------------------------------- 1 | const { 2 | isAny 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks that no implicit split is modeled 12 | * starting from a task. 13 | * 14 | * Users should model the parallel splitting gateway 15 | * explicitly instead. 16 | * 17 | * @type { import('../lib/types.js').RuleFactory } 18 | */ 19 | module.exports = function() { 20 | 21 | function check(node, reporter) { 22 | 23 | if (!isAny(node, [ 24 | 'bpmn:Activity', 25 | 'bpmn:Event' 26 | ])) { 27 | return; 28 | } 29 | 30 | const outgoing = node.outgoing || []; 31 | 32 | const outgoingWithoutCondition = outgoing.filter((flow) => { 33 | return !hasCondition(flow) && !isDefaultFlow(node, flow); 34 | }); 35 | 36 | if (outgoingWithoutCondition.length > 1) { 37 | reporter.report(node.id, 'Flow splits implicitly'); 38 | } 39 | } 40 | 41 | return annotateRule('no-implicit-split', { 42 | check 43 | }); 44 | 45 | }; 46 | 47 | 48 | // helpers ///////////////////////////// 49 | 50 | function hasCondition(flow) { 51 | return !!flow.conditionExpression; 52 | } 53 | 54 | function isDefaultFlow(node, flow) { 55 | return node['default'] === flow; 56 | } -------------------------------------------------------------------------------- /rules/no-implicit-start.js: -------------------------------------------------------------------------------- 1 | const { 2 | is, 3 | isAny 4 | } = require('bpmnlint-utils'); 5 | 6 | const { 7 | annotateRule 8 | } = require('./helper'); 9 | 10 | /** 11 | * A rule that checks that an element is not an implicit start (token spawn). 12 | * 13 | * @type { import('../lib/types.js').RuleFactory } 14 | */ 15 | module.exports = function() { 16 | 17 | function isLinkEvent(node) { 18 | const eventDefinitions = node.eventDefinitions || []; 19 | 20 | return eventDefinitions.length && eventDefinitions.every( 21 | definition => is(definition, 'bpmn:LinkEventDefinition') 22 | ); 23 | } 24 | 25 | function isImplicitStart(node) { 26 | const incoming = node.incoming || []; 27 | 28 | if (is(node, 'bpmn:Activity') && node.isForCompensation) { 29 | return false; 30 | } 31 | 32 | if (is(node.$parent, 'bpmn:AdHocSubProcess')) { 33 | return false; 34 | } 35 | 36 | if (is(node, 'bpmn:SubProcess') && node.triggeredByEvent) { 37 | return false; 38 | } 39 | 40 | if (is(node, 'bpmn:IntermediateCatchEvent') && isLinkEvent(node)) { 41 | return false; 42 | } 43 | 44 | if (isAny(node, [ 'bpmn:StartEvent', 'bpmn:BoundaryEvent' ])) { 45 | return false; 46 | } 47 | 48 | return incoming.length === 0; 49 | } 50 | 51 | function check(node, reporter) { 52 | 53 | if (!isAny(node, [ 'bpmn:Event', 'bpmn:Activity', 'bpmn:Gateway' ])) { 54 | return; 55 | } 56 | 57 | if (isImplicitStart(node)) { 58 | reporter.report(node.id, 'Element is an implicit start'); 59 | } 60 | } 61 | 62 | return annotateRule('no-implicit-start', { 63 | check 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /rules/no-inclusive-gateway.js: -------------------------------------------------------------------------------- 1 | const checkDiscouragedNodeType = require('./helper').checkDiscouragedNodeType; 2 | 3 | module.exports = checkDiscouragedNodeType('bpmn:InclusiveGateway', 'no-inclusive-gateway'); -------------------------------------------------------------------------------- /rules/single-blank-start-event.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks whether not more than one blank start event 12 | * exists per scope. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function check(node, reporter) { 19 | 20 | if (!is(node, 'bpmn:FlowElementsContainer')) { 21 | return; 22 | } 23 | 24 | const flowElements = node.flowElements || []; 25 | 26 | const blankStartEvents = flowElements.filter(function(flowElement) { 27 | 28 | if (!is(flowElement, 'bpmn:StartEvent')) { 29 | return false; 30 | } 31 | 32 | const eventDefinitions = flowElement.eventDefinitions || []; 33 | 34 | return eventDefinitions.length === 0; 35 | }); 36 | 37 | if (blankStartEvents.length > 1) { 38 | const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process'; 39 | 40 | reporter.report(node.id, type + ' has multiple blank start events'); 41 | } 42 | } 43 | 44 | return annotateRule('single-blank-start-event', { 45 | check 46 | }); 47 | 48 | }; -------------------------------------------------------------------------------- /rules/single-event-definition.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that verifies that an event contains maximum one event definition. 12 | * 13 | * @type { import('../lib/types.js').RuleFactory } 14 | */ 15 | module.exports = function() { 16 | 17 | function check(node, reporter) { 18 | 19 | if (!is(node, 'bpmn:Event')) { 20 | return; 21 | } 22 | 23 | const eventDefinitions = node.eventDefinitions || []; 24 | 25 | if (eventDefinitions.length > 1) { 26 | reporter.report(node.id, 'Event has multiple event definitions', [ 'eventDefinitions' ]); 27 | } 28 | } 29 | 30 | return annotateRule('single-event-definition', { 31 | check 32 | }); 33 | 34 | }; -------------------------------------------------------------------------------- /rules/start-event-required.js: -------------------------------------------------------------------------------- 1 | const { 2 | is, 3 | isAny 4 | } = require('bpmnlint-utils'); 5 | 6 | const { 7 | annotateRule 8 | } = require('./helper'); 9 | 10 | 11 | /** 12 | * A rule that checks for the presence of a start event per scope. 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function hasStartEvent(node) { 19 | const flowElements = node.flowElements || []; 20 | 21 | return ( 22 | flowElements.some(node => is(node, 'bpmn:StartEvent')) 23 | ); 24 | } 25 | 26 | function check(node, reporter) { 27 | 28 | if (!isAny(node, [ 29 | 'bpmn:Process', 30 | 'bpmn:SubProcess' 31 | ]) || is(node, 'bpmn:AdHocSubProcess')) { 32 | return; 33 | } 34 | 35 | if (!hasStartEvent(node)) { 36 | const type = is(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process'; 37 | 38 | reporter.report(node.id, type + ' is missing start event'); 39 | } 40 | } 41 | 42 | return annotateRule('start-event-required', { 43 | check 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /rules/sub-process-blank-start-event.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks that start events inside a normal sub-processes 12 | * are blank (do not have an event definition). 13 | * 14 | * @type { import('../lib/types.js').RuleFactory } 15 | */ 16 | module.exports = function() { 17 | 18 | function check(node, reporter) { 19 | 20 | if (!is(node, 'bpmn:SubProcess') || node.triggeredByEvent) { 21 | return; 22 | } 23 | 24 | const flowElements = node.flowElements || []; 25 | 26 | flowElements.forEach(function(flowElement) { 27 | 28 | if (!is(flowElement, 'bpmn:StartEvent')) { 29 | return false; 30 | } 31 | 32 | const eventDefinitions = flowElement.eventDefinitions || []; 33 | 34 | if (eventDefinitions.length > 0) { 35 | reporter.report(flowElement.id, 'Start event must be blank', [ 'eventDefinitions' ]); 36 | } 37 | }); 38 | } 39 | 40 | return annotateRule('sub-process-blank-start-event', { 41 | check 42 | }); 43 | 44 | }; -------------------------------------------------------------------------------- /rules/superfluous-gateway.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | const { 6 | annotateRule 7 | } = require('./helper'); 8 | 9 | 10 | /** 11 | * A rule that checks, whether a gateway has only one source and target. 12 | * 13 | * Those gateways are superfluous since they don't do anything. 14 | * 15 | * @type { import('../lib/types.js').RuleFactory } 16 | */ 17 | module.exports = function() { 18 | 19 | function check(node, reporter) { 20 | 21 | if (!is(node, 'bpmn:Gateway')) { 22 | return; 23 | } 24 | 25 | const incoming = node.incoming || []; 26 | const outgoing = node.outgoing || []; 27 | 28 | if (incoming.length === 1 && outgoing.length === 1) { 29 | reporter.report(node.id, 'Gateway is superfluous. It only has one source and target.'); 30 | } 31 | } 32 | 33 | return annotateRule('superfluous-gateway', { 34 | check 35 | }); 36 | 37 | }; -------------------------------------------------------------------------------- /test/helper.mjs: -------------------------------------------------------------------------------- 1 | import * as utils from 'bpmnlint-utils'; 2 | 3 | export * from '../lib/testers/helper.js'; 4 | 5 | export { expect } from 'chai'; 6 | 7 | export function createRule(ruleFactory) { 8 | return ruleFactory(utils); 9 | } 10 | 11 | 12 | import { dirname } from 'node:path'; 13 | import { fileURLToPath } from 'node:url'; 14 | import { createRequire } from 'node:module'; 15 | 16 | export function stubCJS(path) { 17 | 18 | return { 19 | __dirname: dirname(fileURLToPath(path)), 20 | require: createRequire(path) 21 | }; 22 | } -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-exported/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-plugin-exported", 3 | "version": "0.0.0", 4 | "description": "A test bpmnlint plug-in that exports rules", 5 | "main": "src/index.js", 6 | "sideEffects": false 7 | } 8 | -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-exported/src/bar.js: -------------------------------------------------------------------------------- 1 | module.exports = function bar() { 2 | return { 3 | check() {} 4 | }; 5 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-exported/src/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function foo() { 2 | return { 3 | check() {} 4 | }; 5 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-exported/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | rules: { 5 | 'foo': 'error', 6 | 'bar': 'error', 7 | 'baz': 'error', 8 | 'foo-absolute': 'error' 9 | } 10 | } 11 | }, 12 | rules: { 13 | 'foo': './foo', 14 | 'bar': './bar', 15 | 'foo-absolute': 'bpmnlint-plugin-exported/src/foo' 16 | } 17 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test-ns/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | rules: { 5 | 'no-label-xxx': 'error' 6 | } 7 | } 8 | } 9 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test-ns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ns/bpmnlint-plugin-test", 3 | "version": "0.0.0", 4 | "description": "A namespaced test bpmnlint plug-in", 5 | "main": "index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test-ns/rules/no-label-xxx.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rule that reports xxx labels. 3 | */ 4 | module.exports = function() { 5 | 6 | function check(node, reporter) { 7 | if (/^xxx/.test(node.name || '')) { 8 | reporter.report(node.id, 'Element has non-sense label <' + node.name + '>'); 9 | } 10 | } 11 | 12 | return { 13 | check: check 14 | }; 15 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | extends: 'bpmnlint:recommended', 5 | rules: { 6 | 'no-label-foo': 'error', 7 | 'bpmnlint/no-disconnected': 'warn' 8 | } 9 | } 10 | } 11 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-plugin-test", 3 | "version": "0.0.0", 4 | "description": "A test bpmnlint plug-in", 5 | "main": "index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test/rules/no-label-bar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rule that reports bar labels. 3 | */ 4 | module.exports = function() { 5 | 6 | function check(node, reporter) { 7 | if (/^bar/.test(node.name || '')) { 8 | reporter.report(node.id, 'Element has non-sense label <' + node.name + '>'); 9 | } 10 | } 11 | 12 | return { 13 | check: check 14 | }; 15 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test/rules/no-label-foo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rule that reports foo labels. 3 | */ 4 | module.exports = function() { 5 | 6 | function check(node, reporter) { 7 | if (/^foo/.test(node.name || '')) { 8 | reporter.report(node.id, 'Element has non-sense label <' + node.name + '>'); 9 | } 10 | } 11 | 12 | return { 13 | check: check 14 | }; 15 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test2/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | extends: 'bpmnlint:recommended', 5 | rules: { 6 | 'no-label-xxx': 'error', 7 | 'bpmnlint/no-disconnected': 'warn' 8 | } 9 | } 10 | } 11 | }; -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-plugin-test2", 3 | "version": "0.0.0", 4 | "description": "A test bpmnlint plug-in", 5 | "main": "index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/integration/bpmnlint-plugin-test2/rules/no-label-xxx.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rule that reports xxx labels. 3 | */ 4 | module.exports = function() { 5 | 6 | function check(node, reporter) { 7 | if (/^xxx/.test(node.name || '')) { 8 | reporter.report(node.id, 'Element has non-sense label <' + node.name + '>'); 9 | } 10 | } 11 | 12 | return { 13 | check: check 14 | }; 15 | }; -------------------------------------------------------------------------------- /test/integration/bundling/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /test/integration/bundling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bundling", 3 | "description": "Simple web application to test bpmnlint rule bundling", 4 | "private": true, 5 | "scripts": { 6 | "bundle:webpack": "webpack", 7 | "bundle:rollup": "rollup -c --bundleConfigAsCjs" 8 | }, 9 | "devDependencies": { 10 | "@rollup/plugin-commonjs": "^24.1.0", 11 | "@rollup/plugin-node-resolve": "^15.0.2", 12 | "bpmnlint-loader": "^0.1.6", 13 | "rollup": "^3.21.0", 14 | "rollup-plugin-bpmnlint": "^0.4.1", 15 | "webpack": "^5.80.0", 16 | "webpack-cli": "^5.0.2" 17 | }, 18 | "localDependencies": { 19 | "bpmnlint": "../../..", 20 | "bpmnlint-plugin-test": "../bpmnlint-plugin-test", 21 | "bpmnlint-plugin-exported": "../bpmnlint-plugin-exported" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/integration/bundling/rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import nodeResolve from '@rollup/plugin-node-resolve'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import bpmnlint from 'rollup-plugin-bpmnlint'; 6 | 7 | export default { 8 | input: './src/app.js', 9 | output: { 10 | name: 'App', 11 | file: 'dist/app.rollup.js', 12 | format: 'iife' 13 | }, 14 | plugins: [ 15 | nodeResolve(), 16 | commonjs(), 17 | bpmnlint() 18 | ] 19 | }; -------------------------------------------------------------------------------- /test/integration/bundling/src/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "label-required": 1, 4 | "start-event-required": "info", 5 | "end-event-required": 2, 6 | "bpmnlint-plugin-exported/foo": "error", 7 | "bpmnlint-plugin-exported/foo-absolute": "error" 8 | } 9 | } -------------------------------------------------------------------------------- /test/integration/bundling/src/app.js: -------------------------------------------------------------------------------- 1 | import bpmnlintConfig from './.bpmnlintrc'; 2 | 3 | console.log(bpmnlintConfig); -------------------------------------------------------------------------------- /test/integration/bundling/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: './src/app.js', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'app.webpack.js' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.bpmnlintrc$/i, 14 | use: 'bpmnlint-loader' 15 | } 16 | ] 17 | }, 18 | devtool: false 19 | }; -------------------------------------------------------------------------------- /test/integration/cli/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "label-required": 1, 4 | "start-event-required": 2, 5 | "end-event-required": 2 6 | } 7 | } -------------------------------------------------------------------------------- /test/integration/cli/child/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:test2/recommended", 3 | "rules": { 4 | "test2/no-label-xxx": "warn" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sibling", 3 | "version": "0.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "bpmnlint-plugin-test2": "file:../../bpmnlint-plugin-test2" 7 | }, 8 | "localDependencies": { 9 | "bpmnlint-plugin-test2": "../../bpmnlint-plugin-test2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/integration/cli/diagram-broken.bpmn: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/cli/diagram-import-warnings.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/integration/cli/diagram-invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/integration/cli/diagram-warnings.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/integration/cli/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint/abfcdddefef48c3b1eaeb0b97ce91d4a70f781ab/test/integration/cli/empty/.gitkeep -------------------------------------------------------------------------------- /test/integration/cli/exported.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "exported/foo": "warn", 4 | "exported/foo-absolute": "warn" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/extends-builtin.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "bpmnlint:recommended", 3 | "rules": { 4 | "bpmnlint/no-disconnected": "warn" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/extends-external.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:test/recommended", 3 | "rules": { 4 | "test/no-label-bar": "warn" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/glob/diagram-invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/integration/cli/glob/subfolder/diagram-import-warnings.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/integration/cli/local-rules/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:local/recommended" 3 | } -------------------------------------------------------------------------------- /test/integration/cli/local-rules/lib/bpmnlint-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | extends: 'bpmnlint:recommended', 5 | rules: { 6 | 'no-label-bar': 'error', 7 | 'bpmnlint/label-required': 'off' 8 | } 9 | } 10 | } 11 | }; -------------------------------------------------------------------------------- /test/integration/cli/local-rules/lib/bpmnlint-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-plugin-local", 3 | "version": "0.0.0", 4 | "description": "Local bpmnlint rules", 5 | "main": "index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/integration/cli/local-rules/lib/bpmnlint-plugin/rules/no-label-bar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rule that reports bar labels. 3 | */ 4 | module.exports = function() { 5 | 6 | function check(node, reporter) { 7 | if (/^bar/.test(node.name || '')) { 8 | reporter.report(node.id, 'Element has non-sense label <' + node.name + '>'); 9 | } 10 | } 11 | 12 | return { 13 | check: check 14 | }; 15 | }; -------------------------------------------------------------------------------- /test/integration/cli/local-rules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "local-rules", 3 | "version": "0.0.0", 4 | "description": "An application that provides local rules", 5 | "main": "index.js", 6 | "dependencies": { 7 | "bpmnlint": "file:../../../..", 8 | "bpmnlint-plugin-local": "file:./lib/bpmnlint-plugin" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/integration/cli/ns/diagram-invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/integration/cli/ns/diagram.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/integration/cli/ns/extends.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:@ns/bpmnlint-plugin-test/recommended", 3 | "rules": { 4 | "test2/no-label-xxx": "error" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/ns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ns", 3 | "version": "0.0.0", 4 | "private": true, 5 | "localDependencies": { 6 | "bpmnlint-plugin-test2": "../../bpmnlint-plugin-test2", 7 | "@ns/bpmnlint-plugin-test": "../../bpmnlint-plugin-test-ns" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/integration/cli/ns/uses-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "test2/no-label-xxx": "error", 4 | "@ns/bpmnlint-plugin-test/no-label-xxx": "error" 5 | } 6 | } -------------------------------------------------------------------------------- /test/integration/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli-spec", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "node test.js" 7 | }, 8 | "dependencies": { 9 | "bpmnlint": "file:../../..", 10 | "bpmnlint-plugin-test": "file:../bpmnlint-plugin-test", 11 | "bpmnlint-plugin-exported": "file:../bpmnlint-plugin-exported" 12 | }, 13 | "devDependencies": { 14 | "execa": "^5.1.1" 15 | }, 16 | "localDependencies": { 17 | "bpmnlint": "../../..", 18 | "bpmnlint-plugin-test": "../bpmnlint-plugin-test", 19 | "bpmnlint-plugin-exported": "../bpmnlint-plugin-exported" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/integration/cli/test.js: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | 3 | /** 4 | * Execute bpmnlint as provided via arguments. 5 | * 6 | * Report { stdout, stderr, code } via output 7 | * for further processing. 8 | */ 9 | 10 | 11 | const [ 12 | bin, 13 | ...args 14 | ] = process.argv.slice(2); 15 | 16 | const cwd = process.env.BPMNLINT_TEST_CWD || process.cwd(); 17 | 18 | console.log('---- CMD'); 19 | console.log('%s %s (cwd: %s)', bin, args.join(' '), cwd); 20 | console.log('---- CMD'); 21 | 22 | execa(bin, args, { 23 | cwd 24 | }).then( 25 | function(result) { 26 | return result; 27 | }, 28 | function(err) { 29 | return err; 30 | } 31 | ).then(function(result) { 32 | const { 33 | stdout, 34 | stderr, 35 | exitCode 36 | } = result; 37 | 38 | console.log('---- STDOUT'); 39 | 40 | if (stdout) { 41 | console.log(stdout); 42 | } 43 | 44 | console.log('---- STDOUT'); 45 | console.log('---- STDERR'); 46 | 47 | if (stderr) { 48 | console.log(stderr); 49 | } 50 | 51 | console.log('---- STDERR'); 52 | console.log('---- CODE'); 53 | 54 | console.log(exitCode); 55 | 56 | console.log('---- CODE'); 57 | console.log('---- END'); 58 | }); -------------------------------------------------------------------------------- /test/integration/compilation/.gitignore: -------------------------------------------------------------------------------- 1 | test/bpmnlintrc.actual.js -------------------------------------------------------------------------------- /test/integration/compilation/compile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const compileConfig = require('bpmnlint/lib/support/compile-config'); 4 | 5 | const config = { 6 | extends: [ 7 | 'plugin:test/recommended', 8 | 'plugin:exported/recommended' 9 | ] 10 | }; 11 | 12 | compileConfig(config).then(code => fs.writeFileSync('test/bpmnlintrc.actual.js', code)).catch(err => { 13 | console.error(err); 14 | 15 | process.exit(1); 16 | }); -------------------------------------------------------------------------------- /test/integration/compilation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compilation", 3 | "description": "Simple project to test bpmnlint rule compilation", 4 | "private": true, 5 | "scripts": { 6 | "compile": "node compile.js" 7 | }, 8 | "localDependencies": { 9 | "bpmnlint": "../../..", 10 | "bpmnlint-plugin-test": "../bpmnlint-plugin-test", 11 | "bpmnlint-plugin-exported": "../bpmnlint-plugin-exported" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/rules/ad-hoc-sub-process.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/ad-hoc-sub-process.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('ad-hoc-sub-process', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/ad-hoc-sub-process/valid.bpmn') 20 | } 21 | ], 22 | invalid: [ 23 | { 24 | moddleElement: readModdle(__dirname + '/ad-hoc-sub-process/invalid-start-end.bpmn'), 25 | report: [ 26 | { 27 | id: 'StartEvent', 28 | message: 'A is not allowed in ' 29 | }, 30 | { 31 | id: 'EndEvent', 32 | message: 'An is not allowed in ' 33 | } 34 | ] 35 | }, 36 | { 37 | moddleElement: readModdle(__dirname + '/ad-hoc-sub-process/invalid-intermediate.bpmn'), 38 | report: [ 39 | { 40 | id: 'CatchEvent', 41 | message: 'An intermediate catch event inside must have an outgoing sequence flow' 42 | } 43 | ] 44 | } 45 | ] 46 | }); 47 | -------------------------------------------------------------------------------- /test/rules/ad-hoc-sub-process/invalid-intermediate.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/rules/ad-hoc-sub-process/invalid-start-end.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/rules/end-event-required.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/end-event-required.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('end-event-required', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/end-event-required/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/end-event-required/valid-sub-process.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/end-event-required/valid-sub-process-sub-types.bpmn') 26 | } 27 | ], 28 | invalid: [ 29 | { 30 | moddleElement: readModdle(__dirname + '/end-event-required/invalid.bpmn'), 31 | report: { 32 | id: 'Process', 33 | message: 'Process is missing end event' 34 | } 35 | }, 36 | { 37 | moddleElement: readModdle(__dirname + '/end-event-required/invalid-sub-process.bpmn'), 38 | report: { 39 | id: 'SubProcess', 40 | message: 'Sub process is missing end event' 41 | } 42 | }, 43 | { 44 | moddleElement: readModdle(__dirname + '/end-event-required/invalid-sub-process-sub-types.bpmn'), 45 | report: [ 46 | { 47 | id: 'TRANSACTION', 48 | message: 'Sub process is missing end event' 49 | }, 50 | { 51 | id: 'EVENT_SUBPROCESS', 52 | message: 'Sub process is missing end event' 53 | } 54 | ] 55 | } 56 | ] 57 | }); -------------------------------------------------------------------------------- /test/rules/end-event-required/invalid-sub-process-sub-types.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/rules/end-event-required/invalid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/end-event-required/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/end-event-required/valid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/rules/end-event-required/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/event-sub-process-typed-start-event.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('event-sub-process-typed-start-event', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/valid-empty.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/valid-empty-sub-process.bpmn') 26 | }, 27 | { 28 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/valid-intermediate-event.bpmn') 29 | }, 30 | { 31 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/valid-sub-process.bpmn') 32 | } 33 | ], 34 | invalid: [ 35 | { 36 | moddleElement: readModdle(__dirname + '/event-sub-process-typed-start-event/invalid.bpmn'), 37 | report: { 38 | id: 'StartEvent', 39 | message: 'Start event is missing event definition', 40 | path: [ 'eventDefinitions' ] 41 | } 42 | } 43 | ] 44 | }); -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/valid-empty-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/valid-empty.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/valid-intermediate-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/valid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/event-sub-process-typed-start-event/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/fake-join.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/fake-join.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('fake-join', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/fake-join/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/fake-join/valid-gateway.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/fake-join/invalid-task.bpmn'), 28 | report: { 29 | id: 'Element', 30 | message: 'Incoming flows do not join' 31 | } 32 | }, 33 | { 34 | moddleElement: readModdle(__dirname + '/fake-join/invalid-callActivity.bpmn'), 35 | report: { 36 | id: 'Element', 37 | message: 'Incoming flows do not join' 38 | } 39 | } 40 | ] 41 | }); -------------------------------------------------------------------------------- /test/rules/fake-join/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_1uh5qzh 6 | 7 | 8 | SequenceFlow_1uh5qzh 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/rules/global/invalid-message-missing-reference.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_0r5ftth 6 | 7 | 8 | 9 | Flow_0r5ftth 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/rules/label-required/invalid-boundary-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/label-required/invalid-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/label-required/invalid-lane.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/rules/label-required/invalid-participant.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/rules/label-required/invalid-task.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/label-required/valid-boundary-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/label-required/valid-data-objects.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/label-required/valid-participant-lanes.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/rules/label-required/valid-start-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/rules/no-bpmndi/ignore-edge-without-bpmn-element.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/rules/no-bpmndi/invalid-collapsed-pool.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/rules/no-bpmndi/invalid-no-bpmn-diagram.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/rules/no-bpmndi/valid-empty.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/no-bpmndi/valid-no-lanes.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/no-complex-gateway.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-complex-gateway.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-complex-gateway', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-complex-gateway/valid.bpmn') 20 | } 21 | ], 22 | invalid: [ 23 | { 24 | moddleElement: readModdle(__dirname + '/no-complex-gateway/invalid.bpmn'), 25 | report: { 26 | id: 'Gateway', 27 | message: 'Element type is discouraged' 28 | } 29 | } 30 | ] 31 | }); -------------------------------------------------------------------------------- /test/rules/no-complex-gateway/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-complex-gateway/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-disconnected.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-disconnected.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-disconnected', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-disconnected/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/no-disconnected/valid-text-annotation.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/no-disconnected/valid-event-subprocess.bpmn') 26 | }, 27 | { 28 | moddleElement: readModdle(__dirname + '/no-disconnected/valid-adhoc-subprocess.bpmn') 29 | }, 30 | { 31 | moddleElement: readModdle(__dirname + '/no-disconnected/valid-compensation.bpmn') 32 | } 33 | ], 34 | invalid: [ 35 | { 36 | moddleElement: readModdle(__dirname + '/no-disconnected/invalid.bpmn'), 37 | report: { 38 | id: 'Element', 39 | message: 'Element is not connected' 40 | } 41 | } 42 | ] 43 | }); 44 | -------------------------------------------------------------------------------- /test/rules/no-disconnected/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-disconnected/valid-text-annotation.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-gateway-join-fork.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-gateway-join-fork.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-gateway-join-fork', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-gateway-join-fork/valid-fork.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/no-gateway-join-fork/valid-join.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/no-gateway-join-fork/valid-fork-join-task.bpmn') 26 | } 27 | ], 28 | invalid: [ 29 | { 30 | moddleElement: readModdle(__dirname + '/no-gateway-join-fork/invalid.bpmn'), 31 | report: { 32 | id: 'Gateway', 33 | message: 'Gateway forks and joins' 34 | } 35 | } 36 | ] 37 | }); -------------------------------------------------------------------------------- /test/rules/no-implicit-end.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-implicit-end.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-implicit-end', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-implicit-end/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/no-implicit-end/valid-collaboration.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/no-implicit-end/invalid.bpmn'), 28 | report: [ 29 | { 30 | id: 'INTERMEDIATE_THROW_EVENT', 31 | message: 'Element is an implicit end' 32 | }, 33 | { 34 | id: 'TASK', 35 | message: 'Element is an implicit end', 36 | }, 37 | { 38 | id: 'GATEWAY', 39 | message: 'Element is an implicit end', 40 | }, 41 | { 42 | id: 'START_EVENT', 43 | message: 'Element is an implicit end', 44 | }, 45 | { 46 | id: 'SUB_PROCESS', 47 | message: 'Element is an implicit end', 48 | }, 49 | { 50 | id: 'BOUNDARY', 51 | message: 'Element is an implicit end', 52 | }, 53 | { 54 | id: 'INTERMEDIATE_CATCH_EVENT', 55 | message: 'Element is an implicit end' 56 | }, 57 | { 58 | id: 'LINK_CATCH_EVENT', 59 | message: 'Element is an implicit end' 60 | } 61 | ] 62 | } 63 | ] 64 | }); -------------------------------------------------------------------------------- /test/rules/no-implicit-split.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-implicit-split.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-implicit-split', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-implicit-split/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/no-implicit-split/valid-default-conditional-flow.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/no-implicit-split/invalid-event.bpmn'), 28 | report: { 29 | id: 'Element', 30 | message: 'Flow splits implicitly' 31 | } 32 | }, 33 | { 34 | moddleElement: readModdle(__dirname + '/no-implicit-split/invalid-task.bpmn'), 35 | report: { 36 | id: 'Element', 37 | message: 'Flow splits implicitly' 38 | } 39 | }, 40 | { 41 | moddleElement: readModdle(__dirname + '/no-implicit-split/invalid-call-activity.bpmn'), 42 | report: { 43 | id: 'Element', 44 | message: 'Flow splits implicitly' 45 | } 46 | } 47 | ] 48 | }); -------------------------------------------------------------------------------- /test/rules/no-implicit-start.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-implicit-start.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-implicit-start', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-implicit-start/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/no-implicit-start/valid-collaboration.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/no-implicit-start/invalid.bpmn'), 28 | report: [ 29 | { 30 | id: 'END_EVENT', 31 | message: 'Element is an implicit start', 32 | }, 33 | { 34 | id: 'INTERMEDIATE_THROW_EVENT', 35 | message: 'Element is an implicit start' 36 | }, 37 | { 38 | id: 'TASK', 39 | message: 'Element is an implicit start', 40 | }, 41 | { 42 | id: 'GATEWAY', 43 | message: 'Element is an implicit start', 44 | }, 45 | { 46 | id: 'LINK_THROW', 47 | message: 'Element is an implicit start', 48 | }, 49 | { 50 | id: 'INTERMEDIATE_CATCH_EVENT', 51 | message: 'Element is an implicit start', 52 | } 53 | ] 54 | } 55 | ] 56 | }); -------------------------------------------------------------------------------- /test/rules/no-inclusive-gateway.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/no-inclusive-gateway.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('no-inclusive-gateway', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/no-inclusive-gateway/valid.bpmn') 20 | } 21 | ], 22 | invalid: [ 23 | { 24 | moddleElement: readModdle(__dirname + '/no-inclusive-gateway/invalid.bpmn'), 25 | report: { 26 | id: 'Gateway', 27 | message: 'Element type is discouraged' 28 | } 29 | } 30 | ] 31 | }); -------------------------------------------------------------------------------- /test/rules/no-inclusive-gateway/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-inclusive-gateway/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/ignore-missing-bounds.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/invalid-boundary-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/invalid-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_062v7qx 6 | 7 | 8 | Flow_062v7qx 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/invalid-subprocess.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/valid-boundary-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/no-overlapping-elements/valid-data-objects.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/single-blank-start-event.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('single-blank-start-event', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-empty.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-end-event.bpmn') 26 | }, 27 | { 28 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-sub-process.bpmn') 29 | }, 30 | { 31 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-typed.bpmn') 32 | }, 33 | { 34 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-typed-sub-process.bpmn') 35 | }, 36 | { 37 | moddleElement: readModdle(__dirname + '/single-blank-start-event/valid-scopes.bpmn') 38 | } 39 | ], 40 | invalid: [ 41 | { 42 | moddleElement: readModdle(__dirname + '/single-blank-start-event/invalid.bpmn'), 43 | report: { 44 | id: 'Process', 45 | message: 'Process has multiple blank start events' 46 | } 47 | }, 48 | { 49 | moddleElement: readModdle(__dirname + '/single-blank-start-event/invalid-sub-process.bpmn'), 50 | report: { 51 | id: 'SubProcess', 52 | message: 'Sub process has multiple blank start events' 53 | } 54 | } 55 | ] 56 | }); -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/invalid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid-empty.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid-end-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid-typed-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid-typed.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/rules/single-blank-start-event/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/single-event-definition.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/single-event-definition.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('single-event-definition', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/single-event-definition/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/single-event-definition/valid-blank.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/single-event-definition/invalid.bpmn'), 28 | report: { 29 | id: 'Event', 30 | message: 'Event has multiple event definitions', 31 | path: [ 'eventDefinitions' ] 32 | } 33 | } 34 | ] 35 | }); -------------------------------------------------------------------------------- /test/rules/single-event-definition/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/rules/single-event-definition/valid-blank.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/single-event-definition/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/start-event-required.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/start-event-required.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('start-event-required', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/start-event-required/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/start-event-required/valid-sub-process.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/start-event-required/valid-sub-process-sub-types.bpmn') 26 | } 27 | ], 28 | invalid: [ 29 | { 30 | moddleElement: readModdle(__dirname + '/start-event-required/invalid.bpmn'), 31 | report: { 32 | id: 'Process', 33 | message: 'Process is missing start event' 34 | } 35 | }, 36 | { 37 | moddleElement: readModdle(__dirname + '/start-event-required/invalid-sub-process.bpmn'), 38 | report: { 39 | id: 'SubProcess', 40 | message: 'Sub process is missing start event' 41 | } 42 | }, 43 | { 44 | moddleElement: readModdle(__dirname + '/start-event-required/invalid-sub-process-sub-types.bpmn'), 45 | report: [ 46 | { 47 | id: 'TRANSACTION', 48 | message: 'Sub process is missing start event' 49 | }, 50 | { 51 | id: 'EVENT_SUBPROCESS', 52 | message: 'Sub process is missing start event' 53 | } 54 | ] 55 | } 56 | ] 57 | }); -------------------------------------------------------------------------------- /test/rules/start-event-required/invalid-sub-process-sub-types.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/rules/start-event-required/invalid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/rules/start-event-required/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/start-event-required/valid-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/rules/start-event-required/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/sub-process-blank-start-event.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('sub-process-blank-start-event', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/valid-empty.bpmn') 23 | }, 24 | { 25 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/valid-intermediate-event.bpmn') 26 | }, 27 | { 28 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/valid-event-sub-process.bpmn') 29 | } 30 | ], 31 | invalid: [ 32 | { 33 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/invalid.bpmn'), 34 | report: { 35 | id: 'StartEvent', 36 | message: 'Start event must be blank', 37 | path: [ 'eventDefinitions' ] 38 | } 39 | }, 40 | { 41 | moddleElement: readModdle(__dirname + '/sub-process-blank-start-event/invalid-ad-hoc.bpmn'), 42 | report: { 43 | id: 'StartEvent', 44 | message: 'Start event must be blank', 45 | path: [ 'eventDefinitions' ] 46 | } 47 | } 48 | ] 49 | }); -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/invalid-ad-hoc.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/valid-empty.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/valid-event-sub-process.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/valid-intermediate-event.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/rules/sub-process-blank-start-event/valid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/rules/superfluous-gateway.mjs: -------------------------------------------------------------------------------- 1 | import RuleTester from '../../lib/testers/rule-tester.js'; 2 | 3 | import rule from '../../rules/superfluous-gateway.js'; 4 | 5 | import { 6 | readModdle 7 | } from '../../lib/testers/helper.js'; 8 | 9 | import { stubCJS } from '../helper.mjs'; 10 | 11 | const { 12 | __dirname 13 | } = stubCJS(import.meta.url); 14 | 15 | 16 | RuleTester.verify('superfluous-gateway', rule, { 17 | valid: [ 18 | { 19 | moddleElement: readModdle(__dirname + '/superfluous-gateway/valid.bpmn') 20 | }, 21 | { 22 | moddleElement: readModdle(__dirname + '/superfluous-gateway/valid-none-gateway.bpmn') 23 | } 24 | ], 25 | invalid: [ 26 | { 27 | moddleElement: readModdle(__dirname + '/superfluous-gateway/invalid.bpmn'), 28 | report: { 29 | id: 'Gateway_1', 30 | message: 'Gateway is superfluous. It only has one source and target.' 31 | } 32 | } 33 | ] 34 | }); -------------------------------------------------------------------------------- /test/rules/superfluous-termination/invalid.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_1l5qk1h 6 | 7 | 8 | 9 | Flow_1l5qk1h 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/spec/config-spec.mjs: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import fs from 'node:fs'; 4 | 5 | import allConfig from 'bpmnlint/config/all.js'; 6 | import correctnessConfig from 'bpmnlint/config/correctness.js'; 7 | 8 | 9 | describe('config', function() { 10 | 11 | describe('all', function() { 12 | 13 | function getDefinedRules(folder) { 14 | return fs.readdirSync(folder) 15 | .filter(r => r !== 'helper.js') 16 | .map(r => r.split('.')[0]); 17 | } 18 | 19 | it('should contain all lint rules', function() { 20 | 21 | // given 22 | const definedRules = getDefinedRules('rules'); 23 | 24 | // when 25 | const configuredRules = Object.keys(allConfig.rules); 26 | 27 | // then 28 | expect(configuredRules).to.eql(definedRules); 29 | }); 30 | 31 | }); 32 | 33 | 34 | describe('correctness', function() { 35 | 36 | it('should contain relevant lint rules', function() { 37 | 38 | // given 39 | const expectedRules = [ 40 | 'ad-hoc-sub-process', 41 | 'event-sub-process-typed-start-event', 42 | 'link-event', 43 | 'no-duplicate-sequence-flows', 44 | 'sub-process-blank-start-event' 45 | ]; 46 | 47 | // when 48 | const configuredRules = Object.keys(correctnessConfig.rules); 49 | 50 | // then 51 | expect(configuredRules).to.eql(expectedRules); 52 | }); 53 | 54 | }); 55 | 56 | }); -------------------------------------------------------------------------------- /test/spec/diagram.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/spec/index-spec.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | const { 4 | Linter 5 | } = require('bpmnlint'); 6 | 7 | 8 | describe('index', function() { 9 | 10 | it('should CJS export { Linter }', function() { 11 | expect(Linter).to.exist; 12 | }); 13 | 14 | 15 | it('should ES export { Linter }', async function() { 16 | 17 | // when 18 | const { Linter } = await import('bpmnlint'); 19 | 20 | // then 21 | expect(Linter).to.exist; 22 | }); 23 | 24 | }); -------------------------------------------------------------------------------- /test/spec/process-diagram.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_1 6 | 7 | 8 | Flow_1 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "DOM", 5 | "ES2018" 6 | ], 7 | "strict": true, 8 | "checkJs": true, 9 | "noImplicitAny": false, 10 | "resolveJsonModule": true, 11 | "noEmit": true 12 | }, 13 | "include": [ 14 | "./lib/**/*.d.ts", 15 | "./lib/**/*.js", 16 | "./rules/*.js", 17 | "./config/*.js", 18 | "./bin/*.js" 19 | ] 20 | } --------------------------------------------------------------------------------