@markup-carve/pandoc-carve 0.1.5 → 0.1.6
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 +70 -345
- package/dist/ast-json.js +22 -0
- package/dist/convert.d.ts +4 -0
- package/dist/convert.js +127 -28
- package/dist/diagnostics.js +9 -2
- package/dist/index.js +107 -3
- package/dist/pandoc.d.ts +1 -1
- package/dist/pandoc.js +3 -3
- package/dist/reverse.js +147 -61
- package/dist/row-groups.d.ts +2 -0
- package/dist/row-groups.js +25 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
# pandoc-carve
|
|
2
2
|
|
|
3
|
-
Bidirectional [Carve](https://github.com/markup-carve/carve)
|
|
3
|
+
Bidirectional [Carve](https://github.com/markup-carve/carve) and Pandoc bridge.
|
|
4
|
+
It exports Carve through Pandoc's writers and imports any format handled by a
|
|
5
|
+
Pandoc reader.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Org, MediaWiki, HTML, Markdown, ...) into Carve source.
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
.crv ──parse──▶ Carve AST ──carveToPandoc()──▶ Pandoc JSON ──pandoc -f json -t X──▶ .tex / .typ / .docx / …
|
|
12
|
-
.docx / .tex / … ──pandoc -t json──▶ Pandoc JSON ──pandocToCarve()──▶ Carve AST ──renderCarve──▶ .crv
|
|
7
|
+
```text
|
|
8
|
+
.crv -> Carve AST -> Pandoc JSON -> LaTeX, Typst, DOCX, PDF, EPUB, ...
|
|
9
|
+
DOCX, LaTeX, RST, ... -> Pandoc JSON -> Carve AST -> .crv
|
|
13
10
|
```
|
|
14
11
|
|
|
15
|
-
The bridge maps
|
|
16
|
-
tables with spans, footnotes, math,
|
|
17
|
-
|
|
18
|
-
`` `\alpha`{=latex} `` becomes a Pandoc `RawInline` that the LaTeX writer emits
|
|
19
|
-
and every other writer drops - exactly the pandoc-Markdown semantics the syntax
|
|
20
|
-
was born from.
|
|
12
|
+
The bridge maps parsed nodes instead of converting through HTML. It preserves
|
|
13
|
+
structure such as tables with spans, footnotes, math, admonitions, attributes,
|
|
14
|
+
and target-routed raw content when Pandoc can represent it.
|
|
21
15
|
|
|
22
16
|
## Install
|
|
23
17
|
|
|
@@ -25,368 +19,99 @@ was born from.
|
|
|
25
19
|
npm install @markup-carve/pandoc-carve
|
|
26
20
|
```
|
|
27
21
|
|
|
28
|
-
The CLI
|
|
29
|
-
3.
|
|
30
|
-
or your package manager. Emitting plain JSON (`-t json`) needs no pandoc at all.
|
|
22
|
+
The CLI requires Pandoc 3.x on `PATH` for formats other than JSON. CI currently
|
|
23
|
+
tests Pandoc 3.11. Pandoc is not bundled.
|
|
31
24
|
|
|
32
25
|
## CLI
|
|
33
26
|
|
|
34
27
|
```bash
|
|
35
|
-
#
|
|
28
|
+
# Export
|
|
36
29
|
pandoc-carve doc.crv -t latex -o doc.tex
|
|
37
|
-
|
|
38
|
-
# Carve -> Typst, PDF, DOCX ... any pandoc writer
|
|
39
30
|
pandoc-carve doc.crv -t typst -o doc.typ
|
|
40
|
-
pandoc-carve doc.crv -t pdf -o doc.pdf
|
|
41
31
|
pandoc-carve doc.crv -t docx -o doc.docx
|
|
32
|
+
pandoc-carve doc.crv -t pdf -o doc.pdf
|
|
42
33
|
|
|
43
|
-
#
|
|
44
|
-
pandoc-carve doc.crv -t latex -s -o doc.tex
|
|
45
|
-
|
|
46
|
-
# Just the Pandoc JSON AST (no pandoc needed)
|
|
47
|
-
pandoc-carve doc.crv -t json
|
|
48
|
-
|
|
49
|
-
# Expand contained includes; named files default to their own directory
|
|
50
|
-
pandoc-carve book/main.crv -t json --include-root "$PWD/book"
|
|
51
|
-
|
|
52
|
-
# Keep directives literal for a named file
|
|
53
|
-
pandoc-carve book/main.crv -t json --no-includes
|
|
54
|
-
|
|
55
|
-
# Read from stdin, pass extra args through to pandoc after --
|
|
56
|
-
cat doc.crv | pandoc-carve - -t latex -- --toc
|
|
57
|
-
|
|
58
|
-
# IMPORT: anything pandoc reads -> Carve
|
|
34
|
+
# Import
|
|
59
35
|
pandoc-carve report.docx -f docx -o report.crv
|
|
60
36
|
pandoc-carve paper.tex -f latex -o paper.crv
|
|
61
|
-
pandoc-carve README.md -f markdown -o README.crv
|
|
62
37
|
|
|
63
|
-
#
|
|
38
|
+
# Emit or consume Pandoc JSON without invoking pandoc
|
|
39
|
+
pandoc-carve doc.crv -t json
|
|
64
40
|
pandoc -f rst -t json doc.rst | pandoc-carve - -f json
|
|
65
|
-
|
|
66
|
-
# From a SERIALIZED Carve AST (spec PART 12) - from any engine, not just carve-js
|
|
67
|
-
carve doc.crv --to-json | pandoc-carve - -f carve-json -t latex
|
|
68
41
|
```
|
|
69
42
|
|
|
70
|
-
Anything
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`{ schemaVersion, sourceFormat, diagnostics }` JSON envelope without mixing
|
|
76
|
-
them into document output. `--fail-on-loss` exits with code 3 for degraded or
|
|
77
|
-
dropped findings, while preserved and normalized findings do not fail CI.
|
|
78
|
-
Imports through a Pandoc reader other than `-f json` also exit
|
|
79
|
-
3 because fidelity before the Pandoc JSON boundary cannot be verified; stderr
|
|
80
|
-
names that worst-case gate explicitly. Use `--diagnostics -` for JSON on stderr.
|
|
81
|
-
|
|
82
|
-
## API
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
import { carveToPandoc, carveToPandocJson } from '@markup-carve/pandoc-carve';
|
|
43
|
+
Anything that cannot be mapped faithfully produces a diagnostic. Use
|
|
44
|
+
`--diagnostics report.json` for the versioned JSON report and `--fail-on-loss`
|
|
45
|
+
to return exit code 3 when a conversion degrades or drops content. Imports
|
|
46
|
+
through a Pandoc reader other than `-f json` also return 3 because fidelity
|
|
47
|
+
before the Pandoc JSON boundary cannot be verified.
|
|
86
48
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// diagnostics = [{ code, direction, class, severity, fidelity, confidence, message, ... }]
|
|
91
|
-
// report = { schemaVersion: 2, sourceFormat: 'carve', diagnostics }
|
|
49
|
+
Named input files expand contained includes by default, rooted at their own
|
|
50
|
+
directory. Use `--no-includes` to keep directives literal. Standard input
|
|
51
|
+
expands includes only when `--include-root` is supplied.
|
|
92
52
|
|
|
93
|
-
|
|
94
|
-
|
|
53
|
+
The [CLI reference](docs/reference.md#cli) covers standalone documents,
|
|
54
|
+
additional Pandoc arguments, includes, serialized Carve AST input, and every
|
|
55
|
+
diagnostic option.
|
|
95
56
|
|
|
96
|
-
|
|
97
|
-
opt into contained expansion and receive warnings and dependency identities:
|
|
57
|
+
## API
|
|
98
58
|
|
|
99
59
|
```js
|
|
100
|
-
import {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// dependencies contains resolved and unresolved include identities.
|
|
60
|
+
import {
|
|
61
|
+
carveToPandoc,
|
|
62
|
+
carveToPandocJson,
|
|
63
|
+
pandocToCarve,
|
|
64
|
+
} from '@markup-carve/pandoc-carve'
|
|
65
|
+
|
|
66
|
+
const exported = carveToPandoc('Hello /world/!')
|
|
67
|
+
const json = carveToPandocJson('Hello /world/!')
|
|
68
|
+
const imported = pandocToCarve(json)
|
|
110
69
|
```
|
|
111
70
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
What the converter reads is the **serialized AST of the Carve spec's PART 12**
|
|
117
|
-
(the shape `resources/ast-schema.json` pins), not any implementation's runtime
|
|
118
|
-
tree - so a document another engine already parsed converts the same way,
|
|
119
|
-
whether it arrives as an object or as JSON text:
|
|
120
|
-
|
|
121
|
-
```js
|
|
122
|
-
import { carveAstToPandoc, carveToCarveAst } from '@markup-carve/pandoc-carve';
|
|
71
|
+
Each direction returns structured diagnostics alongside its output. Node.js
|
|
72
|
+
hosts can use `carveToPandocWithIncludes` from the `/node` entry point for
|
|
73
|
+
contained file expansion and dependency tracking.
|
|
123
74
|
|
|
124
|
-
|
|
125
|
-
|
|
75
|
+
Use `carveToCarveAst` to serialize source, `carveAstToPandoc` to convert a
|
|
76
|
+
serialized Carve tree, and `pandocToCarveAst` for the reverse direction. These
|
|
77
|
+
functions consume the versioned AST defined by the Carve specification, not an
|
|
78
|
+
implementation-specific runtime tree.
|
|
126
79
|
|
|
127
|
-
|
|
128
|
-
const ast = carveToCarveAst('Hello /world/!');
|
|
129
|
-
```
|
|
80
|
+
## Round trips
|
|
130
81
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
82
|
+
With round-trip metadata enabled, the test suite requires
|
|
83
|
+
`carve -> Pandoc AST -> carve` to render equivalent normalized HTML across the
|
|
84
|
+
corpus apart from an explicit known-lossy list:
|
|
134
85
|
|
|
135
86
|
```js
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const { carve, warnings, diagnostics, report } = pandocToCarve(pandocJsonString);
|
|
139
|
-
|
|
140
|
-
// Preserve structured fields that Carve 0.1 source cannot spell, including
|
|
141
|
-
// Pandoc's optional short figure/table caption.
|
|
142
|
-
const { ast, warnings: astWarnings } = pandocToCarveAst(pandocJsonString);
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Round-trips are tested as a hard gate: `carve -> pandoc AST -> carve` must
|
|
146
|
-
render byte-identical HTML to the original source across the test corpus.
|
|
147
|
-
For exact restoration of attribute placement (`{.lead}` on a paragraph or
|
|
148
|
-
list), export with `carveToPandoc(src, { roundtrip: true })` or the CLI's
|
|
149
|
-
`--roundtrip` flag. In addition to the existing `carve-block` attribute marker,
|
|
150
|
-
this preserves comments, typed citation fields, and unknown future Carve nodes
|
|
151
|
-
with a versioned private Span/Div envelope. The native citation and readable
|
|
152
|
-
unknown-node fallback remain usable by Pandoc. The marker may be visible in
|
|
153
|
-
writer output and exact preservation is guaranteed only through Pandoc JSON.
|
|
154
|
-
The complete format, safety checks, available source information, and filter
|
|
155
|
-
semantics are documented in [the provenance envelope](docs/roundtrip-provenance.md).
|
|
156
|
-
|
|
157
|
-
Pipe the JSON to pandoc yourself:
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
node -e "import('@markup-carve/pandoc-carve').then(m => process.stdout.write(m.carveToPandocJson(require('fs').readFileSync('doc.crv','utf8'))))" \
|
|
161
|
-
| pandoc -f json -t latex
|
|
87
|
+
const result = carveToPandoc(source, { roundtrip: true })
|
|
162
88
|
```
|
|
163
89
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
| `_underline_`, `~strike~`, `=highlight=` | Underline, Strikeout, Span `.mark` |
|
|
170
|
-
| `{^sup^}`, `{,sub,}` | Superscript, Subscript |
|
|
171
|
-
| Headings + `{#id .class}` attributes | Header with Attr |
|
|
172
|
-
| Tables incl. rowspan/colspan and captions | Table (spans inverted to pandoc's origin-cell model) |
|
|
173
|
-
| `table.rowGroups` counts (PART 12 section 15) | TableHead, one TableBody per group with its RowHeadColumns and intermediate header rows, TableFoot |
|
|
174
|
-
| Cell attributes `\|{#id .cls k=v} text`, row attributes `\| a \|{.cls}` | the `Attr` pandoc's Cell and Row already carry |
|
|
175
|
-
| Footnotes (reference and inline `^[..]`) | Note |
|
|
176
|
-
| Math `` $`..` `` / `` $$`..` `` | Math Inline / Display |
|
|
177
|
-
| Images/quotes with `^ caption` lines | Figure |
|
|
178
|
-
| A bare `::: figure` composite (PART 9 section 4c) | Figure of nested Figures - pandoc's subfigure model - with the group caption on the outer one. An opener carrying a title or a `[label]` is not this production and stays a Div |
|
|
179
|
-
| `::: note` admonitions | Div `.admonition .note` (+ title paragraph) |
|
|
180
|
-
| Tabs / code-group panels, grouping `[label]` | Div; each `[label]` becomes a bold caption so panels stay distinguishable (graceful degradation) |
|
|
181
|
-
| `` `x`{=latex} `` / ```` ```=latex ```` | RawInline / RawBlock (target-routed by pandoc) |
|
|
182
|
-
| Citations `[@key]`, `[+@key]`, `[-@key, p. 33]` | Cite with one Citation per key (AuthorInText / SuppressAuthor / NormalCitation), the locator in the citation suffix, the verbatim source as the Cite content |
|
|
183
|
-
| `@mention`, `#tag`, `:ext[..]`, critic markup | classed Spans (documented degradation) |
|
|
184
|
-
| `[text]{.smallcaps}` | SmallCaps (pandoc's own class convention, both directions) |
|
|
185
|
-
| Frontmatter, nested: maps, block and flow sequences, sequences of maps | Meta, to the depth pandoc's own reader gives it |
|
|
186
|
-
| `::: \|` line blocks (verse) | LineBlock, one entry per line, an empty entry per stanza break |
|
|
187
|
-
| Ordered markers `1.` / `1)` / `a.` / `iv.` | OrderedList with the matching style and delimiter. Pandoc's example list `(@)` and its `(1)` marker have no Carve form and are reported |
|
|
188
|
-
| A no-break space the parser resolved (`\ `) | U+00A0 (the engines publish a private-use sentinel for it) |
|
|
189
|
-
| A reference link, image or footnote nothing defines (`[r][]`, `[^f]`) | the literal source as text, which is what Carve renders, plus a warning naming the label |
|
|
190
|
-
|
|
191
|
-
The complete node-by-node contract lives in the test goldens. Worked
|
|
192
|
-
input/output pairs in both directions - including how interactive constructs
|
|
193
|
-
degrade for print formats - are in [`examples/`](examples/README.md).
|
|
194
|
-
|
|
195
|
-
## Dangerous URL schemes are blanked
|
|
196
|
-
|
|
197
|
-
A link or image destination whose scheme Carve's spec denies (PART 9 §25 -
|
|
198
|
-
`javascript:`, `vbscript:`, `data:`, `file:`, and the OS protocol-handler class
|
|
199
|
-
such as `ms-msdt:`, `search-ms:`, `shell:`, `vscode:` and `jar:`) leaves this
|
|
200
|
-
bridge as an **empty target**, and an `unsafe-url-scheme` diagnostic is emitted
|
|
201
|
-
naming the scheme and the destination that was refused.
|
|
202
|
-
|
|
203
|
-
Carve's own HTML, Markdown and ANSI writers all blank it, and the clause binds
|
|
204
|
-
every target that emits a resolvable URL. Pandoc's targets are not HTML, but
|
|
205
|
-
`pandoc -f json -t html` is one command away - passing the scheme through here
|
|
206
|
-
would not be a narrower policy, it would be the same sink one step removed.
|
|
207
|
-
|
|
208
|
-
The diagnostic class is `lossy`, so `--fail-on-loss` stops on its dropped
|
|
209
|
-
fidelity and a caller reading
|
|
210
|
-
`diagnostics` can act on it. The scheme list and the scheme probe are mirrored
|
|
211
|
-
from the engine rather than invented, and a test drives the engine's own writer
|
|
212
|
-
over both to keep the mirror honest - the probe strips control characters and
|
|
213
|
-
Unicode whitespace before it reads a scheme, so `<U+202F>javascript:` and
|
|
214
|
-
`java<DEL>script:` are caught along with the plain spelling.
|
|
215
|
-
|
|
216
|
-
Nothing else is touched. An `https:`, `mailto:`, `tel:`, `ftp:`, relative or
|
|
217
|
-
fragment destination reaches pandoc exactly as written, and emits no
|
|
218
|
-
diagnostic.
|
|
219
|
-
|
|
220
|
-
> [!NOTE]
|
|
221
|
-
> This covers link and image **destinations**. An `{onclick=…}`, `{style=…}` or
|
|
222
|
-
> `{srcdoc=…}` attribute, which Carve's HTML writer also refuses, still reaches
|
|
223
|
-
> pandoc - see [#163](https://github.com/markup-carve/pandoc-carve/issues/163).
|
|
224
|
-
|
|
225
|
-
## Why a bridge, not a pandoc reader?
|
|
226
|
-
|
|
227
|
-
A native `Text.Pandoc.Readers.Carve` upstream would be a fourth full Carve
|
|
228
|
-
parser, written in Haskell, outside the conformance loop that keeps the three
|
|
229
|
-
official implementations byte-identical against the shared corpus. While the
|
|
230
|
-
spec is on its 0.x line and still moving, that reader would drift on pandoc's
|
|
231
|
-
release cadence and ship stale behavior. The bridge reuses the canonical,
|
|
232
|
-
conformance-tested `@markup-carve/carve` parser instead - correct by
|
|
233
|
-
construction, and it iterates in lockstep with the spec.
|
|
234
|
-
|
|
235
|
-
A pandoc custom Lua reader has the same drift problem (it would reimplement
|
|
236
|
-
the parser in Lua). Once the spec reaches 1.0 and stabilizes, contributing an
|
|
237
|
-
upstream reader becomes attractive - with this bridge's node map and the
|
|
238
|
-
conformance corpus as the oracle any port has to pass.
|
|
239
|
-
|
|
240
|
-
## Options
|
|
241
|
-
|
|
242
|
-
```js
|
|
243
|
-
carveToPandoc(src, {
|
|
244
|
-
roundtrip: true, // stamp attr wrappers for exact re-import
|
|
245
|
-
symbols: { heart: '♥' }, // resolve :name: symbols like the renderer would
|
|
246
|
-
listTable: false, // opt OUT: keep ::: list-table as a degraded div
|
|
247
|
-
citations: false, // opt OUT: read [@key] as an @mention
|
|
248
|
-
extensions: [], // extra Carve extensions for the parse
|
|
249
|
-
});
|
|
250
|
-
```
|
|
90
|
+
The CLI equivalent is `--roundtrip`. Private Span and Div metadata preserves
|
|
91
|
+
comments, attribute placement, typed citations, and future Carve nodes through
|
|
92
|
+
Pandoc JSON. Other writers may expose or discard that metadata. See the
|
|
93
|
+
[provenance envelope](docs/roundtrip-provenance.md) for its format and safety
|
|
94
|
+
rules.
|
|
251
95
|
|
|
252
|
-
|
|
253
|
-
`--no-citations`.
|
|
96
|
+
## Fidelity and limitations
|
|
254
97
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
`@` first and `[@doe1990]` is a `.mention` span, so an imported bibliography
|
|
259
|
-
becomes mentions; with `listTable` off a table this bridge itself wrote as
|
|
260
|
-
`::: list-table` returns as a `Div` of nested lists. Turn them off to get what
|
|
261
|
-
a processor with neither extension enabled would render.
|
|
98
|
+
Pandoc's AST does not represent every Carve construct, and output formats have
|
|
99
|
+
their own limits. The bridge reports those cases rather than silently dropping
|
|
100
|
+
them. Highlights include:
|
|
262
101
|
|
|
263
|
-
|
|
102
|
+
- underline, highlight, and custom attributes depend on target-format support;
|
|
103
|
+
- complex Carve table structure may be normalized by a writer;
|
|
104
|
+
- grouped UI constructs such as tabs degrade to labeled blocks;
|
|
105
|
+
- raw target content only survives in matching writers;
|
|
106
|
+
- comments and attribute placement require round-trip metadata.
|
|
264
107
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
checks it itself rather than trusting a green validator.
|
|
271
|
-
- A pandoc table with block content in a cell (a list, two paragraphs, a code
|
|
272
|
-
block) is imported as `::: list-table` rather than a pipe table, and that is
|
|
273
|
-
now the ONLY reason a table leaves the pipe form. PART 9 §16's pipe-table cell
|
|
274
|
-
holds inlines, so there is no pipe form for it, and the extension's cells are
|
|
275
|
-
list items that hold full blocks. Structure is preserved; what the extension
|
|
276
|
-
cannot spell is reported instead - a body group's attributes, a body boundary
|
|
277
|
-
no header row marks, and body groups that disagree on their row-head column
|
|
278
|
-
count. It converts back to the pandoc table by default; `listTable: false`
|
|
279
|
-
returns the degraded div instead.
|
|
280
|
-
- A pandoc table with ROW-HEAD COLUMNS stays a pipe table. `RowHeadColumns` says
|
|
281
|
-
the leading N cells of every body row are row headers, and a pipe table says
|
|
282
|
-
that per cell: `|= Mercury | 4,879.4 |` is a `<th scope="row">`. Marking the
|
|
283
|
-
cells needs no extension and round-trips, because the export direction derives
|
|
284
|
-
the count back from the leading run.
|
|
285
|
-
- On the way out, one TableBody per RUN of body rows that agree on that leading
|
|
286
|
-
run. Pandoc's `RowHeadColumns` is a count on a body and a `Table` holds a list
|
|
287
|
-
of bodies, so rows that disagree are simply different bodies. A table whose
|
|
288
|
-
body rows all agree emits exactly one body, as before.
|
|
289
|
-
- Pandoc `SmallCaps` has no Carve node and is not getting one: it imports as a
|
|
290
|
-
`[text]{.smallcaps}` span, with a warning saying so. The span is not a dead
|
|
291
|
-
end, though - the export direction reads that class back as `SmallCaps`, the
|
|
292
|
-
same convention pandoc's own markdown reader uses, so small caps survive
|
|
293
|
-
Pandoc -> Carve -> Pandoc and still reach the LaTeX, Typst and DOCX writers.
|
|
294
|
-
Other attributes on the span are preserved around it, exactly as pandoc does.
|
|
295
|
-
- Pandoc `Quoted` imports as literal curly quote characters (`“…”` / `‘…’`).
|
|
296
|
-
Carve has no quote node, and the characters are what an author would have
|
|
297
|
-
typed. This one is genuinely one-way: the text re-exports as `Str`, so the
|
|
298
|
-
quote kind and pandoc's locale-aware quoting are gone. Reported once per
|
|
299
|
-
document.
|
|
300
|
-
- A `ColSpec`'s `ColWidth` is dropped. Carve's table model has no width slot at
|
|
301
|
-
any level and pipe-table source cannot spell one, so there is nowhere to put
|
|
302
|
-
the number and no syntax that would reproduce it - a wontfix rather than a
|
|
303
|
-
gap. The drop is silent on purpose: pandoc derives a `ColWidth` for every
|
|
304
|
-
grid and multiline table from the ASCII column widths, so a diagnostic would
|
|
305
|
-
fire on the ordinary case and report a value the author never chose. Column
|
|
306
|
-
ALIGNMENT is carried in both directions; tables leaving the bridge always
|
|
307
|
-
carry `ColWidthDefault`, which pandoc's writers size themselves.
|
|
308
|
-
- A rowspan that starts in a header row and continues into the body is clipped
|
|
309
|
-
to an empty body cell, with a warning. Carve is the richer model here: its
|
|
310
|
-
rows are one flat list, while pandoc's `TableHead` and `TableBody` hold
|
|
311
|
-
separate row lists and confine a cell's `rowSpan` to its own section. Moving
|
|
312
|
-
the head/body boundary to make the span fit would silently reclassify a row,
|
|
313
|
-
and duplicating the origin's content would invent a cell the author never
|
|
314
|
-
wrote, so the grid keeps its shape and the diagnostic reports the loss.
|
|
315
|
-
- Reverse conversion keeps flattening for display targets: `pandocToCarve`
|
|
316
|
-
serializes through Carve 0.1 source, which has no spelling for row groups or
|
|
317
|
-
a short caption, so those fields survive only on the `pandocToCarveAst` path.
|
|
318
|
-
- Citations cross as pandoc's own citeproc convention, which is lossy in one
|
|
319
|
-
respect: pandoc's `Citation` has no locator field, so Carve's typed
|
|
320
|
-
`locatorLabel`/`locatorValue` pair is serialized into `citationSuffix` behind
|
|
321
|
-
a `, ` and citeproc re-derives it from there. The bridge does not rebuild
|
|
322
|
-
those two fields on the way back - the label table is section 4.2's, it lives
|
|
323
|
-
in the engine, and a second copy here would drift. The locator TEXT round-
|
|
324
|
-
trips byte for byte, so re-parsing the emitted source with the citations
|
|
325
|
-
extension restores the typed pair. With `roundtrip: true`, the private
|
|
326
|
-
provenance wrapper retains the original typed fields directly while its
|
|
327
|
-
enclosed native `Cite` remains available to citeproc. A group whose items mix `AuthorInText` and
|
|
328
|
-
`NormalCitation` cannot be spelled in Carve (the integral `+` is a property of
|
|
329
|
-
the whole cluster) and is imported as integral with a warning.
|
|
330
|
-
- Pandoc keeps its bibliography in document metadata, not in the AST, so
|
|
331
|
-
importing a `Cite` emits no `[@key]:` definition lines. The citation itself
|
|
332
|
-
round-trips: `citationNoteNum` is reproduced the way pandoc's own markdown
|
|
333
|
-
reader computes it (notes closed so far, plus one), so a `Cite` returns
|
|
334
|
-
unchanged rather than unchanged-except-one-integer.
|
|
335
|
-
- Block content inside metadata (pandoc's `MetaBlocks`, e.g. `abstract: |`)
|
|
336
|
-
round-trips as a YAML literal block scalar, the same spelling pandoc's own
|
|
337
|
-
markdown writer emits and its reader turns back into `MetaBlocks` - by the
|
|
338
|
-
FORM, so any key can carry it, not just `abstract`. The scalar keeps every
|
|
339
|
-
line and every blank line between them, so the paragraph structure survives
|
|
340
|
-
rather than flattening into one string. Every other `Meta` shape - maps,
|
|
341
|
-
lists, lists of maps, scalars, booleans - round-trips too.
|
|
342
|
-
- A `rowGroups` partition (a foot, several body groups, a body's own header
|
|
343
|
-
rows or row-head columns) survives into the exchange AST, so
|
|
344
|
-
`pandocToCarveAst` hands it on whole. `pandocToCarve` spells most of it too: a
|
|
345
|
-
pipe table states its head and foot row counts on its attribute line
|
|
346
|
-
(`{header-rows=2 footer-rows=1}`) and marks its row headers on the cells, so
|
|
347
|
-
what flattens into body rows is a second body group and a body's own
|
|
348
|
-
intermediate header rows and attributes - reported, not dropped quietly. Use
|
|
349
|
-
the AST entry point when a second body group has to survive.
|
|
350
|
-
- Stating the foot states the whole partition, and a stated partition is ONE
|
|
351
|
-
body. A body carries one `RowHeadColumns`, so when the body rows disagree on
|
|
352
|
-
how many leading cells are row headers, a table with a foot cannot keep them:
|
|
353
|
-
the reader takes a count every row agrees on, and the disagreement is reported
|
|
354
|
-
from both directions. Without a foot the reader splits a body at every change
|
|
355
|
-
and every run survives.
|
|
356
|
-
- A marker on a HEAD cell (`|=> Name |`) is the column's alignment and becomes
|
|
357
|
-
pandoc's `ColSpec`; a marker on a BODY cell (`|> 12 |`) aligns that cell alone
|
|
358
|
-
and becomes the cell's own `Alignment`. The two are not interchangeable: a
|
|
359
|
-
pandoc cell with `AlignDefault` inherits its ColSpec, so promoting a body
|
|
360
|
-
cell's marker to the column would align cells the author did not.
|
|
361
|
-
- Column alignment needs a header row to live on (`|=> Name |`). A headerless
|
|
362
|
-
pandoc table's column alignment is written onto each cell instead, which
|
|
363
|
-
renders the same; the move is reported.
|
|
364
|
-
- The frontmatter reader covers the YAML subset frontmatter uses, not YAML.
|
|
365
|
-
Anchors, tags, multi-document streams, block scalars and flow maps are out; a
|
|
366
|
-
line that fits no shape is reported and skipped rather than guessed at.
|
|
367
|
-
- Scalars are typed the way pandoc's own frontmatter reader types them: an
|
|
368
|
-
unquoted `true`, `yes`, `on` or `y` (and their negatives) is a `MetaBool`, a
|
|
369
|
-
quoted `"true"` stays text, a null scalar keeps its key as an empty
|
|
370
|
-
`MetaString`, and a number stays text because `Meta` has no number type. This
|
|
371
|
-
matters beyond tidiness - a boolean read as text is not a near miss but an
|
|
372
|
-
inversion, since a pandoc template testing `draft` sees any non-empty string
|
|
373
|
-
as true.
|
|
374
|
-
- Attribute order inside `{...}` is normalized to `#id .class key=val` on
|
|
375
|
-
round-trip - pandoc's Attr has fixed slots, so the author's original order
|
|
376
|
-
is not representable. Semantics are unchanged.
|
|
377
|
-
- Import fidelity is bounded by `renderCarve` (carve fmt): the bridge hands it
|
|
378
|
-
a byte-exact AST, but known fmt issues (e.g. trailing whitespace inside code
|
|
379
|
-
blocks, carve-js issue 340) surface in the serialized output.
|
|
380
|
-
- The pinned engine bounds what the source path can produce. The current git
|
|
381
|
-
pin exports `toAstJson`, so the engine's own exchange serializer is used;
|
|
382
|
-
with an older engine (any published release up to `0.1.3`),
|
|
383
|
-
`src/ast-json.ts` performs the PART 12 section 7 mapping instead, and a pin
|
|
384
|
-
that exports `toAstJson` takes over automatically.
|
|
385
|
-
- Tier-3 visual extensions (mermaid, chart, code-group) arrive as their
|
|
386
|
-
degraded block forms (code blocks / divs), same as Carve's static mode.
|
|
387
|
-
`list-table` is the exception: it converts to a real Pandoc table with full
|
|
388
|
-
block content per cell, unless `listTable: false` / `--no-list-table`.
|
|
108
|
+
Dangerous URL schemes such as `javascript:` are blanked and reported. The
|
|
109
|
+
[URL policy](docs/reference.md#dangerous-url-schemes-are-blanked) documents the
|
|
110
|
+
exact behavior. The [mapping and limitations reference](docs/reference.md#what-maps-to-what)
|
|
111
|
+
contains the node table and per-format limits; the [options section](docs/reference.md#options)
|
|
112
|
+
covers API configuration.
|
|
389
113
|
|
|
390
114
|
## Development
|
|
391
115
|
|
|
392
|
-
Contributor setup,
|
|
116
|
+
Contributor setup, tests, and maintenance commands are in the
|
|
117
|
+
[development guide](docs/development.md).
|
package/dist/ast-json.js
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
*/
|
|
39
39
|
const CHILD_FIELDS = [
|
|
40
40
|
'children',
|
|
41
|
+
'blocks',
|
|
41
42
|
'items',
|
|
42
43
|
'rows',
|
|
43
44
|
'cells',
|
|
@@ -60,6 +61,13 @@ function isObject(value) {
|
|
|
60
61
|
* object, so the runtime tree the caller still holds is never copied and never
|
|
61
62
|
* mutated.
|
|
62
63
|
*/
|
|
64
|
+
/**
|
|
65
|
+
* The six kinds a `:::` container places rather than presents. They are
|
|
66
|
+
* `directive` nodes, not admonitions (carve#2195, carve#2225): a table of
|
|
67
|
+
* contents is not a callout. Releases up to 0.1.7 spell them `admonition`,
|
|
68
|
+
* and the schema refuses that `kind` there.
|
|
69
|
+
*/
|
|
70
|
+
const DIRECTIVE_KINDS = new Set(['bibliography', 'footnotes', 'glossary', 'index', 'references', 'toc']);
|
|
63
71
|
function normalize(node) {
|
|
64
72
|
if (Array.isArray(node)) {
|
|
65
73
|
let changed = false;
|
|
@@ -152,6 +160,20 @@ function normalize(node) {
|
|
|
152
160
|
default:
|
|
153
161
|
break;
|
|
154
162
|
}
|
|
163
|
+
// A reference's label is spelled `label` on the wire, like the `footnote`
|
|
164
|
+
// definition it matches (carve#2213). Releases up to 0.1.7 serialize `id`,
|
|
165
|
+
// which the schema refuses - it sanctions decoding the old property onto
|
|
166
|
+
// the field (PART 12 section 11), which is what this does. Remove it once
|
|
167
|
+
// no supported release emits `id`. After the switch, because the pre-split
|
|
168
|
+
// fold above reaches `footnote_ref` by changing `type` rather than matching it.
|
|
169
|
+
const settled = out ?? node;
|
|
170
|
+
if (settled['type'] === 'admonition' && DIRECTIVE_KINDS.has(String(settled['kind']))) {
|
|
171
|
+
out = { ...settled, type: 'directive' };
|
|
172
|
+
}
|
|
173
|
+
if (settled['type'] === 'footnote_ref' && typeof settled['id'] === 'string') {
|
|
174
|
+
const { id, ...rest } = settled;
|
|
175
|
+
out = { ...rest, label: id };
|
|
176
|
+
}
|
|
155
177
|
return (out ?? node);
|
|
156
178
|
}
|
|
157
179
|
/**
|
package/dist/convert.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export interface ConvertResult {
|
|
|
19
19
|
report: MigrationReport;
|
|
20
20
|
}
|
|
21
21
|
export interface ConvertOptions {
|
|
22
|
+
/** Interpret U+E000 as the old generated-space marker in external AST input.
|
|
23
|
+
* Use only for known legacy producers; literal U+E000 cannot be recovered.
|
|
24
|
+
*/
|
|
25
|
+
legacySpaceSentinels?: boolean;
|
|
22
26
|
/**
|
|
23
27
|
* Stamp attr-wrapper Divs with a `carve-block` key-value marker so the
|
|
24
28
|
* reverse direction can restore the attrs onto the inner block. Off by
|