@illusions-lab/mdi-to-docx 2.0.12 → 2.0.13

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/dist/index.cjs CHANGED
@@ -32,7 +32,14 @@ async function mdiToDocx(tree, profile) {
32
32
  profile,
33
33
  tree.data?.frontmatter?.writingMode
34
34
  );
35
- const children = tree.children.flatMap((node) => block(node, options));
35
+ const definitions = tree.children.filter(
36
+ (node) => node.type === "footnoteDefinition"
37
+ );
38
+ const footnoteIds = new Map(
39
+ definitions.map((definition, index) => [definition.identifier, index + 1])
40
+ );
41
+ const context = { footnoteIds };
42
+ const children = tree.children.flatMap((node) => block(node, options, context));
36
43
  const dimensions = import_mdi_export_profile.PAGE_DIMENSIONS[options.pagination.pageSize];
37
44
  const widthMm = options.pagination.landscape ? dimensions.height : dimensions.width;
38
45
  const heightMm = options.pagination.landscape ? dimensions.width : dimensions.height;
@@ -46,6 +53,16 @@ async function mdiToDocx(tree, profile) {
46
53
  const document = new import_docx.Document({
47
54
  title: options.metadata.title ?? tree.data?.frontmatter?.title,
48
55
  creator: options.metadata.author ?? tree.data?.frontmatter?.author,
56
+ footnotes: Object.fromEntries(
57
+ definitions.map((definition, index) => [
58
+ String(index + 1),
59
+ {
60
+ children: definition.children.flatMap(
61
+ (child) => child.type === "paragraph" ? [new import_docx.Paragraph({ children: inline(child.children, context) })] : []
62
+ )
63
+ }
64
+ ])
65
+ ),
49
66
  styles: {
50
67
  default: {
51
68
  document: {
@@ -176,7 +193,7 @@ function pageNumberSection(options, size) {
176
193
  const paragraph2 = new import_docx.Paragraph({ alignment, children: runs });
177
194
  return page.position.startsWith("top-") ? { headers: { default: new import_docx.Header({ children: [paragraph2] }) } } : { footers: { default: new import_docx.Footer({ children: [paragraph2] }) } };
178
195
  }
179
- function block(node, options) {
196
+ function block(node, options, context) {
180
197
  if (node.type === "heading")
181
198
  return [
182
199
  new import_docx.Paragraph({
@@ -188,23 +205,23 @@ function block(node, options) {
188
205
  import_docx.HeadingLevel.HEADING_5,
189
206
  import_docx.HeadingLevel.HEADING_6
190
207
  ][node.depth - 1],
191
- children: inline(node.children)
208
+ children: inline(node.children, context)
192
209
  })
193
210
  ];
194
- if (node.type === "paragraph") return [paragraph(node.children, options)];
211
+ if (node.type === "paragraph") return [paragraph(node.children, options, void 0, context)];
195
212
  if (node.type === "list")
196
213
  return node.children.flatMap(
197
214
  (item) => item.children.filter((n) => n.type === "paragraph").map(
198
215
  (n) => new import_docx.Paragraph({
199
216
  style: "MdiList",
200
- children: inline(n.children),
217
+ children: inline(n.children, context),
201
218
  bullet: { level: 0 }
202
219
  })
203
220
  )
204
221
  );
205
222
  if (node.type === "blockquote")
206
223
  return node.children.flatMap(
207
- (child) => child.type === "paragraph" ? [paragraph(child.children, options, "MdiQuote")] : block(child, options)
224
+ (child) => child.type === "paragraph" ? [paragraph(child.children, options, "MdiQuote", context)] : block(child, options, context)
208
225
  );
209
226
  if (node.type === "code")
210
227
  return [
@@ -227,13 +244,13 @@ function block(node, options) {
227
244
  children: [new import_docx.TextRun("\u2014 \u2014 \u2014")]
228
245
  })
229
246
  ];
230
- if (node.type === "table") return [tableBlock(node, options)];
247
+ if (node.type === "table") return [tableBlock(node, options, context)];
231
248
  if (node.type === "mdiPagebreak")
232
249
  return [new import_docx.Paragraph({ children: [new import_docx.PageBreak()] })];
233
250
  if (node.type === "mdiBlank") return [new import_docx.Paragraph("")];
234
251
  return [];
235
252
  }
236
- function tableBlock(node, options) {
253
+ function tableBlock(node, options, context) {
237
254
  const columns = Math.max(1, ...node.children.map((row) => row.children.length));
238
255
  const dimensions = import_mdi_export_profile.PAGE_DIMENSIONS[options.pagination.pageSize];
239
256
  const pageWidth = options.pagination.landscape ? dimensions.height : dimensions.width;
@@ -253,7 +270,7 @@ function tableBlock(node, options) {
253
270
  width: { size: columnWidth, type: import_docx.WidthType.DXA },
254
271
  children: [
255
272
  new import_docx.Paragraph({
256
- children: rowIndex === 0 ? [new import_docx.TextRun({ bold: true, children: inline(cell.children) })] : inline(cell.children)
273
+ children: rowIndex === 0 ? [new import_docx.TextRun({ text: inlineText(cell.children), bold: true })] : inline(cell.children, context)
257
274
  })
258
275
  ]
259
276
  })
@@ -262,7 +279,7 @@ function tableBlock(node, options) {
262
279
  )
263
280
  });
264
281
  }
265
- function paragraph(nodes, options, style) {
282
+ function paragraph(nodes, options, style, context) {
266
283
  const fontSizeMm = (() => {
267
284
  const dimensions = import_mdi_export_profile.PAGE_DIMENSIONS[options.pagination.pageSize];
268
285
  const primary = options.typesetting.writingMode === "vertical" ? (options.pagination.landscape ? dimensions.width : dimensions.height) - options.pagination.margins.top - options.pagination.margins.bottom : (options.pagination.landscape ? dimensions.height : dimensions.width) - options.pagination.margins.left - options.pagination.margins.right;
@@ -278,26 +295,35 @@ function paragraph(nodes, options, style) {
278
295
  )
279
296
  }
280
297
  },
281
- children: [...prefix, ...inline(nodes)]
298
+ children: [...prefix, ...inline(nodes, context)]
282
299
  });
283
300
  }
284
- function inline(nodes) {
301
+ function inline(nodes, context) {
285
302
  return nodes.flatMap((node) => {
286
303
  if (node.type === "text") return [new import_docx.TextRun(node.value)];
287
304
  if (node.type === "break" || node.type === "mdiBreak")
288
305
  return [new import_docx.TextRun({ break: 1 })];
289
306
  if (node.type === "emphasis")
290
- return [new import_docx.TextRun({ italics: true, children: inline(node.children) })];
307
+ return [new import_docx.TextRun({ italics: true, children: inline(node.children, context) })];
291
308
  if (node.type === "strong")
292
- return [new import_docx.TextRun({ bold: true, children: inline(node.children) })];
309
+ return [new import_docx.TextRun({ bold: true, children: inline(node.children, context) })];
293
310
  if (node.type === "delete")
294
- return [new import_docx.TextRun({ strike: true, children: inline(node.children) })];
311
+ return [new import_docx.TextRun({ strike: true, children: inline(node.children, context) })];
295
312
  if (node.type === "inlineCode")
296
313
  return [new import_docx.TextRun({ text: node.value, font: "Courier New" })];
297
314
  if (node.type === "image")
298
315
  return [new import_docx.TextRun({ text: node.alt ? `[Image: ${node.alt}]` : "[Image]" })];
299
- if (node.type === "footnoteReference")
300
- return [new import_docx.TextRun({ text: `[${node.label ?? node.identifier}]`, superScript: true })];
316
+ if (node.type === "link")
317
+ return [
318
+ new import_docx.ExternalHyperlink({
319
+ link: node.url,
320
+ children: inline(node.children, context)
321
+ })
322
+ ];
323
+ if (node.type === "footnoteReference") {
324
+ const id = context.footnoteIds.get(node.identifier);
325
+ return id ? [new import_docx.FootnoteReferenceRun(id)] : [];
326
+ }
301
327
  if (node.type === "mdiRuby") return [rawRun(rubyXml(node.base, node.ruby))];
302
328
  if (node.type === "mdiTcy")
303
329
  return [
@@ -307,10 +333,13 @@ function inline(nodes) {
307
333
  )}</w:t></w:r>`
308
334
  )
309
335
  ];
310
- if ("children" in node) return inline(node.children);
336
+ if ("children" in node) return inline(node.children, context);
311
337
  return [];
312
338
  });
313
339
  }
340
+ function inlineText(nodes) {
341
+ return nodes.map((node) => node.type === "text" ? node.value : "children" in node ? inlineText(node.children) : "").join("");
342
+ }
314
343
  function mmToTwips(mm) {
315
344
  return Math.round(mm / 25.4 * 1440);
316
345
  }
package/dist/index.js CHANGED
@@ -2,7 +2,9 @@
2
2
  import {
3
3
  AlignmentType,
4
4
  Document,
5
+ ExternalHyperlink,
5
6
  Footer,
7
+ FootnoteReferenceRun,
6
8
  HeadingLevel,
7
9
  Header,
8
10
  ImportedXmlComponent,
@@ -28,7 +30,14 @@ async function mdiToDocx(tree, profile) {
28
30
  profile,
29
31
  tree.data?.frontmatter?.writingMode
30
32
  );
31
- const children = tree.children.flatMap((node) => block(node, options));
33
+ const definitions = tree.children.filter(
34
+ (node) => node.type === "footnoteDefinition"
35
+ );
36
+ const footnoteIds = new Map(
37
+ definitions.map((definition, index) => [definition.identifier, index + 1])
38
+ );
39
+ const context = { footnoteIds };
40
+ const children = tree.children.flatMap((node) => block(node, options, context));
32
41
  const dimensions = PAGE_DIMENSIONS[options.pagination.pageSize];
33
42
  const widthMm = options.pagination.landscape ? dimensions.height : dimensions.width;
34
43
  const heightMm = options.pagination.landscape ? dimensions.width : dimensions.height;
@@ -42,6 +51,16 @@ async function mdiToDocx(tree, profile) {
42
51
  const document = new Document({
43
52
  title: options.metadata.title ?? tree.data?.frontmatter?.title,
44
53
  creator: options.metadata.author ?? tree.data?.frontmatter?.author,
54
+ footnotes: Object.fromEntries(
55
+ definitions.map((definition, index) => [
56
+ String(index + 1),
57
+ {
58
+ children: definition.children.flatMap(
59
+ (child) => child.type === "paragraph" ? [new Paragraph({ children: inline(child.children, context) })] : []
60
+ )
61
+ }
62
+ ])
63
+ ),
45
64
  styles: {
46
65
  default: {
47
66
  document: {
@@ -172,7 +191,7 @@ function pageNumberSection(options, size) {
172
191
  const paragraph2 = new Paragraph({ alignment, children: runs });
173
192
  return page.position.startsWith("top-") ? { headers: { default: new Header({ children: [paragraph2] }) } } : { footers: { default: new Footer({ children: [paragraph2] }) } };
174
193
  }
175
- function block(node, options) {
194
+ function block(node, options, context) {
176
195
  if (node.type === "heading")
177
196
  return [
178
197
  new Paragraph({
@@ -184,23 +203,23 @@ function block(node, options) {
184
203
  HeadingLevel.HEADING_5,
185
204
  HeadingLevel.HEADING_6
186
205
  ][node.depth - 1],
187
- children: inline(node.children)
206
+ children: inline(node.children, context)
188
207
  })
189
208
  ];
190
- if (node.type === "paragraph") return [paragraph(node.children, options)];
209
+ if (node.type === "paragraph") return [paragraph(node.children, options, void 0, context)];
191
210
  if (node.type === "list")
192
211
  return node.children.flatMap(
193
212
  (item) => item.children.filter((n) => n.type === "paragraph").map(
194
213
  (n) => new Paragraph({
195
214
  style: "MdiList",
196
- children: inline(n.children),
215
+ children: inline(n.children, context),
197
216
  bullet: { level: 0 }
198
217
  })
199
218
  )
200
219
  );
201
220
  if (node.type === "blockquote")
202
221
  return node.children.flatMap(
203
- (child) => child.type === "paragraph" ? [paragraph(child.children, options, "MdiQuote")] : block(child, options)
222
+ (child) => child.type === "paragraph" ? [paragraph(child.children, options, "MdiQuote", context)] : block(child, options, context)
204
223
  );
205
224
  if (node.type === "code")
206
225
  return [
@@ -223,13 +242,13 @@ function block(node, options) {
223
242
  children: [new TextRun("\u2014 \u2014 \u2014")]
224
243
  })
225
244
  ];
226
- if (node.type === "table") return [tableBlock(node, options)];
245
+ if (node.type === "table") return [tableBlock(node, options, context)];
227
246
  if (node.type === "mdiPagebreak")
228
247
  return [new Paragraph({ children: [new PageBreak()] })];
229
248
  if (node.type === "mdiBlank") return [new Paragraph("")];
230
249
  return [];
231
250
  }
232
- function tableBlock(node, options) {
251
+ function tableBlock(node, options, context) {
233
252
  const columns = Math.max(1, ...node.children.map((row) => row.children.length));
234
253
  const dimensions = PAGE_DIMENSIONS[options.pagination.pageSize];
235
254
  const pageWidth = options.pagination.landscape ? dimensions.height : dimensions.width;
@@ -249,7 +268,7 @@ function tableBlock(node, options) {
249
268
  width: { size: columnWidth, type: WidthType.DXA },
250
269
  children: [
251
270
  new Paragraph({
252
- children: rowIndex === 0 ? [new TextRun({ bold: true, children: inline(cell.children) })] : inline(cell.children)
271
+ children: rowIndex === 0 ? [new TextRun({ text: inlineText(cell.children), bold: true })] : inline(cell.children, context)
253
272
  })
254
273
  ]
255
274
  })
@@ -258,7 +277,7 @@ function tableBlock(node, options) {
258
277
  )
259
278
  });
260
279
  }
261
- function paragraph(nodes, options, style) {
280
+ function paragraph(nodes, options, style, context) {
262
281
  const fontSizeMm = (() => {
263
282
  const dimensions = PAGE_DIMENSIONS[options.pagination.pageSize];
264
283
  const primary = options.typesetting.writingMode === "vertical" ? (options.pagination.landscape ? dimensions.width : dimensions.height) - options.pagination.margins.top - options.pagination.margins.bottom : (options.pagination.landscape ? dimensions.height : dimensions.width) - options.pagination.margins.left - options.pagination.margins.right;
@@ -274,26 +293,35 @@ function paragraph(nodes, options, style) {
274
293
  )
275
294
  }
276
295
  },
277
- children: [...prefix, ...inline(nodes)]
296
+ children: [...prefix, ...inline(nodes, context)]
278
297
  });
279
298
  }
280
- function inline(nodes) {
299
+ function inline(nodes, context) {
281
300
  return nodes.flatMap((node) => {
282
301
  if (node.type === "text") return [new TextRun(node.value)];
283
302
  if (node.type === "break" || node.type === "mdiBreak")
284
303
  return [new TextRun({ break: 1 })];
285
304
  if (node.type === "emphasis")
286
- return [new TextRun({ italics: true, children: inline(node.children) })];
305
+ return [new TextRun({ italics: true, children: inline(node.children, context) })];
287
306
  if (node.type === "strong")
288
- return [new TextRun({ bold: true, children: inline(node.children) })];
307
+ return [new TextRun({ bold: true, children: inline(node.children, context) })];
289
308
  if (node.type === "delete")
290
- return [new TextRun({ strike: true, children: inline(node.children) })];
309
+ return [new TextRun({ strike: true, children: inline(node.children, context) })];
291
310
  if (node.type === "inlineCode")
292
311
  return [new TextRun({ text: node.value, font: "Courier New" })];
293
312
  if (node.type === "image")
294
313
  return [new TextRun({ text: node.alt ? `[Image: ${node.alt}]` : "[Image]" })];
295
- if (node.type === "footnoteReference")
296
- return [new TextRun({ text: `[${node.label ?? node.identifier}]`, superScript: true })];
314
+ if (node.type === "link")
315
+ return [
316
+ new ExternalHyperlink({
317
+ link: node.url,
318
+ children: inline(node.children, context)
319
+ })
320
+ ];
321
+ if (node.type === "footnoteReference") {
322
+ const id = context.footnoteIds.get(node.identifier);
323
+ return id ? [new FootnoteReferenceRun(id)] : [];
324
+ }
297
325
  if (node.type === "mdiRuby") return [rawRun(rubyXml(node.base, node.ruby))];
298
326
  if (node.type === "mdiTcy")
299
327
  return [
@@ -303,10 +331,13 @@ function inline(nodes) {
303
331
  )}</w:t></w:r>`
304
332
  )
305
333
  ];
306
- if ("children" in node) return inline(node.children);
334
+ if ("children" in node) return inline(node.children, context);
307
335
  return [];
308
336
  });
309
337
  }
338
+ function inlineText(nodes) {
339
+ return nodes.map((node) => node.type === "text" ? node.value : "children" in node ? inlineText(node.children) : "").join("");
340
+ }
310
341
  function mmToTwips(mm) {
311
342
  return Math.round(mm / 25.4 * 1440);
312
343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@illusions-lab/mdi-to-docx",
3
- "version": "2.0.12",
3
+ "version": "2.0.13",
4
4
  "description": "Render MDI (.mdi) documents to DOCX",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  "dependencies": {
26
26
  "docx": "^9.0.0",
27
27
  "@types/mdast": "^4.0.0",
28
- "@illusions-lab/mdi-export-profile": "2.0.7",
29
- "mdast-util-mdi": "2.0.11"
28
+ "@illusions-lab/mdi-export-profile": "2.0.8",
29
+ "mdast-util-mdi": "2.0.12"
30
30
  },
31
31
  "devDependencies": {
32
32
  "remark-parse": "^11.0.0",
@@ -36,7 +36,7 @@
36
36
  "typescript": "^5.6.0",
37
37
  "vitest": "^2.1.0",
38
38
  "@types/node": "^20.0.0",
39
- "@illusions-lab/mdi-remark": "2.0.12"
39
+ "@illusions-lab/mdi-remark": "2.0.13"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsup src/index.ts --format esm,cjs --dts",