site/node_modules/rehype-citation/dist/node/generator.mjs

948 lines
69 KiB
JavaScript
Raw Permalink Normal View History

2024-10-14 06:09:33 +00:00
import { visit } from 'unist-util-visit';
import fetch from 'cross-fetch';
import { parseFragment } from 'parse5';
import { fromParse5 } from 'hast-util-from-parse5';
function _wrapRegExp() {
_wrapRegExp = function (e, r) {
return new BabelRegExp(e, void 0, r);
};
var e = RegExp.prototype,
r = new WeakMap();
function BabelRegExp(e, t, p) {
var o = new RegExp(e, t);
return r.set(o, p || r.get(e)), _setPrototypeOf(o, BabelRegExp.prototype);
}
function buildGroups(e, t) {
var p = r.get(t);
return Object.keys(p).reduce(function (r, t) {
var o = p[t];
if ("number" == typeof o) r[t] = e[o];else {
for (var i = 0; void 0 === e[o[i]] && i + 1 < o.length;) i++;
r[t] = e[o[i]];
}
return r;
}, Object.create(null));
}
return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (r) {
var t = e.exec.call(this, r);
if (t) {
t.groups = buildGroups(t, this);
var p = t.indices;
p && (p.groups = buildGroups(p, this));
}
return t;
}, BabelRegExp.prototype[Symbol.replace] = function (t, p) {
if ("string" == typeof p) {
var o = r.get(this);
return e[Symbol.replace].call(this, t, p.replace(/\$<([^>]+)>/g, function (e, r) {
var t = o[r];
return "$" + (Array.isArray(t) ? t.join("$") : t);
}));
}
if ("function" == typeof p) {
var i = this;
return e[Symbol.replace].call(this, t, function () {
var e = arguments;
return "object" != typeof e[e.length - 1] && (e = [].slice.call(e)).push(buildGroups(e, i)), p.apply(this, e);
});
}
return e[Symbol.replace].call(this, t, p);
}, _wrapRegExp.apply(this, arguments);
}
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
Object.defineProperty(subClass, "prototype", {
writable: false
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
// Regex adapted from https://github.com/Zettlr/Zettlr/blob/develop/source/common/util/extract-citations.ts
/**
* Citation detection: The first alternative matches "full" citations surrounded
* by square brackets, whereas the second one matches in-text citations,
* optionally with suffixes.
*
* * Group 1 matches regular "full" citations
* * Group 2 matches in-text citations (not surrounded by brackets)
* * Group 3 matches optional square-brackets suffixes to group 2 matches
*
* For more information, see https://pandoc.org/MANUAL.html#extension-citations
*
* @var {RegExp}
*/
const citationRE = /(?:\[((?:[\0-Z\\\^-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*@(?:[\0-Z\\\^-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)\])|(?<=[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]|^|(\x2D))(?:@((?:[0-9A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00
/**
* I hate everything at this. This can match every single possible variation on
* whatever the f*** you can possibly do within square brackets according to the
* documentation. I opted for named groups for these because otherwise I have no
* idea what I have been doing here.
*
* * Group prefix: Contains the prefix, ends with a dash if we should suppress the author
* * Group citekey: Contains the actual citekey, can be surrounded in curly brackets
* * Group explicitLocator: Contains an explicit locator statement. If given, we MUST ignore any form of locator in the suffix
* * Group explicitLocatorInSuffix: Same as above, but not concatenated to the citekey
* * Group suffix: Contains the suffix, but may start with a locator (if explicitLocator and explicitLocatorInSuffix are not given)
*
* @var {RegExp}
*/
const fullCitationRE = /*#__PURE__*/_wrapRegExp(/((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)?(?:@((?:[0-9A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-
prefix: 1,
citekey: 2,
explicitLocator: 3,
explicitLocatorInSuffix: 4,
suffix: 5
});
/**
* This regular expression matches locator ranges, like the following:
*
* * 23-45, and further (here it matches up to, not including the comma)
* * 45
* * 15423
* * 14235-12532
* * 12-34, 23, 56
* * 12, 23-14, 23
* * 12, 54, 12-23
* * 1, 1-4
* * 3
* * NEW NEW NEW: Now also matches Roman numerals as sometimes used in forewords!
*
* @var {RegExp}
*/
const locatorRE = /^(?:[\d, -]*\d|[ivxlcdm, -]*[ivxlcdm])/i;
/**
* The locatorLabels have been sourced from the Citr library. Basically it's just
* a map with valid CSL locator labels and an array of possible natural labels
* which a user might want to write (instead of the standardized labels).
*
* @var {{ [key: string]: string[] }}}
*/
const locatorLabels = {
book: ['Buch', 'Bücher', 'B.', 'book', 'books', 'bk.', 'bks.', 'livre', 'livres', 'liv.'],
chapter: ['Kapitel', 'Kap.', 'chapter', 'chapters', 'chap.', 'chaps', 'chapitre', 'chapitres'],
column: ['Spalte', 'Spalten', 'Sp.', 'column', 'columns', 'col.', 'cols', 'colonne', 'colonnes'],
figure: ['Abbildung', 'Abbildungen', 'Abb.', 'figure', 'figures', 'fig.', 'figs'],
folio: ['Blatt', 'Blätter', 'Fol.', 'folio', 'folios', 'fol.', 'fols', 'fᵒ', 'fᵒˢ'],
issue: ['Nummer', 'Nummern', 'Nr.', 'number', 'numbers', 'no.', 'nos.', 'numéro', 'numéros', 'nᵒ', 'nᵒˢ'],
line: ['Zeile', 'Zeilen', 'Z', 'line', 'lines', 'l.', 'll.', 'ligne', 'lignes'],
note: ['Note', 'Noten', 'N.', 'note', 'notes', 'n.', 'nn.'],
opus: ['Opus', 'Opera', 'op.', 'opus', 'opera', 'opp.'],
page: ['Seite', 'Seiten', 'S.', 'page', 'pages', 'p.', 'pp.'],
paragraph: ['Absatz', 'Absätze', 'Abs.', '¶', '¶¶', 'paragraph', 'paragraphs', 'para.', 'paras', 'paragraphe', 'paragraphes', 'paragr.'],
part: ['Teil', 'Teile', 'part', 'parts', 'pt.', 'pts', 'partie', 'parties', 'part.'],
section: ['Abschnitt', 'Abschnitte', 'Abschn.', '§', '§§', 'section', 'sections', 'sec.', 'secs', 'sect.'],
'sub verbo': ['sub verbo', 'sub verbis', 's.&#160;v.', 's.&#160;vv.', 's.v.', 's.vv.'],
verse: ['Vers', 'Verse', 'V.', 'verse', 'verses', 'v.', 'vv.', 'verset', 'versets'],
volume: ['Band', 'Bände', 'Bd.', 'Bde.', 'volume', 'volumes', 'vol.', 'vols.']
};
/**
* Parses a given citation string and return entries and isComposite flag required for cite-proc.
* Adapted from https://github.com/Zettlr/Zettlr/blob/develop/source/common/util/extract-citations.ts
*
* @param {RegExpMatchArray} regexMatch Cite string in the form of '[@item]' or '@item'
* @return {[CiteItem[], boolean]} [entries, isComposite]
*/
const parseCitation = regexMatch => {
/** @type {CiteItem[]} */
let entries = [];
let isComposite = false;
const fullCitation = regexMatch[1];
const inTextSuppressAuthor = regexMatch[2];
const inTextCitation = regexMatch[3];
const optionalSuffix = regexMatch[4];
if (fullCitation !== undefined) {
// Handle citations in the form of [@item1; @item2]
for (const citationPart of fullCitation.split(';')) {
const match = fullCitationRE.exec(citationPart.trim());
if (match === null) {
continue; // Faulty citation
}
// Prefix is the portion before @ e.g. [see @item1] or an empty string
// We explicitly cast groups since we have groups in our RegExp and as
// such the groups object will be set.
/** @type {CiteItem} */
const thisCitation = {
id: match.groups.citekey.replace(/{(.+)}/, '$1'),
prefix: undefined,
locator: undefined,
label: 'page',
'suppress-author': false,
suffix: undefined
};
// First, deal with the prefix. The speciality here is that it can
// indicate if we should suppress the author.
const rawPrefix = match.groups.prefix;
if (rawPrefix !== undefined) {
thisCitation['suppress-author'] = rawPrefix.trim().endsWith('-');
if (thisCitation['suppress-author']) {
thisCitation.prefix = rawPrefix.substring(0, rawPrefix.trim().length - 1).trim();
} else {
thisCitation.prefix = rawPrefix.trim();
}
}
// Second, deal with the suffix. This one can be much more tricky than
// the prefix. We have three alternatives where the locator may be
// present: If we have an explicitLocator or an explicitLocatorInSuffix,
// we should extract the locator from there and leave the actual suffix
// untouched. Only if those two alternatives are not present, then we
// have a look at the rawSuffix and extract a (potential) locator.
const explicitLocator = match.groups.explicitLocator;
const explicitLocatorInSuffix = match.groups.explicitLocatorInSuffix;
const rawSuffix = match.groups.suffix;
let suffixToParse;
let containsLocator = true;
if (explicitLocator === undefined && explicitLocatorInSuffix === undefined) {
// Potential locator in rawSuffix. Only in this case should we overwrite
// the suffix (hence the same if-condition below)
suffixToParse = rawSuffix;
containsLocator = false;
} else if (explicitLocatorInSuffix !== undefined || explicitLocator !== undefined) {
suffixToParse = explicitLocator !== undefined ? explicitLocator : explicitLocatorInSuffix;
thisCitation.suffix = rawSuffix == null ? void 0 : rawSuffix.trim();
}
const {
label,
locator,
suffix
} = parseSuffix(suffixToParse, containsLocator);
thisCitation.locator = locator;
if (label !== undefined) {
thisCitation.label = label;
}
if (explicitLocator === undefined && explicitLocatorInSuffix === undefined) {
thisCitation.suffix = suffix;
} else if (suffix !== undefined && thisCitation.locator !== undefined) {
// If we're here, we should not change the suffix, but parseSuffix may
// have put something into the suffix return. If we're here, that will
// definitely be a part of the locator.
thisCitation.locator += suffix;
}
entries.push(thisCitation);
}
} else {
// We have an in-text citation, so we can take a shortcut
isComposite = true;
entries.push(_extends({
prefix: undefined,
id: inTextCitation.replace(/{(.+)}/, '$1'),
'suppress-author': inTextSuppressAuthor !== undefined
}, parseSuffix(optionalSuffix, false)));
}
return [entries, isComposite];
};
/**
* This takes a suffix and extracts optional label and locator from this. Pass
* true for the containsLocator property to indicate to this function that what
* it got was not a regular suffix with an optional locator, but an explicit
* locator so it knows it just needs to look for an optional label.
*
* @param {string} suffix The suffix to parse
* @param {boolean} containsLocator If true, forces parseSuffix to return a locator
*
* @return {CiteItemSuffix} An object containing three optional properties locator, label, or suffix.
*/
function parseSuffix(suffix, containsLocator) {
/** @type {CiteItemSuffix} */
const retValue = {
locator: undefined,
label: 'page',
suffix: undefined
};
if (suffix === undefined) {
return retValue;
}
// Make sure the suffix does not start or end with spaces
suffix = suffix.trim();
// If there is a label, the suffix must start with it
for (const label in locatorLabels) {
for (const natural of locatorLabels[label]) {
if (suffix.toLowerCase().startsWith(natural.toLowerCase())) {
retValue.label = label;
if (containsLocator) {
// The suffix actually is the full locator, we just had to extract
// the label from it. There is no remaining suffix.
retValue.locator = suffix.substr(natural.length).trim();
} else {
// The caller indicated that this is a regular suffix, so we must also
// extract the locator from what is left after label extraction.
retValue.suffix = suffix.substr(natural.length).trim();
const match = locatorRE.exec(retValue.suffix);
if (match !== null) {
retValue.locator = match[0]; // Extract the full match
retValue.suffix = retValue.suffix.substr(match[0].length).trim();
}
}
return retValue; // Early exit
}
}
}
// If we're here, there was no explicit label given, but the caller has indicated
// that this suffix MUST contain a locator. This means that the whole suffix is
// the locator.
if (containsLocator) {
retValue.locator = suffix;
} else {
// The caller has not indicated that the whole suffix is the locator, so it
// can be at the beginning. We only accept simple page/number ranges here.
// For everything, the user should please be more specific.
const match = locatorRE.exec(suffix);
if (match !== null) {
retValue.locator = match[0]; // Full match is the locator
retValue.suffix = suffix.substr(match[0].length).trim(); // The rest is the suffix.
}
}
return retValue;
}
const readFile = async path => {
if (isValidHttpUrl(path)) {
return fetch(path).then(response => response.text()).then(data => data);
} else {
{
return import('fs').then(fs => fs.readFileSync(path, 'utf8'));
}
}
};
/**
* Check if valid URL
* https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
*
* @param {string} str
* @return {boolean}
*/
const isValidHttpUrl = str => {
let url;
try {
url = new URL(str);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
};
/**
* Get bibliography by merging options and vfile data
*
* @param {import('./generator.js').Options} options
* @param {import('vfile').VFile} file
*/
const getBibliography = async (options, file) => {
var _file$data;
/** @type {string[]} */
let bibliography = [];
if (options.bibliography) {
bibliography = typeof options.bibliography === 'string' ? [options.bibliography] : options.bibliography;
// @ts-ignore
} else if (file != null && (_file$data = file.data) != null && (_file$data = _file$data.frontmatter) != null && _file$data.bibliography) {
// @ts-ignore
bibliography = typeof file.data.frontmatter.bibliography === 'string' ? [file.data.frontmatter.bibliography] : file.data.frontmatter.bibliography;
// If local path, get absolute path
for (let i = 0; i < bibliography.length; i++) {
if (!isValidHttpUrl(bibliography[i])) {
{
bibliography[i] = await import('path').then(path => path.join(options.path || file.cwd, bibliography[i]));
}
}
}
}
return bibliography;
};
/**
* Load CSL - supports predefined name from config.templates.data or http, file path (nodejs)
*
* @param {*} Cite cite object from citation-js
* @param {string} format CSL name e.g. apa or file path to CSL file
* @param {string} root optional root path
*/
const loadCSL = async (Cite, format, root = '') => {
const config = Cite.plugins.config.get('@csl');
if (!Object.keys(config.templates.data).includes(format)) {
const cslName = `customCSL-${Math.random().toString(36).slice(2, 7)}`;
let cslPath = '';
if (isValidHttpUrl(format)) cslPath = format;else {
cslPath = await import('path').then(path => path.join(root, format));
}
try {
config.templates.add(cslName, await readFile(cslPath));
} catch (err) {
throw new Error(`Input CSL option, ${format}, is invalid or is an unknown file.`);
}
return cslName;
} else {
return format;
}
};
/**
* Load locale - supports predefined name from config.locales.data or http, file path (nodejs)
*
* @param {*} Cite cite object from citation-js
* @param {string} format locale name
* @param {string} root optional root path
*/
const loadLocale = async (Cite, format, root = '') => {
const config = Cite.plugins.config.get('@csl');
if (!Object.keys(config.locales.data).includes(format)) {
let localePath = '';
if (isValidHttpUrl(format)) localePath = format;else {
localePath = await import('path').then(path => path.join(root, format));
}
try {
const file = await readFile(localePath);
const xmlLangRe = /xml:lang="(.+)"/;
const localeName = file.match(xmlLangRe)[1];
config.locales.add(localeName, file);
return localeName;
} catch (err) {
throw new Error(`Input locale option, ${format}, is invalid or is an unknown file.`);
}
} else {
return format;
}
};
/**
* Get citation format
*
* @param {*} citeproc citeproc
* @returns string
*/
const getCitationFormat = citeproc => {
const info = citeproc.cslXml.dataObj.children[0];
const node = info.children.find(x => x['attrs'] && x['attrs']['citation-format']);
// citation-format takes 5 possible values
// https://docs.citationstyles.org/en/stable/specification.html#toc-entry-14
/** @type {'author-date' | 'author' | 'numeric' | 'note' | 'label'} */
const citationFormat = node['attrs']['citation-format'];
return citationFormat;
};
/**
* Get registry objects that matches a list of relevantIds
* If sorted is false, retrieve registry item in the order of the given relevantIds
*
* @param {*} citeproc citeproc
* @param {string[]} relevantIds
* @param {boolean} sorted
* @return {*} registry objects that matches Ids, in the correct order
*/
const getSortedRelevantRegistryItems = (citeproc, relevantIds, sorted) => {
const res = [];
if (sorted) {
// If sorted follow registry order
for (const item of citeproc.registry.reflist) {
if (relevantIds.includes(item.id)) res.push(item);
}
} else {
// Otherwise follow the relevantIds
for (const id of relevantIds) {
res.push(citeproc.registry.reflist.find(x => x.id === id));
}
}
return res;
};
/**
* Split a string into two parts based on a given index position
*
* @param {string} str
* @param {number} index
* @return {string[]}
*/
const split = (str, index) => {
return [str.slice(0, index), str.slice(index)];
};
/**
* Check if two registry objects belong to the same author
* Currently only checks on family name
*
* @param {*} item registry object
* @param {*} item2 registry object
* @return {boolean}
*/
const isSameAuthor = (item, item2) => {
const authorList = item.ref.author;
const authorList2 = item2.ref.author;
if (authorList.length !== authorList2.length) return false;
for (let i = 0; i < authorList.length; i++) {
if (authorList[i].family !== authorList2[i].family) return false;
}
return true;
};
/**
* Convert HTML to HAST node
*
* @param {string} html
*/
const htmlToHast = html => {
const p5ast = parseFragment(html);
// @ts-ignore
return fromParse5(p5ast).children[0];
};
/**
* @typedef {import('./types').CiteItem} CiteItem
* @typedef {import('./types').Mode} Mode
* @typedef {import('./types').Options} Options
*/
/**
* Generate citation using citeproc
* This accounts for prev citations and additional properties
*
* @param {*} citeproc
* @param {Mode} mode
* @param {CiteItem[]} entries
* @param {string} citationIdRoot
* @param {number} citationId
* @param {any[]} citationPre
* @param {Options} options
* @param {boolean} isComposite
* @param {import('./types').CitationFormat} citationFormat
* @return {[string, string]}
*/
const genCitation = (citeproc, mode, entries, citationIdRoot, citationId, citationPre, options, isComposite, citationFormat) => {
const {
inlineClass,
linkCitations
} = options;
const key = `${citationIdRoot}-${citationId}`;
const c = citeproc.processCitationCluster({
citationID: key,
citationItems: entries,
properties: mode === 'in-text' ? {
noteIndex: 0,
mode: isComposite ? 'composite' : ''
} : {
noteIndex: citationId,
mode: isComposite ? 'composite' : ''
}
}, citationPre.length > 0 ? citationPre : [], []);
// c = [ { bibchange: true, citation_errors: [] }, [ [ 0, '(1)', 'CITATION-1' ] ]]
const citationText = c[1].find(x => x[2] === key)[1];
const ids = `citation--${entries.map(x => x.id.toLowerCase()).join('--')}--${citationId}`;
if (mode === 'note') {
// Use cite-fn-{id} to denote footnote from citation, will clean it up later to follow gfm "user-content" format
return [citationText, htmlToHast(`<span class="${(inlineClass != null ? inlineClass : []).join(' ')}" id=${ids}><sup><a href="#cite-fn-${citationId}" id="cite-fnref-${citationId}" data-footnote-ref aria-describedby="footnote-label">${citationId}</a></sup></span>`)];
} else if (linkCitations && citationFormat === 'numeric') {
// e.g. [1, 2]
let i = 0;
const refIds = entries.map(e => e.id);
const output = citationText.replace(/\d+/g, function (d) {
const url = `<a href="#bib-${refIds[i].toLowerCase()}">${d}</a>`;
i++;
return url;
});
return [citationText, htmlToHast(`<span class="${(inlineClass != null ? inlineClass : []).join(' ')}" id=${ids}>${output}</span>`)];
} else if (linkCitations && citationFormat === 'author-date') {
// E.g. (see Nash, 1950, pp. 1213, 1951); (Nash, 1950; Xie, 2016)
if (entries.length === 1) {
// Do not link bracket
const output = isComposite ? `<a href="#bib-${entries[0].id.toLowerCase()}">${citationText}</a>` : `${citationText.slice(0, 1)}<a href="#bib-${entries[0].id.toLowerCase()}">${citationText.slice(1, -1)}</a>${citationText.slice(-1)}`;
return [citationText, htmlToHast(`<span class="${(inlineClass != null ? inlineClass : []).join(' ')}" id=${ids}>${output}</span>`)];
} else {
// Retrieve the items in the correct order and attach link each of them
const refIds = entries.map(e => e.id);
const results = getSortedRelevantRegistryItems(citeproc, refIds, citeproc.opt.sort_citations);
const output = [];
let str = citationText;
for (const [i, item] of results.entries()) {
// Need to compare author. If same just match on date.
const id = item.id;
let citeMatch = item.ambig;
// If author is the same as the previous, some styles like apa collapse the author
if (i > 0 && isSameAuthor(results[i - 1], item) && str.indexOf(citeMatch) === -1) {
// Just match on year
citeMatch = item.ref.issued.year.toString();
}
const startPos = str.indexOf(citeMatch);
const [start, rest] = split(str, startPos);
output.push(start); // Irrelevant parts
const url = `<a href="#bib-${id.toLowerCase()}">${rest.substring(0, citeMatch.length)}</a>`;
output.push(url);
str = rest.substr(citeMatch.length);
}
output.push(str);
return [citationText, htmlToHast(`<span class="${(inlineClass != null ? inlineClass : []).join(' ')}" id=${ids}>${output.join('')}</span>`)];
}
} else {
return [citationText, htmlToHast(`<span class="${(inlineClass != null ? inlineClass : []).join(' ')}" id=${ids}>${citationText}</span>`)];
}
};
/**
* Generate bibliography in html and convert it to hast
*
* @param {*} citeproc
*/
const genBiblioNode = citeproc => {
const [params, bibBody] = citeproc.makeBibliography();
const bibliography = '<div id="refs" class="references csl-bib-body">\n' + bibBody.join('') + '</div>';
const biblioNode = htmlToHast(bibliography);
// Add citekey id to each bibliography entry.
biblioNode.children.filter(node => {
var _node$properties;
return (_node$properties = node.properties) == null || (_node$properties = _node$properties.className) == null ? void 0 : _node$properties.includes('csl-entry');
}).forEach((node, i) => {
const citekey = params.entry_ids[i][0].toLowerCase();
node.properties = node.properties || {};
node.properties.id = 'bib-' + citekey;
});
return biblioNode;
};
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
*/
/**
* Create new footnote section node based on footnoteArray mappings
*
* @param {{int: string}} citationDict
* @param {{type: 'citation' | 'existing', oldId: string}[]} footnoteArray
* @param {Element | undefined} footnoteSection
* @return {Element}
*/
const genFootnoteSection = (citationDict, footnoteArray, footnoteSection) => {
/** @type {Element} */
const list = {
type: 'element',
tagName: 'ol',
properties: {},
children: [{
type: 'text',
value: '\n'
}]
};
let oldFootnoteList;
if (footnoteSection) {
/** @type {Element} */ // @ts-ignore - for some reason, the type does not narrow even after filtering
oldFootnoteList = footnoteSection.children.filter(n => n.type == "element").find(n => n.tagName === 'ol');
}
for (const [idx, item] of footnoteArray.entries()) {
const {
type,
oldId
} = item;
if (type === 'citation') {
list.children.push({
type: 'element',
tagName: 'li',
properties: {
id: `user-content-fn-${idx + 1}`
},
children: [{
type: 'element',
tagName: 'p',
properties: {},
children: [htmlToHast(`<span>${citationDict[oldId]}</span>`), {
type: 'element',
tagName: 'a',
properties: {
href: `#user-content-fnref-${idx + 1}`,
dataFootnoteBackref: true,
className: ['data-footnote-backref'],
ariaLabel: 'Back to content'
},
children: [{
type: 'text',
value: '↩'
}]
}]
}, {
type: 'text',
value: '\n'
}]
});
} else if (type === 'existing') {
// @ts-ignore
const liNode = oldFootnoteList.children.find(n => n.tagName === 'li' && n.properties.id === `user-content-fn-${oldId}`);
liNode.properties.id = `user-content-fn-${idx + 1}`;
const aNode = liNode.children[1].children.find(n => n.tagName === 'a');
aNode.properties.href = `#user-content-fnref-${idx + 1}`;
list.children.push(liNode);
}
}
/** @type {Element} */
const newfootnoteSection = {
type: 'element',
tagName: 'section',
properties: {
dataFootnotes: true,
className: ['footnotes']
},
children: [{
type: 'element',
tagName: 'h2',
properties: {
className: ['sr-only'],
id: 'footnote-label'
},
children: [{
type: 'text',
value: 'Footnotes'
}]
}, {
type: 'text',
value: '\n'
}, list]
};
return newfootnoteSection;
};
const defaultCiteFormat = 'apa';
const permittedTags = ['div', 'p', 'span', 'li', 'td', 'th'];
const idRoot = 'CITATION';
/**
* Rehype plugin that formats citations in markdown documents and insert bibliography in html format
*
* [-@wadler1990] --> (1990)
* [@hughes1989, sec 3.4] --> (Hughes 1989, sec 3.4)
* [see @wadler1990; and @hughes1989, pp. 4] --> (see Wadler 1990 and Hughes 1989, pp. 4)
*
* @param {*} Cite cite object from citation-js configured with the required CSLs
* @return {import('unified').Plugin<[Options?], Root>}
*/
const rehypeCitationGenerator = Cite => {
return (options = {}) => {
return async (tree, file) => {
var _file$data, _options$inlineBibCla;
/** @type {string[]} */
let bibtexFile = [];
/** @type {string} */ // @ts-ignore
const inputCiteformat = options.csl || (file == null || (_file$data = file.data) == null || (_file$data = _file$data.frontmatter) == null ? void 0 : _file$data.csl) || defaultCiteFormat;
const inputLang = options.lang || 'en-US';
const config = Cite.plugins.config.get('@csl');
const citeFormat = await loadCSL(Cite, inputCiteformat, options.path);
const lang = await loadLocale(Cite, inputLang, options.path);
let bibliography = await getBibliography(options, file);
if (bibliography.length === 0) {
return;
}
for (let i = 0; i < bibliography.length; i++) {
if (isValidHttpUrl(bibliography[i])) {
const response = await fetch(bibliography[i]);
bibtexFile.push(await response.text());
} else {
{
bibtexFile.push(await readFile(bibliography[i]));
}
}
}
const citations = new Cite(bibtexFile);
const citationIds = citations.data.map(x => x.id);
const citationPre = [];
const citationDict = {};
let citationId = 1;
const citeproc = config.engine(citations.data, citeFormat, lang, 'html');
/** @type {Mode} */
const mode = citeproc.opt.xclass;
const citationFormat = getCitationFormat(citeproc);
visit(tree, 'text', (node, idx, parent) => {
const match = node.value.match(citationRE);
if (!match || 'tagName' in parent && !permittedTags.includes(parent.tagName)) return;
let citeStartIdx = match.index;
let citeEndIdx = match.index + match[0].length;
// If we have an in-text citation and we should suppress the author, the
// match.index does NOT include the positive lookbehind, so we have to manually
// shift "from" to one before.
if (match[2] !== undefined) {
citeStartIdx--;
}
const newChildren = [];
// if preceding string
if (citeStartIdx !== 0) {
// create a new child node
newChildren.push({
type: 'text',
value: node.value.slice(0, citeStartIdx)
});
}
const [entries, isComposite] = parseCitation(match);
// If id is not in citation file (e.g. route alias or js package), abort process
for (const citeItem of entries) {
if (!citationIds.includes(citeItem.id)) return;
}
const [citedText, citedTextNode] = genCitation(citeproc, mode, entries, idRoot, citationId, citationPre, options, isComposite, citationFormat);
citationDict[citationId] = citedText;
// Prepare citationPre and citationId for the next cite instance
citationPre.push([`${idRoot}-${citationId}`, 0]);
citationId = citationId + 1;
newChildren.push(citedTextNode);
// if trailing string
if (citeEndIdx < node.value.length) {
newChildren.push({
type: 'text',
value: node.value.slice(citeEndIdx)
});
}
// insert into the parent
// @ts-ignore
parent.children = [...parent.children.slice(0, idx), ...newChildren, ...parent.children.slice(idx + 1)];
});
if (options.noCite) {
citeproc.updateItems(options.noCite.map(x => x.replace('@', '')));
}
if (citeproc.registry.mylist.length >= 1 && (!options.suppressBibliography || ((_options$inlineBibCla = options.inlineBibClass) == null ? void 0 : _options$inlineBibCla.length) > 0)) {
const biblioNode = genBiblioNode(citeproc);
let bilioInserted = false;
const biblioMap = {};
biblioNode.children.filter(node => {
var _node$properties;
return (_node$properties = node.properties) == null || (_node$properties = _node$properties.className) == null ? void 0 : _node$properties.includes('csl-entry');
}).forEach(node => {
const citekey = node.properties.id.split('-').slice(1).join('-');
biblioMap[citekey] = _extends({}, node);
biblioMap[citekey].properties = {
id: 'inlinebib-' + citekey
};
});
// Insert it at ^ref, if not found insert it as the last element of the tree
visit(tree, 'element', (node, idx, parent) => {
var _options$inlineBibCla2, _node$properties2;
// Add inline bibliography
if (((_options$inlineBibCla2 = options.inlineBibClass) == null ? void 0 : _options$inlineBibCla2.length) > 0 && (_node$properties2 = node.properties) != null && (_node$properties2 = _node$properties2.id) != null && _node$properties2.toString().startsWith('citation-')) {
// id is citation--nash1951--nash1950--1
const [, ...citekeys] = node.properties.id.toString().split('--');
const citationID = citekeys.pop();
/** @type {Element} */
const inlineBibNode = {
type: 'element',
tagName: 'div',
properties: {
className: options.inlineBibClass,
id: `inlineBib--${citekeys.join('--')}--${citationID}`
},
children: citekeys.map(citekey => {
const aBibNode = biblioMap[citekey];
aBibNode.properties = {
class: 'inline-entry',
id: `inline--${citekey}--${citationID}`
};
return aBibNode;
})
};
parent.children.push(inlineBibNode);
}
// Add bibliography
if (!options.suppressBibliography && (node.tagName === 'p' || node.tagName === 'div') && node.children.length >= 1 && node.children[0].type === 'text' && node.children[0].value === '[^ref]') {
parent.children[idx] = biblioNode;
bilioInserted = true;
}
});
if (!options.suppressBibliography && !bilioInserted) {
tree.children.push(biblioNode);
}
}
let footnoteSection;
visit(tree, 'element', (node, index, parent) => {
if (node.tagName === 'section' && node.properties.dataFootnotes) {
footnoteSection = node;
parent.children.splice(index, 1);
}
});
// Need to adjust footnote numbering based on existing ones already assigned
// And insert them into the footnote section (if exists)
// Footnote comes after bibliography
if (mode === 'note' && Object.keys(citationDict).length > 0) {
/** @type {{type: 'citation' | 'existing', oldId: string}[]} */
let fnArray = [];
let index = 1;
visit(tree, 'element', node => {
if (node.tagName === 'sup' && node.children[0].type === 'element') {
let nextNode = node.children[0];
if (nextNode.tagName === 'a') {
/** @type {{href: string, id: string}} */ // @ts-ignore
const {
href,
id
} = nextNode.properties;
if (href.includes('fn') && id.includes('fnref')) {
const oldId = href.split('-').pop();
fnArray.push({
type: href.includes('cite') ? 'citation' : 'existing',
oldId
});
// Update ref number
nextNode.properties.href = `#user-content-fn-${index}`;
nextNode.properties.id = `user-content-fnref-${index}`;
// @ts-ignore
nextNode.children[0].value = index.toString();
index += 1;
}
}
}
});
// @ts-ignore
const newFootnoteSection = genFootnoteSection(citationDict, fnArray, footnoteSection);
tree.children.push(newFootnoteSection);
} else {
if (footnoteSection) tree.children.push(footnoteSection);
}
};
};
};
export { rehypeCitationGenerator as default };
//# sourceMappingURL=generator.mjs.map