rehype-citation
Version:
rehype plugin to add citation and bibliography from bibtex files
320 lines (319 loc) • 9.46 kB
JavaScript
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r &&
(o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})),
t.push.apply(t, o);
}
return t;
}
function _objectSpread(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2
? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
: ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
}
else {
obj[key] = value;
}
return obj;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, 'string');
return 'symbol' == typeof i ? i : i + '';
}
function _toPrimitive(t, r) {
if ('object' != typeof t || !t)
return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || 'default');
if ('object' != typeof i)
return i;
throw new TypeError('@@toPrimitive must return a primitive value.');
}
return ('string' === r ? String : Number)(t);
}
import { util, logger } from '../../core/index.js';
import moo from 'moo';
import config from '../config.js';
import { defaultStrings } from './constants.js';
const identifier = /[a-zA-Z_][a-zA-Z0-9_:+-]*/;
const whitespace = {
comment: /%.*/,
whitespace: {
match: /\s+/,
lineBreaks: true,
},
};
const lexer = moo.states({
main: {
junk: {
match: /@[cC][oO][mM][mM][eE][nN][tT].+|[^@]+/,
lineBreaks: true,
},
at: {
match: '@',
push: 'entry',
},
},
entry: _objectSpread(_objectSpread({}, whitespace), {}, {
otherEntryType: {
match: /[sS][tT][rR][iI][nN][gG]|[pP][rR][eE][aA][mM][bB][lL][eE]/,
next: 'otherEntryContents',
},
dataEntryType: {
match: identifier,
next: 'dataEntryContents',
},
}),
otherEntryContents: _objectSpread(_objectSpread({}, whitespace), {}, {
lbrace: {
match: /[{(]/,
next: 'fields',
},
}),
dataEntryContents: _objectSpread(_objectSpread({}, whitespace), {}, {
lbrace: {
match: /[{(]/,
next: 'dataEntryContents',
},
label: /[^,\s]+/,
comma: {
match: ',',
next: 'fields',
},
}),
fields: _objectSpread(_objectSpread({}, whitespace), {}, {
identifier,
number: /-?\d+/,
hash: '#',
equals: '=',
comma: ',',
quote: {
match: '"',
push: 'quotedLiteral',
},
lbrace: {
match: '{',
push: 'bracedLiteral',
},
rbrace: {
match: /[})]/,
pop: true,
},
}),
quotedLiteral: {
lbrace: {
match: '{',
push: 'bracedLiteral',
},
quote: {
match: '"',
pop: true,
},
text: {
match: /(?:\\[\\{]|[^{"])+/,
lineBreaks: true,
},
},
bracedLiteral: {
lbrace: {
match: '{',
push: 'bracedLiteral',
},
rbrace: {
match: '}',
pop: true,
},
text: {
match: /(?:\\[\\{}]|[^{}])+/,
lineBreaks: true,
},
},
});
const delimiters = {
'(': ')',
'{': '}',
};
export const bibtexGrammar = new util.Grammar({
Main() {
const entries = [];
while (true) {
while (this.matchToken('junk')) {
this.consumeToken('junk');
}
if (this.matchEndOfFile()) {
break;
}
entries.push(this.consumeRule('Entry'));
}
return entries.filter(Boolean);
},
_() {
let oldToken;
while (oldToken !== this.token) {
oldToken = this.token;
this.consumeToken('whitespace', true);
this.consumeToken('comment', true);
}
},
Entry() {
this.consumeToken('at');
this.consumeRule('_');
const type = (this.matchToken('otherEntryType')
? this.consumeToken('otherEntryType')
: this.consumeToken('dataEntryType')).value.toLowerCase();
this.consumeRule('_');
const openBrace = this.consumeToken('lbrace').value;
this.consumeRule('_');
let result;
if (type === 'string') {
const [key, value] = this.consumeRule('Field');
this.state.strings[key] = value;
}
else if (type === 'preamble') {
this.consumeRule('Expression');
}
else {
const label = this.consumeToken('label').value;
this.consumeRule('_');
this.consumeToken('comma');
this.consumeRule('_');
const entryBody = this.consumeRule('EntryBody');
result = _objectSpread({
type,
label,
}, entryBody);
}
this.consumeRule('_');
const closeBrace = this.consumeToken('rbrace').value;
if (closeBrace !== delimiters[openBrace]) {
logger.warn('[plugin-bibtex]', `entry started with "${openBrace}", but ends with "${closeBrace}"`);
}
return result;
},
EntryBody() {
const output = {
properties: {},
};
while (this.matchToken('identifier')) {
const [field, value] = this.consumeRule('Field');
let annotationField;
let annotationName = 'default';
if (field.endsWith(config.biber.annotationMarker)) {
annotationField = field.slice(0, -config.biber.annotationMarker.length);
}
else if (field.includes(config.biber.annotationMarker + config.biber.namedAnnotationMarker)) {
;
[annotationField, annotationName] = field.split(config.biber.annotationMarker + config.biber.namedAnnotationMarker);
}
if (annotationField) {
if (!output.annotations) {
output.annotations = {};
}
if (!output.annotations[annotationField]) {
output.annotations[annotationField] = {};
}
output.annotations[annotationField][annotationName] = value;
}
else {
output.properties[field] = value;
}
this.consumeRule('_');
if (this.consumeToken('comma', true)) {
this.consumeRule('_');
}
else {
break;
}
}
return output;
},
Field() {
const field = this.consumeToken('identifier').value.toLowerCase();
this.consumeRule('_');
this.consumeToken('equals');
this.consumeRule('_');
const value = this.consumeRule('Expression');
return [field, value];
},
Expression() {
let output = this.consumeRule('ExpressionPart');
this.consumeRule('_');
while (this.matchToken('hash')) {
this.consumeToken('hash');
this.consumeRule('_');
output += this.consumeRule('ExpressionPart').toString();
this.consumeRule('_');
}
return output;
},
ExpressionPart() {
if (this.matchToken('identifier')) {
return this.state.strings[this.consumeToken('identifier').value.toLowerCase()] || '';
}
else if (this.matchToken('number')) {
return parseInt(this.consumeToken('number'));
}
else if (this.matchToken('quote')) {
return this.consumeRule('QuoteString');
}
else {
return this.consumeRule('BracketString');
}
},
QuoteString() {
let output = '';
this.consumeToken('quote');
while (!this.matchToken('quote')) {
output += this.consumeRule('Text');
}
this.consumeToken('quote');
return output;
},
BracketString() {
let output = '';
this.consumeToken('lbrace');
while (!this.matchToken('rbrace')) {
output += this.consumeRule('Text');
}
this.consumeToken('rbrace');
return output;
},
Text() {
if (this.matchToken('lbrace')) {
return `{${this.consumeRule('BracketString')}}`;
}
else {
return this.consumeToken('text').value;
}
},
}, {
strings: defaultStrings,
});
export function parse(text) {
return bibtexGrammar.parse(lexer.reset(text));
}