|
| 1 | +import type { CitationRenderer } from 'citation-js-utils'; |
| 2 | +import { InlineCite } from 'citation-js-utils'; |
1 | 3 | import type { Plugin } from 'unified';
|
2 |
| -import type { Root } from 'myst-spec'; |
3 |
| -import type { GenericNode } from 'myst-common'; |
| 4 | +import type { StaticPhrasingContent, Parent, Root } from 'myst-spec'; |
| 5 | +import type { References } from 'myst-common'; |
4 | 6 | import { selectAll } from 'unist-util-select';
|
| 7 | +import type { Cite, CiteKind, CiteGroup } from 'myst-spec-ext'; |
5 | 8 |
|
6 |
| -/** |
7 |
| - * Add fake children to the citations |
8 |
| - */ |
9 |
| -export async function addCiteChildrenTransform(tree: Root): Promise<void> { |
10 |
| - const links = selectAll('cite', tree) as GenericNode[]; |
11 |
| - links.forEach(async cite => { |
12 |
| - if (cite.children && cite.children.length > 0) return; |
13 |
| - cite.error = true; |
14 |
| - cite.children = [{ type: 'text', value: cite.label }]; |
| 9 | +function pushCite( |
| 10 | + references: Pick<References, 'cite'>, |
| 11 | + citeRenderer: CitationRenderer, |
| 12 | + label: string |
| 13 | +) { |
| 14 | + if (!references.cite) { |
| 15 | + references.cite = { order: [], data: {} }; |
| 16 | + } |
| 17 | + if (!references.cite?.data[label]) { |
| 18 | + references.cite.order.push(label); |
| 19 | + } |
| 20 | + references.cite.data[label] = { |
| 21 | + // TODO: this number isn't right? Should be the last time it was seen, not the current size. |
| 22 | + number: references.cite.order.length, |
| 23 | + doi: citeRenderer[label]?.getDOI(), |
| 24 | + html: citeRenderer[label]?.render() |
| 25 | + }; |
| 26 | +} |
| 27 | + |
| 28 | +export function combineCitationRenderers(renderers: CitationRenderer[]) { |
| 29 | + const combined: CitationRenderer = {}; |
| 30 | + renderers.forEach(renderer => { |
| 31 | + Object.keys(renderer).forEach(key => { |
| 32 | + if (combined[key]) { |
| 33 | + console.log(`Duplicate citation with id: ${key}`); |
| 34 | + } |
| 35 | + combined[key] = renderer[key]; |
| 36 | + }); |
15 | 37 | });
|
| 38 | + return combined; |
| 39 | +} |
| 40 | + |
| 41 | +function addCitationChildren( |
| 42 | + cite: Cite, |
| 43 | + renderer: CitationRenderer, |
| 44 | + kind: CiteKind = 'parenthetical' |
| 45 | +): boolean { |
| 46 | + const render = renderer[cite.label as string]; |
| 47 | + try { |
| 48 | + const children = render?.inline( |
| 49 | + kind === 'narrative' ? InlineCite.t : InlineCite.p, |
| 50 | + { |
| 51 | + prefix: cite.prefix, |
| 52 | + suffix: cite.suffix |
| 53 | + } |
| 54 | + ) as StaticPhrasingContent[]; |
| 55 | + if (children) { |
| 56 | + cite.children = children; |
| 57 | + return true; |
| 58 | + } |
| 59 | + } catch (error) { |
| 60 | + // pass |
| 61 | + } |
| 62 | + cite.error = true; |
| 63 | + return false; |
| 64 | +} |
| 65 | + |
| 66 | +function hasChildren(node: Parent) { |
| 67 | + return node.children && node.children.length > 0; |
16 | 68 | }
|
17 | 69 |
|
18 |
| -export const addCiteChildrenPlugin: Plugin<[], Root, Root> = () => tree => { |
19 |
| - addCiteChildrenTransform(tree); |
| 70 | +type Options = { |
| 71 | + renderer: CitationRenderer; |
| 72 | + references: Pick<References, 'cite'>; |
20 | 73 | };
|
| 74 | + |
| 75 | +export function transformCitations(mdast: Root, opts: Options) { |
| 76 | + // TODO: this can be simplified if typescript doesn't die on the parent |
| 77 | + const citeGroups = selectAll('citeGroup', mdast) as CiteGroup[]; |
| 78 | + citeGroups.forEach(node => { |
| 79 | + const kind = node.kind; |
| 80 | + node.children?.forEach(cite => { |
| 81 | + addCitationChildren(cite, opts.renderer, kind); |
| 82 | + }); |
| 83 | + }); |
| 84 | + const citations = selectAll('cite', mdast) as Cite[]; |
| 85 | + citations.forEach(cite => { |
| 86 | + const citeLabel = cite.label as string; |
| 87 | + // push cites in order of appearance in the document |
| 88 | + pushCite(opts.references, opts.renderer, citeLabel); |
| 89 | + if (hasChildren(cite)) return; |
| 90 | + // These are picked up as they are *not* cite groups |
| 91 | + const success = addCitationChildren(cite, opts.renderer); |
| 92 | + if (!success) { |
| 93 | + console.error(`β οΈ Could not find citation: ${cite.label}`); |
| 94 | + } |
| 95 | + }); |
| 96 | +} |
| 97 | + |
| 98 | +export const addCiteChildrenPlugin: Plugin<[Options], Root, Root> = |
| 99 | + opts => (tree, vfile) => { |
| 100 | + transformCitations(tree, opts); |
| 101 | + }; |
0 commit comments