Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ or `fix(layout): keep the label of a moved gateway`.

- [ ] `npm run typecheck` passes
- [ ] `npm run gate` passes
- [ ] `src/guide.ts` and the matching page in `docs/` are updated (only if a command, option or output changed)
- [ ] `src/report/guide.ts` and the matching page in `docs/` are updated (only if a command, option or output changed)
- [ ] PR title is a changelog-worthy Conventional-Commit summary

## Linked issue
Expand Down
31 changes: 27 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,32 @@ them on a private corpus without copying it into the repository, are described i

## How the package is laid out

- `src/`: the CLI (`cli.ts`), the in-memory API (`api.ts`, exported from `index.ts`), the ops, the
layout engine and the Camunda 7 / 8 rules (`platform/`). `src/guide.ts` is the source of
`bpmn guide`, `bpmn kinds` and `--help`.
- `src/`: the three entries (`cli.ts`, `api.ts`, the browser-safe `index.ts`) and one folder per
stage of the pipeline. `src/report/guide.ts` is the source of `bpmn guide`, `bpmn kinds` and
`--help`.

| folder | holds |
| --- | --- |
| `shared/` | errors and the debug sink, used everywhere |
| `model/` | the moddle tree, kinds, `Doc`, the `ChangeSet` of a mutation |
| `ids/` | id words and slugs, the file's id style, the declared id conventions |
| `xml/` | the text-preserving write, the positional XML reader, the encoding |
| `platform/` | Camunda 7 / 8 knowledge; `profiles/` holds the engine rules that load on demand |
| `ops/` | the semantic edits (`add`, `connect`, `set`, ...) and the ops JSON of `apply` |
| `diagram/` | the drawing (BPMN DI) and the geometry building blocks the engines share |
| `layout/` | the layout engines: `clean/` redraws, `incremental.ts` keeps a hand-made drawing |
| `format/` | the format ops (`place`, `align`, `color`, `label`, `route`, `space`, `tidy`, `compact`) |
| `render/` | the SVG of a diagram |
| `validate/` | structural validation, the validator hook, the bpmnlint validator |
| `query/` | the read views: `show`, `find`, `--around`, `--context`, the selectors |
| `pipeline/` | the transactions: `mutateDoc`, `layoutDoc`, `checkDoc` |
| `report/` | what a command prints, as text and JSON, and the guide |
| `node/` | the Node-only layer (`@miragon/bpmn-cli/node`): files, the `.bpmnlintrc`, PNG |

