site/node_modules/rehype-citation/dist/node/src/utils.js

194 lines
6 KiB
JavaScript
Raw Normal View History

2024-10-14 06:09:33 +00:00
import fetch from 'cross-fetch';
export const isNode = typeof window === 'undefined';
export const readFile = async (path) => {
if (isValidHttpUrl(path)) {
return fetch(path)
.then((response) => response.text())
.then((data) => data);
}
else {
if (isNode) {
return import('fs').then((fs) => fs.readFileSync(path, 'utf8'));
}
else {
throw new Error(`Cannot read non valid URL in node env.`);
}
}
};
/**
* Check if valid URL
* https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
*
* @param {string} str
* @return {boolean}
*/
export 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
*/
export const getBibliography = async (options, file) => {
let bibliography = '';
if (options.bibliography) {
bibliography = options.bibliography;
// @ts-ignore
}
else if (file?.data?.frontmatter?.bibliography) {
// @ts-ignore
bibliography = file.data.frontmatter.bibliography;
// If local path, get absolute path
if (!isValidHttpUrl(bibliography)) {
if (isNode) {
bibliography = await import('path').then((path) => path.join(options.path || file.cwd, bibliography));
}
else {
throw new Error(`Cannot read non valid bibliography URL in node env.`);
}
}
}
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
*/
export 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 {
if (isNode)
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
*/
export 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 {
if (isNode)
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
*/
export 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
*/
export 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[]}
*/
export 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}
*/
export 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;
};