@jarenjs/mermaid 0.46.5 → 0.49.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -158,13 +158,73 @@ flowchart LR
158
158
  Fully laid out: **flowchart**, **sequence**, **state** (through the
159
159
  flowchart engine via an adapter — states as rounded nodes, `[*]` as a
160
160
  filled start dot and an end ring, verbatim transition labels on the
161
- edges; composite states stay flattened in v1). Structured panels /
162
- chart: **class**, **ER**, **gantt**, **pie**. Parse-accepted with an
161
+ edges; composite states stay flattened in v1) and **gantt** (a real time
162
+ axis — see below). Structured panels / chart: **class**, **ER**,
163
+ **pie**. Parse-accepted with an
163
164
  honest "not yet laid out" placeholder: mindmap, gitGraph, journey,
164
165
  timeline, quadrantChart, requirement. The benchmark's coverage scorecard reports this
165
166
  without hiding gaps.
166
167
 
167
168
 
169
+ ## Gantt: the date directives are interpreted
170
+
171
+ A Gantt diagram is parsed into a **resolved schedule** and rendered as a
172
+ timeline — an aligned time axis, section bands, bars, milestone
173
+ diamonds, dependency connectors and shaded excluded days:
174
+
175
+ ```mermaid
176
+ gantt
177
+ title Release plan
178
+ dateFormat YYYY-MM-DD
179
+ excludes weekends
180
+ tickInterval 1week
181
+ section Build
182
+ Design : done, a1, 2024-01-04, 3d
183
+ Implement : active, a2, after a1, 5d
184
+ section Ship
185
+ Review : crit, a3, after a2, 2d
186
+ Launch : milestone, m1, after a3, 0d
187
+ ```
188
+
189
+ Every directive means something. `dateFormat` compiles to a strict date
190
+ parser, `axisFormat` to the axis labels, `tickInterval` and `weekday` to
191
+ the tick boundaries, and `excludes`/`weekend` to a working calendar that
192
+ moves a duration-derived end past the days it skips. Tick positions come
193
+ from `@jarenjs/core/dates` — the same ladder the chart component's time
194
+ axis reads — so a timeline and a chart cannot disagree about where a
195
+ week begins.
196
+
197
+ Each task carries `{ id, flags, start, end, duration, after, line }`
198
+ beside its raw `info`, with `start`/`end` as epoch milliseconds and the
199
+ interval half-open. The grammar, the vendored conformance table and the
200
+ three deliberate divergences from Mermaid are in
201
+ [docs/MERMAID-FORMAT.md §4.4](./docs/MERMAID-FORMAT.md). The load-bearing
202
+ one: **there is no clock here**, so a schedule with no dated anchor is a
203
+ line-aware error rather than a diagram whose meaning changes daily.
204
+
205
+ Mermaid's `dateFormat` is moment's grammar and its `axisFormat` is
206
+ strftime; neither is LDML, so each has its own tokenizer and each token
207
+ is mapped individually onto the core date kernel. An unsupported token
208
+ is refused by name rather than passed through as text.
209
+
210
+ **Localized labels take an injected provider.** This engine ships no
211
+ month or weekday names, so `axisFormat %b %Y` (or a `dateFormat` with
212
+ `MMMM`) needs one — the same `DateNames` record
213
+ `@jarenjs/locales`' `compileDateLocale(pack).names` produces:
214
+
215
+ ```js
216
+ import { compileDateLocale, nl } from '@jarenjs/locales';
217
+ renderMermaid(source, { dateNames: compileDateLocale(nl).names });
218
+ ```
219
+
220
+ Without a provider a name token is a compile error, not a silent English
221
+ fallback. Task ids and dates stay ASCII data either way — an RTL name is
222
+ text, not syntax.
223
+
224
+ At scale, parse (which is also schedule resolution), layout and render
225
+ are reported separately in the benchmark: <!--bm:mermaid.ganttScale-->at 10,000 tasks, ~34 ms to parse and resolve, ~3.8 ms to lay out and ~7.7 ms to render<!--/bm-->.
226
+
227
+
168
228
  ## Styling, notes and interaction
169
229
 
170
230
  **`classDef` / `class` / `style` now paint.** The parser always recorded them;
@@ -261,7 +321,7 @@ detection + Jison + validation — so it is heavy and noisy; these are
261
321
  representative, not a bare-grammar microbenchmark.)
262
322
 