Where new code goes: a change of the model's semantics is an op (`ops/`); reading the model
without changing it is a view (`query/`); deciding positions is `layout/`, or `format/` when a
user asks for it; geometry more than one engine needs is `diagram/`; text or JSON for the user
is `report/`; anything that touches the file system or `process` is `node/` or `cli.ts`.
- `test/`: the vitest suite. It also pins the documentation: the README quick start and the
examples in `docs/` must match what the CLI prints. `test/setup.ts` clears `BPMN_KEEP_FLOW_IDS`
of your shell before every file, so the tests see the CLI's defaults.
Expand Down Expand Up @@ -85,7 +108,7 @@ names of zeebe content, see [Camunda 8](docs/camunda.md#camunda-8)).
generated from them via [release-please](https://github.com/googleapis/release-please), so a
`feat` or `fix` title is what users read in the release notes.
- **Documentation moves with the code.** A change to a command, an option or an output also
changes `src/guide.ts` and the matching page in `docs/`.
changes `src/report/guide.ts` and the matching page in `docs/`.

## Before opening a PR

Expand Down
12 changes: 6 additions & 6 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ npx vitest run test/isomorphic.test.ts
loads, naming both (Rollup / Vite would put that module into the host's
entry chunk and make the rule chunk import the entry chunk back: a host
whose entry module awaits a Camunda 7 / 8 call at top level would hang;
the rules get the core helpers from `src/platform/rule-kit.ts`); and
the rules get the core helpers from `src/platform/profiles/rule-kit.ts`); and
when a size is over its budget in
`tools/iso/budget.json` (KB = 1024 bytes; set to the measured size plus
about 2 %: raise one only in the change that needs it, with the reason in
Expand All @@ -421,7 +421,7 @@ npx vitest run test/isomorphic.test.ts
installed `camunda-bpmn-moddle`; regenerate it with
`node tools/gen-camunda-descriptor.mjs`.
- The same file bundles `src/index.ts` as an ESM split bundle (what Vite or
esbuild make of the `import()` in `src/platform/profile.ts`), writes a
esbuild make of the `import()` in `src/platform/profiles/profile.ts`), writes a
fresh copy per case, loads it with Node's own loader and records which
chunk is evaluated: a plain model (also with a host validator, whose error, pre-existing
error and covered lint warning come out like Node's) and
Expand Down Expand Up @@ -596,7 +596,7 @@ draw the engine corpus with the same CLI.
## Roundtrip fidelity

A write changes the text of the elements it changed and nothing else
(src/preserve.ts, src/mirror.ts; [contract.md](contract.md#what-a-write-changes)). Two checks
(src/xml/preserve.ts, src/model/mirror.ts; [contract.md](contract.md#what-a-write-changes)). Two checks
keep it that way:

- `test/roundtrip.test.ts` (part of `npx vitest run`): the synthetic
Expand Down Expand Up @@ -628,7 +628,7 @@ not from the writer: a file without a diagram is drawn, the incremental
layout completes missing DI (an edge, a shape, the plane's `bpmnElement`)
and routes a line that does not reach its ends again (`detachedEdge`).
With `--no-layout` every no-op must be byte-identical; a `fall-backs` count
above zero means preserve.ts could not keep a file's text (the note says
above zero means xml/preserve.ts could not keep a file's text (the note says
why) and deserves a synthetic fixture.

## Speaking ids
Expand Down Expand Up @@ -664,7 +664,7 @@ flow between two lanes).

## Engine checks (Camunda 7)

The Camunda 7 profile of `bpmn validate` (`src/platform/c7.ts`) states for
The Camunda 7 profile of `bpmn validate` (`src/platform/profiles/c7.ts`) states for
every rule what the engines do. `test/c7-profile.test.ts` holds one synthetic
model per rule (82 models) with the expected codes and the engine verdict;
by default it checks only the profile. To re-check the verdicts against live
Expand Down Expand Up @@ -704,7 +704,7 @@ element by element.

## Engine checks (Camunda 8)

The Camunda 8 profile (`src/platform/c8.ts`) states for every rule what
The Camunda 8 profile (`src/platform/profiles/c8.ts`) states for every rule what
Camunda 8.9 does; placement and known names come from the Zeebe descriptor
(`src/platform/zeebe.ts`, the inlined `zeebe-bpmn-moddle`; regenerate with
`node tools/gen-camunda-descriptor.mjs`, `test/isomorphic.test.ts` fails
Expand Down
36 changes: 18 additions & 18 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
* whether it is byte-identical to the input (nothing to save), and what the
* CLI prints with --json (changes, warnings, layout, validation, import
* warnings; the model view with `show`), without the XML. renderMutation
* (src/report.ts) gives the CLI's text for it. The CLI's guards apply: a
* (src/report/command-report.ts) gives the CLI's text for it. The CLI's guards apply: a
* lossy import (E_IMPORT_LOSSY), validation errors the ops would introduce
* (E_VALIDATION) and content a retype would delete (E_WOULD_DROP_CONTENT)
* are refused unless `force`; the layout modes, the format ops and the
* platform profile's new findings work as in the CLI, a host's own
* validators (`validators`) run in the same transaction, `keepFlowIds`
* keeps flow ids like `--keep-flow-ids`, and `conventions` are the declared
* id conventions new ids follow (src/conventions.ts; a host that lints with
* id conventions new ids follow (src/ids/conventions.ts; a host that lints with
* element-id-naming builds them from the same configuration). The result
* keeps the input's text wherever the ops changed nothing (the
* text-preserving step).
Expand All @@ -37,25 +37,25 @@
* candidates, hint, op), exactly what the CLI prints with --json; the input
* string is never modified.
*/
import { parseOps } from './batch.js';
import type { DebugSink } from './debug.js';
import { parseOps } from './ops/batch.js';
import type { DebugSink } from './shared/debug.js';
import { layoutProblems, type LayoutMetrics } from './diagram/metrics.js';
import { layoutView, type LayoutView } from './diagram/view.js';
import { aroundView, elementContext, type AroundView, type ElementContext } from './context.js';
import type { BpmnlintSeverity } from './bpmnlint.js';
import { conventionsView, type ConventionsView, type IdConventions, type Undeclared } from './conventions.js';
import { Doc, type NewDocOptions } from './document.js';
import { CliError, usageError } from './errors.js';
import { renderAround, renderContext, renderDetail, renderLayoutView, renderView } from './format.js';
import { assertKindToken } from './kinds.js';
import type { LayoutEngine } from './layout.js';
import { layoutView, type LayoutView } from './query/layout-view.js';
import { aroundView, elementContext, type AroundView, type ElementContext } from './query/context.js';
import type { BpmnlintSeverity } from './validate/bpmnlint-validator.js';
import { conventionsView, type ConventionsView, type IdConventions, type Undeclared } from './ids/conventions.js';
import { Doc, type NewDocOptions } from './model/document.js';
import { CliError, usageError } from './shared/errors.js';
import { renderAround, renderContext, renderDetail, renderLayoutView, renderView } from './report/view-text.js';
import { assertKindToken } from './model/kinds.js';
import type { LayoutEngine } from './layout/index.js';
import { listAllExtensions, type ExtensionInfo } from './ops/ext.js';
import type { Op } from './ops/types.js';
import { assertLayoutOptions, assertLossless, checkDoc, layoutDoc, mutateDoc, type LayoutMode, type MutationResult } from './pipeline.js';
import type { PlatformChoice } from './platform/profile.js';
import { mutationReport, validationReport, type MutationReport, type ValidationReport } from './report.js';
import type { Validator, ValidatorFinding } from './validators.js';
import { buildView, elementDetail, findElements, scopeView, type ElementDetail, type FindHit, type ModelView } from './view.js';
import { assertLayoutOptions, assertLossless, checkDoc, layoutDoc, mutateDoc, type LayoutMode, type MutationResult } from './pipeline/index.js';
import type { PlatformChoice } from './platform/profiles/profile.js';
import { mutationReport, validationReport, type MutationReport, type ValidationReport } from './report/command-report.js';
import type { Validator, ValidatorFinding } from './validate/validators.js';
import { buildView, elementDetail, findElements, scopeView, type ElementDetail, type FindHit, type ModelView } from './query/model-view.js';

/** Options of the writing functions (the CLI's mutation flags). */
export interface EditOptions {
Expand Down
34 changes: 17 additions & 17 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { basename, dirname, extname, join } from 'node:path';
import { Command, CommanderError, type Command as CommandType, InvalidArgumentError, Option } from 'commander';
import { BOUNDARY_SIDE_VALUES, COLOR_VALUES, LABEL_SIDE_VALUES, parseOps, SIDE_VALUES } from './batch.js';
import { setLayoutDebug } from './debug.js';
import { BOUNDARY_SIDE_VALUES, COLOR_VALUES, LABEL_SIDE_VALUES, parseOps, SIDE_VALUES } from './ops/batch.js';
import { setLayoutDebug } from './shared/debug.js';
import { layoutProblems } from './diagram/metrics.js';
import { changeHighlight, RENDER_MAX_SIZE, renderSvg, scaleText, type RenderHighlight, type RenderOptions, type RenderProblem } from './diagram/render.js';
import { changeHighlight, RENDER_MAX_SIZE, renderSvg, scaleText, type RenderHighlight, type RenderOptions, type RenderProblem } from './render/index.js';
import { renderShown, viewDoc, type ViewOptions } from './api.js';
import { AROUND_DEFAULT_DEPTH } from './context.js';
import { conventionsView, renderConventions, type Undeclared } from './conventions.js';
import { assertProcessId, assertTarget, Doc } from './document.js';
import { CliError, ioError, isCliError, usageError, type Warning } from './errors.js';
import { renderExtensionList, renderFind, renderMetrics, renderView } from './format.js';
import { guideShort, guideText, guideTopic, KINDS_SECTION_NAMES, kindsJson, kindsSectionJson, kindsSectionText, kindsSections, kindsText } from './guide.js';
import { assertKindToken } from './kinds.js';
import { AROUND_DEFAULT_DEPTH } from './query/context.js';
import { conventionsView, renderConventions, type Undeclared } from './ids/conventions.js';
import { assertProcessId, assertTarget, Doc } from './model/document.js';
import { CliError, ioError, isCliError, usageError, type Warning } from './shared/errors.js';
import { renderExtensionList, renderFind, renderMetrics, renderView } from './report/view-text.js';
import { guideShort, guideText, guideTopic, KINDS_SECTION_NAMES, kindsJson, kindsSectionJson, kindsSectionText, kindsSections, kindsText } from './report/guide.js';
import { assertKindToken } from './model/kinds.js';
import { projectIdConventions } from './node/bpmnlint.js';
import { assertNewFile, checkFile, checkLoadedDoc, decodeXmlBytes, layoutDocToFile, layoutFile, mutateDocToFile, mutateFile, readDoc, readXml, type FileMutationOptions } from './node/files.js';
import { renderPng } from './node/render.js';
import { listAllExtensions } from './ops/ext.js';
import { parseSpaceAmount, type AddOp, type AlignOp, type ColorOp, type CompactOp, type ConnectOp, type ExtOp, type LabelOp, type MoveOp, type Op, type OrderOp, type PlaceOp, type RemoveOp, type RetypeOp, type RouteOp, type SetOp, type SpaceOp, type TidyOp, type TriggerOptions } from './ops/types.js';
import { assertLossless, LAYOUT_MODES, type LayoutMode, type MutationResult } from './pipeline.js';
import { PLATFORM_CHOICES, type PlatformChoice } from './platform/profile.js';
import { advisoryValidators, mutationReport, mutationSummary, mutationWarnings, renderMutation, renderSummary, renderValidation, validationReport, validatorTag, type MutationReport, type MutationSummary } from './report.js';
import { findElements } from './view.js';
import { assertLossless, LAYOUT_MODES, type LayoutMode, type MutationResult } from './pipeline/index.js';
import { PLATFORM_CHOICES, type PlatformChoice } from './platform/profiles/profile.js';
import { advisoryValidators, mutationReport, mutationSummary, mutationWarnings, renderMutation, renderSummary, renderValidation, validationReport, validatorTag, type MutationReport, type MutationSummary } from './report/command-report.js';
import { findElements } from './query/model-view.js';

/** The package version (dist/cli.js and src/cli.ts both sit one level below package.json). */
const VERSION = (JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { version: string }).version;
Expand Down Expand Up @@ -82,7 +82,7 @@ function printJson(value: unknown, pretty?: boolean, out: Out = process.stdout):
}

/**
* The result of a mutation (src/report.ts: the same for the CLI and the
* The result of a mutation (src/report/command-report.ts: the same for the CLI and the
* in-memory API); --render draws the result (its own block / `render` key);
* --strict exits 5 on any warning (process.exitCode, like every exit code:
* process.exit would cut off what a pipe has not taken yet, beyond 64 KB on macOS).
Expand Down Expand Up @@ -444,7 +444,7 @@ function kindArg(value: string): string {
return value;
}

/* The same rules `apply` enforces on ops JSON (batch.ts), spelled with CLI flags. */
/* The same rules `apply` enforces on ops JSON (ops/batch.ts), spelled with CLI flags. */

const PLACEMENT_FLAGS = ['after', 'before', 'flow', 'in', 'on'] as const;
const FLOW_FLAGS: Record<string, string> = { flowName: '--flow-name', flowId: '--flow-id', condition: '--condition', language: '--language', default: '--default' };
Expand Down Expand Up @@ -908,7 +908,7 @@ async function runFormat(file: string, op: Op, o: RawOpts): Promise<void> {
}, opts.json);
}

/** --path <fromId> <toId> [--via <flowId...>], --kind <kind>, --branch <flowId> (src/diagram/select.ts). */
/** --path <fromId> <toId> [--via <flowId...>], --kind <kind>, --branch <flowId> (src/query/select.ts). */
function withSelectors(cmd: CommandType, what: string): CommandType {
return cmd
.option('--path <ids...>', `${what} every node${what === 'colour' ? ' and flow' : ''} on the shortest sequence-flow path <fromId> <toId>`)
Expand Down
2 changes: 1 addition & 1 deletion src/diagram/containers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* same border as before, in the same order, with their labels.
* Every function returns the ids it moved or resized.
*/
import { SIZES } from '../layout/types.js';
import { SIZES } from './types.js';
import { bottom, copyBox, cy, median, right, type Box } from './geom.js';
import { boundariesOf, contentOf, isLeaf, type DEdge, type DShape, type Plane, type Semantics } from './plane.js';

Expand Down
10 changes: 5 additions & 5 deletions src/diagram/drawn-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* draws or keys by id (processes, collaborations, participants, lanes,
* flow nodes, sequence and message flows, data object / store references,
* text annotations, groups, associations, data associations) that has no
* id one in the file's id style (src/idstyle.ts; `allocate` claims it in
* id one in the file's id style (src/ids/style.ts; `allocate` claims it in
* the caller's registry, by default a registry of every id of `defs`), and
* returns them in document order: its name, else its kind and `In <parent>`;
* a connection names its ends. Nodes and containers get theirs first, so
Expand All @@ -22,10 +22,10 @@
* id-less elements it changes nothing. The incremental layout does not
* need it: it never draws an element without id (the element keeps no DI).
*/
import { IdRegistry } from '../ids.js';
import { contextOf as placeOf, flowRequest, IdStyle, kindRequest, labelOf, typeRequest, type IdRequest } from '../idstyle.js';
import { kindLabel, kindOf, triggerOf } from '../kinds.js';
import { indexById, is, isDiElement, walk, type El } from '../model.js';
import { IdRegistry } from '../ids/slugs.js';
import { contextOf as placeOf, flowRequest, IdStyle, kindRequest, labelOf, typeRequest, type IdRequest } from '../ids/style.js';
import { kindLabel, kindOf, triggerOf } from '../model/kinds.js';
import { indexById, is, isDiElement, walk, type El } from '../model/moddle.js';

/** Element types a full redraw draws (shape, edge or plane) or keys by id. */
const DRAWN = [
Expand Down
2 changes: 1 addition & 1 deletion src/diagram/geom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* about moddle or DI. Boxes are `{x, y, width, height}` in diagram pixels,
* y grows downwards.
*/
import type { Box, Point } from '../layout/types.js';
import type { Box, Point } from './types.js';

export type { Box, Point };

Expand Down
4 changes: 2 additions & 2 deletions src/diagram/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* the plane elements; nothing else runs then.
* - membersOf(groups): element id -> the ids of the groups that held it,
* outermost (largest old box) first; the clean engine keeps the parts of
* a scope no flow connects together by it (layout/place.ts).
* a scope no flow connects together by it (layout/clean/place.ts).
* - redrawGroups(moddle, defs, groups), after the redraw, in the order of
* the old boxes' areas (smallest first, so a group inside another one is
* drawn first and counts as its member): a group whose element is gone,
Expand Down Expand Up @@ -56,7 +56,7 @@
* materialise empty collections on the semantic elements).
*/
import type { BpmnModdle } from 'bpmn-moddle';
import { addTo, is, type El } from '../model.js';
import { addTo, is, type El } from '../model/moddle.js';
import { bottom, containsPoint, cx, cy, inside, overlaps, right, union, unionAll, type Box, type Point } from './geom.js';
import { idOf, raw, rawList } from './plane.js';

Expand Down
4 changes: 2 additions & 2 deletions src/diagram/labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
* above / below the longest horizontal segment, left / right of the
* longest vertical one (undefined when the flow has none).
*/
import { labelSize } from '../layout/types.js';
import { is } from '../model.js';
import { labelSize } from './types.js';
import { is } from '../model/moddle.js';
import { bottom, cx, cy, inside, overlaps, right, segmentHits, type Box } from './geom.js';
import { frameOf, raw, titleOf, type DEdge, type DShape, type Plane } from './plane.js';
import { attachedSide } from './router.js';
Expand Down
Loading
Loading