remark-docx 0.1.7 → 0.1.8
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 +3 -3
- package/lib/index.js +134 -122
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +134 -122
- package/lib/index.mjs.map +1 -1
- package/lib/{transformer.d.ts → mdast-to-docx.d.ts} +1 -13
- package/lib/plugin.d.ts +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ If you have some feature requests or improvements, please create a [issue](https
|
|
|
23
23
|
- [ ] code
|
|
24
24
|
- [ ] yaml
|
|
25
25
|
- [ ] toml
|
|
26
|
-
- [
|
|
26
|
+
- [x] definition
|
|
27
27
|
- [x] footnoteDefinition
|
|
28
28
|
- [x] text
|
|
29
29
|
- [x] emphasis
|
|
@@ -33,8 +33,8 @@ If you have some feature requests or improvements, please create a [issue](https
|
|
|
33
33
|
- [x] break
|
|
34
34
|
- [x] link
|
|
35
35
|
- [x] image
|
|
36
|
-
- [
|
|
37
|
-
- [
|
|
36
|
+
- [x] linkReference
|
|
37
|
+
- [x] imageReference
|
|
38
38
|
- [x] footnote
|
|
39
39
|
- [x] footnoteReference
|
|
40
40
|
- [x] LaTeX support with math and inlineMath ([remark-math](https://github.com/remarkjs/remark-math) is required)
|
package/lib/index.js
CHANGED
|
@@ -427,13 +427,44 @@ const DEFAULT_NUMBERINGS = [
|
|
|
427
427
|
},
|
|
428
428
|
},
|
|
429
429
|
];
|
|
430
|
+
const createFootnoteRegistry = () => {
|
|
431
|
+
const idToInternalId = new Map();
|
|
432
|
+
const defs = new Map();
|
|
433
|
+
const getId = (id) => {
|
|
434
|
+
let internalId = idToInternalId.get(id);
|
|
435
|
+
if (internalId == null) {
|
|
436
|
+
idToInternalId.set(id, (internalId = idToInternalId.size + 1));
|
|
437
|
+
}
|
|
438
|
+
return internalId;
|
|
439
|
+
};
|
|
440
|
+
return {
|
|
441
|
+
ref: (id) => {
|
|
442
|
+
return getId(id);
|
|
443
|
+
},
|
|
444
|
+
def: (id, def) => {
|
|
445
|
+
const internalId = getId(id);
|
|
446
|
+
defs.set(internalId, def);
|
|
447
|
+
},
|
|
448
|
+
footnotes: () => {
|
|
449
|
+
return defs.entries().reduce((acc, [key, def]) => {
|
|
450
|
+
acc[key] = def;
|
|
451
|
+
return acc;
|
|
452
|
+
}, {});
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
};
|
|
430
456
|
const mdastToDocx = async (node, { output = "buffer", title, subject, creator, keywords, description, lastModifiedBy, revision, styles, background, }, images) => {
|
|
431
|
-
const
|
|
432
|
-
|
|
457
|
+
const definition = {};
|
|
458
|
+
unistUtilVisit.visit(node, "definition", (node) => {
|
|
459
|
+
definition[node.identifier] = node.url;
|
|
460
|
+
});
|
|
461
|
+
const footnote = createFootnoteRegistry();
|
|
462
|
+
const nodes = convertNodes(node.children, {
|
|
433
463
|
deco: {},
|
|
434
464
|
images,
|
|
435
465
|
indent: 0,
|
|
436
|
-
|
|
466
|
+
def: definition,
|
|
467
|
+
footnote,
|
|
437
468
|
});
|
|
438
469
|
const doc = new docx.Document({
|
|
439
470
|
title,
|
|
@@ -445,7 +476,7 @@ const mdastToDocx = async (node, { output = "buffer", title, subject, creator, k
|
|
|
445
476
|
revision,
|
|
446
477
|
styles,
|
|
447
478
|
background,
|
|
448
|
-
footnotes,
|
|
479
|
+
footnotes: footnote.footnotes(),
|
|
449
480
|
sections: [{ children: nodes }],
|
|
450
481
|
numbering: {
|
|
451
482
|
config: [
|
|
@@ -467,42 +498,27 @@ const mdastToDocx = async (node, { output = "buffer", title, subject, creator, k
|
|
|
467
498
|
return docx.Packer.toBlob(doc);
|
|
468
499
|
}
|
|
469
500
|
};
|
|
470
|
-
const getOrCreateFootnoteId = (identifier, registry) => {
|
|
471
|
-
if (!(identifier in registry)) {
|
|
472
|
-
registry[identifier] = Object.keys(registry).length + 1;
|
|
473
|
-
}
|
|
474
|
-
return registry[identifier];
|
|
475
|
-
};
|
|
476
501
|
const convertNodes = (nodes, ctx) => {
|
|
477
502
|
const results = [];
|
|
478
|
-
let footnotes = {};
|
|
479
503
|
for (const node of nodes) {
|
|
480
504
|
switch (node.type) {
|
|
481
505
|
case "paragraph": {
|
|
482
|
-
|
|
483
|
-
results.push(paragraph);
|
|
484
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
506
|
+
results.push(buildParagraph(node, ctx));
|
|
485
507
|
break;
|
|
486
508
|
}
|
|
487
509
|
case "heading": {
|
|
488
|
-
|
|
489
|
-
results.push(heading);
|
|
490
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
510
|
+
results.push(buildHeading(node, ctx));
|
|
491
511
|
break;
|
|
492
512
|
}
|
|
493
513
|
case "thematicBreak":
|
|
494
514
|
results.push(buildThematicBreak());
|
|
495
515
|
break;
|
|
496
516
|
case "blockquote": {
|
|
497
|
-
|
|
498
|
-
results.push(...blockquoteNodes);
|
|
499
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
517
|
+
results.push(...buildBlockquote(node, ctx));
|
|
500
518
|
break;
|
|
501
519
|
}
|
|
502
520
|
case "list": {
|
|
503
|
-
|
|
504
|
-
results.push(...listNodes);
|
|
505
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
521
|
+
results.push(...buildList(node, ctx));
|
|
506
522
|
break;
|
|
507
523
|
}
|
|
508
524
|
case "listItem":
|
|
@@ -527,11 +543,10 @@ const convertNodes = (nodes, ctx) => {
|
|
|
527
543
|
// FIXME: unimplemented
|
|
528
544
|
break;
|
|
529
545
|
case "definition":
|
|
530
|
-
//
|
|
546
|
+
// noop
|
|
531
547
|
break;
|
|
532
548
|
case "footnoteDefinition": {
|
|
533
|
-
|
|
534
|
-
footnotes[footnoteId] = buildFootnoteDefinition(node, ctx);
|
|
549
|
+
registerFootnoteDefinition(node, ctx);
|
|
535
550
|
break;
|
|
536
551
|
}
|
|
537
552
|
case "text":
|
|
@@ -541,12 +556,11 @@ const convertNodes = (nodes, ctx) => {
|
|
|
541
556
|
case "strong":
|
|
542
557
|
case "delete": {
|
|
543
558
|
const { type, children } = node;
|
|
544
|
-
const
|
|
559
|
+
const nodes = convertNodes(children, {
|
|
545
560
|
...ctx,
|
|
546
561
|
deco: { ...ctx.deco, [type]: true },
|
|
547
562
|
});
|
|
548
563
|
results.push(...nodes);
|
|
549
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
550
564
|
break;
|
|
551
565
|
}
|
|
552
566
|
case "inlineCode":
|
|
@@ -557,24 +571,24 @@ const convertNodes = (nodes, ctx) => {
|
|
|
557
571
|
results.push(buildBreak());
|
|
558
572
|
break;
|
|
559
573
|
case "link": {
|
|
560
|
-
|
|
561
|
-
results.push(link);
|
|
562
|
-
footnotes = { ...footnotes, ...nestedFootnotes };
|
|
574
|
+
results.push(buildLink(node, ctx));
|
|
563
575
|
break;
|
|
564
576
|
}
|
|
565
577
|
case "image":
|
|
566
578
|
results.push(buildImage(node, ctx.images));
|
|
567
579
|
break;
|
|
568
580
|
case "linkReference":
|
|
569
|
-
|
|
581
|
+
results.push(...buildLinkReference(node, ctx));
|
|
570
582
|
break;
|
|
571
|
-
case "imageReference":
|
|
572
|
-
|
|
583
|
+
case "imageReference": {
|
|
584
|
+
const image = buildImageReference(node, ctx);
|
|
585
|
+
if (image) {
|
|
586
|
+
results.push(image);
|
|
587
|
+
}
|
|
573
588
|
break;
|
|
589
|
+
}
|
|
574
590
|
case "footnote": {
|
|
575
|
-
|
|
576
|
-
results.push(footnoteRef);
|
|
577
|
-
footnotes = { ...footnotes, ...footnoteData };
|
|
591
|
+
// inline footnote was removed in mdast v5
|
|
578
592
|
break;
|
|
579
593
|
}
|
|
580
594
|
case "footnoteReference":
|
|
@@ -591,14 +605,11 @@ const convertNodes = (nodes, ctx) => {
|
|
|
591
605
|
break;
|
|
592
606
|
}
|
|
593
607
|
}
|
|
594
|
-
return
|
|
595
|
-
nodes: results,
|
|
596
|
-
footnotes,
|
|
597
|
-
};
|
|
608
|
+
return results;
|
|
598
609
|
};
|
|
599
610
|
const buildParagraph = ({ children }, ctx) => {
|
|
600
611
|
const list = ctx.list;
|
|
601
|
-
const
|
|
612
|
+
const nodes = convertNodes(children, ctx);
|
|
602
613
|
if (list && list.checked != null) {
|
|
603
614
|
nodes.unshift(new docx.CheckBox({
|
|
604
615
|
checked: list.checked,
|
|
@@ -606,30 +617,27 @@ const buildParagraph = ({ children }, ctx) => {
|
|
|
606
617
|
uncheckedState: { value: "2610" },
|
|
607
618
|
}));
|
|
608
619
|
}
|
|
609
|
-
return {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
620
|
+
return new docx.Paragraph({
|
|
621
|
+
children: nodes,
|
|
622
|
+
indent: ctx.indent > 0
|
|
623
|
+
? {
|
|
624
|
+
start: docx.convertInchesToTwip(INDENT * ctx.indent),
|
|
625
|
+
}
|
|
626
|
+
: undefined,
|
|
627
|
+
...(list &&
|
|
628
|
+
(list.ordered
|
|
613
629
|
? {
|
|
614
|
-
|
|
630
|
+
numbering: {
|
|
631
|
+
reference: ORDERED_LIST_REF,
|
|
632
|
+
level: list.level,
|
|
633
|
+
},
|
|
615
634
|
}
|
|
616
|
-
:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
level: list.level,
|
|
623
|
-
},
|
|
624
|
-
}
|
|
625
|
-
: {
|
|
626
|
-
bullet: {
|
|
627
|
-
level: list.level,
|
|
628
|
-
},
|
|
629
|
-
})),
|
|
630
|
-
}),
|
|
631
|
-
footnotes: nestedFootnotes,
|
|
632
|
-
};
|
|
635
|
+
: {
|
|
636
|
+
bullet: {
|
|
637
|
+
level: list.level,
|
|
638
|
+
},
|
|
639
|
+
})),
|
|
640
|
+
});
|
|
633
641
|
};
|
|
634
642
|
const buildHeading = ({ children, depth }, ctx) => {
|
|
635
643
|
let headingLevel;
|
|
@@ -653,14 +661,11 @@ const buildHeading = ({ children, depth }, ctx) => {
|
|
|
653
661
|
headingLevel = docx.HeadingLevel.HEADING_5;
|
|
654
662
|
break;
|
|
655
663
|
}
|
|
656
|
-
const
|
|
657
|
-
return {
|
|
658
|
-
heading:
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}),
|
|
662
|
-
footnotes: nestedFootnotes,
|
|
663
|
-
};
|
|
664
|
+
const nodes = convertNodes(children, ctx);
|
|
665
|
+
return new docx.Paragraph({
|
|
666
|
+
heading: headingLevel,
|
|
667
|
+
children: nodes,
|
|
668
|
+
});
|
|
664
669
|
};
|
|
665
670
|
const buildThematicBreak = (_) => {
|
|
666
671
|
return new docx.Paragraph({
|
|
@@ -668,31 +673,28 @@ const buildThematicBreak = (_) => {
|
|
|
668
673
|
});
|
|
669
674
|
};
|
|
670
675
|
const buildBlockquote = ({ children }, ctx) => {
|
|
671
|
-
|
|
672
|
-
|
|
676
|
+
return convertNodes(children, {
|
|
677
|
+
...ctx,
|
|
678
|
+
indent: ctx.indent + 1,
|
|
679
|
+
});
|
|
673
680
|
};
|
|
674
681
|
const buildList = ({ children, ordered, start: _start, spread: _spread }, ctx) => {
|
|
675
682
|
const list = {
|
|
676
683
|
level: ctx.list ? ctx.list.level + 1 : 0,
|
|
677
684
|
ordered: !!ordered,
|
|
678
685
|
};
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const { nodes, footnotes: itemFootnotes } = buildListItem(item, {
|
|
686
|
+
return children.flatMap((item) => {
|
|
687
|
+
return buildListItem(item, {
|
|
682
688
|
...ctx,
|
|
683
689
|
list,
|
|
684
690
|
});
|
|
685
|
-
allFootnotes = { ...allFootnotes, ...itemFootnotes };
|
|
686
|
-
return nodes;
|
|
687
691
|
});
|
|
688
|
-
return { nodes: allNodes, footnotes: allFootnotes };
|
|
689
692
|
};
|
|
690
693
|
const buildListItem = ({ children, checked, spread: _spread }, ctx) => {
|
|
691
|
-
|
|
694
|
+
return convertNodes(children, {
|
|
692
695
|
...ctx,
|
|
693
696
|
...(ctx.list && { list: { ...ctx.list, checked: checked !== null && checked !== void 0 ? checked : undefined } }),
|
|
694
697
|
});
|
|
695
|
-
return { nodes, footnotes: nestedFootnotes };
|
|
696
698
|
};
|
|
697
699
|
const buildTable = ({ children, align }, ctx) => {
|
|
698
700
|
const cellAligns = align === null || align === void 0 ? void 0 : align.map((a) => {
|
|
@@ -721,7 +723,7 @@ const buildTableRow = ({ children }, ctx, cellAligns) => {
|
|
|
721
723
|
});
|
|
722
724
|
};
|
|
723
725
|
const buildTableCell = ({ children }, ctx, align) => {
|
|
724
|
-
const
|
|
726
|
+
const nodes = convertNodes(children, ctx);
|
|
725
727
|
return new docx.TableCell({
|
|
726
728
|
children: [
|
|
727
729
|
new docx.Paragraph({
|
|
@@ -737,7 +739,7 @@ const buildHtml = ({ value }) => {
|
|
|
737
739
|
children: [buildText(value, {})],
|
|
738
740
|
});
|
|
739
741
|
};
|
|
740
|
-
const buildCode = ({ value, lang: _lang, meta: _meta }) => {
|
|
742
|
+
const buildCode = ({ value, lang: _lang, meta: _meta, }) => {
|
|
741
743
|
// FIXME: transform to text for now
|
|
742
744
|
return new docx.Paragraph({
|
|
743
745
|
children: [buildText(value, {})],
|
|
@@ -768,17 +770,14 @@ const buildText = (text, deco) => {
|
|
|
768
770
|
const buildBreak = (_) => {
|
|
769
771
|
return new docx.TextRun({ text: "", break: 1 });
|
|
770
772
|
};
|
|
771
|
-
const buildLink = ({ children, url
|
|
772
|
-
const
|
|
773
|
-
return {
|
|
774
|
-
link:
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}),
|
|
778
|
-
footnotes: nestedFootnotes,
|
|
779
|
-
};
|
|
773
|
+
const buildLink = ({ children, url }, ctx) => {
|
|
774
|
+
const nodes = convertNodes(children, ctx);
|
|
775
|
+
return new docx.ExternalHyperlink({
|
|
776
|
+
link: url,
|
|
777
|
+
children: nodes,
|
|
778
|
+
});
|
|
780
779
|
};
|
|
781
|
-
const buildImage = ({ url
|
|
780
|
+
const buildImage = ({ url }, images) => {
|
|
782
781
|
const img = images[url];
|
|
783
782
|
invariant(img, `Fetch image was failed: ${url}`);
|
|
784
783
|
const { image, width, height } = img;
|
|
@@ -790,32 +789,25 @@ const buildImage = ({ url, title: _title, alt: _alt }, images) => {
|
|
|
790
789
|
},
|
|
791
790
|
});
|
|
792
791
|
};
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
// Convert each node and extract the first result as a paragraph
|
|
800
|
-
const { nodes } = convertNodes([node], ctx);
|
|
801
|
-
if (nodes[0] instanceof docx.Paragraph) {
|
|
802
|
-
return nodes[0];
|
|
803
|
-
}
|
|
804
|
-
// For non-paragraph content, wrap in a paragraph
|
|
805
|
-
return new docx.Paragraph({ children: nodes });
|
|
806
|
-
});
|
|
807
|
-
return {
|
|
808
|
-
footnoteRef: new docx.FootnoteReferenceRun(footnoteId),
|
|
809
|
-
footnoteData: {
|
|
810
|
-
[footnoteId]: { children: footnoteContent },
|
|
811
|
-
},
|
|
812
|
-
};
|
|
792
|
+
const buildLinkReference = ({ children, identifier }, ctx) => {
|
|
793
|
+
const def = ctx.def[identifier];
|
|
794
|
+
if (def == null) {
|
|
795
|
+
return convertNodes(children, ctx);
|
|
796
|
+
}
|
|
797
|
+
return [buildLink({ children, url: def }, ctx)];
|
|
813
798
|
};
|
|
814
|
-
const
|
|
815
|
-
|
|
799
|
+
const buildImageReference = ({ identifier }, ctx) => {
|
|
800
|
+
const def = ctx.def[identifier];
|
|
801
|
+
if (def == null) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
return buildImage({ url: def }, ctx.images);
|
|
805
|
+
};
|
|
806
|
+
const registerFootnoteDefinition = ({ children, identifier }, ctx) => {
|
|
807
|
+
const definition = {
|
|
816
808
|
children: children.map((node) => {
|
|
817
809
|
// Convert each node and extract the first result as a paragraph
|
|
818
|
-
const
|
|
810
|
+
const nodes = convertNodes([node], ctx);
|
|
819
811
|
if (nodes[0] instanceof docx.Paragraph) {
|
|
820
812
|
return nodes[0];
|
|
821
813
|
}
|
|
@@ -823,10 +815,10 @@ const buildFootnoteDefinition = ({ children }, ctx) => {
|
|
|
823
815
|
return new docx.Paragraph({ children: nodes });
|
|
824
816
|
}),
|
|
825
817
|
};
|
|
818
|
+
ctx.footnote.def(identifier, definition);
|
|
826
819
|
};
|
|
827
820
|
const buildFootnoteReference = ({ identifier }, ctx) => {
|
|
828
|
-
|
|
829
|
-
return new docx.FootnoteReferenceRun(footnoteId);
|
|
821
|
+
return new docx.FootnoteReferenceRun(ctx.footnote.ref(identifier));
|
|
830
822
|
};
|
|
831
823
|
|
|
832
824
|
const plugin = function (opts = {}) {
|
|
@@ -839,14 +831,34 @@ const plugin = function (opts = {}) {
|
|
|
839
831
|
unistUtilVisit.visit(node, "image", (node) => {
|
|
840
832
|
imageList.push(node);
|
|
841
833
|
});
|
|
834
|
+
const defs = new Map();
|
|
835
|
+
unistUtilVisit.visit(node, "definition", (node) => {
|
|
836
|
+
defs.set(node.identifier, node);
|
|
837
|
+
});
|
|
838
|
+
unistUtilVisit.visit(node, "imageReference", (node) => {
|
|
839
|
+
const maybeImage = defs.get(node.identifier);
|
|
840
|
+
if (maybeImage) {
|
|
841
|
+
imageList.push(maybeImage);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
842
844
|
if (imageList.length === 0) {
|
|
843
845
|
return node;
|
|
844
846
|
}
|
|
845
847
|
const imageResolver = opts.imageResolver;
|
|
846
848
|
invariant(imageResolver, "options.imageResolver is not defined.");
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
849
|
+
const resolved = new Set();
|
|
850
|
+
const promises = [];
|
|
851
|
+
imageList.forEach(({ url }) => {
|
|
852
|
+
if (!resolved.has(url)) {
|
|
853
|
+
resolved.add(url);
|
|
854
|
+
promises.push((async () => {
|
|
855
|
+
const img = await imageResolver(url);
|
|
856
|
+
return { img, url };
|
|
857
|
+
})());
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
images = (await Promise.all(promises)).reduce((acc, { img, url }) => {
|
|
861
|
+
acc[url] = img;
|
|
850
862
|
return acc;
|
|
851
863
|
}, {});
|
|
852
864
|
return node;
|