Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/resources/tools/ast-tracing/convert-pandoc-json.js
12922 views
1
// converts our trace filter's JSON output to a more compact format
2
// whose diffs are easier to read
3
//
4
// this doesn't work on arbitrary pandoc JSON, since we
5
// treat custom nodes in a particular way
6
7
const postProcessStrings = (array) => {
8
if (array.length === 0) {
9
return [];
10
}
11
const result = [array[0]];
12
for (const el of array.slice(1)) {
13
if (
14
typeof result[result.length - 1] !== "string" ||
15
typeof el !== "string"
16
) {
17
result.push(el);
18
continue;
19
}
20
result[result.length - 1] = result[result.length - 1] + el;
21
}
22
if (result.length === 1 && typeof result[0] === "string") {
23
return result[0];
24
}
25
return result;
26
};
27
28
const convertListAttributes = (listAttr) => ({
29
start: listAttr[0],
30
style: listAttr[1].t,
31
delimiter: listAttr[2].t,
32
});
33
34
const convertAttr = (attr) =>
35
`('${attr[0]}', [${attr[1].map((s) => `'${s}'`).join(", ")}], [${attr[2]
36
.map((s) => `'${s}'`)
37
.join(", ")}])`;
38
const convertCitation = (c) => c;
39
40
// FIXME
41
const convertCaption = (caption) => caption;
42
43
const convertColSpec = (colSpec) => {
44
const align = colSpec[0].t;
45
const width = colSpec[1].c;
46
47
return `(${align}, ${width})`;
48
};
49
50
const convertCell = (cell) => {
51
return {
52
t: "TableCell",
53
attr: convertAttr(cell[0]),
54
alignment: cell[1].t,
55
row_span: cell[2],
56
col_span: cell[3],
57
content: convert(cell[4]),
58
};
59
};
60
61
const convertRow = (row) => {
62
return {
63
t: "TableRow",
64
attr: convertAttr(row[0]),
65
cells: row[1].map(convertCell),
66
};
67
};
68
69
const convertTableHead = (head) => {
70
return {
71
t: "TableHead",
72
attr: convertAttr(head[0]),
73
rows: head[1].map(convertRow),
74
};
75
};
76
77
const convertTableBody = (body) => {
78
return {
79
t: "TableBody",
80
row_head_columns: body[1],
81
attr: convertAttr(body[0]),
82
intermediate_head: body[2].map(convertRow),
83
body: body[3].map(convertRow),
84
};
85
};
86
87
const convertTableFoot = (foot) => {
88
return {
89
t: "TableFoot",
90
attr: convertAttr(foot[0]),
91
rows: foot[1].map(convertRow),
92
};
93
};
94
95
const convert = (data) => {
96
if (Array.isArray(data)) {
97
return postProcessStrings(data.map(convert).flat());
98
}
99
if (typeof data === "object") {
100
const constMap = {
101
Space: " ",
102
Null: null,
103
SingleQuote: "'",
104
DoubleQuote: '"',
105
};
106
if (constMap[data.t]) {
107
return constMap[data.t];
108
}
109
if (data.t === "Str") return data.c;
110
if (
111
[
112
"BlockQuote",
113
"BulletList",
114
"Plain",
115
"Para",
116
"Strong",
117
"Emph",
118
"Underline",
119
"Strikeout",
120
"Quoted",
121
"SingleQuote",
122
"Note",
123
].includes(data.t)
124
) {
125
return {
126
t: data.t,
127
content: convert(data.c),
128
};
129
}
130
if (
131
["AlignLeft", "AlignRight", "AlignCenter", "AlignDefault"].includes(
132
data.t
133
)
134
) {
135
return data.t;
136
}
137
if (data.t === "Table") {
138
return {
139
t: data.t,
140
attr: convertAttr(data.c[0]),
141
caption: convertCaption(data.c[1]),
142
colspecs: data.c[2].map(convertColSpec),
143
head: convertTableHead(data.c[3]),
144
body: data.c[4].map(convertTableBody),
145
foot: convertTableFoot(data.c[5]),
146
};
147
}
148
if (data.t === "Code") {
149
return {
150
t: data.t,
151
attr: convertAttr(data.c[0]),
152
text: data.c[1],
153
};
154
}
155
if (data.t === "Cite") {
156
return {
157
t: data.t,
158
content: convert(data.c[1]),
159
citations: data.c[0].map(convertCitation),
160
};
161
}
162
if (
163
data.t === "Div" &&
164
data.c[0][2].find((x) => x[0] === "__quarto_custom_table")
165
) {
166
const attributes = Object.fromEntries(data.c[0][2]);
167
const entry = attributes["__quarto_custom_table"];
168
// const t = attributes["__quarto_custom_type"];
169
const customTable = JSON.parse(entry);
170
return {
171
customAST: true,
172
...customTable,
173
scaffold: convert(data.c.slice(1)),
174
};
175
}
176
if (data.t === "Div" || data.t === "Span") {
177
return {
178
t: data.t,
179
attr: convertAttr(data.c[0]),
180
content: convert(data.c.slice(1)),
181
};
182
}
183
if (data.t === "Header") {
184
return {
185
t: data.t,
186
level: data.c[0],
187
attr: convertAttr(data.c[1]),
188
content: convert(data.c.slice(2)),
189
};
190
}
191
if (data.t === "Link") {
192
return {
193
t: data.t,
194
attr: convertAttr(data.c[0]),
195
content: convert(data.c[1]),
196
target: data.c[2][0],
197
title: data.c[2][1],
198
};
199
}
200
if (data.t === "Image") {
201
return {
202
t: data.t,
203
attr: convertAttr(data.c[0]),
204
caption: convert(data.c[1]),
205
src: data.c[2][0],
206
title: data.c[2][1],
207
};
208
}
209
if (data.t === "CodeBlock") {
210
return {
211
t: data.t,
212
attr: convertAttr(data.c[0]),
213
text: data.c[1],
214
};
215
}
216
if (data.t === "OrderedList") {
217
return {
218
t: data.t,
219
listAttributes: convertListAttributes(data.c[0]),
220
content: convert(data.c[1]),
221
};
222
}
223
if (data.t === "MetaInlines" || data.t === "MetaBlocks") {
224
return postProcessStrings(data.c.map(convert));
225
}
226
if (data.t === "MetaBool") {
227
return data.c;
228
}
229
if (data.t === "SoftBreak") {
230
return "";
231
}
232
if (data.t === "LineBreak") {
233
return "\n";
234
}
235
236
if (data.t === "MetaString") {
237
return data.c;
238
}
239
if (data.t === "MetaList") {
240
return postProcessStrings(data.c.map(convert));
241
}
242
if (data.t === "MetaMap") {
243
return convertMeta(data.c);
244
}
245
if (data.t === "RawBlock" || data.t === "RawInline") {
246
return {
247
t: data.t,
248
format: data.c[0],
249
text: data.c[1],
250
};
251
}
252
if (data.t === "Figure") {
253
return {
254
t: data.t,
255
attr: convertAttr(data.c[0]),
256
content: convert(data.c[2]),
257
caption: convertCaption(data.c[1]),
258
};
259
}
260
if (data.t === "DefinitionList") {
261
return {
262
t: data.t,
263
content: data.c.map(convert),
264
};
265
}
266
if (data.t === "Math") {
267
return {
268
t: data.t,
269
displayType: data.c[0].t,
270
text: data.c[1],
271
};
272
}
273
if (data.t === "HorizontalRule") {
274
return data;
275
}
276
throw new Error(`Can't handle type ${data.t}`);
277
} else if (typeof data === "string") {
278
return data;
279
}
280
return {
281
name: "<value>",
282
children: [],
283
};
284
};
285
286
const convertMeta = (meta) => {
287
return Object.fromEntries(
288
Object.entries(meta).map(([key, value]) => {
289
return [key, convert(value)];
290
})
291
);
292
};
293
294
export const convertDoc = (doc) => {
295
return {
296
meta: convertMeta(doc.meta),
297
"pandoc-api-version": doc["pandoc-api-version"].join(","),
298
blocks: doc.blocks.map(convert),
299
};
300
};
301
302