├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── bin ├── cli.js └── playground.js ├── config └── rollup.run-config.js ├── diagrams └── example.bpmn ├── docs └── screenshot.png ├── example ├── .bpmnlintrc ├── bpmnlint-plugin-local │ ├── .gitignore │ ├── README.md │ ├── docs │ │ └── rules │ │ │ ├── examples │ │ │ ├── no-manual-task-correct.bpmn │ │ │ └── no-manual-task-incorrect.bpmn │ │ │ └── no-manual-task.md │ ├── index.js │ ├── package.json │ ├── rules │ │ ├── no-manual-task.js │ │ └── target-namespace.js │ └── test.js ├── package.json └── simple.bpmn ├── package-lock.json ├── package.json ├── renovate.json ├── rollup.config.js └── src ├── .bpmnlintrc ├── client.js └── index.html /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [ push, pull_request ] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | 7 | strategy: 8 | matrix: 9 | node-version: [20, 22] 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | - name: Use Node.js ${{matrix.node-version}} 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: ${{matrix.node-version}} 18 | cache: 'npm' 19 | - name: Install dependencies 20 | run: npm ci 21 | - name: Build 22 | run: npm run all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to [bpmnlint-playground](https://github.com/bpmn-io/bpmnlint-playground) are documented here. We use [semantic versioning](http://semver.org/) for releases. 4 | 5 | ## Unreleased 6 | 7 | ___Note:__ Yet to be released changes appear here._ 8 | 9 | ## 0.8.0 10 | 11 | * `DEPS`: update to `bpmn-js@18` 12 | * `DEPS`: update to `bpmnlint@10.3.1` 13 | * `DEPS`: update to `bpmn-js-bpmnlint@0.22.3` 14 | 15 | ## 0.7.0 16 | 17 | * `DEPS`: update to `bpmn-js@16` 18 | * `DEPS`: update to `bpmnlint@10` 19 | * `DEPS`: update to `bpmn-js-bpmnlint@0.22.2` 20 | 21 | ## 0.6.0 22 | 23 | * `DEPS`: update to `bpmn-js@14` 24 | * `DEPS`: update to `bpmnlint@9.2` 25 | 26 | ## 0.5.0 27 | 28 | * `DEPS`: update to `bpmn-js@13` 29 | * `DEPS`: update to `bpmnlint@8` 30 | * `DEPS`: update various utility dependencies 31 | 32 | ## 0.4.0 33 | 34 | * `CHORE`: turn into module 35 | * `DEPS`: update to `rollup@3` 36 | 37 | ## 0.3.0 38 | 39 | * `FEAT`: suggest creating `.bpmnlintrc` file for local plug-in 40 | * `CHORE`: color help output 41 | 42 | ## ... 43 | 44 | Check `git log` for earlier history. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bpmnlint-playground 2 | 3 | [![CI](https://github.com/bpmn-io/bpmnlint-playground/actions/workflows/CI.yml/badge.svg)](https://github.com/bpmn-io/bpmnlint-playground/actions/workflows/CI.yml) 4 | 5 | A playground to try out [bpmnlint](https://github.com/bpmn-io/bpmnlint) diagram validation and implement and new rules. 6 | 7 | ![playground](./docs/screenshot.png) 8 | 9 | 10 | ## Run the Playground 11 | 12 | Run the playground in any directory with a `.bpmnlintrc` file: 13 | 14 | ```sh 15 | npx bpmnlint-playground 16 | ``` 17 | 18 | 19 | During plug-in development the playground can be useful to test rules in action: 20 | 21 | ```sh 22 | # create a plugin with the name 23 | npx create-bpmnlint-plugin foo 24 | cd bpmnlint-plugin-foo 25 | 26 | # install dependencies 27 | npm install 28 | 29 | # load recommended rules 30 | echo '{ "extends": "plugin:foo/recommended" }' > .bpmnlintrc 31 | 32 | # run playground 33 | npx bpmnlint-playground 34 | ``` 35 | 36 | Once started, the playground watches your local [`.bpmnlintrc` file](https://github.com/bpmn-io/bpmnlint#configuration), rebuilds it on change, and refreshes the view accordingly. 37 | 38 | 39 | ## Related 40 | 41 | * [bpmnlint](https://github.com/bpmn-io/bpmnlint) 42 | * [bpmnlint-plugin-example](https://github.com/bpmn-io/bpmnlint-plugin-example) 43 | * [create-bpmnlint-plugin](https://github.com/nikku/create-bpmnlint-plugin) 44 | * [bpmn-js-bpmnlint](https://github.com/bpmn-io/bpmn-js-bpmnlint) 45 | 46 | 47 | ## License 48 | 49 | MIT 50 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import path from 'node:path'; 4 | import fs from 'node:fs'; 5 | 6 | const bpmnlintrcPath = path.join(process.cwd(), '.bpmnlintrc'); 7 | 8 | import colors from 'ansi-colors'; 9 | 10 | import colorSupport from 'color-support'; 11 | 12 | colors.enabled = colorSupport.hasBasic; 13 | 14 | 15 | import { run as runPlayground } from './playground.js'; 16 | 17 | const { 18 | magenta, 19 | bold, 20 | grey 21 | } = colors; 22 | 23 | async function getLocalPluginName() { 24 | 25 | try { 26 | const pkg = await import(process.cwd() + '/package.json'); 27 | 28 | const name = pkg.name; 29 | 30 | const match = /^bpmnlint-plugin-(.*)$/.exec(name); 31 | 32 | if (match) { 33 | return match[1]; 34 | } 35 | } catch (err) { 36 | // ignore 37 | } 38 | 39 | return null; 40 | } 41 | 42 | 43 | function cmd(str) { 44 | return bold(magenta(str)); 45 | } 46 | 47 | function highlight(str) { 48 | return bold(str); 49 | } 50 | 51 | async function run() { 52 | 53 | const argv = process.argv.slice(2); 54 | 55 | const [ arg ] = argv; 56 | 57 | if (arg === '--help') { 58 | console.log(`usage: bpmnlint-playground [diagram]`); 59 | 60 | return; 61 | } 62 | 63 | if (!fs.existsSync(bpmnlintrcPath)) { 64 | 65 | const pluginName = await getLocalPluginName(); 66 | 67 | console.error(` 68 | Cannot not find local ${highlight('.bpmnlintrc')} file, please create one: 69 | 70 | ${cmd('npx bpmnlint --init')} 71 | 72 | Refer to https://github.com/bpmn-io/bpmnlint#configuration for details. 73 | `); 74 | 75 | if (pluginName) { 76 | console.error(` 77 | Alternatively, initialize a config based on this plug-ins ${highlight(`recommended`)} rules: 78 | 79 | ${cmd(`echo '{ "extends": "plugin:${pluginName}/recommended" }' > .bpmnlintrc`)} 80 | `) 81 | } else { 82 | console.error(` 83 | Alternatively, create a new bpmnlint-plugin first: 84 | 85 | ${cmd('npx create-bpmnlint-plugin my-plugin')} 86 | 87 | Refer to https://github.com/nikku/create-bpmnlint-plugin#usage for usage. 88 | `); 89 | } 90 | 91 | return process.exit(1); 92 | } 93 | 94 | if (arg) { 95 | 96 | const relativePath = path.relative(process.cwd(), path.resolve(arg)); 97 | 98 | if (relativePath.startsWith('../')) { 99 | console.error(`diagram <${arg}> must be within working directory`); 100 | 101 | return process.exit(1); 102 | } 103 | 104 | if (!fs.existsSync(arg)) { 105 | console.error(`diagram <${arg}> does not exist`); 106 | 107 | return process.exit(1); 108 | } 109 | 110 | } 111 | 112 | console.log('Opening playground...'); 113 | 114 | return runPlayground(arg); 115 | } 116 | 117 | 118 | run().catch(err => { 119 | console.error('failed to start bpmnlint-playground', err); 120 | 121 | process.exit(1); 122 | }); -------------------------------------------------------------------------------- /bin/playground.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import url from 'node:url'; 3 | 4 | import { watch as rollupWatch } from 'rollup'; 5 | 6 | import { 7 | loadConfigFile as loadRollupConfig 8 | } from 'rollup/dist/loadConfigFile.js'; 9 | 10 | import exitHook from 'exit-hook'; 11 | 12 | 13 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 14 | 15 | export async function run(diagram) { 16 | const rollupConfig = path.join(__dirname, '..', 'config', 'rollup.run-config.js'); 17 | 18 | if (diagram) { 19 | // share diagram with app 20 | process.env.BPMNLINT_PLAYGROUND_OPEN_DIAGRAM = diagram; 21 | } 22 | 23 | const { 24 | options, 25 | warnings 26 | } = await loadRollupConfig(rollupConfig, { format: 'es' }); 27 | 28 | warnings.flush(); 29 | 30 | const watchOptions = options.map(options => ({ 31 | ...options, 32 | onwarn(warning) { 33 | 34 | const { 35 | code, 36 | source 37 | } = warning; 38 | 39 | warnings.add(warning); 40 | } 41 | })); 42 | 43 | const watcher = rollupWatch(watchOptions); 44 | 45 | watcher.on('event', event => { 46 | 47 | const { 48 | result, 49 | error, 50 | code 51 | } = event; 52 | 53 | if (result) { 54 | result.close(); 55 | } 56 | 57 | if (code === 'ERROR') { 58 | warnings.add(error); 59 | } 60 | 61 | if (code === 'END') { 62 | console.log(warnings.count > 0 ? 'Bundling failed.' : 'Bundled.'); 63 | 64 | warnings.flush(); 65 | } 66 | }); 67 | 68 | exitHook(() => { 69 | watcher.close(); 70 | }); 71 | } -------------------------------------------------------------------------------- /config/rollup.run-config.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import os from 'node:os'; 4 | import url from 'node:url'; 5 | 6 | import exitHook from 'exit-hook'; 7 | 8 | import livereload from 'rollup-plugin-livereload'; 9 | import serve from 'rollup-plugin-serve'; 10 | 11 | import resolve from '@rollup/plugin-node-resolve'; 12 | import commonjs from '@rollup/plugin-commonjs'; 13 | 14 | import bpmnlint from 'rollup-plugin-bpmnlint'; 15 | 16 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 17 | 18 | 19 | const publicDir = path.join(__dirname, '..', 'public'); 20 | 21 | const tmpPublicDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bpmnlint-playground-')); 22 | 23 | const tmpConfigFile = path.join(tmpPublicDir, 'bpmnlint-config.js'); 24 | 25 | exitHook(() => { 26 | console.log('Cleaning up...'); 27 | 28 | if (fs.existsSync(tmpConfigFile)) { 29 | fs.unlinkSync(tmpConfigFile); 30 | } 31 | 32 | fs.rmdirSync(tmpPublicDir); 33 | }); 34 | 35 | const diagram = process.env.BPMNLINT_PLAYGROUND_OPEN_DIAGRAM; 36 | 37 | export default { 38 | input: '.bpmnlintrc', 39 | output: { 40 | format: 'es', 41 | file: tmpConfigFile 42 | }, 43 | plugins: [ 44 | resolve(), 45 | commonjs(), 46 | bpmnlint(), 47 | serve({ 48 | verbose: false, 49 | openPage: `/?linting=1${diagram ? `&diagram=${encodeURIComponent(diagram)}` : ''}`, 50 | open: true, 51 | contentBase: [ 52 | tmpPublicDir, 53 | process.cwd(), 54 | publicDir 55 | ], 56 | }), 57 | livereload({ 58 | watch: tmpPublicDir, 59 | verbose: false 60 | }) 61 | ], 62 | watch: { 63 | clearScreen: false 64 | } 65 | }; -------------------------------------------------------------------------------- /diagrams/example.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_0t9dyy4 6 | SequenceFlow_1ge46mh 7 | 8 | SequenceFlow_10d6h3a 9 | SequenceFlow_1dzm18n 10 | 11 | 12 | SequenceFlow_1bpznq3 13 | SequenceFlow_10d6h3a 14 | SequenceFlow_0rz4mzx 15 | 16 | 17 | SequenceFlow_0rz4mzx 18 | SequenceFlow_1pol4sw 19 | 20 | 21 | SequenceFlow_0cx35wm 22 | SequenceFlow_05m0kip 23 | 24 | 25 | SequenceFlow_1dzm18n 26 | SequenceFlow_1pol4sw 27 | SequenceFlow_0cx35wm 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | SequenceFlow_1bpznq3 36 | 37 | 38 | 39 | SequenceFlow_05m0kip 40 | 41 | 42 | 43 | 44 | SequenceFlow_1xib75z 45 | 46 | 47 | 48 | 49 | SequenceFlow_1ge46mh 50 | SequenceFlow_1yu5yeq 51 | 52 | 53 | 54 | SequenceFlow_1yu5yeq 55 | SequenceFlow_0zjbpms 56 | SequenceFlow_1udgk24 57 | 58 | 59 | 60 | 61 | SequenceFlow_1udgk24 62 | SequenceFlow_0ftjyrx 63 | SequenceFlow_1vsv7r8 64 | SequenceFlow_0jd8d0i 65 | 66 | 67 | SequenceFlow_0ftjyrx 68 | SequenceFlow_0rkllvh 69 | 70 | 71 | 72 | 73 | SequenceFlow_1vsv7r8 74 | SequenceFlow_0zdc0ci 75 | 76 | 77 | 78 | 79 | SequenceFlow_0jd8d0i 80 | SequenceFlow_10by6md 81 | 82 | 83 | 84 | 85 | SequenceFlow_0zdc0ci 86 | SequenceFlow_10by6md 87 | SequenceFlow_1p7rbgq 88 | 89 | 90 | 91 | 92 | SequenceFlow_1p7rbgq 93 | 94 | 95 | 96 | 97 | 98 | SequenceFlow_1xib75z 99 | SequenceFlow_130hgg8 100 | 101 | 102 | SequenceFlow_1364l2a 103 | SequenceFlow_1qdqk69 104 | SequenceFlow_091wldx 105 | 106 | 107 | 108 | 109 | SequenceFlow_091wldx 110 | SequenceFlow_0zjbpms 111 | 112 | SequenceFlow_17nxcr4 113 | 114 | 115 | SequenceFlow_17nxcr4 116 | SequenceFlow_10zdsna 117 | 118 | 119 | 120 | 121 | SequenceFlow_10zdsna 122 | SequenceFlow_0z16g3i 123 | 124 | 125 | 126 | SequenceFlow_0z16g3i 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | SequenceFlow_1qdqk69 135 | SequenceFlow_0t9dyy4 136 | 137 | 138 | SequenceFlow_0rkllvh 139 | SequenceFlow_130hgg8 140 | SequenceFlow_1364l2a 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmnlint-playground/9d0657915d3d7e3d9e3bb7963efef4ce2e8e15a9/docs/screenshot.png -------------------------------------------------------------------------------- /example/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "bpmnlint:recommended", 4 | "plugin:local/recommended" 5 | ], 6 | "rules": { 7 | "local/no-manual-task": "warn" 8 | } 9 | } -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/README.md: -------------------------------------------------------------------------------- 1 | # bpmnlint-plugin-local 2 | 3 | A bpmlint plug-in based on the [bpmnlint plug-in example](https://github.com/bpmn-io/bpmnlint-plugin-example). 4 | 5 | 6 | ## About 7 | 8 | This plugin contributes [rules](#add-rules) and [configuration](#add-configuration) under the `local` prefix to bpmnlint. 9 | 10 | 11 | ## Add Rules 12 | 13 | The [`./rules`](./rules) folder contains rules that are made available via 14 | this plug-in. Configure them with the `local` prefix in your `.bpmnlintrc`: 15 | 16 | ```json 17 | { 18 | "rules": { 19 | "local/no-manual-task": "warn" 20 | } 21 | } 22 | ``` 23 | 24 | Checkout [`./test.js`](./test.js) to learn how to test your rules. 25 | 26 | 27 | ## Add Configuration 28 | 29 | As part of the [`./index.js`](./index.js) the plug-in exposes configurations 30 | to extend from using `extends` in the bpmnlint configuration: 31 | 32 | ```json 33 | { 34 | "extends": [ 35 | "bpmnlint:recommended", 36 | "plugin:local/recommended" 37 | ] 38 | } 39 | ``` -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/docs/rules/examples/no-manual-task-correct.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/docs/rules/examples/no-manual-task-incorrect.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/docs/rules/no-manual-task.md: -------------------------------------------------------------------------------- 1 | # No Manual Task (no-manual-task) 2 | 3 | Checks that the diagram does not contain manual tasks that have no execution semantics. 4 | 5 | Example of __incorrect__ usage of this rule: 6 | 7 | ![Incorrect usage example](./examples/no-manual-task-incorrect.png) 8 | 9 | Cf. [`no-manual-task-incorrect.bpmn`](./examples/no-manual-task-incorrect.bpmn). 10 | 11 | 12 | Example of __correct__ usage of this rule: 13 | 14 | ![Correct usage example](./examples/no-manual-task-correct.png) 15 | 16 | Cf. [`no-manual-task-correct.bpmn`](./examples/no-manual-task-correct.bpmn). 17 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configs: { 3 | recommended: { 4 | rules: { 5 | 'target-namespace': 'error' 6 | } 7 | }, 8 | all: { 9 | rules: { 10 | 'target-namespace': 'warn', 11 | 'no-manual-task': 'warn' 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-plugin-local", 3 | "version": "0.0.0", 4 | "description": "The bpmnlint local plug-in", 5 | "main": "index.js", 6 | "scripts": { 7 | "all": "npm test", 8 | "test": "mocha test.js" 9 | }, 10 | "keywords": [ 11 | "bpmnlint", 12 | "plugin" 13 | ], 14 | "devDependencies": { 15 | "bpmnlint": "^11.0.0", 16 | "chai": "^4.2.0", 17 | "mocha": "^10.0.0" 18 | }, 19 | "dependencies": { 20 | "bpmnlint-utils": "^1.0.2" 21 | }, 22 | "files": [ 23 | "rules", 24 | "index.js" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/rules/no-manual-task.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | 6 | /** 7 | * Rule that reports manual tasks being used. 8 | */ 9 | module.exports = function() { 10 | 11 | function check(node, reporter) { 12 | if (is(node, 'bpmn:ManualTask')) { 13 | reporter.report(node.id, 'Element has disallowed type bpmn:ManualTask'); 14 | } 15 | } 16 | 17 | return { 18 | check: check 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/rules/target-namespace.js: -------------------------------------------------------------------------------- 1 | const { 2 | is 3 | } = require('bpmnlint-utils'); 4 | 5 | 6 | /** 7 | * Rule that reports missing targetNamespace on bpmn:Definitions. 8 | */ 9 | module.exports = function() { 10 | 11 | function check(node, reporter) { 12 | if (is(node, 'bpmn:Definitions') && !node.targetNamespace) { 13 | reporter.report(node.id, 'Element is missing targetNamespace'); 14 | } 15 | } 16 | 17 | return { 18 | check: check 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /example/bpmnlint-plugin-local/test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | const { createModdle } = require('bpmnlint/lib/testers/helper'); 4 | 5 | const RuleTester = require('bpmnlint/lib/testers/rule-tester'); 6 | 7 | const manualTaskRule = require('./rules/no-manual-task'); 8 | const targetNamespaceRule = require('./rules/target-namespace'); 9 | 10 | 11 | RuleTester.verify('no-manual-task', manualTaskRule, { 12 | valid: [ 13 | { 14 | moddleElement: createModdle( 15 | '', 16 | 'bpmn:StartEvent' 17 | ) 18 | } 19 | ], 20 | invalid: [ 21 | { 22 | moddleElement: createModdle( 23 | '', 24 | 'bpmn:ManualTask' 25 | ), 26 | report: { 27 | id: 'manualTask', 28 | message: 'Element has disallowed type bpmn:ManualTask' 29 | } 30 | } 31 | ] 32 | }); 33 | 34 | 35 | RuleTester.verify('target-namespace', targetNamespaceRule, { 36 | valid: [ 37 | { 38 | moddleElement: createModdle( 39 | '', 40 | ) 41 | } 42 | ], 43 | invalid: [ 44 | { 45 | moddleElement: createModdle( 46 | '', 47 | ), 48 | report: { 49 | id: 'definitions', 50 | message: 'Element is missing targetNamespace' 51 | } 52 | } 53 | ] 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-playground-test", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "bpmnlint playground test project", 6 | "author": { 7 | "name": "Nico Rehwaldt", 8 | "url": "https://github.com/nikku" 9 | }, 10 | "scripts": { 11 | "all": "run-s clean scaffold start", 12 | "clean": "npm uninstall bpmnlint-plugin-local && rm -rf bpmnlint-plugin-local", 13 | "scaffold": "npx create-bpmnlint-plugin local && npm install bpmnlint-plugin-local@./bpmnlint-plugin-local", 14 | "start": "bpmnlint-playground simple.bpmn" 15 | }, 16 | "license": "MIT", 17 | "devDependencies": { 18 | "bpmnlint": "^11.0.0", 19 | "bpmnlint-playground": "..", 20 | "del-cli": "^6.0.0", 21 | "npm-run-all2": "^8.0.0" 22 | }, 23 | "dependencies": { 24 | "bpmnlint-plugin-local": "file:bpmnlint-plugin-local" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/simple.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SequenceFlow_0z7a9lp 6 | 7 | 8 | SequenceFlow_0z7a9lp 9 | SequenceFlow_10nkxkj 10 | 11 | 12 | 13 | SequenceFlow_10nkxkj 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmnlint-playground", 3 | "version": "0.8.0", 4 | "description": "A bpmnlint playground", 5 | "type": "module", 6 | "bin": { 7 | "bpmnlint-playground": "./bin/cli.js" 8 | }, 9 | "scripts": { 10 | "all": "run-s bundle", 11 | "bundle": "rollup -c --bundleConfigAsCjs", 12 | "start": "(cd example && npm start)", 13 | "prepublishOnly": "run-s bundle", 14 | "test": "echo 'no tests yet <3' && exit 0" 15 | }, 16 | "author": { 17 | "name": "Nico Rehwaldt", 18 | "url": "https://github.com/nikku" 19 | }, 20 | "engines": { 21 | "node": ">= 20" 22 | }, 23 | "license": "MIT", 24 | "devDependencies": { 25 | "bpmn-js": "^18.0.0", 26 | "bpmn-js-bpmnlint": "^0.22.3", 27 | "bpmnlint": "^11.0.0", 28 | "downloadjs": "^1.4.7", 29 | "file-drops": "^0.5.0", 30 | "npm-run-all2": "^8.0.0", 31 | "rollup-plugin-copy": "^3.5.0", 32 | "rollup-plugin-string": "^3.0.0" 33 | }, 34 | "dependencies": { 35 | "@rollup/plugin-commonjs": "^28.0.1", 36 | "@rollup/plugin-node-resolve": "^16.0.0", 37 | "ansi-colors": "^4.1.3", 38 | "color-support": "^1.1.3", 39 | "exit-hook": "^3.2.0", 40 | "rollup": "^4.30.1", 41 | "rollup-plugin-bpmnlint": "^0.4.1", 42 | "rollup-plugin-livereload": "^2.0.5", 43 | "rollup-plugin-serve": "^3.0.0" 44 | }, 45 | "files": [ 46 | "bin", 47 | "config", 48 | "public" 49 | ], 50 | "workspaces": [ 51 | "example" 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>bpmn-io/renovate-config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import copy from 'rollup-plugin-copy'; 4 | 5 | import { string } from 'rollup-plugin-string'; 6 | 7 | import bpmnlint from 'rollup-plugin-bpmnlint'; 8 | 9 | const publicDir = 'public'; 10 | 11 | export default [ 12 | { 13 | input: 'src/client.js', 14 | external: [ './bpmnlint-config.js'], 15 | output: { 16 | sourcemap: true, 17 | format: 'iife', 18 | name: 'BpmnlintPlayground', 19 | file: `${publicDir}/client.js` 20 | }, 21 | plugins: [ 22 | string({ 23 | include: '**/*.bpmn' 24 | }), 25 | resolve(), 26 | commonjs(), 27 | copy({ 28 | targets: [ 29 | { src: 'src/index.html', dest: publicDir }, 30 | { src: 'node_modules/bpmn-js/dist/assets', dest: `${publicDir}/assets/bpmn-js` }, 31 | { src: 'node_modules/bpmn-js-bpmnlint/dist/assets', dest: `${publicDir}/assets/bpmn-js-bpmnlint` } 32 | ] 33 | }) 34 | ] 35 | }, 36 | { 37 | input: 'src/.bpmnlintrc', 38 | output: { 39 | sourcemap: true, 40 | format: 'es', 41 | file: `${publicDir}/bpmnlint-config.js` 42 | }, 43 | plugins: [ 44 | resolve(), 45 | commonjs(), 46 | bpmnlint() 47 | ] 48 | } 49 | ]; 50 | -------------------------------------------------------------------------------- /src/.bpmnlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "bpmnlint:recommended" 3 | } -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import lintModule from 'bpmn-js-bpmnlint'; 2 | 3 | import BpmnModeler from 'bpmn-js/lib/Modeler'; 4 | 5 | import defaultDiagramXML from '../diagrams/example.bpmn'; 6 | 7 | import fileDrop from 'file-drops'; 8 | 9 | import download from 'downloadjs'; 10 | 11 | 12 | function loadConfig() { 13 | return import('./bpmnlint-config.js').catch(err => { 14 | console.log(err); 15 | 16 | return {}; 17 | }); 18 | } 19 | 20 | function run(bpmnlintConfig) { 21 | 22 | var modeler = new BpmnModeler({ 23 | container: '#canvas', 24 | additionalModules: [ 25 | lintModule 26 | ], 27 | linting: { 28 | bpmnlint: bpmnlintConfig, 29 | active: getUrlParam('linting') 30 | } 31 | }); 32 | 33 | loadDiagram().then(diagramXML => modeler.importXML(diagramXML)).catch(err => { 34 | window.alert(err.message); 35 | }); 36 | 37 | modeler.on('linting.toggle', function(event) { 38 | 39 | var active = event.active; 40 | 41 | setUrlParam('linting', active); 42 | }); 43 | 44 | modeler.on('import.parse.start', function(event) { 45 | var xml = event.xml; 46 | 47 | window.localStorage.setItem('diagramXML', xml); 48 | }); 49 | 50 | var dndHandler = fileDrop('Drop BPMN Diagram here.', function(files) { 51 | modeler.importXML(files[0].contents); 52 | }); 53 | 54 | document.querySelector('#download-button').addEventListener('click', function(event) { 55 | 56 | modeler.saveXML({ format: true }, function(err, xml) { 57 | if (!err) { 58 | download(xml, 'diagram.bpmn', 'application/xml'); 59 | } 60 | }); 61 | }); 62 | 63 | document.querySelector('body').addEventListener('dragover', dndHandler); 64 | 65 | } 66 | 67 | 68 | loadConfig().then(run); 69 | 70 | // helpers ///////////////////////////////// 71 | 72 | function setUrlParam(name, value) { 73 | 74 | var url = new URL(window.location.href); 75 | 76 | if (value) { 77 | url.searchParams.set(name, 1); 78 | } else { 79 | url.searchParams.delete(name); 80 | } 81 | 82 | window.history.replaceState({}, null, url.href); 83 | } 84 | 85 | function getUrlParam(name) { 86 | var url = new URL(window.location.href); 87 | 88 | return url.searchParams.get(name); 89 | } 90 | 91 | async function loadDiagram() { 92 | 93 | var diagramURL = getUrlParam('diagram'); 94 | 95 | if (diagramURL) { 96 | return fetch(diagramURL).then(r => { 97 | if (r.ok) { 98 | return r.text(); 99 | } 100 | 101 | throw new Error('failed to load ' + diagramURL); 102 | }); 103 | } 104 | 105 | var diagramXML = window.localStorage.getItem('diagramXML'); 106 | 107 | if (diagramXML) { 108 | return diagramXML; 109 | } 110 | 111 | return defaultDiagramXML; 112 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 105 | 106 | 107 | 108 | 109 | 110 | bpmnlint playground 111 | 112 | 113 |
114 | 115 |
Hint: Drop a BPMN file to validate it.
116 | 117 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | --------------------------------------------------------------------------------