263
323
  The headless **parse → layout → SVG string** rows are jaren-only
264
- (<!--bm:mermaid.svgMs-->~0.46 ms for a 25-node flowchart, ~1.4 ms at 100 nodes<!--/bm-->): mermaid.js
324
+ (<!--bm:mermaid.svgMs-->~0.68 ms for a 25-node flowchart, ~1 ms at 100 nodes<!--/bm-->): mermaid.js
265
325
  needs a browser DOM (`getBBox`) to render, so there is no fair
266
326
  full-render head-to-head — producing a complete standalone SVG in pure
267
327
  Node is a capability it lacks.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @file The working calendar behind `excludes`: which whole days a
3
+ * schedule skips, and what a duration therefore means.
4
+ *
5
+ * Two implementations of one answer live here on purpose. The exported
6
+ * one expresses an excluded stretch as half-open day intervals and
7
+ * measures it with `@jarenjs/core/series` (`mergeIntervals`,
8
+ * `coverageOf`) — the suite's one interval algebra, so a weekend here
9
+ * and an unavailability there are the same kind of thing. The other,
10
+ * {@link pushEndDayByDay}, is the naive day-stepping loop the shipped
11
+ * Mermaid renderer runs; it exists so the fast answer has an
12
+ * independent oracle to be differential-tested against, and it is the
13
+ * definition when the two disagree.
14
+ *
15
+ * The rule is Mermaid's, not an invention: an excluded day pushes a
16
+ * task's END out by a whole day, the task's own START is left where the
17
+ * document put it, and an end that was written as an explicit DATE is
18
+ * never pushed at all — only one derived from a duration is. Days are
19
+ * UTC days, because this engine has no local zone and no clock.
20
+ *
21
+ * This is not a holiday service. A schedule skips exactly the weekends,
22
+ * weekday names and dates its own `excludes` line lists.
23
+ */
24
+ /** Milliseconds in a UTC day. */
25
+ export declare const DAY_MS = 86400000;
26
+ /**
27
+ * Mermaid's own guard: a schedule whose exclusions swallow every day
28
+ * would push an end forever, so the walk stops and the caller reports a
29
+ * `JM` error instead of hanging.
30
+ */
31
+ export declare const MAX_PUSH_DAYS = 10000;
32
+ /**
33
+ * The index of the UTC day holding `ms`, counting from 1970-01-01.
34
+ * @param {number} ms
35
+ * @returns {number}
36
+ */
37
+ export declare function dayIndexOf(ms: number): number;
38
+ export type Excluder = {
39
+ /**
40
+ * whether the document excludes anything at all
41
+ */
42
+ any: boolean;
43
+ isExcludedDay: (dayIndex: number) => boolean;
44
+ /**
45
+ * merged half-open day spans over `[fromDay, toDay)`
46
+ */
47
+ intervalsIn: (fromDay: number, toDay: number) => {
48
+ start: number;
49
+ end: number;
50
+ }[];
51
+ };
52
+ /**
53
+ * @typedef {object} Excluder
54
+ * @property {boolean} any whether the document excludes anything at all
55
+ * @property {(dayIndex: number) => boolean} isExcludedDay
56
+ * @property {(fromDay: number, toDay: number) => { start: number, end: number }[]}
57
+ * intervalsIn merged half-open day spans over `[fromDay, toDay)`
58
+ */
59
+ /**
60
+ * Build the working calendar a document's `excludes`, `weekend` and
61
+ * `dateFormat` describe.
62
+ *
63
+ * @param {{ weekends: boolean, weekdays: number[], days: number[] }} rules -
64
+ * `weekdays` are ISO weekday numbers, `days` are day indexes
65
+ * @param {number} weekendStart - ISO weekday the weekend starts on
66
+ * @returns {Excluder}
67
+ */
68
+ export declare function createExcluder(rules: {
69
+ weekends: boolean;
70
+ weekdays: number[];
71
+ days: number[];
72
+ }, weekendStart: number): Excluder;
73
+ /**
74
+ * Push a task's end past the excluded days its span crosses.
75
+ *
76
+ * The probes are the instants `start + k days` for `k` from 1, which is
77
+ * what makes this a *day* rule rather than a millisecond one: a task
78
+ * that starts at noon on Friday and lasts a day ends at noon on Monday
79
+ * when the weekend is excluded, not at midnight.
80
+ *
81
+ * @param {number} start - epoch milliseconds
82
+ * @param {number} end - epoch milliseconds, at or after `start`
83
+ * @param {Excluder} excluder
84
+ * @returns {number} the pushed end, or `NaN` when the exclusions never
85
+ * let the task finish
86
+ */
87
+ export declare function pushEndPastExclusions(start: number, end: number, excluder: Excluder): number;
88
+ /**
89
+ * The oracle: Mermaid's own day-stepping loop, written plainly. Kept as
90
+ * a test double rather than as the implementation because it walks
91
+ * every day of a span one at a time, but it is the definition of the
92
+ * answer {@link pushEndPastExclusions} computes.
93
+ *
94
+ * @param {number} start - epoch milliseconds
95
+ * @param {number} end - epoch milliseconds
96
+ * @param {Excluder} excluder
97
+ * @returns {number} the pushed end, or `NaN` when it never finishes
98
+ */
99
+ export declare function pushEndDayByDay(start: number, end: number, excluder: Excluder): number;
@@ -28,11 +28,17 @@ export { MERMAID_VERSION, diagramDocument, walkSequence, flowNode, flowEdge, seq
28
28
  /**
29
29
  * Convenience: parse → layout → render, error-safe, in one call.
30
30
  * @param {string} source
31
- * @param {{ theme?: any }} [options]
31
+ * @param {{ theme?: any, dateNames?: import('@jarenjs/core/dates').DateNames,
32
+ * parseFrontmatter?: (text: string) => any }} [options] - `dateNames`
33
+ * is the locale-name record a Gantt's `dateFormat`/`axisFormat` needs
34
+ * for a month or weekday token (`compileDateLocale(pack).names`);
35
+ * this engine ships none of its own
32
36
  * @returns {any} an SVG vnode
33
37
  */
34
38
  export declare function renderMermaid(source: string, options?: {
35
39
  theme?: any;
40
+ dateNames?: import('@jarenjs/core/dates').DateNames;
41
+ parseFrontmatter?: (text: string) => any;
36
42
  }): any;
37
43
  export type CompiledMermaid = {
38
44
  /**
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @file Gantt layout: a resolved schedule → a deterministic
3
+ * `PositionedDiagram` scene. Pure, host-free and vnode-free, so the
4
+ * geometry can be asserted without rendering anything.
5
+ *
6
+ * The time axis is not this file's invention: the domain comes from the
7
+ * AST, the tick positions come from `@jarenjs/core/dates` — either the
8
+ * document's own `tickInterval` through `timeTicksEvery`, or the shared
9
+ * step ladder through `axisTicksTime`, the same one the chart component
10
+ * draws its time axis with — and the tick labels come from the
11
+ * document's `axisFormat` compiled once here. A second tick ladder in a
12
+ * renderer would be a second set of positions.
13
+ *
14
+ * Two decisions are made here rather than in the renderer, because both
15
+ * are geometry:
16
+ *
17
+ * - **Tick labels thin out.** `tickInterval 1day` over two years is 730
18
+ * ticks; the marks all stay, and labels are kept at the largest
19
+ * stride that leaves them from overlapping. The scene says which.
20
+ * - **Row names elide.** A name wider than the label column is cut with
21
+ * an ellipsis at layout time, so the measured width and the drawn text
22
+ * are the same string.
23
+ */
24
+ /**
25
+ * @param {any} ast gantt AST (`parser/gantt.js`)
26
+ * @param {{ dateNames?: import('@jarenjs/core/dates').DateNames }} [options]
27
+ * @returns {any} PositionedDiagram
28
+ */
29
+ export declare function layoutGantt(ast: any, options?: {
30
+ dateNames?: import('@jarenjs/core/dates').DateNames;
31
+ }): any;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @file The three Gantt token grammars, adapted to the core date
3
+ * kernel. Mermaid's Gantt header speaks **three different pattern
4
+ * languages** and none of them is Unicode LDML:
5
+ *
6
+ * - `dateFormat` is dayjs' `customParseFormat` vocabulary (moment's
7
+ * spelling): `YYYY-MM-DD`, where `YYYY` is the calendar year;
8
+ * - `axisFormat` is d3-time-format's strftime vocabulary: `%Y-%m-%d`;
9
+ * - `tickInterval` and a task's duration are two small regular
10
+ * grammars of their own (`1week`, `3d`).
11
+ *
12
+ * Each gets its own tokenizer here, and each token is mapped
13
+ * individually onto the LDML token `compileDateFormat`/`compileDateParser`
14
+ * understand. Handing `YYYY` straight to the core compiler would be a
15
+ * silent lie — LDML's `YYYY` is the *week-numbering* year, which is
16
+ * moment's most reported footgun and the reason `@jarenjs/core/dates`
17
+ * refuses that spelling in the first place.
18
+ *
19
+ * Every function here returns `{ value, error }` rather than throwing:
20
+ * the caller owns the source line, and a `JM` error without one is not
21
+ * worth much.
22
+ *
23
+ * The vendored conformance table for all of this — which tokens the
24
+ * shipped Mermaid version documents, which of them this engine
25
+ * implements and why the rest are refused — is
26
+ * `test/mermaid/fixtures/gantt-grammar.json`, pinned against this file
27
+ * by `test/mermaid/gantt.test.js`.
28
+ */
29
+ /** Mermaid's own default when a diagram declares no `dateFormat`. */
30
+ export declare const DEFAULT_DATE_FORMAT = "YYYY-MM-DD";
31
+ /** Mermaid's own default when a diagram declares no `axisFormat`. */
32
+ export declare const DEFAULT_AXIS_FORMAT = "%Y-%m-%d";
33
+ /** dayjs token → the LDML token that reads and writes the same field. */
34
+ declare const MOMENT_TO_LDML: Readonly<{
35
+ YYYY: "yyyy";
36
+ YY: "yy";
37
+ Y: "y";
38
+ MMMM: "MMMM";
39
+ MMM: "MMM";
40
+ MM: "MM";
41
+ M: "M";
42
+ DD: "dd";
43
+ D: "d";
44
+ HH: "HH";
45
+ H: "H";
46
+ hh: "hh";
47
+ h: "h";
48
+ mm: "mm";
49
+ m: "m";
50
+ ss: "ss";
51
+ s: "s";
52
+ SSS: "SSS";
53
+ S: "S";
54
+ A: "a";
55
+ a: "a";
56
+ ZZ: "XX";
57
+ Z: "XXX";
58
+ }>;
59
+ /** Documented dayjs tokens this engine will not read, and why. */
60
+ declare const MOMENT_REFUSED: Readonly<{
61
+ SS: "the fraction tokens are tenths (S) and milliseconds (SSS); there is no hundredths field";
62
+ Q: "a quarter is derived from a month, not a field of a date";
63
+ Do: "the ordinal suffix is locale text and this engine ships no ordinal data";
64
+ DDDD: "the day of the year is derived from a date, not a field of one";
65
+ DDD: "the day of the year is derived from a date, not a field of one";
66
+ X: "an epoch second is a whole value, not a calendar field";
67
+ x: "an epoch millisecond is a whole value, not a calendar field";
68
+ ww: "the ISO week number is derived from a date, not a field of one";
69
+ w: "the ISO week number is derived from a date, not a field of one";
70
+ }>;
71
+ /** strftime specifier → the LDML fragment that writes the same field. */
72
+ declare const STRFTIME_TO_LDML: Readonly<{
73
+ Y: "yyyy";
74
+ y: "yy";
75
+ m: "MM";
76
+ d: "dd";
77
+ H: "HH";
78
+ I: "hh";
79
+ M: "mm";
80
+ S: "ss";
81
+ L: "SSS";
82
+ j: "DDD";
83
+ a: "EEE";
84
+ A: "EEEE";
85
+ b: "MMM";
86
+ B: "MMMM";
87
+ p: "a";
88
+ Z: "XX";
89
+ x: "MM'/'dd'/'yyyy";
90
+ X: "HH':'mm':'ss";
91
+ '%': "'%'";
92
+ }>;
93
+ /** Documented strftime specifiers this engine will not write, and why. */
94
+ declare const STRFTIME_REFUSED: Readonly<{
95
+ e: "a space-padded day is a token the formatter does not have";
96
+ c: "it expands to %e, which is unsupported";
97
+ U: "d3 counts weeks from the first Sunday; the core week number is ISO, so the two disagree";
98
+ W: "d3 counts weeks from the first Monday; the core week number is ISO, so the two disagree";
99
+ w: "d3 numbers Sunday 0; the core weekday number is ISO, where 1 is Monday";
100
+ f: "microseconds are below the millisecond this suite measures time in";
101
+ g: "the week-based year is a second year field, and the core parts record has one";
102
+ G: "the week-based year is a second year field, and the core parts record has one";
103
+ q: "a quarter on a time axis is a label, not a tick this engine plans";
104
+ Q: "an epoch is a whole value, not a calendar field";
105
+ s: "an epoch is a whole value, not a calendar field";
106
+ u: "the ISO weekday number is a tick label this engine does not plan";
107
+ V: "the ISO week number is a tick label this engine does not plan";
108
+ }>;
109
+ export type Adapted = {
110
+ value: any;
111
+ error: string | null;
112
+ };
113
+ /**
114
+ * Adapt a Mermaid `dateFormat` to an LDML pattern.
115
+ * @param {string} pattern - a dayjs `customParseFormat` pattern
116
+ * @returns {Adapted} `value` is the LDML pattern
117
+ */
118
+ export declare function momentToLdml(pattern: string): Adapted;
119
+ /**
120
+ * Adapt a Mermaid `axisFormat` to an LDML pattern. This is a separate
121
+ * grammar from `dateFormat` on purpose: `%m` is a month and `m` is a
122
+ * minute, so one table serving both would silently mis-read half of
123
+ * every axis.
124
+ * @param {string} pattern - a d3-time-format (strftime) pattern
125
+ * @returns {Adapted} `value` is the LDML pattern
126
+ */
127
+ export declare function strftimeToLdml(pattern: string): Adapted;
128
+ /**
129
+ * Whether an LDML pattern uses a token that needs locale names, and
130
+ * which token that is. Used to turn `compileDateFormat`'s TypeError into
131
+ * a `JM` error that names the Mermaid spelling and the way out.
132
+ * @param {string} ldml
133
+ * @returns {string | null}
134
+ */
135
+ export declare function ldmlNeedsNames(ldml: string): string | null;
136
+ /**
137
+ * Parse a `tickInterval` directive.
138
+ * @param {string} text
139
+ * @returns {Adapted} `value` is `{ amount, unit }`, the unit a core
140
+ * `DATE_UNITS` member
141
+ */
142
+ export declare function parseTickInterval(text: string): Adapted;
143
+ /**
144
+ * Parse a task duration (`3d`, `1.5w`, `2M`).
145
+ * @param {string} text
146
+ * @returns {Adapted} `value` is `{ amount, unit }`, or `null` with no
147
+ * error when the text is simply not a duration (the caller then tries
148
+ * to read it as a date)
149
+ */
150
+ export declare function parseTaskDuration(text: string): Adapted;
151
+ /** ISO weekday numbers, 1 is Monday — the numbering `isoWeekdayFromDays` uses. */
152
+ export declare const ISO_WEEKDAYS: Readonly<{
153
+ monday: 1;
154
+ tuesday: 2;
155
+ wednesday: 3;
156
+ thursday: 4;
157
+ friday: 5;
158
+ saturday: 6;
159
+ sunday: 7;
160
+ }>;
161
+ /**
162
+ * Parse a `weekday` directive: which day a whole-week tick starts on.
163
+ * @param {string} text
164
+ * @returns {Adapted} `value` is an ISO weekday, 1 to 7
165
+ */
166
+ export declare function parseWeekday(text: string): Adapted;
167
+ /**
168
+ * Parse a `weekend` directive: the first of the two days
169
+ * `excludes weekends` removes.
170
+ * @param {string} text
171
+ * @returns {Adapted} `value` is an ISO weekday, 5 (friday) or 6 (saturday)
172
+ */
173
+ export declare function parseWeekend(text: string): Adapted;
174
+ export { MOMENT_TO_LDML, MOMENT_REFUSED, STRFTIME_TO_LDML, STRFTIME_REFUSED };
@@ -1,11 +1,41 @@
1
1
  /**
2
- * @file Gantt grammar → gantt AST: a schedule DAG. Header
3
- * directives (`title`, `dateFormat`, `axisFormat`, `excludes`) go to
4
- * `meta`; `section` groups tasks; task rows keep their raw metadata
5
- * string (`:done, id, 2014-01-06, 3d`) verbatim, geometry-free.
2
+ * @file Gantt grammar → gantt AST: a schedule, resolved.
3
+ *
4
+ * Header directives (`title`, `dateFormat`, `axisFormat`, `excludes`,
5
+ * `tickInterval`, `weekday`, `weekend`, `todayMarker`) are kept verbatim
6
+ * in `meta` so the printer stays a fixed point, and are *also*
7
+ * interpreted into `rules` — a compiled date parser, an axis pattern, a
8
+ * tick step, a working calendar. `section` groups tasks; a task row
9
+ * keeps its raw metadata string (`:done, id, 2014-01-06, 3d`) in `info`
10
+ * for the same printing reason and gains the semantic members a
11
+ * timeline needs: `{ id, flags, start, end, duration, after, line }`,
12
+ * with `start`/`end` epoch milliseconds and the interval half-open.
13
+ *
14
+ * The AST stays geometry-free and plain JSON: no compiled closure and
15
+ * no coordinate crosses this boundary. Layout re-compiles the axis
16
+ * pattern from `rules`, once per diagram.
17
+ *
18
+ * Three refusals are the whole difference from a permissive reader, and
19
+ * each one is a `JM` error carrying its source line:
20
+ *
21
+ * 1. **No clock.** Mermaid starts an undated first task *today*. This
22
+ * engine has no clock (`docs/ROADMAP.md`, "there is no now"), so a
23
+ * schedule with no dated anchor is an error rather than a diagram
24
+ * that means something different tomorrow.
25
+ * 2. **No silent literals.** An unsupported `dateFormat` or
26
+ * `axisFormat` token is refused by name; Mermaid passes it through as
27
+ * literal text, which turns a typo into a date that reads as
28
+ * something else.
29
+ * 3. **No guessed dependencies.** A missing id, a duplicate id, a cycle
30
+ * and a reversed or empty span are all errors.
6
31
  */
7
32
  /**
8
33
  * @param {string[]} lines
34
+ * @param {number} [offset] - 0-based index of `lines[0]` in the source
35
+ * @param {string} [_header]
36
+ * @param {{ dateNames?: import('@jarenjs/core/dates').DateNames }} [options]
9
37
  * @returns {object}
10
38
  */
11
- export declare function parseGantt(lines: string[]): object;
39
+ export declare function parseGantt(lines: string[], offset?: number, _header?: string, options?: {
40
+ dateNames?: import('@jarenjs/core/dates').DateNames;
41
+ }): object;
@@ -14,9 +14,13 @@ export declare const SECONDARY_TYPES: Set<string>;
14
14
  /**
15
15
  * Parse Mermaid source into a `DiagramDocument`.
16
16
  * @param {string} source
17
- * @param {{ parseFrontmatter?: (text: string) => any }} [options]
17
+ * @param {{ parseFrontmatter?: (text: string) => any,
18
+ * dateNames?: import('@jarenjs/core/dates').DateNames }} [options] -
19
+ * `dateNames` is the locale-name record a Gantt's `dateFormat` needs
20
+ * for a month name token; this engine ships none of its own
18
21
  * @returns {import('../ast.js').DiagramDocument}
19
22
  */
20
23
  export declare function parseMermaid(source: string, options?: {
21
24
  parseFrontmatter?: (text: string) => any;
25
+ dateNames?: import('@jarenjs/core/dates').DateNames;
22
26
  }): import('../ast.js').DiagramDocument;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file Gantt renderer: a positioned schedule → pure-vnode SVG. A real
3
+ * timeline — an aligned time axis, section bands, task bars, milestone
4
+ * diamonds, dependency connectors and shaded excluded days — where the
5
+ * engine used to draw a list of strings in a box.
6
+ *
7
+ * It imports `@jarenjs/core` and `@jarenjs/view` and nothing else: the
8
+ * tick ladder is core's, so this file acquires no dependency on the
9
+ * chart component to draw a time axis.
10
+ *
11
+ * Accessibility: the root carries a `<title>` (the diagram's own title,
12
+ * or a generic one) and a `<desc>` naming the span and the task count,
13
+ * so a screen reader gets the shape of the schedule rather than a
14
+ * hundred unlabelled rectangles. Bars carry `data-id` and a status
15
+ * class, which is also what an interactive host hit-tests on.
16
+ */
17
+ /**
18
+ * @param {any} scene PositionedDiagram (gantt)
19
+ * @param {{ tokens: Record<string,string>, cssVars: Record<string,string> }} theme
20
+ * @param {string} hash
21
+ * @returns {any}
22
+ */
23
+ export declare function renderGantt(scene: any, theme: {
24
+ tokens: Record<string, string>;
25
+ cssVars: Record<string, string>;
26
+ }, hash: string): any;
@@ -2,7 +2,7 @@
2
2
  * @file Renderers for the first-class types beyond flowchart and
3
3
  * sequence, and an honest placeholder for the deferred secondary
4
4
  * types. Pie is
5
- * a real chart; class/ER/state/gantt render as structured panels — a
5
+ * a real chart; class and ER render as structured panels — a
6
6
  * readable, geometry-light view that renders without error and so counts
7
7
  * honestly in the coverage scorecard. Secondary types (mindmap,
8
8
  * gitGraph, journey, timeline) render a labeled "not yet laid out"
@@ -105,7 +105,7 @@ whose grammar permits those characters in ids.
105
105
  (`{ kind:'block', blockType, branches:[{ label, statements[] }] }`) for
106
106
  loop/opt/alt/par/critical/break.
107
107
 
108
- ### 4.3 class / er / state / gantt / pie
108
+ ### 4.3 class / er / state / pie
109
109
 
110
110
  Class: `{ classes:[{ name, label, members[] }], relations[] }`. ER:
111
111
  `{ entities:[{ name, attributes[] }], relationships[] }`. State:
@@ -118,10 +118,112 @@ the guard (nesting counted), the effect starts at the first `/` after
118
118
  it (or the first `/` at all when there is no guard), and a label that
119
119
  fits no pattern — an unmatched `[`, or text between `]` and `/` — reads
120
120
  whole as the event, which keeps plain labels meaning what they always
121
- meant. Gantt: `{ meta, sections:[{ name, tasks[] }] }`. Pie:
122
- `{ title, showData, slices:[{ label, value }] }`.
121
+ meant. Pie: `{ title, showData, slices:[{ label, value }] }`.
123
122
 
124
- ### 4.4 Secondary types
123
+ Gantt is `{ meta, rules, sections:[{ name, tasks[] }], domain }` and is
124
+ described in §4.4, because it is the one AST that carries a *resolved*
125
+ answer rather than only what the source said.
126
+
127
+ ### 4.4 gantt — a resolved schedule
128
+
129
+ ```
130
+ { meta, rules, sections:[{ name, tasks[] }], domain:{ start, end } }
131
+ ```
132
+
133
+ `meta` holds the header lines **verbatim** (`title`, `dateFormat`,
134
+ `axisFormat`, `excludes`, `tickInterval`, `weekday`, `weekend`,
135
+ `todayMarker`), which is what keeps `printGantt` a fixed point. `rules`
136
+ holds the same directives *interpreted*:
137
+ `{ dateFormat, axisFormat, tick, weekStart, weekendStart, excludes,
138
+ todayMarker }`, where `tick` is `{ amount, unit }` or `null`,
139
+ `weekStart`/`weekendStart` are ISO weekdays (1 is Monday), and
140
+ `excludes` is `{ weekends, weekdays[], days[] }` with `days` as day
141
+ indexes from 1970-01-01.
142
+
143
+ A task is
144
+ `{ name, info, id, flags, start, end, duration, after, line }`. `info`
145
+ is the raw metadata string the printer emits; the rest is the schedule.
146
+ `start`/`end` are epoch milliseconds and the interval is **half-open**,
147
+ so `end` is the first instant the task no longer occupies — except a
148
+ milestone, which is the instant `start === end`. `flags` is the subset
149
+ of `done`, `active`, `crit`, `milestone` in that order. `domain` is the
150
+ half-open span every task falls inside, and it is what layout scales
151
+ against.
152
+
153
+ The AST stays geometry-free and plain JSON: no compiled closure and no
154
+ coordinate crosses this boundary. `layout/gantt.js` re-compiles the axis
155
+ pattern from `rules`, once per diagram.
156
+
157
+ #### The three grammars
158
+
159
+ A Gantt header speaks three pattern languages and **none of them is
160
+ Unicode LDML**, so each gets its own tokenizer and each token is mapped
161
+ individually onto the core vocabulary (`src/parser/gantt-grammar.js`):
162
+
163
+ | Directive | Grammar | Example |
164
+ |---|---|---|
165
+ | `dateFormat` | dayjs `customParseFormat` (moment's spelling) | `DD-MM-YYYY` |
166
+ | `axisFormat` | d3-time-format (strftime) | `%Y-%m-%d` |
167
+ | `tickInterval` | `^([1-9]\d*)(millisecond\|second\|minute\|hour\|day\|week\|month)$` | `1week` |
168
+ | a task duration | `^(\d+(?:\.\d+)?)([Mdhmswy]\|ms)$` | `3d`, `1.5w`, `2M` |
169
+
170
+ Handing `YYYY` straight to the core compiler would be a silent lie —
171
+ LDML's `YYYY` is the *week-numbering* year, so `YYYY-MM-DD` read as LDML
172
+ answers 2019 for `2018-12-31`. Note also that moment's `m` is a minute
173
+ while strftime's `%m` is a month: one shared table would mis-read half
174
+ of every diagram.
175
+
176
+ An **unsupported** token is a `JM` error naming the token and the
177
+ reason, not a literal passed through. The vendored conformance table —
178
+ every documented token of the Mermaid version this engine tracks, marked
179
+ supported or refused with its reason, plus the provenance of where it
180
+ was read — is `test/mermaid/fixtures/gantt-grammar.json`, and
181
+ `test/mermaid/gantt.test.js` pins the code against it in both
182
+ directions.
183
+
184
+ Both directions is a closed loop, so the table is also checked against
185
+ the shipped chunk it was read from: dayjs' parse table is inlined there,
186
+ and every token Mermaid really accepts must appear. A token in neither
187
+ list is not refused — it becomes literal text, and then the diagram's
188
+ own dates are what look wrong. `w`, `ww` and `Y` reached the tree that
189
+ way; the week numbers are now refused by name (a week number is derived
190
+ from a date rather than a field of one, which is why `compileDateParser`
191
+ will not read one either) and `Y` is the signed variable-width year
192
+ core's `y` already spells.
193
+
194
+ #### Task forms and working days
195
+
196
+ The field-count rule is Mermaid's own: one field is an end, two are a
197
+ start and an end, three add an explicit id in front, and the four flags
198
+ lead. `after <id …>` starts a task at the latest end of those ids;
199
+ `until <id …>` ends it at the earliest start of those. Ids may be
200
+ forward references — resolution is a topological pass — and a cycle, a
201
+ missing id, a duplicate id, a reversed span and an empty non-milestone
202
+ span are all refused with the offending line.
203
+
204
+ `excludes` removes whole days: `weekends` (the `weekend` day and the one
205
+ after it, saturday by default), weekday names, and dates written in the
206
+ document's own `dateFormat`. An end derived from a *duration* is pushed
207
+ out one day per excluded day it crosses; an end written as an explicit
208
+ *date* is the author's answer and is never pushed. The probes are the
209
+ instants `start + k days`, so a task starting Friday noon and lasting a
210
+ day ends Monday noon. Days are UTC days. This is not a holiday service:
211
+ a schedule skips exactly what its own `excludes` line lists.
212
+
213
+ #### Three deliberate divergences from Mermaid
214
+
215
+ 1. **No clock.** Mermaid starts an undated first task *today*. This
216
+ engine has no clock anywhere (`docs/ROADMAP.md`, "there is no now"),
217
+ so a schedule with no dated anchor is a `JM` error rather than a
218
+ diagram that means something different tomorrow. In practice only the
219
+ first task needs a date. For the same reason `todayMarker` is kept
220
+ in `meta` and never drawn.
221
+ 2. **No silent literals** — an unsupported pattern token is refused by
222
+ name, where Mermaid passes it through as text.
223
+ 3. **No guessed dependencies** — Mermaid falls back to today for an
224
+ `after` naming an unknown id; this engine refuses.
225
+
226
+ ### 4.5 Secondary types
125
227
 
126
228
  mindmap, gitGraph, journey, timeline, quadrantChart, requirement
127
229
  parse-accept into `{ diagram, lines[] }` and render an honest "not yet
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/mermaid",
3
3
  "private": false,
4
- "version": "0.46.5",
4
+ "version": "0.49.2",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -77,8 +77,8 @@
77
77
  "prepack": "npm run build:types"
78
78
  },
79
79
  "dependencies": {
80
- "@jarenjs/charts": "^0.46.5",
81
- "@jarenjs/core": "^0.46.5",
82
- "@jarenjs/view": "^0.46.5"
80
+ "@jarenjs/charts": "^0.49.2",
81
+ "@jarenjs/core": "^0.49.2",
82
+ "@jarenjs/view": "^0.49.2"
83
83
  }
84
84
  }