Quartz sync: Mar 1, 2025, 2:26 PM

This commit is contained in:
Anthony Berg
2025-03-01 14:26:36 +01:00
parent 16c38cf522
commit 8fb1f81784
19490 changed files with 2553421 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Timothy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,163 @@
![rehype citation](./rehype-citation.png)
# Rehype-Citation
[![GitHub Repo stars](https://img.shields.io/github/stars/timlrx/rehype-citation?style=social)](https://GitHub.com/timlrx/rehype-citation/stargazers/)
[![GitHub forks](https://img.shields.io/github/forks/timlrx/rehype-citation?style=social)](https://GitHub.com/timlrx/rehype-citation/network/)
[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Ftimlrxx)](https://twitter.com/timlrxx)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/timlrx)](https://github.com/sponsors/timlrx)
[![DOI](https://zenodo.org/badge/419657013.svg)](https://zenodo.org/doi/10.5281/zenodo.10004327)
[rehype](https://github.com/wooorm/rehype) plugin to nicely format citations in markdown documents and insert bibliography in html format. It is meant to be used as a server side plugin and neatly integrates [citeproc-js](https://github.com/Juris-M/citeproc-js) and [citation-js](https://github.com/citation-js/citation-js) within the remark-rehype ecosystem. Parsing of citations and all the wonderful regexes are adapted from [Zettlr](https://github.com/Zettlr/Zettlr).
It supports both normal citations (such as [@foo]) and in-text citation (such as @foo), as well as author-date, numerical, and note styles.
Note styles is only compatible with Github Formatted Markdown (GFM). It is recommended to run `remark-gfm` before `rehype-citation` to ensure all footnote elements are correctly formatted.
API and options follows very closely to [Rmarkdown](https://bookdown.org/yihui/rmarkdown-cookbook/bibliography.html) and [Pandoc](https://pandoc.org/MANUAL.html#citations)
## Examples
- [Citations with Bibliography](https://rehype-citation.netlify.app)
- [Suppress Bibliography](https://rehype-citation.netlify.app/suppress-bibliography)
- [Custom CSL](https://rehype-citation.netlify.app/custom-csl)
- [Footnote style](https://rehype-citation.netlify.app/footnote-style)
- [Link Citations](https://rehype-citation.netlify.app/link-citations)
## Installation
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
```js
npm install rehype-citation
```
## Usage
If you are using the plugin in a node environment, import from `rehype-citation/node`. For browser environments, import from `rehype-citation/browser`.
The following files are exported:
`generator`, generator function. Can be used to generate a rehype citation plugin. Takes in a citation-js `Cite` class.
`cite`, a citation-js `Cite` instance. Add your own CSL / locales before passing in to the plugin generator .
`rehype-citation`, re-exports the above 2 packages with a pre-configured `rehype-citation` plugin ready to use. Importing from `rehype-citation` directs to this file.
Use this package [as a rehype plugin](https://github.com/rehypejs/rehype/blob/master/doc/plugins.md#using-plugins).
Some examples of how you might do that:
```js
import rehype from 'rehype'
import rehypeCitation from 'rehype-citation'
rehype().use(rehypeCitation).process(/* some html */)
```
## Sample markdown to HTML output
Input:
```md
My markdown text [@Nash1950]
```
HTML Output:
```html
<div>My markdown text (Nash, 1950)</div>
<div id="refs" class="references csl-bib-body">
<div class="csl-entry">
Nash, J. (1950). Equilibrium points in n-person games.
<i>Proceedings of the National Academy of Sciences</i>, <i>36</i>(1), 4849.
</div>
</div>
```
## Generating your own remark citation plugins
The default plugin comes configured with the `en-US` locale and the following CSL styles: apa, vancouver, harvard1, chicago and mla.
Use the generator function to customize your own remark-citation plugin and add your own [CSL styles](https://github.com/citation-style-language/styles) or [locales](https://github.com/citation-style-language/locales).
```js
import Cite from 'rehype-citation/cite'
import rehypeCitationGenerator from 'rehype-citation/generator'
import myStyle from '../style'
import myLocale from '../locale'
const config = Cite.plugins.config.get('@csl')
config.templates.add('mystyle', myStyle)
config.locales.add('myLocale', myLocale)
const rehypeCitation = rehypeCitationGenerator(Cite)
```
## API
`rehype().use(rehypeCitation, [options])`
If no `bibliography` file is passed, the plugin will be skipped.
### options
#### options.bibliography
Type: `string|string[]`.
By default, if no `bibliography` file is passed, the plugin will be skipped.
Name or path to Bibtex, CSL-JSON or CFF file. If multiple files are provided, they will be merged.
#### options.path
Type: `string`.
Default: `process.cwd()`.
Required, path to file. Will be joined with `options.bibliography` and `options.csl`, if provided.
#### options.csl
Type: `'apa'|'vancouver'|'harvard1'|'chicago'|'mla'|string`.
Default: `apa`.
For the main `rehypeCitation` plugin, one of 'apa', 'vancouver', 'harvard1', 'chicago', 'mla'. A local file path or URL to a valid CSL file is also accepted. Can also be specified as a frontmatter option in the markdown file to override the default.
#### options.lang
Type: `string`.
Default: `en-US`.
Locale to use in formatting citations. Defaults to `en-US`. A local file path or URL to a valid locale file is also accepted.
#### options.suppressBibliography
Type: `boolean`.
Default: `false`.
Suppress bibliography? By default, biliography is inserted after the entire markdown file. If the file contains `[^ref]`, the biliography will be inserted there instead.
#### options.noCite
Type: `string[]`.
Citation IDs (@item1) to include in the bibliography even if they are not cited in the document. Can also be specified as a frontmatter option in the markdown file.
#### options.inlineClass
Type: `string[]`.
Array of classes for inline citations.
#### options.inlineBibClass
Type: `string[]`.
Array of classes for inline bibliography. Leave empty to disable inline bibliography.
#### options.linkCitations
Type: `boolean`.
Default: `false`.
If true, citations will be hyperlinked to the corresponding bibliography entries (for author-date and numeric styles only).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
export default rehypeCitation;
import Cite from './src/cite.js';
import rehypeCitationGenerator from './src/generator.js';
import rehypeCitation from './src/index.js';
export { Cite, rehypeCitationGenerator };

View File

@@ -0,0 +1,5 @@
import rehypeCitation from './src/index.js';
import Cite from './src/cite.js';
import rehypeCitationGenerator from './src/generator.js';
export { Cite, rehypeCitationGenerator };
export default rehypeCitation;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export default async;
declare function async(data: any, options: any, callback: any): any;

View File

@@ -0,0 +1,15 @@
function async(data, options, callback) {
if (typeof options === 'function' && !callback) {
callback = options;
options = undefined;
}
const promise = new this().setAsync(data, options);
if (typeof callback === 'function') {
promise.then(callback);
return undefined;
}
else {
return promise;
}
}
export default async;

View File

@@ -0,0 +1,3 @@
export function getIds(): any;
export function format(format: any, ...options: any[]): any;
export function get(options?: {}): any;

View File

@@ -0,0 +1,58 @@
import { validateOutputOptions as validate } from './validate.js';
import { format as formatData } from '../plugins/output.js';
import { clean as parseCsl } from '../plugins/input/csl.js';
export function getIds() {
return this.data.map((entry) => entry.id);
}
export function format(format, ...options) {
return formatData(format, parseCsl(this.data), ...options);
}
export function get(options = {}) {
validate(options);
const parsedOptions = Object.assign({}, this.defaultOptions, this._options.output, options);
const { type, style } = parsedOptions;
const [styleType, styleFormat] = style.split('-');
const newStyle = styleType === 'citation' ? 'bibliography' : styleType === 'csl' ? 'data' : styleType;
const newType = type === 'string' ? 'text' : type === 'json' ? 'object' : type;
let formatOptions;
switch (newStyle) {
case 'bibliography': {
const { lang, append, prepend } = parsedOptions;
formatOptions = {
template: styleFormat,
lang,
format: newType,
append,
prepend,
};
break;
}
case 'data':
case 'bibtex':
case 'bibtxt':
case 'ndjson':
case 'ris':
formatOptions = {
type: newType,
};
break;
default:
throw new Error(`Invalid style "${newStyle}"`);
}
const result = this.format(newStyle, Object.assign(formatOptions, options._newOptions));
const { format } = parsedOptions;
if (format === 'real' &&
newType === 'html' &&
typeof document !== 'undefined' &&
typeof document.createElement === 'function') {
const tmp = document.createElement('div');
tmp.innerHTML = result;
return tmp.firstChild;
}
else if (format === 'string' && typeof result === 'object') {
return JSON.stringify(result);
}
else {
return result;
}
}

View File

@@ -0,0 +1,8 @@
export default Cite;
declare function Cite(data: any, options?: {}): Cite;
declare class Cite {
constructor(data: any, options?: {});
_options: {};
log: any[];
data: any[];
}

View File

@@ -0,0 +1,24 @@
// @ts-nocheck
import * as log from './log.js';
import * as options from './options.js';
import * as set from './set.js';
import * as sort from './sort.js';
import * as get from './get.js';
import * as staticMethods from './static.js';
function Cite(data, options = {}) {
if (!(this instanceof Cite)) {
return new Cite(data, options);
}
this._options = options;
this.log = [];
this.data = [];
this.set(data, options);
this.options(options);
return this;
}
Object.assign(Cite.prototype, log, options, set, sort, get);
Cite.prototype[Symbol.iterator] = function* () {
yield* this.data;
};
Object.assign(Cite, staticMethods);
export default Cite;

View File

@@ -0,0 +1,5 @@
export function currentVersion(): any;
export function retrieveVersion(versnum?: number): any;
export function retrieveLastVersion(): any;
export function undo(number?: number): any;
export function save(): any;

View File

@@ -0,0 +1,25 @@
function currentVersion() {
return this.log.length;
}
function retrieveVersion(versnum = 1) {
if (versnum <= 0 || versnum > this.currentVersion()) {
return null;
}
else {
const [data, options] = this.log[versnum - 1];
const image = new this.constructor(JSON.parse(data), JSON.parse(options));
image.log = this.log.slice(0, versnum);
return image;
}
}
function undo(number = 1) {
return this.retrieveVersion(this.currentVersion() - number);
}
function retrieveLastVersion() {
return this.retrieveVersion(this.currentVersion());
}
function save() {
this.log.push([JSON.stringify(this.data), JSON.stringify(this._options)]);
return this;
}
export { currentVersion, retrieveVersion, retrieveLastVersion, undo, save };

View File

@@ -0,0 +1,7 @@
export function options(options: any, log: any): any;
export namespace defaultOptions {
let format: string;
let type: string;
let style: string;
let lang: string;
}

View File

@@ -0,0 +1,16 @@
import { validateOutputOptions as validate } from './validate.js';
const defaultOptions = {
format: 'real',
type: 'json',
style: 'csl',
lang: 'en-US',
};
function options(options, log) {
validate(options);
if (log) {
this.save();
}
Object.assign(this._options, options);
return this;
}
export { options, defaultOptions };

View File

@@ -0,0 +1,18 @@
export function add(data: any, options?: {}, log?: boolean): any;
export function addAsync(data: any, options?: {}, log?: boolean): Promise<any>;
export function set(data: any, options?: {}, log?: boolean): any;
export class set {
constructor(data: any, options?: {}, log?: boolean);
data: any[];
}
export function setAsync(data: any, options?: {}, log?: boolean): Promise<any>;
export class setAsync {
constructor(data: any, options?: {}, log?: boolean);
data: any[];
}
export function reset(log: any): this;
export class reset {
constructor(log: any);
data: any[];
_options: {};
}

View File

@@ -0,0 +1,50 @@
// @ts-nocheck
import { chain as parseInput, chainAsync as parseInputAsync } from '../plugins/input/index.js';
import fetchId from '../util/fetchId.js';
function add(data, options = {}, log = false) {
if (options === true || log === true) {
this.save();
}
this.data.push(...parseInput(data, options));
this.data
.filter((entry) => !Object.prototype.hasOwnProperty.call(entry, 'id'))
.forEach((entry) => {
entry.id = fetchId(this.getIds(), 'temp_id_');
});
return this;
}
async function addAsync(data, options = {}, log = false) {
if (options === true || log === true) {
this.save();
}
this.data.push(...(await parseInputAsync(data, options)));
this.data
.filter((entry) => !Object.prototype.hasOwnProperty.call(entry, 'id'))
.forEach((entry) => {
entry.id = fetchId(this.getIds(), 'temp_id_');
});
return this;
}
function set(data, options = {}, log = false) {
if (options === true || log === true) {
this.save();
}
this.data = [];
return typeof options !== 'boolean' ? this.add(data, options) : this.add(data);
}
async function setAsync(data, options = {}, log = false) {
if (options === true || log === true) {
this.save();
}
this.data = [];
return typeof options !== 'boolean' ? this.addAsync(data, options) : this.addAsync(data);
}
function reset(log) {
if (log) {
this.save();
}
this.data = [];
this._options = {};
return this;
}
export { add, addAsync, set, setAsync, reset };

View File

@@ -0,0 +1 @@
export function sort(method: any[], log: any): any;

View File

@@ -0,0 +1,46 @@
import { getLabel } from '../plugin-common/output/label.js';
import { format as getName } from '@citation-js/name';
function getComparisonValue(obj, prop, label = prop === 'label') {
let value = label ? getLabel(obj) : obj[prop];
switch (prop) {
case 'author':
case 'editor':
return value.map((name) => name.literal || name.family || getName(name));
case 'accessed':
case 'issued':
return value['date-parts'][0];
case 'page':
return value.split('-').map((num) => parseInt(num));
case 'edition':
case 'issue':
case 'volume':
value = parseInt(value);
return !isNaN(value) ? value : -Infinity;
default:
return value || -Infinity;
}
}
function compareProp(entryA, entryB, prop, flip = /^!/.test(prop)) {
prop = prop.replace(/^!/, '');
const a = getComparisonValue(entryA, prop);
const b = getComparisonValue(entryB, prop);
return (flip ? -1 : 1) * (a > b ? 1 : a < b ? -1 : 0);
}
function getSortCallback(...props) {
return (a, b) => {
const keys = props.slice();
let output = 0;
while (!output && keys.length) {
output = compareProp(a, b, keys.shift());
}
return output;
};
}
function sort(method = [], log) {
if (log) {
this.save();
}
this.data.sort(typeof method === 'function' ? method : getSortCallback(...method, 'label'));
return this;
}
export { sort };

View File

@@ -0,0 +1,2 @@
export { default as async } from "./async.js";
export * from "./validate.js";

View File

@@ -0,0 +1,2 @@
export { default as async } from './async.js';
export * from './validate.js';

View File

@@ -0,0 +1,2 @@
export function validateOutputOptions(options: any): boolean;
export function validateOptions(options: any): boolean;

View File

@@ -0,0 +1,56 @@
const formats = ['real', 'string'];
const types = ['json', 'html', 'string', 'rtf'];
const styles = ['csl', 'bibtex', 'bibtxt', 'citation-*', 'ris', 'ndjson'];
const wrapperTypes = ['string', 'function'];
export function validateOutputOptions(options) {
if (typeof options !== 'object') {
throw new TypeError('Options not an object!');
}
const { format, type, style, lang, append, prepend } = options;
if (format && !formats.includes(format)) {
throw new TypeError(`Option format ("${format}") should be one of: ${formats}`);
}
else if (type && !types.includes(type)) {
throw new TypeError(`Option type ("${type}") should be one of: ${types}`);
}
else if (style && !styles.includes(style) && !/^citation/.test(style)) {
throw new TypeError(`Option style ("${style}") should be one of: ${styles}`);
}
else if (lang && typeof lang !== 'string') {
throw new TypeError(`Option lang should be a string, but is a ${typeof lang}`);
}
else if (prepend && !wrapperTypes.includes(typeof prepend)) {
throw new TypeError(`Option prepend should be a string or a function, but is a ${typeof prepend}`);
}
else if (append && !wrapperTypes.includes(typeof append)) {
throw new TypeError(`Option append should be a string or a function, but is a ${typeof append}`);
}
if (/^citation/.test(style) && type === 'json') {
throw new Error(`Combination type/style of json/citation-* is not valid: ${type}/${style}`);
}
return true;
}
export function validateOptions(options) {
if (typeof options !== 'object') {
throw new TypeError('Options should be an object');
}
if (options.output) {
validateOutputOptions(options.output);
}
else if (options.maxChainLength && typeof options.maxChainLength !== 'number') {
throw new TypeError('Option maxChainLength should be a number');
}
else if (options.forceType && typeof options.forceType !== 'string') {
throw new TypeError('Option forceType should be a string');
}
else if (options.generateGraph != null && typeof options.generateGraph !== 'boolean') {
throw new TypeError('Option generateGraph should be a boolean');
}
else if (options.strict != null && typeof options.strict !== 'boolean') {
throw new TypeError('Option strict should be a boolean');
}
else if (options.target != null && typeof options.target !== 'string') {
throw new TypeError('Option target should be a boolean');
}
return true;
}

View File

@@ -0,0 +1,6 @@
export const version: 0.7;
import Cite from './Cite/index.js';
import * as plugins from './plugins/index.js';
import * as util from './util/index.js';
import logger from './logger.js';
export { Cite, plugins, util, logger };

View File

@@ -0,0 +1,7 @@
import Cite from './Cite/index.js';
import * as plugins from './plugins/index.js';
import * as util from './util/index.js';
import logger from './logger.js';
import './plugin-common/index.js';
export const version = 0.7;
export { Cite, plugins, util, logger };

View File

@@ -0,0 +1,8 @@
export default logger;
declare namespace logger {
function _output(level: any, scope: any, msg: any): void;
let _console: any;
let _log: any[];
let _levels: string[];
let level: string;
}

View File

@@ -0,0 +1,23 @@
const logger = {
_output(level, scope, msg) {
this._log.push(scope, msg);
if (this._levels.indexOf(level) < this._levels.indexOf(this.level)) {
return;
}
this._console.log(scope, ...msg);
},
_console: null,
_log: [],
_levels: ['http', 'debug', 'unmapped', 'info', 'warn', 'error', 'silent'],
level: 'silent',
};
for (const level of logger._levels) {
logger[level] = (scope, ...msg) => logger._output(level, scope, msg);
}
if (typeof console.Console === 'function') {
logger._console = new console.Console(process.stderr);
}
else {
logger._console = console;
}
export default logger;

View File

@@ -0,0 +1,7 @@
import * as plugins from '../plugins';
import { ref, formats as input } from './input/';
import output from './output/';
plugins.add(ref, {
input,
output,
});

View File

@@ -0,0 +1 @@
export function parse(): any[];

View File

@@ -0,0 +1,3 @@
export function parse() {
return [];
}

View File

@@ -0,0 +1 @@
export function parse(input: any): any;

View File

@@ -0,0 +1,3 @@
export function parse(input) {
return input.value || input.textContent;
}

View File

@@ -0,0 +1,61 @@
export const ref: "@else";
export namespace parsers {
export { empty };
export { json };
export { jquery };
export { html };
}
export const formats: {
'@empty/text': {
parse: typeof empty.parse;
parseType: {
dataType: string;
predicate: (input: any) => boolean;
};
};
'@empty/whitespace+text': {
parse: typeof empty.parse;
parseType: {
dataType: string;
predicate: RegExp;
};
};
'@empty': {
parse: typeof empty.parse;
parseType: {
dataType: string;
predicate: (input: any) => boolean;
};
};
'@else/json': {
parse: typeof json.parse;
parseType: {
dataType: string;
predicate: RegExp;
};
};
'@else/url': {
parseType: {
dataType: string;
predicate: RegExp;
};
};
'@else/jquery': {
parse: typeof jquery.parse;
parseType: {
dataType: string;
predicate(input: any): boolean;
};
};
'@else/html': {
parse: typeof html.parse;
parseType: {
dataType: string;
predicate(input: any): boolean;
};
};
};
import * as empty from './empty.js';
import * as json from './json.js';
import * as jquery from './jquery.js';
import * as html from './html.js';

View File

@@ -0,0 +1,66 @@
// @ts-nocheck
import * as empty from './empty.js';
import * as json from './json.js';
import * as jquery from './jquery.js';
import * as html from './html.js';
export const ref = '@else';
export const parsers = {
empty,
json,
jquery,
html,
};
export const formats = {
'@empty/text': {
parse: empty.parse,
parseType: {
dataType: 'String',
predicate: (input) => input === '',
},
},
'@empty/whitespace+text': {
parse: empty.parse,
parseType: {
dataType: 'String',
predicate: /^\s+$/,
},
},
'@empty': {
parse: empty.parse,
parseType: {
dataType: 'Primitive',
predicate: (input) => input == null,
},
},
'@else/json': {
parse: json.parse,
parseType: {
dataType: 'String',
predicate: /^\s*(\{[\S\s]*\}|\[[\S\s]*\])\s*$/,
},
},
'@else/url': {
parseType: {
dataType: 'String',
predicate: /^https?:\/\/(([\w-]+\.)*[\w-]+)(:\d+)?(\/[^?/]*)*(\?[^#]*)?(#.*)?$/i,
},
},
'@else/jquery': {
parse: jquery.parse,
parseType: {
dataType: 'ComplexObject',
predicate(input) {
return typeof jQuery !== 'undefined' && input instanceof jQuery;
},
},
},
'@else/html': {
parse: html.parse,
parseType: {
dataType: 'ComplexObject',
predicate(input) {
return typeof HTMLElement !== 'undefined' && input instanceof HTMLElement;
},
},
},
};

View File

@@ -0,0 +1 @@
export function parse(input: any): any;

View File

@@ -0,0 +1,3 @@
export function parse(input) {
return input.val() || input.text() || input.html();
}

View File

@@ -0,0 +1,2 @@
declare function parseJSON(str: any): any;
export { parseJSON as parse, parseJSON as default };

View File

@@ -0,0 +1,24 @@
import logger from '../../logger.js';
const substituters = [
[/((?:\[|:|,)\s*)'((?:\\'|[^'])*?[^\\])?'(?=\s*(?:\]|}|,))/g, '$1"$2"'],
[
/((?:(?:"|]|}|\/[gmiuys]|\.|(?:\d|\.|-)*\d)\s*,|{)\s*)(?:"([^":\n]+?)"|'([^":\n]+?)'|([^":\n]+?))(\s*):/g,
'$1"$2$3$4"$5:',
],
];
function parseJSON(str) {
if (typeof str !== 'string') {
return JSON.parse(str);
}
try {
return JSON.parse(str);
}
catch (e) {
logger.debug('[plugin-common]', 'Invalid JSON, switching to experimental parser');
substituters.forEach(([regex, subst]) => {
str = str.replace(regex, subst);
});
return JSON.parse(str);
}
}
export { parseJSON as parse, parseJSON as default };

View File

@@ -0,0 +1,13 @@
declare const _default: {
data(data: any, { type, format, version }?: {
type: any;
format?: any;
version?: string;
}): any;
ndjson(data: any, { version }?: {
version?: string;
}): any;
} & {
label(data: any): any;
};
export default _default;

View File

@@ -0,0 +1,3 @@
import json from './json.js';
import label from './label.js';
export default Object.assign({}, json, label);

View File

@@ -0,0 +1,12 @@
export function getJsonWrapper(src: any): any;
declare namespace _default {
function data(data: any, { type, format, version }?: {
type: any;
format?: any;
version?: string;
}): any;
function ndjson(data: any, { version }?: {
version?: string;
}): any;
}
export default _default;

View File

@@ -0,0 +1,70 @@
// @ts-nocheck
import * as plugins from '../../plugins/index.js';
import * as util from '../../util/index.js';
import logger from '../../logger.js';
function appendCommas(string, index, array) {
return string + (index < array.length - 1 ? ',' : '');
}
function getJsonObject(src, dict) {
const isArray = Array.isArray(src);
let entries;
if (isArray) {
entries = src.map((entry) => getJsonValue(entry, dict));
}
else {
entries = Object.keys(src)
.filter((prop) => JSON.stringify(src[prop]))
.map((prop) => `"${prop}": ${getJsonValue(src[prop], dict)}`);
}
entries = entries.map(appendCommas).map((entry) => dict.listItem.join(entry));
entries = dict.list.join(entries.join(''));
return isArray ? `[${entries}]` : `{${entries}}`;
}
function getJsonValue(src, dict) {
if (typeof src === 'object' && src !== null) {
if (src.length === 0) {
return '[]';
}
else if (Object.keys(src).length === 0) {
return '{}';
}
else {
return getJsonObject(src, dict);
}
}
else {
return JSON.stringify(src);
}
}
function getJson(src, dict) {
let entries = src.map((entry) => getJsonObject(entry, dict));
entries = entries.map(appendCommas).map((entry) => dict.entry.join(entry));
entries = entries.join('');
return dict.bibliographyContainer.join(`[${entries}]`);
}
export function getJsonWrapper(src) {
return getJson(src, plugins.dict.get('html'));
}
export default {
data(data, { type, format = type || 'text', version = '1.0.2' } = {}) {
if (version < '1.0.2') {
data = util.downgradeCsl(data);
}
if (format === 'object') {
return util.deepCopy(data);
}
else if (format === 'text') {
return JSON.stringify(data, null, 2);
}
else {
logger.warn('[core]', 'This feature (JSON output with special formatting) is unstable. See https://github.com/larsgw/citation.js/issues/144');
return getJson(data, plugins.dict.get(format));
}
},
ndjson(data, { version = '1.0.2' } = {}) {
if (version < '1.0.2') {
data = util.downgradeCsl(data);
}
return data.map((entry) => JSON.stringify(entry)).join('\n');
},
};

View File

@@ -0,0 +1,5 @@
declare namespace _default {
function label(data: any): any;
}
export default _default;
export function getLabel(entry: any): any;

View File

@@ -0,0 +1,28 @@
function getLabel(entry) {
if ('citation-label' in entry) {
return entry['citation-label'];
}
let res = '';
if (entry.author) {
res += entry.author[0].family || entry.author[0].literal;
}
if (entry.issued && entry.issued['date-parts'] && entry.issued['date-parts'][0]) {
res += entry.issued['date-parts'][0][0];
}
if (entry['year-suffix']) {
res += entry['year-suffix'];
}
else if (entry.title) {
res += entry.title.replace(/<\/?.*?>/g, '').match(/^(?:(?:the|a|an)\s+)?(\S+)/i)[1];
}
return res;
}
export { getLabel };
export default {
label(data) {
return data.reduce((object, entry) => {
object[entry.id] = getLabel(entry);
return object;
}, {});
},
};

View File

@@ -0,0 +1,5 @@
export function add(ref: any, config: any): void;
export function get(ref: any): any;
export function has(ref: any): any;
export function remove(ref: any): void;
export function list(): string[];

View File

@@ -0,0 +1,16 @@
const configs = {};
export function add(ref, config) {
configs[ref] = config;
}
export function get(ref) {
return configs[ref];
}
export function has(ref) {
return Object.prototype.hasOwnProperty.call(configs, ref);
}
export function remove(ref) {
delete configs[ref];
}
export function list() {
return Object.keys(configs);
}

View File

@@ -0,0 +1,35 @@
export function add(name: any, dict: any): void;
export function remove(name: any): void;
export function has(name: any): any;
export function list(): string[];
export function get(name: any): any;
export const register: Register;
export namespace htmlDict {
let wr_start: string;
let wr_end: string;
let en_start: string;
let en_end: string;
let ul_start: string;
let ul_end: string;
let li_start: string;
let li_end: string;
}
export namespace textDict {
let wr_start_1: string;
export { wr_start_1 as wr_start };
let wr_end_1: string;
export { wr_end_1 as wr_end };
let en_start_1: string;
export { en_start_1 as en_start };
let en_end_1: string;
export { en_end_1 as en_end };
let ul_start_1: string;
export { ul_start_1 as ul_start };
let ul_end_1: string;
export { ul_end_1 as ul_end };
let li_start_1: string;
export { li_start_1 as li_start };
let li_end_1: string;
export { li_end_1 as li_end };
}
import Register from '../util/register.js';

View File

@@ -0,0 +1,68 @@
import Register from '../util/register.js';
function validate(name, dict) {
if (typeof name !== 'string') {
throw new TypeError(`Invalid dict name, expected string, got ${typeof name}`);
}
else if (typeof dict !== 'object') {
throw new TypeError(`Invalid dict, expected object, got ${typeof dict}`);
}
for (const entryName in dict) {
const entry = dict[entryName];
if (!Array.isArray(entry) || entry.some((part) => typeof part !== 'string')) {
throw new TypeError(`Invalid dict entry "${entryName}", expected array of strings`);
}
}
}
export const register = new Register({
html: {
bibliographyContainer: ['<div class="csl-bib-body">', '</div>'],
entry: ['<div class="csl-entry">', '</div>'],
list: ['<ul style="list-style-type:none">', '</ul>'],
listItem: ['<li>', '</li>'],
},
text: {
bibliographyContainer: ['', '\n'],
entry: ['', '\n'],
list: ['\n', ''],
listItem: ['\t', '\n'],
},
});
export function add(name, dict) {
validate(name, dict);
register.set(name, dict);
}
export function remove(name) {
register.remove(name);
}
export function has(name) {
return register.has(name);
}
export function list() {
return register.list();
}
export function get(name) {
if (!register.has(name)) {
throw new Error(`Dict "${name}" unavailable`);
}
return register.get(name);
}
export const htmlDict = {
wr_start: '<div class="csl-bib-body">',
wr_end: '</div>',
en_start: '<div class="csl-entry">',
en_end: '</div>',
ul_start: '<ul style="list-style-type:none">',
ul_end: '</ul>',
li_start: '<li>',
li_end: '</li>',
};
export const textDict = {
wr_start: '',
wr_end: '\n',
en_start: '',
en_end: '\n',
ul_start: '\n',
ul_end: '',
li_start: '\t',
li_end: '\n',
};

View File

@@ -0,0 +1,9 @@
export function add(ref: any, plugins?: {}): void;
export function remove(ref: any): void;
export function has(ref: any): boolean;
export function list(): string[];
import * as input from './input/index.js';
import * as output from './output.js';
import * as dict from './dict.js';
import * as config from './config.js';
export { input, output, dict, config };

View File

@@ -0,0 +1,48 @@
// @ts-nocheck
import * as input from './input/index.js';
import * as output from './output.js';
import * as dict from './dict.js';
import * as config from './config.js';
const registers = {
input,
output,
dict,
config,
};
const indices = {};
export function add(ref, plugins = {}) {
const mainIndex = (indices[ref] = {});
for (const type in plugins) {
if (type === 'config') {
mainIndex.config = {
[ref]: plugins.config,
};
registers.config.add(ref, plugins.config);
continue;
}
const typeIndex = (mainIndex[type] = {});
const typePlugins = plugins[type];
for (const name in typePlugins) {
const typePlugin = typePlugins[name];
typeIndex[name] = true;
registers[type].add(name, typePlugin);
}
}
}
export function remove(ref) {
const mainIndex = indices[ref];
for (const type in mainIndex) {
const typeIndex = mainIndex[type];
for (const name in typeIndex) {
registers[type].remove(name);
}
}
delete indices[ref];
}
export function has(ref) {
return ref in indices;
}
export function list() {
return Object.keys(indices);
}
export { input, output, dict, config };

View File

@@ -0,0 +1,4 @@
export function chain(...args: any[]): any;
export function chainLink(input: any): any;
export function chainAsync(...args: any[]): Promise<any>;
export function chainLinkAsync(input: any): Promise<any>;

View File

@@ -0,0 +1,115 @@
// @ts-nocheck
import { deepCopy, upgradeCsl } from '../../util/index.js';
import logger from '../../logger.js';
import { get as getTypeInfo } from './register.js';
import { type as parseType } from './type.js';
import { data as parseData, dataAsync as parseDataAsync } from './data.js';
import { applyGraph, removeGraph } from './graph.js';
function prepareParseGraph(graph) {
return graph
.reduce((array, next) => {
const last = array[array.length - 1];
if (last && last.type === next.type) {
last.count = last.count + 1 || 2;
}
else {
array.push(next);
}
return array;
}, [])
.map((element) => (element.count > 1 ? element.count + 'x ' : '') + element.type)
.join(' -> ');
}
class ChainParser {
constructor(input, options = {}) {
this.options = Object.assign({
generateGraph: true,
forceType: parseType(input),
maxChainLength: 10,
strict: true,
target: '@csl/list+object',
}, options);
this.type = this.options.forceType;
this.data = typeof input === 'object' ? deepCopy(input) : input;
this.graph = [
{
type: this.type,
data: input,
},
];
this.iteration = 0;
}
iterate() {
if (this.iteration !== 0) {
const typeInfo = getTypeInfo(this.type);
if (typeInfo && typeInfo.outputs) {
this.type = typeInfo.outputs;
}
else {
this.type = parseType(this.data);
}
this.graph.push({
type: this.type,
});
}
if (this.error || this.type === this.options.target) {
return false;
}
else if (this.iteration >= this.options.maxChainLength) {
this.error = new RangeError(`Max. number of parsing iterations reached (${prepareParseGraph(this.graph)})`);
return false;
}
else {
this.iteration++;
return true;
}
}
end() {
if (this.error) {
logger.error('[core]', this.error.message);
if (this.options.strict !== false) {
throw this.error;
}
else {
return [];
}
}
else if (this.options.target === '@csl/list+object') {
return upgradeCsl(this.data).map(this.options.generateGraph ? (entry) => applyGraph(entry, this.graph) : removeGraph);
}
else {
return this.data;
}
}
}
export const chain = (...args) => {
const chain = new ChainParser(...args);
while (chain.iterate()) {
try {
chain.data = parseData(chain.data, chain.type);
}
catch (e) {
chain.error = e;
}
}
return chain.end();
};
export const chainLink = (input) => {
const type = parseType(input);
const output = type.match(/array|object/) ? deepCopy(input) : input;
return parseData(output, type);
};
export const chainAsync = async (...args) => {
const chain = new ChainParser(...args);
while (chain.iterate()) {
chain.data = await parseDataAsync(chain.data, chain.type).catch((e) => {
chain.error = e;
});
}
return chain.end();
};
export const chainLinkAsync = async (input) => {
const type = parseType(input);
const output = type.match(/array|object/) ? deepCopy(input) : input;
return parseDataAsync(output, type);
};

View File

@@ -0,0 +1,2 @@
export { parseCsl as clean };
declare function parseCsl(data: any, bestGuessConversions?: boolean): any;

View File

@@ -0,0 +1,352 @@
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 { parse as parseName } from '@citation-js/name';
const NAME = 1;
const NAME_LIST = 2;
const DATE = 3;
const TYPE = 4;
const entryTypes = {
article: true,
'article-journal': true,
'article-magazine': true,
'article-newspaper': true,
bill: true,
book: true,
broadcast: true,
chapter: true,
classic: true,
collection: true,
dataset: true,
document: true,
entry: true,
'entry-dictionary': true,
'entry-encyclopedia': true,
event: true,
figure: true,
graphic: true,
hearing: true,
interview: true,
legal_case: true,
legislation: true,
manuscript: true,
map: true,
motion_picture: true,
musical_score: true,
pamphlet: true,
'paper-conference': true,
patent: true,
performance: true,
periodical: true,
personal_communication: true,
post: true,
'post-weblog': true,
regulation: true,
report: true,
review: true,
'review-book': true,
software: true,
song: true,
speech: true,
standard: true,
thesis: true,
treaty: true,
webpage: true,
'journal-article': 'article-journal',
'book-chapter': 'chapter',
'posted-content': 'manuscript',
'proceedings-article': 'paper-conference',
dissertation: 'thesis',
};
const fieldTypes = {
author: NAME_LIST,
chair: NAME_LIST,
'collection-editor': NAME_LIST,
compiler: NAME_LIST,
composer: NAME_LIST,
'container-author': NAME_LIST,
contributor: NAME_LIST,
curator: NAME_LIST,
director: NAME_LIST,
editor: NAME_LIST,
'editorial-director': NAME_LIST,
'executive-producer': NAME_LIST,
guest: NAME_LIST,
host: NAME_LIST,
interviewer: NAME_LIST,
illustrator: NAME_LIST,
narrator: NAME_LIST,
organizer: NAME_LIST,
'original-author': NAME_LIST,
performer: NAME_LIST,
producer: NAME_LIST,
'reviewed-author': NAME_LIST,
recipient: NAME_LIST,
'script-writer': NAME_LIST,
'series-creator': NAME_LIST,
translator: NAME_LIST,
accessed: DATE,
'available-date': DATE,
container: DATE,
'event-date': DATE,
issued: DATE,
'original-date': DATE,
submitted: DATE,
type: TYPE,
categories: 'object',
custom: 'object',
id: ['string', 'number'],
language: 'string',
journalAbbreviation: 'string',
shortTitle: 'string',
abstract: 'string',
annote: 'string',
archive: 'string',
archive_collection: 'string',
archive_location: 'string',
'archive-place': 'string',
authority: 'string',
'call-number': 'string',
'chapter-number': 'string',
'citation-number': 'string',
'citation-key': 'string',
'citation-label': 'string',
'collection-number': 'string',
'collection-title': 'string',
'container-title': 'string',
'container-title-short': 'string',
dimensions: 'string',
division: 'string',
DOI: 'string',
edition: ['string', 'number'],
event: 'string',
'event-title': 'string',
'event-place': 'string',
'first-reference-note-number': 'string',
genre: 'string',
ISBN: 'string',
ISSN: 'string',
issue: ['string', 'number'],
jurisdiction: 'string',
keyword: 'string',
locator: 'string',
medium: 'string',
note: 'string',
number: ['string', 'number'],
'number-of-pages': 'string',
'number-of-volumes': ['string', 'number'],
'original-publisher': 'string',
'original-publisher-place': 'string',
'original-title': 'string',
page: 'string',
'page-first': 'string',
'part-number': ['string', 'number'],
'part-title': 'string',
PMCID: 'string',
PMID: 'string',
printing: 'string',
publisher: 'string',
'publisher-place': 'string',
references: 'string',
'reviewed-title': 'string',
'reviewed-genre': 'string',
scale: 'string',
section: 'string',
source: 'string',
status: 'string',
supplement: ['string', 'number'],
title: 'string',
'title-short': 'string',
URL: 'string',
version: 'string',
volume: ['string', 'number'],
'volume-title': 'string',
'volume-title-short': 'string',
'year-suffix': 'string',
};
function correctName(name, bestGuessConversions) {
if (typeof name === 'object' && name !== null && (name.literal || name.given || name.family)) {
if (name.ORCID || name.orcid || name._ORCID) {
name = _objectSpread({
_orcid: name.ORCID || name.orcid || name._ORCID,
}, name);
delete name.ORCID;
delete name.orcid;
delete name._ORCID;
}
return name;
}
else if (!bestGuessConversions) {
return undefined;
}
else if (typeof name === 'string') {
return parseName(name);
}
}
function correctNameList(nameList, bestGuessConversions) {
if (nameList instanceof Array) {
const names = nameList.map((name) => correctName(name, bestGuessConversions)).filter(Boolean);
return names.length ? names : undefined;
}
}
function correctDateParts(dateParts, bestGuessConversions) {
if (dateParts.every((part) => typeof part === 'number')) {
return dateParts;
}
else if (!bestGuessConversions || dateParts.some((part) => isNaN(parseInt(part)))) {
return undefined;
}
else {
return dateParts.map((part) => parseInt(part));
}
}
function correctDate(date, bestGuessConversions) {
const dp = 'date-parts';
if (typeof date !== 'object' || date === null) {
return undefined;
}
else if (date[dp] instanceof Array && date[dp].every((part) => part instanceof Array)) {
const range = date[dp]
.map((dateParts) => correctDateParts(dateParts, bestGuessConversions))
.filter(Boolean);
return range.length
? _objectSpread(_objectSpread({}, date), {}, {
'date-parts': range,
})
: undefined;
}
else if (date instanceof Array && date.every((part) => part[dp] instanceof Array)) {
const range = date
.map((dateParts) => correctDateParts(dateParts[dp], bestGuessConversions))
.filter(Boolean);
return range.length
? {
'date-parts': range,
}
: undefined;
}
else if (date[dp] instanceof Array) {
const dateParts = correctDateParts(date[dp], bestGuessConversions);
return (dateParts && {
'date-parts': [dateParts],
});
}
else if ('literal' in date || 'raw' in date) {
return date;
}
}
function correctType(type, bestGuessConversions) {
type = correctField('language', type, bestGuessConversions);
if (entryTypes[type] === true) {
return type;
}
if (bestGuessConversions) {
if (type in entryTypes) {
return entryTypes[type];
}
else if (type.toLowerCase() !== type) {
return correctType(type.toLowerCase(), bestGuessConversions);
}
}
return undefined;
}
function correctField(fieldName, value, bestGuessConversions) {
const fieldType = [].concat(fieldTypes[fieldName]);
switch (fieldTypes[fieldName]) {
case NAME:
return correctName(value, bestGuessConversions);
case NAME_LIST:
return correctNameList(value, bestGuessConversions);
case DATE:
return correctDate(value, bestGuessConversions);
case TYPE:
return correctType(value, bestGuessConversions);
}
if (bestGuessConversions) {
if (typeof value === 'string' &&
fieldType.includes('number') &&
!fieldType.includes('string') &&
!isNaN(+value)) {
return parseFloat(value);
}
else if (typeof value === 'number' &&
fieldType.includes('string') &&
!fieldType.includes('number')) {
return value.toString();
}
else if (Array.isArray(value) && value.length) {
return correctField(fieldName, value[0], bestGuessConversions);
}
}
if (fieldType.includes(typeof value)) {
return value;
}
}
function parseCsl(data, bestGuessConversions = true) {
return data.map(function (entry) {
const clean = {};
for (const field in entry) {
const correction = correctField(field, entry[field], bestGuessConversions);
if (correction !== undefined) {
clean[field] = correction;
}
}
return clean;
});
}
export { parseCsl as clean };

View File

@@ -0,0 +1,9 @@
export function data(input: any, type: any): any;
export function dataAsync(input: any, type: any): Promise<any>;
export function addDataParser(format: any, { parser, async }: {
parser: any;
async: any;
}): void;
export function hasDataParser(type: any, async: any): any;
export function removeDataParser(type: any, async: any): void;
export function listDataParser(async: any): string[];

View File

@@ -0,0 +1,58 @@
import { chain, chainAsync } from './chain.js';
const parsers = {};
const asyncParsers = {};
const nativeParsers = {
'@csl/object': (input) => [input],
'@csl/list+object': (input) => input,
'@else/list+object': (input) => input.map(chain).flat(),
'@invalid': () => {
throw new Error('This format is not supported or recognized');
},
};
const nativeAsyncParsers = {
'@else/list+object': async (input) => (await Promise.all(input.map(chainAsync))).flat(),
};
export function data(input, type) {
if (typeof parsers[type] === 'function') {
return parsers[type](input);
}
else if (typeof nativeParsers[type] === 'function') {
return nativeParsers[type](input);
}
else {
throw new TypeError(`No synchronous parser found for ${type}`);
}
}
export async function dataAsync(input, type) {
if (typeof asyncParsers[type] === 'function') {
return asyncParsers[type](input);
}
else if (typeof nativeAsyncParsers[type] === 'function') {
return nativeAsyncParsers[type](input);
}
else if (hasDataParser(type, false)) {
return data(input, type);
}
else {
throw new TypeError(`No parser found for ${type}`);
}
}
export function addDataParser(format, { parser, async }) {
if (async) {
asyncParsers[format] = parser;
}
else {
parsers[format] = parser;
}
}
export function hasDataParser(type, async) {
return async
? asyncParsers[type] || nativeAsyncParsers[type]
: parsers[type] || nativeParsers[type];
}
export function removeDataParser(type, async) {
delete (async ? asyncParsers : parsers)[type];
}
export function listDataParser(async) {
return Object.keys(async ? asyncParsers : parsers);
}

View File

@@ -0,0 +1,2 @@
export function typeOf(thing: any): any;
export function dataTypeOf(thing: any): "String" | "Array" | "SimpleObject" | "ComplexObject" | "Primitive";

View File

@@ -0,0 +1,28 @@
export function typeOf(thing) {
switch (thing) {
case undefined:
return 'Undefined';
case null:
return 'Null';
default:
return thing.constructor.name;
}
}
export function dataTypeOf(thing) {
switch (typeof thing) {
case 'string':
return 'String';
case 'object':
if (Array.isArray(thing)) {
return 'Array';
}
else if (typeOf(thing) === 'Object') {
return 'SimpleObject';
}
else if (typeOf(thing) !== 'Null') {
return 'ComplexObject';
}
default:
return 'Primitive';
}
}

View File

@@ -0,0 +1,2 @@
export function applyGraph(entry: any, graph: any): any;
export function removeGraph(entry: any): any;

View File

@@ -0,0 +1,14 @@
export function applyGraph(entry, graph) {
if (entry._graph) {
const index = graph.findIndex(({ type }) => type === '@else/list+object');
if (index !== -1) {
graph.splice(index + 1, 0, ...entry._graph.slice(0, -1));
}
}
entry._graph = graph;
return entry;
}
export function removeGraph(entry) {
delete entry._graph;
return entry;
}

View File

@@ -0,0 +1,5 @@
export const util: any;
export * from "./register";
export * from "./chain";
export * from "./type";
export * from "./data";

View File

@@ -0,0 +1,9 @@
import * as dataType from './dataType.js';
import * as graph from './graph.js';
import * as parser from './parser.js';
import * as csl from './csl.js';
export const util = Object.assign({}, dataType, graph, parser, csl);
export * from './register';
export * from './chain';
export * from './type';
export * from './data';

View File

@@ -0,0 +1,37 @@
export class TypeParser {
constructor(data: any);
data: any;
validateDataType(): void;
validateParseType(): void;
validateTokenList(): void;
validatePropertyConstraint(): void;
validateElementConstraint(): void;
validateExtends(): void;
validate(): void;
parseTokenList(): ((input: any) => any)[];
parsePropertyConstraint(): ((input: any) => any)[];
parseElementConstraint(): ((input: any) => any)[];
parsePredicate(): any[];
getCombinedPredicate(): any;
getDataType(): any;
get dataType(): any;
get predicate(): any;
get extends(): any;
}
export class DataParser {
constructor(parser: any, { async }?: {
async: any;
});
parser: any;
async: any;
validate(): void;
}
export class FormatParser {
constructor(format: any, parsers?: {});
format: any;
typeParser: TypeParser;
dataParser: DataParser;
asyncDataParser: DataParser;
validateFormat(): void;
validate(): void;
}

View File

@@ -0,0 +1,232 @@
// @ts-nocheck
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 { type, typeMatcher } from './type.js';
class TypeParser {
constructor(data) {
_defineProperty(this, 'validDataTypes', [
'String',
'Array',
'SimpleObject',
'ComplexObject',
'Primitive',
]);
this.data = data;
}
validateDataType() {
const dataType = this.data.dataType;
if (dataType && !this.validDataTypes.includes(dataType)) {
throw new RangeError(`dataType was ${dataType}; expected one of ${this.validDataTypes}`);
}
}
validateParseType() {
const predicate = this.data.predicate;
if (predicate && !(predicate instanceof RegExp || typeof predicate === 'function')) {
throw new TypeError(`predicate was ${typeof predicate}; expected RegExp or function`);
}
}
validateTokenList() {
const tokenList = this.data.tokenList;
if (tokenList && typeof tokenList !== 'object') {
throw new TypeError(`tokenList was ${typeof tokenList}; expected object or RegExp`);
}
}
validatePropertyConstraint() {
const propertyConstraint = this.data.propertyConstraint;
if (propertyConstraint && typeof propertyConstraint !== 'object') {
throw new TypeError(`propertyConstraint was ${typeof propertyConstraint}; expected array or object`);
}
}
validateElementConstraint() {
const elementConstraint = this.data.elementConstraint;
if (elementConstraint && typeof elementConstraint !== 'string') {
throw new TypeError(`elementConstraint was ${typeof elementConstraint}; expected string`);
}
}
validateExtends() {
const extend = this.data.extends;
if (extend && typeof extend !== 'string') {
throw new TypeError(`extends was ${typeof extend}; expected string`);
}
}
validate() {
if (this.data === null || typeof this.data !== 'object') {
throw new TypeError(`typeParser was ${typeof this.data}; expected object`);
}
this.validateDataType();
this.validateParseType();
this.validateTokenList();
this.validatePropertyConstraint();
this.validateElementConstraint();
this.validateExtends();
}
parseTokenList() {
let tokenList = this.data.tokenList;
if (!tokenList) {
return [];
}
else if (tokenList instanceof RegExp) {
tokenList = {
token: tokenList,
};
}
const { token, split = /\s+/, trim = true, every = true } = tokenList;
const trimInput = (input) => (trim ? input.trim() : input);
const testTokens = every ? 'every' : 'some';
const predicate = (input) => trimInput(input)
.split(split)[testTokens]((part) => token.test(part));
return [predicate];
}
parsePropertyConstraint() {
const constraints = [].concat(this.data.propertyConstraint || []);
return constraints.map(({ props, match, value }) => {
props = [].concat(props);
switch (match) {
case 'any':
case 'some':
return (input) => props.some((prop) => prop in input && (!value || value(input[prop])));
case 'none':
return (input) => !props.some((prop) => prop in input && (!value || value(input[prop])));
case 'every':
default:
return (input) => props.every((prop) => prop in input && (!value || value(input[prop])));
}
});
}
parseElementConstraint() {
const constraint = this.data.elementConstraint;
return !constraint ? [] : [(input) => input.every((entry) => type(entry) === constraint)];
}
parsePredicate() {
if (this.data.predicate instanceof RegExp) {
return [this.data.predicate.test.bind(this.data.predicate)];
}
else if (this.data.predicate) {
return [this.data.predicate];
}
else {
return [];
}
}
getCombinedPredicate() {
const predicates = [
...this.parsePredicate(),
...this.parseTokenList(),
...this.parsePropertyConstraint(),
...this.parseElementConstraint(),
];
if (predicates.length === 0) {
return () => true;
}
else if (predicates.length === 1) {
return predicates[0];
}
else {
return (input) => predicates.every((predicate) => predicate(input));
}
}
getDataType() {
if (this.data.dataType) {
return this.data.dataType;
}
else if (this.data.predicate instanceof RegExp) {
return 'String';
}
else if (this.data.tokenList) {
return 'String';
}
else if (this.data.elementConstraint) {
return 'Array';
}
else {
return 'Primitive';
}
}
get dataType() {
return this.getDataType();
}
get predicate() {
return this.getCombinedPredicate();
}
get extends() {
return this.data.extends;
}
}
class DataParser {
constructor(parser, { async } = {}) {
this.parser = parser;
this.async = async;
}
validate() {
const parser = this.parser;
if (typeof parser !== 'function') {
throw new TypeError(`parser was ${typeof parser}; expected function`);
}
}
}
class FormatParser {
constructor(format, parsers = {}) {
this.format = format;
if (parsers.parseType) {
this.typeParser = new TypeParser(parsers.parseType);
}
if (parsers.parse) {
this.dataParser = new DataParser(parsers.parse, {
async: false,
});
}
if (parsers.parseAsync) {
this.asyncDataParser = new DataParser(parsers.parseAsync, {
async: true,
});
}
}
validateFormat() {
const format = this.format;
if (!typeMatcher.test(format)) {
throw new TypeError(`format name was "${format}"; didn't match expected pattern`);
}
}
validate() {
this.validateFormat();
if (this.typeParser) {
this.typeParser.validate();
}
if (this.dataParser) {
this.dataParser.validate();
}
if (this.asyncDataParser) {
this.asyncDataParser.validate();
}
}
}
export { TypeParser, DataParser, FormatParser };

View File

@@ -0,0 +1,5 @@
export function add(format: any, parsers: any): void;
export function get(format: any): any;
export function remove(format: any): void;
export function has(format: any): boolean;
export function list(): string[];

View File

@@ -0,0 +1,49 @@
import { FormatParser } from './parser.js';
import { addTypeParser, removeTypeParser } from './type.js';
import { addDataParser, removeDataParser } from './data.js';
const formats = {};
export function add(format, parsers) {
const formatParser = new FormatParser(format, parsers);
formatParser.validate();
const index = formats[format] || (formats[format] = {});
if (formatParser.typeParser) {
addTypeParser(format, formatParser.typeParser);
index.type = true;
}
if (formatParser.dataParser) {
addDataParser(format, formatParser.dataParser);
index.data = true;
}
if (formatParser.asyncDataParser) {
addDataParser(format, formatParser.asyncDataParser);
index.asyncData = true;
}
if (parsers.outputs) {
index.outputs = parsers.outputs;
}
}
export function get(format) {
return formats[format];
}
export function remove(format) {
const index = formats[format];
if (!index) {
return;
}
if (index.type) {
removeTypeParser(format);
}
if (index.data) {
removeDataParser(format);
}
if (index.asyncData) {
removeDataParser(format, true);
}
delete formats[format];
}
export function has(format) {
return format in formats;
}
export function list() {
return Object.keys(formats);
}

View File

@@ -0,0 +1,17 @@
export function type(input: any): any;
export function addTypeParser(format: any, { dataType, predicate, extends: extend }: {
dataType: any;
predicate: any;
extends: any;
}): void;
export function hasTypeParser(type: any): any;
export function removeTypeParser(type: any): void;
export function listTypeParser(): string[];
export function treeTypeParser(): {
name: string;
children: {
name: string;
children: any;
}[];
};
export const typeMatcher: RegExp;

View File

@@ -0,0 +1,101 @@
import logger from '../../logger.js';
import { dataTypeOf } from './dataType.js';
const types = {};
const dataTypes = {};
const unregExts = {};
function parseNativeTypes(input, dataType) {
switch (dataType) {
case 'Array':
if (input.length === 0 || input.every((entry) => type(entry) === '@csl/object')) {
return '@csl/list+object';
}
else {
return '@else/list+object';
}
case 'SimpleObject':
case 'ComplexObject':
return '@csl/object';
default:
return '@invalid';
}
}
function matchType(typeList = [], data) {
for (const type of typeList) {
if (types[type].predicate(data)) {
return matchType(types[type].extensions, data) || type;
}
}
}
export function type(input) {
const dataType = dataTypeOf(input);
if (dataType === 'Array' && input.length === 0) {
return parseNativeTypes(input, dataType);
}
const match = matchType(dataTypes[dataType], input);
return match || parseNativeTypes(input, dataType);
}
export function addTypeParser(format, { dataType, predicate, extends: extend }) {
let extensions = [];
if (format in unregExts) {
extensions = unregExts[format];
delete unregExts[format];
logger.debug('[core]', `Subclasses "${extensions}" finally registered to parent type "${format}"`);
}
const object = {
predicate,
extensions,
};
types[format] = object;
if (extend) {
const parentTypeParser = types[extend];
if (parentTypeParser) {
parentTypeParser.extensions.push(format);
}
else {
if (!unregExts[extend]) {
unregExts[extend] = [];
}
unregExts[extend].push(format);
logger.debug('[core]', `Subclass "${format}" is waiting on parent type "${extend}"`);
}
}
else {
const typeList = dataTypes[dataType] || (dataTypes[dataType] = []);
typeList.push(format);
}
}
export function hasTypeParser(type) {
return Object.prototype.hasOwnProperty.call(types, type);
}
export function removeTypeParser(type) {
delete types[type];
const typeLists = [
...Object.keys(dataTypes).map((key) => dataTypes[key]),
...Object.keys(types)
.map((type) => types[type].extensions)
.filter((list) => list.length > 0),
];
typeLists.forEach((typeList) => {
const index = typeList.indexOf(type);
if (index > -1) {
typeList.splice(index, 1);
}
});
}
export function listTypeParser() {
return Object.keys(types);
}
export function treeTypeParser() {
const attachNode = (name) => ({
name,
children: types[name].extensions.map(attachNode),
});
return {
name: 'Type tree',
children: Object.keys(dataTypes).map((name) => ({
name,
children: dataTypes[name].map(attachNode),
})),
};
}
export const typeMatcher = /^(?:@(.+?))(?:\/(?:(.+?)\+)?(?:(.+)))?$/;

View File

@@ -0,0 +1,7 @@
export function add(name: any, formatter: any): void;
export function remove(name: any): void;
export function has(name: any): any;
export function list(): string[];
export function format(name: any, data: any, ...options: any[]): any;
export const register: Register;
import Register from '../util/register.js';

View File

@@ -0,0 +1,29 @@
import Register from '../util/register.js';
function validate(name, formatter) {
if (typeof name !== 'string') {
throw new TypeError(`Invalid output format name, expected string, got ${typeof name}`);
}
else if (typeof formatter !== 'function') {
throw new TypeError(`Invalid formatter, expected function, got ${typeof formatter}`);
}
}
export const register = new Register();
export function add(name, formatter) {
validate(name, formatter);
register.set(name, formatter);
}
export function remove(name) {
register.remove(name);
}
export function has(name) {
return register.has(name);
}
export function list() {
return register.list();
}
export function format(name, data, ...options) {
if (!register.has(name)) {
throw new Error(`Output format "${name}" unavailable`);
}
return register.get(name)(data, ...options);
}

View File

@@ -0,0 +1,2 @@
export function upgradeCsl(item: any): any;
export function downgradeCsl(item: any): any;

View File

@@ -0,0 +1,86 @@
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);
}
export function upgradeCsl(item) {
if (Array.isArray(item)) {
return item.map(upgradeCsl);
}
item = _objectSpread({}, item);
if ('event' in item) {
item['event-title'] = item.event;
delete item.event;
}
if (item.type === 'book' && 'version' in item) {
item.type = 'software';
}
return item;
}
export function downgradeCsl(item) {
if (Array.isArray(item)) {
return item.map(downgradeCsl);
}
item = _objectSpread({}, item);
if ('event-title' in item) {
item.event = item['event-title'];
delete item['event-title'];
}
if (item.type === 'software') {
item.type = 'book';
}
return item;
}

View File

@@ -0,0 +1,2 @@
export function deepCopy(value: any, seen?: Set<any>): any;
export default deepCopy;

View File

@@ -0,0 +1,25 @@
export function deepCopy(value, seen = new Set()) {
if (typeof value !== 'object' ||
value === null ||
(value.constructor !== Object && value.constructor !== Array)) {
return value;
}
if (seen.has(value)) {
throw new TypeError('Recursively copying circular structure');
}
seen.add(value);
let copy;
if (value.constructor === Array) {
copy = value.map((value) => deepCopy(value, seen));
}
else {
const object = {};
for (const key in value) {
object[key] = deepCopy(value[key], seen);
}
copy = object;
}
seen.delete(value);
return copy;
}
export default deepCopy;

View File

@@ -0,0 +1,4 @@
export function fetchFile(url: any, opts: any): any;
export function fetchFileAsync(url: any, opts: any): Promise<any>;
export function setUserAgent(newUserAgent: any): void;
export default fetchFile;

View File

@@ -0,0 +1,95 @@
// @ts-nocheck
import syncFetch from 'sync-fetch';
import fetchPolyfill from 'fetch-ponyfill';
import logger from '../logger.js';
const isBrowser = typeof location !== 'undefined' && typeof navigator !== 'undefined';
const { fetch: asyncFetch, Headers: asyncHeaders } = typeof fetch === 'function' && isBrowser
? {
fetch,
Headers,
}
: fetchPolyfill();
let userAgent = `Citation.js/0.7`;
if (typeof process !== 'undefined' &&
process &&
process.release &&
process.release.name === 'node' &&
process.version) {
userAgent += ` Node.js/${process.version}`;
}
function normaliseHeaders(headers) {
const result = {};
const entries = headers instanceof asyncHeaders || headers instanceof syncFetch.Headers
? Array.from(headers)
: Object.entries(headers);
for (const [name, header] of entries) {
result[name.toLowerCase()] = header.toString();
}
return result;
}
function parseOpts(opts = {}) {
const reqOpts = {
headers: {
accept: '*/*',
},
method: 'GET',
checkContentType: opts.checkContentType,
};
if (userAgent && !isBrowser) {
reqOpts.headers['user-agent'] = userAgent;
}
if (opts.body) {
reqOpts.method = 'POST';
const isJson = typeof opts.body !== 'string';
reqOpts.body = isJson ? JSON.stringify(opts.body) : opts.body;
reqOpts.headers['content-type'] = isJson ? 'application/json' : 'text/plain';
}
if (opts.headers) {
Object.assign(reqOpts.headers, normaliseHeaders(opts.headers));
}
return reqOpts;
}
function sameType(request, response) {
if (!request.accept || request.accept === '*/*' || !response['content-type']) {
return true;
}
const [a, b] = response['content-type'].split(';')[0].trim().split('/');
return request.accept
.split(',')
.map((type) => type.split(';')[0].trim().split('/'))
.some(([c, d]) => (c === a || c === '*') && (d === b || d === '*'));
}
function checkResponse(response, opts) {
const { status, headers } = response;
let error;
if (status >= 400) {
error = new Error(`Server responded with status code ${status}`);
}
else if (opts.checkContentType === true && !sameType(opts.headers, normaliseHeaders(headers))) {
error = new Error(`Server responded with content-type ${headers.get('content-type')}`);
}
if (error) {
error.status = status;
error.headers = headers;
error.body = response.body;
throw error;
}
return response;
}
export function fetchFile(url, opts) {
const reqOpts = parseOpts(opts);
logger.http('[core]', reqOpts.method, url, reqOpts);
const response = checkResponse(syncFetch(url, reqOpts), reqOpts);
return response.text();
}
export async function fetchFileAsync(url, opts) {
const reqOpts = parseOpts(opts);
logger.http('[core]', reqOpts.method, url, reqOpts);
return asyncFetch(url, reqOpts)
.then((response) => checkResponse(response, reqOpts))
.then((response) => response.text());
}
export function setUserAgent(newUserAgent) {
userAgent = newUserAgent;
}
export default fetchFile;

View File

@@ -0,0 +1,2 @@
export default fetchId;
declare function fetchId(list: any, prefix: any): string;

View File

@@ -0,0 +1,8 @@
function fetchId(list, prefix) {
let id;
while (id === undefined || list.includes(id)) {
id = `${prefix}${Math.random().toString().slice(2)}`;
}
return id;
}
export default fetchId;

View File

@@ -0,0 +1,15 @@
export class Grammar {
constructor(rules: any, state: any);
rules: any;
defaultState: any;
mainRule: string;
log: any[];
parse(iterator: any, mainRule: any): any;
lexer: any;
token: any;
state: any;
matchEndOfFile(): boolean;
matchToken(type: any): boolean;
consumeToken(type: any, optional: any): any;
consumeRule(rule: any): any;
}

View File

@@ -0,0 +1,45 @@
import { deepCopy } from './deepCopy.js';
class Grammar {
constructor(rules, state) {
this.rules = rules;
this.defaultState = state;
this.mainRule = Object.keys(rules)[0];
this.log = [];
}
parse(iterator, mainRule) {
this.lexer = iterator;
this.token = this.lexer.next();
this.state = deepCopy(this.defaultState);
this.log = [];
return this.consumeRule(mainRule || this.mainRule);
}
matchEndOfFile() {
return !this.token;
}
matchToken(type) {
return this.token && type === this.token.type;
}
consumeToken(type, optional) {
const token = this.token;
if (!type || (token && token.type === type)) {
this.token = this.lexer.next();
return token;
}
else if (optional) {
return undefined;
}
else {
const got = token ? `"${token.type}"` : 'EOF';
const error = new SyntaxError(this.lexer.formatError(token, `expected "${type}", got ${got}`));
error.message += ` (${this.log.join('->')})`;
throw error;
}
}
consumeRule(rule) {
this.log.push(rule);
const result = this.rules[rule].call(this);
this.log.pop();
return result;
}
}
export { Grammar };

View File

@@ -0,0 +1,12 @@
import { upgradeCsl } from './csl.js';
import { downgradeCsl } from './csl.js';
import deepCopy from './deepCopy.js';
import { fetchFile } from './fetchFile.js';
import { fetchFileAsync } from './fetchFile.js';
import { setUserAgent } from './fetchFile.js';
import fetchId from './fetchId.js';
import TokenStack from './stack.js';
import Register from './register.js';
import { Grammar } from './grammar.js';
import { Translator } from './translator.js';
export { upgradeCsl, downgradeCsl, deepCopy, fetchFile, fetchFileAsync, setUserAgent, fetchId, TokenStack, Register, Grammar, Translator };

View File

@@ -0,0 +1,9 @@
import { upgradeCsl, downgradeCsl } from './csl.js';
import deepCopy from './deepCopy.js';
import { fetchFile, fetchFileAsync, setUserAgent } from './fetchFile.js';
import fetchId from './fetchId.js';
import TokenStack from './stack.js';
import Register from './register.js';
import { Grammar } from './grammar.js';
import { Translator } from './translator.js';
export { upgradeCsl, downgradeCsl, deepCopy, fetchFile, fetchFileAsync, setUserAgent, fetchId, TokenStack, Register, Grammar, Translator, };

View File

@@ -0,0 +1,12 @@
export default Register;
declare class Register {
constructor(data?: {});
data: {};
set(key: any, value: any): this;
add(...args: any[]): this;
delete(key: any): this;
remove(...args: any[]): this;
get(key: any): any;
has(key: any): any;
list(): string[];
}

View File

@@ -0,0 +1,29 @@
class Register {
constructor(data = {}) {
this.data = data;
}
set(key, value) {
this.data[key] = value;
return this;
}
add(...args) {
return this.set(...args);
}
delete(key) {
delete this.data[key];
return this;
}
remove(...args) {
return this.delete(...args);
}
get(key) {
return this.data[key];
}
has(key) {
return Object.prototype.hasOwnProperty.call(this.data, key);
}
list() {
return Object.keys(this.data);
}
}
export default Register;

View File

@@ -0,0 +1,28 @@
export default TokenStack;
declare class TokenStack {
static getPatternText(pattern: any): string;
static getMatchCallback(pattern: any): any;
constructor(array: any);
stack: any;
index: number;
current: any;
tokensLeft(): number;
matches(pattern: any): any;
matchesSequence(sequence: any): any;
consumeToken(pattern?: RegExp, { inverse, spaced }?: {
inverse?: boolean;
spaced?: boolean;
}): any;
consumeWhitespace(pattern?: RegExp, { optional }?: {
optional?: boolean;
}): any;
consumeN(length: any): any;
consumeSequence(sequence: any): any;
consume(pattern?: RegExp, { min, max, inverse, tokenMap, tokenFilter }?: {
min?: number;
max?: number;
inverse?: boolean;
tokenMap: any;
tokenFilter: any;
}): any;
}

View File

@@ -0,0 +1,100 @@
// @ts-nocheck
class TokenStack {
constructor(array) {
this.stack = array;
this.index = 0;
this.current = this.stack[this.index];
}
static getPatternText(pattern) {
return `"${pattern instanceof RegExp ? pattern.source : pattern}"`;
}
static getMatchCallback(pattern) {
if (Array.isArray(pattern)) {
const matches = pattern.map(TokenStack.getMatchCallback);
return (token) => matches.some((matchCallback) => matchCallback(token));
}
else if (pattern instanceof Function) {
return pattern;
}
else if (pattern instanceof RegExp) {
return (token) => pattern.test(token);
}
else {
return (token) => pattern === token;
}
}
tokensLeft() {
return this.stack.length - this.index;
}
matches(pattern) {
return TokenStack.getMatchCallback(pattern)(this.current, this.index, this.stack);
}
matchesSequence(sequence) {
const part = this.stack.slice(this.index, this.index + sequence.length).join('');
return typeof sequence === 'string'
? part === sequence
: sequence.every((pattern, index) => TokenStack.getMatchCallback(pattern)(part[index]));
}
consumeToken(pattern = /^[\s\S]$/, { inverse = false, spaced = true } = {}) {
if (spaced) {
this.consumeWhitespace();
}
const token = this.current;
const match = TokenStack.getMatchCallback(pattern)(token, this.index, this.stack);
if (match) {
this.current = this.stack[++this.index];
}
else {
throw new SyntaxError(`Unexpected token at index ${this.index}: Expected ${TokenStack.getPatternText(pattern)}, got "${token}"`);
}
if (spaced) {
this.consumeWhitespace();
}
return token;
}
consumeWhitespace(pattern = /^\s$/, { optional = true } = {}) {
return this.consume(pattern, {
min: +!optional,
});
}
consumeN(length) {
if (this.tokensLeft() < length) {
throw new SyntaxError('Not enough tokens left');
}
const start = this.index;
while (length--) {
this.current = this.stack[++this.index];
}
return this.stack.slice(start, this.index).join('');
}
consumeSequence(sequence) {
if (this.matchesSequence(sequence)) {
return this.consumeN(sequence.length);
}
else {
throw new SyntaxError(`Expected "${sequence}", got "${this.consumeN(sequence.length)}"`);
}
}
consume(pattern = /^[\s\S]$/, { min = 0, max = Infinity, inverse = false, tokenMap, tokenFilter } = {}) {
const start = this.index;
const match = TokenStack.getMatchCallback(pattern);
while (match(this.current, this.index, this.stack) !== inverse) {
this.current = this.stack[++this.index];
}
let consumed = this.stack.slice(start, this.index);
if (consumed.length < min) {
throw new SyntaxError(`Not enough ${TokenStack.getPatternText(pattern)}`);
}
else if (consumed.length > max) {
throw new SyntaxError(`Too many ${TokenStack.getPatternText(pattern)}`);
}
if (tokenMap) {
consumed = consumed.map(tokenMap);
}
if (tokenFilter) {
consumed = consumed.filter(tokenFilter);
}
return consumed.join('');
}
}
export default TokenStack;

View File

@@ -0,0 +1,9 @@
export class Translator {
constructor(props: any);
convertToSource: (input: any) => {};
convertToTarget: (input: any) => {};
}
export namespace Translator {
let CONVERT_TO_SOURCE: symbol;
let CONVERT_TO_TARGET: symbol;
}

View File

@@ -0,0 +1,105 @@
// @ts-nocheck
function createConditionEval(condition) {
return function conditionEval(input) {
if (typeof condition === 'boolean') {
return condition;
}
return Object.keys(condition).every((prop) => {
const value = condition[prop];
if (value === true) {
return prop in input;
}
else if (value === false) {
return !(prop in input);
}
else if (typeof value === 'function') {
return value(input[prop]);
}
else if (Array.isArray(value)) {
return value.includes(input[prop]);
}
else {
return input[prop] === value;
}
});
};
}
function parsePropStatement(prop, toSource) {
let inputProp;
let outputProp;
let convert;
let condition;
if (typeof prop === 'string') {
inputProp = outputProp = prop;
}
else if (prop) {
inputProp = toSource ? prop.target : prop.source;
outputProp = toSource ? prop.source : prop.target;
if (prop.convert) {
convert = toSource ? prop.convert.toSource : prop.convert.toTarget;
}
if (prop.when) {
condition = toSource ? prop.when.target : prop.when.source;
if (condition != null) {
condition = createConditionEval(condition);
}
}
}
else {
return null;
}
inputProp = [].concat(inputProp).filter(Boolean);
outputProp = [].concat(outputProp).filter(Boolean);
return {
inputProp,
outputProp,
convert,
condition,
};
}
function createConverter(props, toSource) {
toSource = toSource === Translator.CONVERT_TO_SOURCE;
props = props.map((prop) => parsePropStatement(prop, toSource)).filter(Boolean);
return function converter(input) {
const output = {};
for (const { inputProp, outputProp, convert, condition } of props) {
if (outputProp.length === 0) {
continue;
}
else if (condition && !condition(input)) {
continue;
}
else if (inputProp.length !== 0 && inputProp.every((prop) => !(prop in input))) {
continue;
}
let outputData = inputProp.map((prop) => input[prop]);
if (convert) {
try {
const converted = convert.apply(input, outputData);
outputData = outputProp.length === 1 ? [converted] : converted;
}
catch (cause) {
throw new Error(`Failed to convert ${inputProp} to ${outputProp}`, {
cause,
});
}
}
outputProp.forEach((prop, index) => {
const value = outputData[index];
if (value !== undefined) {
output[prop] = value;
}
});
}
return output;
};
}
class Translator {
constructor(props) {
this.convertToSource = createConverter(props, Translator.CONVERT_TO_SOURCE);
this.convertToTarget = createConverter(props, Translator.CONVERT_TO_TARGET);
}
}
Translator.CONVERT_TO_SOURCE = Symbol('convert to source');
Translator.CONVERT_TO_TARGET = Symbol('convert to target');
export { Translator };

Some files were not shown because too many files have changed in this diff Show More