rehype-citation
Version:
rehype plugin to add citation and bibliography from bibtex files
1 lines • 77.2 kB
Source Map (JSON)
{"version":3,"file":"generator.mjs","sources":["../../src/regex.js","../../src/parse-citation.js","../../src/utils.js","../../src/html-transform-node.js","../../src/gen-citation.js","../../src/gen-biblio.js","../../src/gen-footnote.js","../../src/generator.js"],"sourcesContent":["// Regex adapted from https://github.com/Zettlr/Zettlr/blob/develop/source/common/util/extract-citations.ts\n\n/**\n * Citation detection: The first alternative matches \"full\" citations surrounded\n * by square brackets, whereas the second one matches in-text citations,\n * optionally with suffixes.\n *\n * * Group 1 matches regular \"full\" citations\n * * Group 2 matches in-text citations (not surrounded by brackets)\n * * Group 3 matches optional square-brackets suffixes to group 2 matches\n *\n * For more information, see https://pandoc.org/MANUAL.html#extension-citations\n *\n * @var {RegExp}\n */\nexport const citationRE =\n /(?:\\[([^[\\]]*@[^[\\]]+)\\])|(?<=\\s|^|(-))(?:@([\\p{L}\\d_][^\\s]*[\\p{L}\\d_]|\\{.+\\})(?:\\s+\\[(.*?)\\])?)/u\n\n/**\n * I hate everything at this. This can match every single possible variation on\n * whatever the f*** you can possibly do within square brackets according to the\n * documentation. I opted for named groups for these because otherwise I have no\n * idea what I have been doing here.\n *\n * * Group prefix: Contains the prefix, ends with a dash if we should suppress the author\n * * Group citekey: Contains the actual citekey, can be surrounded in curly brackets\n * * Group explicitLocator: Contains an explicit locator statement. If given, we MUST ignore any form of locator in the suffix\n * * Group explicitLocatorInSuffix: Same as above, but not concatenated to the citekey\n * * Group suffix: Contains the suffix, but may start with a locator (if explicitLocator and explicitLocatorInSuffix are not given)\n *\n * @var {RegExp}\n */\nexport const fullCitationRE =\n /(?<prefix>.+)?(?:@(?<citekey>[\\p{L}\\d_][^\\s{]*[\\p{L}\\d_]|\\{.+\\}))(?:\\{(?<explicitLocator>.*)\\})?(?:,\\s+(?:\\{(?<explicitLocatorInSuffix>.*)\\})?(?<suffix>.*))?/u\n\n/**\n * This regular expression matches locator ranges, like the following:\n *\n * * 23-45, and further (here it matches up to, not including the comma)\n * * 45\n * * 15423\n * * 14235-12532\n * * 12-34, 23, 56\n * * 12, 23-14, 23\n * * 12, 54, 12-23\n * * 1, 1-4\n * * 3\n * * NEW NEW NEW: Now also matches Roman numerals as sometimes used in forewords!\n *\n * @var {RegExp}\n */\nexport const locatorRE = /^(?:[\\d, -]*\\d|[ivxlcdm, -]*[ivxlcdm])/i\n","/**\n * @typedef {import('./types').CiteItem} CiteItem\n * @typedef {import('./types').CiteItemSuffix} CiteItemSuffix\n */\n\nimport { fullCitationRE, locatorRE } from './regex.js'\n\n/**\n * The locatorLabels have been sourced from the Citr library. Basically it's just\n * a map with valid CSL locator labels and an array of possible natural labels\n * which a user might want to write (instead of the standardized labels).\n *\n * @var {{ [key: string]: string[] }}}\n */\nconst locatorLabels = {\n book: ['Buch', 'Bücher', 'B.', 'book', 'books', 'bk.', 'bks.', 'livre', 'livres', 'liv.'],\n chapter: ['Kapitel', 'Kap.', 'chapter', 'chapters', 'chap.', 'chaps', 'chapitre', 'chapitres'],\n column: ['Spalte', 'Spalten', 'Sp.', 'column', 'columns', 'col.', 'cols', 'colonne', 'colonnes'],\n figure: ['Abbildung', 'Abbildungen', 'Abb.', 'figure', 'figures', 'fig.', 'figs'],\n folio: ['Blatt', 'Blätter', 'Fol.', 'folio', 'folios', 'fol.', 'fols', 'fᵒ', 'fᵒˢ'],\n issue: [\n 'Nummer',\n 'Nummern',\n 'Nr.',\n 'number',\n 'numbers',\n 'no.',\n 'nos.',\n 'numéro',\n 'numéros',\n 'nᵒ',\n 'nᵒˢ',\n ],\n line: ['Zeile', 'Zeilen', 'Z', 'line', 'lines', 'l.', 'll.', 'ligne', 'lignes'],\n note: ['Note', 'Noten', 'N.', 'note', 'notes', 'n.', 'nn.'],\n opus: ['Opus', 'Opera', 'op.', 'opus', 'opera', 'opp.'],\n page: ['Seite', 'Seiten', 'S.', 'page', 'pages', 'p.', 'pp.'],\n paragraph: [\n 'Absatz',\n 'Absätze',\n 'Abs.',\n '¶',\n '¶¶',\n 'paragraph',\n 'paragraphs',\n 'para.',\n 'paras',\n 'paragraphe',\n 'paragraphes',\n 'paragr.',\n ],\n part: ['Teil', 'Teile', 'part', 'parts', 'pt.', 'pts', 'partie', 'parties', 'part.'],\n section: [\n 'Abschnitt',\n 'Abschnitte',\n 'Abschn.',\n '§',\n '§§',\n 'section',\n 'sections',\n 'sec.',\n 'secs',\n 'sect.',\n ],\n 'sub verbo': ['sub verbo', 'sub verbis', 's. v.', 's. vv.', 's.v.', 's.vv.'],\n verse: ['Vers', 'Verse', 'V.', 'verse', 'verses', 'v.', 'vv.', 'verset', 'versets'],\n volume: ['Band', 'Bände', 'Bd.', 'Bde.', 'volume', 'volumes', 'vol.', 'vols.'],\n}\n\n/**\n * Parses a given citation string and return entries and isComposite flag required for cite-proc.\n * Adapted from https://github.com/Zettlr/Zettlr/blob/develop/source/common/util/extract-citations.ts\n *\n * @param {RegExpMatchArray} regexMatch Cite string in the form of '[@item]' or '@item'\n * @return {[CiteItem[], boolean]} [entries, isComposite]\n */\nexport const parseCitation = (regexMatch) => {\n /** @type {CiteItem[]} */\n let entries = []\n let isComposite = false\n const fullCitation = regexMatch[1]\n const inTextSuppressAuthor = regexMatch[2]\n const inTextCitation = regexMatch[3]\n const optionalSuffix = regexMatch[4]\n\n if (fullCitation !== undefined) {\n // Handle citations in the form of [@item1; @item2]\n for (const citationPart of fullCitation.split(';')) {\n const match = fullCitationRE.exec(citationPart.trim())\n if (match === null) {\n continue // Faulty citation\n }\n // Prefix is the portion before @ e.g. [see @item1] or an empty string\n // We explicitly cast groups since we have groups in our RegExp and as\n // such the groups object will be set.\n /** @type {CiteItem} */\n const thisCitation = {\n id: match.groups.citekey.replace(/{(.+)}/, '$1'),\n prefix: undefined,\n locator: undefined,\n label: 'page',\n 'suppress-author': false,\n suffix: undefined,\n }\n\n // First, deal with the prefix. The speciality here is that it can\n // indicate if we should suppress the author.\n const rawPrefix = match.groups.prefix\n if (rawPrefix !== undefined) {\n thisCitation['suppress-author'] = rawPrefix.trim().endsWith('-')\n if (thisCitation['suppress-author']) {\n thisCitation.prefix = rawPrefix.substring(0, rawPrefix.trim().length - 1).trim()\n } else {\n thisCitation.prefix = rawPrefix.trim()\n }\n }\n\n // Second, deal with the suffix. This one can be much more tricky than\n // the prefix. We have three alternatives where the locator may be\n // present: If we have an explicitLocator or an explicitLocatorInSuffix,\n // we should extract the locator from there and leave the actual suffix\n // untouched. Only if those two alternatives are not present, then we\n // have a look at the rawSuffix and extract a (potential) locator.\n const explicitLocator = match.groups.explicitLocator\n const explicitLocatorInSuffix = match.groups.explicitLocatorInSuffix\n const rawSuffix = match.groups.suffix\n\n let suffixToParse\n let containsLocator = true\n if (explicitLocator === undefined && explicitLocatorInSuffix === undefined) {\n // Potential locator in rawSuffix. Only in this case should we overwrite\n // the suffix (hence the same if-condition below)\n suffixToParse = rawSuffix\n containsLocator = false\n } else if (explicitLocatorInSuffix !== undefined || explicitLocator !== undefined) {\n suffixToParse = explicitLocator !== undefined ? explicitLocator : explicitLocatorInSuffix\n thisCitation.suffix = rawSuffix?.trim()\n }\n\n const { label, locator, suffix } = parseSuffix(suffixToParse, containsLocator)\n thisCitation.locator = locator\n\n if (label !== undefined) {\n thisCitation.label = label\n }\n\n if (explicitLocator === undefined && explicitLocatorInSuffix === undefined) {\n thisCitation.suffix = suffix\n } else if (suffix !== undefined && thisCitation.locator !== undefined) {\n // If we're here, we should not change the suffix, but parseSuffix may\n // have put something into the suffix return. If we're here, that will\n // definitely be a part of the locator.\n thisCitation.locator += suffix\n }\n\n entries.push(thisCitation)\n }\n } else {\n // We have an in-text citation, so we can take a shortcut\n isComposite = true\n entries.push({\n prefix: undefined,\n id: inTextCitation.replace(/{(.+)}/, '$1'),\n 'suppress-author': inTextSuppressAuthor !== undefined,\n ...parseSuffix(optionalSuffix, false), // Populate more depending on the suffix\n })\n }\n return [entries, isComposite]\n}\n\n/**\n * This takes a suffix and extracts optional label and locator from this. Pass\n * true for the containsLocator property to indicate to this function that what\n * it got was not a regular suffix with an optional locator, but an explicit\n * locator so it knows it just needs to look for an optional label.\n *\n * @param {string} suffix The suffix to parse\n * @param {boolean} containsLocator If true, forces parseSuffix to return a locator\n *\n * @return {CiteItemSuffix} An object containing three optional properties locator, label, or suffix.\n */\nfunction parseSuffix(suffix, containsLocator) {\n /** @type {CiteItemSuffix} */\n const retValue = {\n locator: undefined,\n label: 'page',\n suffix: undefined,\n }\n\n if (suffix === undefined) {\n return retValue\n }\n\n // Make sure the suffix does not start or end with spaces\n suffix = suffix.trim()\n\n // If there is a label, the suffix must start with it\n for (const label in locatorLabels) {\n for (const natural of locatorLabels[label]) {\n if (suffix.toLowerCase().startsWith(natural.toLowerCase())) {\n retValue.label = label\n if (containsLocator) {\n // The suffix actually is the full locator, we just had to extract\n // the label from it. There is no remaining suffix.\n retValue.locator = suffix.substr(natural.length).trim()\n } else {\n // The caller indicated that this is a regular suffix, so we must also\n // extract the locator from what is left after label extraction.\n retValue.suffix = suffix.substr(natural.length).trim()\n const match = locatorRE.exec(retValue.suffix)\n if (match !== null) {\n retValue.locator = match[0] // Extract the full match\n retValue.suffix = retValue.suffix.substr(match[0].length).trim()\n }\n }\n\n return retValue // Early exit\n }\n }\n }\n\n // If we're here, there was no explicit label given, but the caller has indicated\n // that this suffix MUST contain a locator. This means that the whole suffix is\n // the locator.\n if (containsLocator) {\n retValue.locator = suffix\n } else {\n // The caller has not indicated that the whole suffix is the locator, so it\n // can be at the beginning. We only accept simple page/number ranges here.\n // For everything, the user should please be more specific.\n const match = locatorRE.exec(suffix)\n if (match !== null) {\n retValue.locator = match[0] // Full match is the locator\n retValue.suffix = suffix.substr(match[0].length).trim() // The rest is the suffix.\n }\n }\n\n return retValue\n}\n","import fetch from 'cross-fetch'\n\nexport const isNode = typeof window === 'undefined'\n\nexport const readFile = async (path) => {\n if (isValidHttpUrl(path)) {\n try {\n const response = await fetch(path)\n return await response.text()\n } catch (error) {\n throw new Error(`Cannot fetch bibliography URL: ${error}.`)\n }\n } else {\n if (isNode) {\n try {\n return import('fs').then((fs) => fs.readFileSync(path, 'utf8'))\n } catch (error) {\n throw new Error(`Cannot read non valid URL in node env.`)\n }\n }\n }\n}\n\n/**\n * Check if valid URL\n * https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url\n *\n * @param {string} str\n * @return {boolean}\n */\nexport const isValidHttpUrl = (str) => {\n let url\n\n try {\n url = new URL(str)\n } catch (_) {\n return false\n }\n\n return url.protocol === 'http:' || url.protocol === 'https:' || url.protocol === 'blob:'\n}\n\n/**\n * Get bibliography by merging options and vfile data\n *\n * @param {import('./generator.js').Options} options\n * @param {import('vfile').VFile} file\n */\nexport const getBibliography = async (options, file) => {\n /** @type {string[]} */\n let bibliography = []\n const frontmatterBibliography = getFrontmatterField(file, 'bibliography')\n if (options.bibliography) {\n bibliography =\n typeof options.bibliography === 'string' ? [options.bibliography] : options.bibliography\n } else if (frontmatterBibliography) {\n bibliography =\n typeof frontmatterBibliography === 'string'\n ? [frontmatterBibliography]\n : frontmatterBibliography\n }\n // If local path, get absolute path\n for (let i = 0; i < bibliography.length; i++) {\n if (!isValidHttpUrl(bibliography[i])) {\n // Case options.path is provided and non empty\n if (options.path) {\n // if node env we construct the full path using options.path\n if (isNode) {\n bibliography[i] = await import('path').then((path) =>\n path.join(options.path, bibliography[i])\n )\n // else we throw as it's non valid http url\n } else {\n throw new Error(`Cannot read non valid bibliography URL.`)\n }\n // Case options.path is empt\n } else {\n // if node env we construct the full path using default `process.cwd`\n if (isNode) {\n bibliography[i] = await import('path').then((path) =>\n path.join(file.cwd, bibliography[i])\n )\n // else as it's a non valid http url we throw as a base url must be provided using options.path\n } else {\n throw new Error(\n `Non valid bibliography URL: Provide a full valid path for biblio ${bibliography[i]} or set an appropriate \"options.path\"`\n )\n }\n }\n }\n }\n\n return bibliography\n}\n\n/**\n * Load CSL - supports predefined name from config.templates.data or http, file path (nodejs)\n *\n * @param {*} Cite cite object from citation-js\n * @param {string} format CSL name e.g. apa or file path to CSL file\n * @param {string} root optional root path\n */\nexport const loadCSL = async (Cite, format, root = '') => {\n const config = Cite.plugins.config.get('@csl')\n if (!Object.keys(config.templates.data).includes(format)) {\n const cslName = `customCSL-${Math.random().toString(36).slice(2, 7)}`\n let cslPath = ''\n if (isValidHttpUrl(format)) cslPath = format\n else {\n if (isNode) cslPath = await import('path').then((path) => path.join(root, format))\n }\n try {\n config.templates.add(cslName, await readFile(cslPath))\n } catch (err) {\n throw new Error(`Input CSL option, ${format}, is invalid or is an unknown file.`)\n }\n return cslName\n } else {\n return format\n }\n}\n\n/**\n * Load locale - supports predefined name from config.locales.data or http, file path (nodejs)\n *\n * @param {*} Cite cite object from citation-js\n * @param {string} format locale name\n * @param {string} root optional root path\n */\nexport const loadLocale = async (Cite, format, root = '') => {\n const config = Cite.plugins.config.get('@csl')\n if (!Object.keys(config.locales.data).includes(format)) {\n let localePath = ''\n if (isValidHttpUrl(format)) localePath = format\n else {\n if (isNode) localePath = await import('path').then((path) => path.join(root, format))\n }\n try {\n const file = await readFile(localePath)\n const xmlLangRe = /xml:lang=\"(.+)\"/\n const localeName = file.match(xmlLangRe)[1]\n config.locales.add(localeName, file)\n return localeName\n } catch (err) {\n throw new Error(`Input locale option, ${format}, is invalid or is an unknown file.`)\n }\n } else {\n return format\n }\n}\n\n/**\n * Get citation format\n *\n * @param {*} citeproc citeproc\n * @returns string\n */\nexport const getCitationFormat = (citeproc) => {\n const info = citeproc.cslXml.dataObj.children[0]\n const node = info.children.find((x) => x['attrs'] && x['attrs']['citation-format'])\n // citation-format takes 5 possible values\n // https://docs.citationstyles.org/en/stable/specification.html#toc-entry-14\n /** @type {'author-date' | 'author' | 'numeric' | 'note' | 'label'} */\n const citationFormat = node['attrs']['citation-format']\n return citationFormat\n}\n\n/**\n * Get registry objects that matches a list of relevantIds\n * If sorted is false, retrieve registry item in the order of the given relevantIds\n *\n * @param {*} citeproc citeproc\n * @param {string[]} relevantIds\n * @param {boolean} sorted\n * @return {*} registry objects that matches Ids, in the correct order\n */\nexport const getSortedRelevantRegistryItems = (citeproc, relevantIds, sorted) => {\n const res = []\n if (sorted) {\n // If sorted follow registry order\n for (const item of citeproc.registry.reflist) {\n if (relevantIds.includes(item.id)) res.push(item)\n }\n } else {\n // Otherwise follow the relevantIds\n for (const id of relevantIds) {\n res.push(citeproc.registry.reflist.find((x) => x.id === id))\n }\n }\n return res\n}\n\n/**\n * Split a string into two parts based on a given index position\n *\n * @param {string} str\n * @param {number} index\n * @return {string[]}\n */\nexport const split = (str, index) => {\n return [str.slice(0, index), str.slice(index)]\n}\n\n/**\n * Check if two registry objects belong to the same author\n * Currently only checks on family name\n *\n * @param {*} item registry object\n * @param {*} item2 registry object\n * @return {boolean}\n */\nexport const isSameAuthor = (item, item2) => {\n const authorList = item.ref.author\n const authorList2 = item2.ref.author\n if (authorList.length !== authorList2.length) return false\n for (let i = 0; i < authorList.length; i++) {\n if (authorList[i].family !== authorList2[i].family) return false\n }\n return true\n}\n\n/**\n * @typedef {Object} FrontmatterSource\n * @property {Record<string, any>} [matter]\n * @property {Record<string, any>} [frontmatter]\n * @property {{ frontmatter?: Record<string, any> }} [astro]\n */\n\n/**\n * @param {{ data?: FrontmatterSource }} file\n * @param {string} fieldName\n * @returns {any}\n */\nexport const getFrontmatterField = (file, fieldName) => {\n if (!file || !file.data) {\n return undefined\n }\n\n const sources = [file.data.matter, file.data.frontmatter, file.data.astro?.frontmatter]\n\n for (const source of sources) {\n if (source && fieldName in source) {\n return source[fieldName]\n }\n }\n\n return undefined\n}\n\n/**\n * Get bibliography entry text for a citation ID\n *\n * @param {*} citeproc citeproc engine\n * @param {string} id citation ID\n * @return {string} formatted bibliography entry without HTML tags\n */\nexport const getBibliographyEntryText = (citeproc, id) => {\n try {\n // Save the current state\n const originalItemIds = [...citeproc.registry.mylist]\n\n // Since creating bibliography affects the state we need to save the current state and restore it\n citeproc.updateItems([id])\n const bibOutput = citeproc.makeBibliography([id])\n if (!bibOutput || !bibOutput[1] || bibOutput[1].length === 0) {\n citeproc.updateItems(originalItemIds)\n return ''\n }\n\n // Get the text\n let entryText = bibOutput[1][0].replace(/<[^>]*>/g, '')\n entryText = entryText.replace(/\\s+/g, ' ').trim()\n\n // Restore the original state\n citeproc.updateItems(originalItemIds)\n\n return entryText\n } catch (error) {\n console.error('Error getting bibliography entry text:', error)\n return ''\n }\n}\n","import { parseFragment } from 'parse5'\nimport { fromParse5 } from 'hast-util-from-parse5'\n\n/**\n * Convert HTML to HAST node\n *\n * @param {string} html\n */\nexport const htmlToHast = (html) => {\n const p5ast = parseFragment(html)\n // @ts-ignore\n return fromParse5(p5ast).children[0]\n}\n","/**\n * @typedef {import('./types').CiteItem} CiteItem\n * @typedef {import('./types').Mode} Mode\n * @typedef {import('./types').Options} Options\n */\n\nimport {\n getSortedRelevantRegistryItems,\n split,\n isSameAuthor,\n getBibliographyEntryText,\n} from './utils.js'\nimport { htmlToHast } from './html-transform-node.js'\n\n/**\n * Generate citation using citeproc\n * This accounts for prev citations and additional properties\n *\n * @param {*} citeproc\n * @param {Mode} mode\n * @param {CiteItem[]} entries\n * @param {string} citationIdRoot\n * @param {number} citationId\n * @param {any[]} citationPre\n * @param {Options} options\n * @param {boolean} isComposite\n * @param {import('./types').CitationFormat} citationFormat\n * @return {[string, string]}\n */\nexport const genCitation = (\n citeproc,\n mode,\n entries,\n citationIdRoot,\n citationId,\n citationPre,\n options,\n isComposite,\n citationFormat\n) => {\n const { inlineClass, linkCitations, showTooltips = false, tooltipAttribute = 'title' } = options\n const key = `${citationIdRoot}-${citationId}`\n const c = citeproc.processCitationCluster(\n {\n citationID: key,\n citationItems: entries,\n properties:\n mode === 'in-text'\n ? { noteIndex: 0, mode: isComposite ? 'composite' : '' }\n : { noteIndex: citationId, mode: isComposite ? 'composite' : '' },\n },\n citationPre.length > 0 ? citationPre : [],\n []\n )\n\n const citationText = c[1].find((x) => x[2] === key)[1]\n const ids = `citation--${entries.map((x) => x.id.toLowerCase()).join('--')}--${citationId}`\n\n // Generate tooltip map for each entry if enabled\n const tooltipMap = {}\n if (showTooltips) {\n entries.forEach((entry) => {\n const entryText = getBibliographyEntryText(citeproc, entry.id)\n // Escape quotes and HTML entities for attribute value\n tooltipMap[entry.id.toLowerCase()] = entryText.replace(/\"/g, '"').replace(/&/g, '&')\n })\n }\n\n // Wrapper tooltip for the span element (combined tooltip for all entries)\n const wrapperTooltipAttr = showTooltips\n ? ` ${tooltipAttribute}=\"${entries.map((e) => tooltipMap[e.id.toLowerCase()]).join('; ')}\"`\n : ''\n\n if (mode === 'note') {\n return [\n citationText,\n htmlToHast(\n `<span class=\"${(inlineClass ?? []).join(\n ' '\n )}\" id=${ids}${wrapperTooltipAttr}><sup><a href=\"#cite-fn-${citationId}\" id=\"cite-fnref-${citationId}\" data-footnote-ref aria-describedby=\"footnote-label\">${citationId}</a></sup></span>`\n ),\n ]\n } else if (linkCitations && citationFormat === 'numeric') {\n // e.g. [1, 2]\n let i = 0\n const refIds = entries.map((e) => e.id)\n const output = citationText.replace(/\\d+/g, function (d) {\n const refId = refIds[i].toLowerCase()\n const tooltipAttr = showTooltips ? ` ${tooltipAttribute}=\"${tooltipMap[refId]}\"` : ''\n const url = `<a href=\"#bib-${refId}\"${tooltipAttr}>${d}</a>`\n i++\n return url\n })\n\n return [\n citationText,\n htmlToHast(`<span class=\"${(inlineClass ?? []).join(' ')}\" id=${ids}>${output}</span>`),\n ]\n } else if (linkCitations && citationFormat === 'author-date') {\n // E.g. (see Nash, 1950, pp. 12–13, 1951); (Nash, 1950; Xie, 2016)\n if (entries.length === 1) {\n // Do not link bracket\n const refId = entries[0].id.toLowerCase()\n const tooltipAttr = showTooltips ? ` ${tooltipAttribute}=\"${tooltipMap[refId]}\"` : ''\n\n const output = isComposite\n ? `<a href=\"#bib-${refId}\"${tooltipAttr}>${citationText}</a>`\n : `${citationText.slice(0, 1)}<a href=\"#bib-${refId}\"${tooltipAttr}>${citationText.slice(\n 1,\n -1\n )}</a>${citationText.slice(-1)}`\n\n return [\n citationText,\n htmlToHast(`<span class=\"${(inlineClass ?? []).join(' ')}\" id=${ids}>${output}</span>`),\n ]\n } else {\n // Retrieve the items in the correct order and attach link each of them\n const refIds = entries.map((e) => e.id)\n const results = getSortedRelevantRegistryItems(citeproc, refIds, citeproc.opt.sort_citations)\n const output = []\n let str = citationText\n\n for (const [i, item] of results.entries()) {\n // Need to compare author. If same just match on date.\n const id = item.id\n let citeMatch = item.ambig\n // If author is the same as the previous, some styles like apa collapse the author\n if (i > 0 && isSameAuthor(results[i - 1], item) && str.indexOf(citeMatch) === -1) {\n // Just match on year\n citeMatch = item.ref.issued.year.toString()\n }\n const startPos = str.indexOf(citeMatch)\n const [start, rest] = split(str, startPos)\n output.push(start) // Irrelevant parts\n\n const refId = id.toLowerCase()\n const tooltipAttr = showTooltips ? ` ${tooltipAttribute}=\"${tooltipMap[refId]}\"` : ''\n const url = `<a href=\"#bib-${refId}\"${tooltipAttr}>${rest.substring(\n 0,\n citeMatch.length\n )}</a>`\n\n output.push(url)\n str = rest.substring(citeMatch.length)\n }\n output.push(str)\n return [\n citationText,\n htmlToHast(\n `<span class=\"${(inlineClass ?? []).join(' ')}\" id=${ids}>${output.join('')}</span>`\n ),\n ]\n }\n } else {\n return [\n citationText,\n htmlToHast(\n `<span class=\"${(inlineClass ?? []).join(\n ' '\n )}\" id=${ids}${wrapperTooltipAttr}>${citationText}</span>`\n ),\n ]\n }\n}\n","import { htmlToHast } from './html-transform-node.js'\n\n/**\n * Generate bibliography in html and convert it to hast\n *\n * @param {*} citeproc\n */\nexport const genBiblioNode = (citeproc) => {\n const [params, bibBody] = citeproc.makeBibliography()\n const bibliography =\n '<div id=\"refs\" class=\"references csl-bib-body\">\\n' + bibBody.join('') + '</div>'\n const biblioNode = htmlToHast(bibliography)\n\n // Add citekey id to each bibliography entry.\n biblioNode.children\n .filter((node) => node.properties?.className?.includes('csl-entry'))\n .forEach((node, i) => {\n const citekey = params.entry_ids[i][0].toLowerCase()\n node.properties = node.properties || {}\n node.properties.id = 'bib-' + citekey\n })\n return biblioNode\n}\n","/**\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').ElementContent} ElementContent\n */\n\nimport { htmlToHast } from './html-transform-node.js'\n\n/**\n * Create new footnote section node based on footnoteArray mappings\n *\n * @param {{int: string}} citationDict\n * @param {{type: 'citation' | 'existing', oldId: string}[]} footnoteArray\n * @param {Element | undefined} footnoteSection\n * @return {Element}\n */\nexport const genFootnoteSection = (citationDict, footnoteArray, footnoteSection) => {\n /** @type {Element} */\n const list = {\n type: 'element',\n tagName: 'ol',\n properties: {},\n children: [{ type: 'text', value: '\\n' }],\n }\n let oldFootnoteList\n if (footnoteSection) {\n /** @type {Element} */ // @ts-ignore - for some reason, the type does not narrow even after filtering\n oldFootnoteList = footnoteSection.children.filter(n => (n.type == \"element\")).find((n) => (n.tagName === 'ol'))\n }\n for (const [idx, item] of footnoteArray.entries()) {\n const { type, oldId } = item\n if (type === 'citation') {\n list.children.push({\n type: 'element',\n tagName: 'li',\n properties: { id: `user-content-fn-${idx + 1}` },\n children: [\n {\n type: 'element',\n tagName: 'p',\n properties: {},\n children: [\n htmlToHast(`<span>${citationDict[oldId]}</span>`),\n {\n type: 'element',\n tagName: 'a',\n properties: {\n href: `#user-content-fnref-${idx + 1}`,\n dataFootnoteBackref: true,\n className: ['data-footnote-backref'],\n ariaLabel: 'Back to content',\n },\n children: [{ type: 'text', value: '↩' }],\n },\n ],\n },\n { type: 'text', value: '\\n' },\n ],\n })\n } else if (type === 'existing') {\n // @ts-ignore\n const liNode = oldFootnoteList.children.find(\n (n) => n.tagName === 'li' && n.properties.id === `user-content-fn-${oldId}`\n )\n liNode.properties.id = `user-content-fn-${idx + 1}`\n const aNode = liNode.children[1].children.find((n) => n.tagName === 'a')\n aNode.properties.href = `#user-content-fnref-${idx + 1}`\n list.children.push(liNode)\n }\n }\n\n /** @type {Element} */\n const newfootnoteSection = {\n type: 'element',\n tagName: 'section',\n properties: { dataFootnotes: true, className: ['footnotes'] },\n children: [\n {\n type: 'element',\n tagName: 'h2',\n properties: { className: ['sr-only'], id: 'footnote-label' },\n children: [{ type: 'text', value: 'Footnotes' }],\n },\n { type: 'text', value: '\\n' },\n list,\n ],\n }\n return newfootnoteSection\n}\n","/**\n * @typedef {import('hast').Node} Node\n * @typedef {import('hast').Parent} Parent\n * @typedef {import('hast').Root} Root\n * @typedef {import('hast').Element} Element\n * @typedef {import('unist-util-visit').Visitor<Node>} Visitor\n * @typedef {import('./types').CiteItem} CiteItem\n * @typedef {import('./types').Mode} Mode\n * @typedef {import('./types').Options} Options\n */\n\nimport { visit } from 'unist-util-visit'\nimport fetch from 'cross-fetch'\nimport { parseCitation } from './parse-citation.js'\nimport { genCitation } from './gen-citation.js'\nimport { genBiblioNode } from './gen-biblio.js'\nimport { genFootnoteSection } from './gen-footnote.js'\nimport { citationRE } from './regex.js'\nimport {\n isNode,\n isValidHttpUrl,\n readFile,\n getBibliography,\n loadCSL,\n loadLocale,\n getCitationFormat,\n getFrontmatterField,\n} from './utils.js'\n\nconst defaultCiteFormat = 'apa'\nconst permittedTags = ['div', 'p', 'span', 'li', 'td', 'th']\nconst idRoot = 'CITATION'\n\n/**\n * Rehype plugin that formats citations in markdown documents and insert bibliography in html format\n *\n * [-@wadler1990] --> (1990)\n * [@hughes1989, sec 3.4] --> (Hughes 1989, sec 3.4)\n * [see @wadler1990; and @hughes1989, pp. 4] --> (see Wadler 1990 and Hughes 1989, pp. 4)\n *\n * @param {*} Cite cite object from citation-js configured with the required CSLs\n * @return {import('unified').Plugin<[Options?], Root>}\n */\nconst rehypeCitationGenerator = (Cite) => {\n return (options = {}) => {\n return async (tree, file) => {\n /** @type {string[]} */\n let bibtexFile = []\n const inputCiteformat =\n /** @type {string} */\n options.csl || getFrontmatterField(file, 'csl') || defaultCiteFormat\n const noCite =\n /** @type {string[] | false} */\n options.noCite || getFrontmatterField(file, 'noCite') || false\n const inputLang = options.lang || 'en-US'\n const config = Cite.plugins.config.get('@csl')\n const citeFormat = await loadCSL(Cite, inputCiteformat, options.path)\n const lang = await loadLocale(Cite, inputLang, options.path)\n\n let bibliography = await getBibliography(options, file)\n if (bibliography.length === 0) {\n return\n }\n\n for (let i = 0; i < bibliography.length; i++) {\n /**\n * getBibibliography is building full path/url safely in both node and browser\n * If it's a valid http url, we can try to fetch safely \n * else we can try to read from file system safely \n */\n if (isValidHttpUrl(bibliography[i])) {\n try {\n const response = await fetch(bibliography[i])\n bibtexFile.push(await response.text())\n } catch (error) {\n throw new Error(`Cannot fetch bibliography URL: ${error}.`)\n }\n } else {\n try {\n bibtexFile.push(await readFile(bibliography[i]))\n } catch (error) {\n throw new Error(`Cannot read non valid bibliography URL in node env.`)\n }\n }\n }\n const citations = new Cite(bibtexFile, { generateGraph: false })\n const citationIds = citations.data.map((x) => x.id)\n const citationPre = []\n const citationDict = {}\n let citationId = 1\n const citeproc = config.engine(citations.data, citeFormat, lang, 'html')\n /** @type {Mode} */\n const mode = citeproc.opt.xclass\n const citationFormat = getCitationFormat(citeproc)\n let parsedEntries = []\n visit(tree, 'text', (node, idx, parent) => {\n const match = node.value.match(citationRE)\n if (!match || ('tagName' in parent && !permittedTags.includes(parent.tagName))) return\n let citeStartIdx = match.index\n let citeEndIdx = match.index + match[0].length\n // If we have an in-text citation and we should suppress the author, the\n // match.index does NOT include the positive lookbehind, so we have to manually\n // shift \"from\" to one before.\n if (match[2] !== undefined) {\n citeStartIdx--\n }\n const newChildren = []\n // if preceding string\n if (citeStartIdx !== 0) {\n // create a new child node\n newChildren.push({\n type: 'text',\n value: node.value.slice(0, citeStartIdx),\n })\n }\n\n const [entries, isComposite] = parseCitation(match)\n parsedEntries = entries\n\n // If id is not in citation file (e.g. route alias or js package), abort process\n for (const citeItem of entries) {\n if (!citationIds.includes(citeItem.id)) return\n }\n const [citedText, citedTextNode] = genCitation(\n citeproc,\n mode,\n entries,\n idRoot,\n citationId,\n citationPre,\n options,\n isComposite,\n citationFormat\n )\n citationDict[citationId] = citedText\n\n // Prepare citationPre and citationId for the next cite instance\n citationPre.push([`${idRoot}-${citationId}`, 0])\n citationId = citationId + 1\n\n newChildren.push(citedTextNode)\n\n // if trailing string\n if (citeEndIdx < node.value.length) {\n newChildren.push({\n type: 'text',\n value: node.value.slice(citeEndIdx),\n })\n }\n\n // insert into the parent\n // @ts-ignore\n parent.children = [\n ...parent.children.slice(0, idx),\n ...newChildren,\n ...parent.children.slice(idx + 1),\n ]\n })\n\n if (noCite) {\n if (noCite.length === 1 && noCite[0] === '@*') {\n citeproc.updateItems(citationIds)\n } else {\n const mergedIds = citations.data\n .filter((x) => noCite.map((x) => x.replace('@', '')).includes(x['citation-key']))\n .map((x) => x.id)\n .concat(parsedEntries.map((x) => x.id))\n\n citeproc.updateItems(mergedIds)\n }\n }\n\n if (\n citeproc.registry.mylist.length >= 1 &&\n (!options.suppressBibliography || options.inlineBibClass?.length > 0)\n ) {\n const biblioNode = genBiblioNode(citeproc)\n let bilioInserted = false\n\n const biblioMap = {}\n biblioNode.children\n .filter((node) => node.properties?.className?.includes('csl-entry'))\n .forEach((node) => {\n const citekey = node.properties.id.split('-').slice(1).join('-')\n biblioMap[citekey] = { ...node }\n biblioMap[citekey].properties = { id: 'inlinebib-' + citekey }\n })\n\n // Insert it at ^ref, if not found insert it as the last element of the tree\n visit(tree, 'element', (node, idx, parent) => {\n // Add inline bibliography\n if (\n options.inlineBibClass?.length > 0 &&\n node.properties?.id?.toString().startsWith('citation-')\n ) {\n // id is citation--nash1951--nash1950--1\n const [, ...citekeys] = node.properties.id.toString().split('--')\n const citationID = citekeys.pop()\n\n /** @type {Element} */\n const inlineBibNode = {\n type: 'element',\n tagName: 'div',\n properties: {\n className: options.inlineBibClass,\n id: `inlineBib--${citekeys.join('--')}--${citationID}`,\n },\n children: citekeys.map((citekey) => {\n const aBibNode = biblioMap[citekey]\n aBibNode.properties = {\n class: 'inline-entry',\n id: `inline--${citekey}--${citationID}`,\n }\n return aBibNode\n }),\n }\n parent.children.push(inlineBibNode)\n }\n\n // Add bibliography\n if (\n !options.suppressBibliography &&\n (node.tagName === 'p' || node.tagName === 'div') &&\n node.children.length >= 1 &&\n node.children[0].type === 'text' &&\n node.children[0].value === '[^ref]'\n ) {\n parent.children[idx] = biblioNode\n bilioInserted = true\n }\n })\n\n if (!options.suppressBibliography && !bilioInserted) {\n tree.children.push(biblioNode)\n }\n }\n\n let footnoteSection\n visit(tree, 'element', (node, index, parent) => {\n if (node.tagName === 'section' && node.properties.dataFootnotes) {\n footnoteSection = node\n parent.children.splice(index, 1)\n }\n })\n\n // Need to adjust footnote numbering based on existing ones already assigned\n // And insert them into the footnote section (if exists)\n // Footnote comes after bibliography\n if (mode === 'note' && Object.keys(citationDict).length > 0) {\n /** @type {{type: 'citation' | 'existing', oldId: string}[]} */\n let fnArray = []\n let index = 1\n visit(tree, 'element', (node) => {\n if (node.tagName === 'sup' && node.children[0].type === 'element') {\n let nextNode = node.children[0]\n if (nextNode.tagName === 'a') {\n /** @type {{href: string, id: string}} */ // @ts-ignore\n const { href, id } = nextNode.properties\n if (href.includes('fn') && id.includes('fnref')) {\n const oldId = href.split('-').pop()\n fnArray.push({\n type: href.includes('cite') ? 'citation' : 'existing',\n oldId,\n })\n // Update ref number\n nextNode.properties.href = `#user-content-fn-${index}`\n nextNode.properties.id = `user-content-fnref-${index}`\n // @ts-ignore\n nextNode.children[0].value = index.toString()\n index += 1\n }\n }\n }\n })\n // @ts-ignore\n const newFootnoteSection = genFootnoteSection(citationDict, fnArray, footnoteSection)\n tree.children.push(newFootnoteSection)\n } else {\n if (footnoteSection) tree.children.push(footnoteSection)\n }\n }\n }\n}\n\nexport default rehypeCitationGenerator\n"],"names":["citationRE","fullCitationRE","_wrapRegExp","prefix","citekey","explicitLocator","explicitLocatorInSuffix","suffix","locatorRE","locatorLabels","book","chapter","column","figure","folio","issue","line","note","opus","page","paragraph","part","section","verse","volume","parseCitation","regexMatch","entries","isComposite","fullCitation","inTextSuppressAuthor","inTextCitation","optionalSuffix","undefined","citationPart","split","match","exec","trim","thisCitation","id","groups","replace","locator","label","rawPrefix","endsWith","substring","length","rawSuffix","suffixToParse","containsLocator","parseSuffix","push","_extends","retValue","natural","toLowerCase","startsWith","substr","readFile","path","isValidHttpUrl","response","fetch","text","error","Error","then","fs","readFileSync","str","url","URL","_","protocol","getBibliography","options","file","bibliography","frontmatterBibliography","getFrontmatterField","i","join","cwd","loadCSL","Cite","format","root","config","plugins","get","Object","keys","templates","data","includes","cslName","Math","random","toString","slice","cslPath","add","err","loadLocale","locales","localePath","xmlLangRe","localeName","getCitationFormat","citeproc","info","cslXml","dataObj","children","node","find","x","citationFormat","getSortedRelevantRegistryItems","relevantIds","sorted","res","item","registry","reflist","index","isSameAuthor","item2","authorList","ref","author","authorList2","family","fieldName","_file$data$astro","sources","matter","frontmatter","astro","source","getBibliographyEntryText","originalItemIds","mylist","updateItems","bibOutput","makeBibliography","entryText","console","htmlToHast","html","p5ast","parseFragment","fromParse5","genCitation","mode","citationIdRoot","citationId","citationPre","inlineClass","linkCitations","showTooltips","tooltipAttribute","key","c","processCitationCluster","citationID","citationItems","properties","noteIndex","citationText","ids","map","tooltipMap","forEach","entry","wrapperTooltipAttr","e","refIds","output","d","refId","tooltipAttr","results","opt","sort_citations","citeMatch","ambig","indexOf","issued","year","startPos","start","rest","genBiblioNode","params","bibBody","biblioNode","filter","_node$properties","className","entry_ids","genFootnoteSection","citationDict","footnoteArray","footnoteSection","list","type","tagName","value","oldFootnoteList","n","idx","oldId","href","dataFootnoteBackref","ariaLabel","liNode","aNode","newfootnoteSection","dataFootnotes","defaultCiteFormat","permittedTags","idRoot","rehypeCitationGenerator","tree","_options$inlineBibCla","bibtexFile","inputCiteformat","csl","noCite","inputLang","lang","citeFormat","citations","generateGraph","citationIds","engine","xclass","parsedEntries","visit","parent","citeStartIdx","citeEndIdx","newChildren","citeItem","citedText","citedTextNode","mergedIds","concat","suppressBibliography","inlineBibClass","bilioInserted","biblioMap","_options$inlineBibCla2","_node$properties2","citekeys","pop","inlineBibNode","aBibNode","class","splice","fnArray","nextNode","newFootnoteSection"],"mappings":"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