@retorquere/bibtex-parser 12.1.13 → 13.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +14 -10
  2. package/dist/esm/crossref.d.ts.map +1 -0
  3. package/dist/esm/crossref.js.map +1 -1
  4. package/dist/esm/fields.d.ts.map +1 -0
  5. package/dist/esm/fields.js.map +1 -1
  6. package/dist/{types → esm}/index.d.ts +41 -12
  7. package/dist/esm/index.d.ts.map +1 -0
  8. package/dist/esm/index.js +27 -3
  9. package/dist/esm/index.js.map +3 -3
  10. package/dist/{types → esm}/jabref.d.ts +9 -21
  11. package/dist/esm/jabref.d.ts.map +1 -0
  12. package/dist/esm/jabref.js +2 -3
  13. package/dist/esm/jabref.js.map +3 -3
  14. package/dist/esm/merge.d.ts.map +1 -0
  15. package/dist/esm/merge.js.map +1 -1
  16. package/dist/esm/prepositions.d.ts.map +1 -0
  17. package/dist/esm/prepositions.js.map +1 -1
  18. package/dist/{types → esm}/sentence-case.d.ts +1 -0
  19. package/dist/esm/sentence-case.d.ts.map +1 -0
  20. package/dist/esm/sentence-case.js +126 -28
  21. package/dist/esm/sentence-case.js.map +3 -3
  22. package/dist/esm/tokenizer.d.ts.map +1 -0
  23. package/dist/esm/tokenizer.js.map +1 -1
  24. package/dist/esm/verbatim.d.ts +90 -0
  25. package/dist/esm/verbatim.d.ts.map +1 -0
  26. package/dist/esm/verbatim.js +10 -0
  27. package/dist/esm/verbatim.js.map +3 -3
  28. package/dist/esm/yield.d.ts.map +1 -0
  29. package/dist/esm/yield.js.map +1 -1
  30. package/dist/wasm/bibtex_parser.d.ts +12 -9
  31. package/dist/wasm/bibtex_parser.js +13 -23
  32. package/dist/wasm/bibtex_parser_bg.wasm +0 -0
  33. package/dist/wasm/bibtex_parser_bg.wasm.d.ts +2 -3
  34. package/dist/wasm/english.json +27 -0
  35. package/dist/wasm/index.d.ts +105 -29
  36. package/dist/wasm/index.js +54 -51
  37. package/dist/wasm/jabref.d.ts +9 -21
  38. package/dist/wasm/jabref.js +3 -3
  39. package/package.json +25 -26
  40. package/dist/cjs/crossref.js +0 -201
  41. package/dist/cjs/crossref.js.map +0 -7
  42. package/dist/cjs/fields.js +0 -7186
  43. package/dist/cjs/fields.js.map +0 -7
  44. package/dist/cjs/index.js +0 -1126
  45. package/dist/cjs/index.js.map +0 -7
  46. package/dist/cjs/jabref.js +0 -135
  47. package/dist/cjs/jabref.js.map +0 -7
  48. package/dist/cjs/merge.js +0 -37
  49. package/dist/cjs/merge.js.map +0 -7
  50. package/dist/cjs/prepositions.js +0 -111
  51. package/dist/cjs/prepositions.js.map +0 -7
  52. package/dist/cjs/sentence-case.js +0 -98
  53. package/dist/cjs/sentence-case.js.map +0 -7
  54. package/dist/cjs/tokenizer.js +0 -194
  55. package/dist/cjs/tokenizer.js.map +0 -7
  56. package/dist/cjs/verbatim.js +0 -403
  57. package/dist/cjs/verbatim.js.map +0 -7
  58. package/dist/cjs/yield.js +0 -26
  59. package/dist/cjs/yield.js.map +0 -7
  60. package/dist/types/crossref.d.ts.map +0 -1
  61. package/dist/types/fields.d.ts.map +0 -1
  62. package/dist/types/index.d.ts.map +0 -1
  63. package/dist/types/jabref.d.ts.map +0 -1
  64. package/dist/types/merge.d.ts.map +0 -1
  65. package/dist/types/prepositions.d.ts.map +0 -1
  66. package/dist/types/sentence-case.d.ts.map +0 -1
  67. package/dist/types/tokenizer.d.ts.map +0 -1
  68. package/dist/types/verbatim.d.ts +0 -66
  69. package/dist/types/verbatim.d.ts.map +0 -1
  70. package/dist/types/yield.d.ts.map +0 -1
  71. /package/dist/{types → esm}/crossref.d.ts +0 -0
  72. /package/dist/{types → esm}/fields.d.ts +0 -0
  73. /package/dist/{types → esm}/merge.d.ts +0 -0
  74. /package/dist/{types → esm}/prepositions.d.ts +0 -0
  75. /package/dist/{types → esm}/tokenizer.d.ts +0 -0
  76. /package/dist/{types → esm}/yield.d.ts +0 -0
@@ -4,7 +4,7 @@ const connectedInnerWord = /-\p{Lu}\p{Ll}*(?=-|$)/ug;
4
4
  function titleCase(s) {
5
5
  return s.replace(/^(.)(.+)/, (match, car, cdr) => `${car}${cdr.toLowerCase()}`);
6
6
  }
7
- function wordSC(token, allCaps, subSentence, hyphenated) {
7
+ function wordSC(token, allCaps, subSentence, hyphenatedPart = false) {
8
8
  if (token.type === "domain") return token.text.toLowerCase();
9
9
  if (token.type !== "word") return token.text;
10
10
  if (token.text.match(/^I'/)) return titleCase(token.text);
@@ -14,24 +14,120 @@ function wordSC(token, allCaps, subSentence, hyphenated) {
14
14
  }
15
15
  if (token.subtype === "preposition") return token.text.toLowerCase();
16
16
  if (token.subtype === "acronym") return token.text;
17
- if (token.text.match(/^[B-Z]$/)) return hyphenated ? token.text.toLowerCase() : token.text;
17
+ if (hyphenatedPart && token.shape.match(/^Xx*$/)) return token.text.toLowerCase();
18
+ if (token.shape === "X" && Array.from(token.text).length === 1) return token.text === "A" ? "a" : token.text;
18
19
  const shape = token.shape.replace(/[^-Xxd]/g, "");
19
20
  if (shape.match(/^[Xd]+$/)) return allCaps ? token.text.toLowerCase() : token.text;
20
21
  if (shape.match(/x.*X/)) return token.text;
21
22
  return token.text.toLowerCase();
22
23
  }
24
+ function globalRegex(pattern) {
25
+ return new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
26
+ }
27
+ function stripMarkup(source, pattern) {
28
+ const markup = [];
29
+ if (pattern) {
30
+ let removed = 0;
31
+ for (const match of source.matchAll(globalRegex(pattern))) {
32
+ if (!match[0].length) continue;
33
+ markup.push({ sourceStart: match.index, position: match.index - removed, text: match[0] });
34
+ removed += match[0].length;
35
+ }
36
+ }
37
+ const text = pattern ? source.replace(globalRegex(pattern), "") : source;
38
+ const visibleOffset = (sourceOffset) => {
39
+ let removed = 0;
40
+ for (const tag of markup) {
41
+ if (sourceOffset <= tag.sourceStart) break;
42
+ removed += Math.min(tag.text.length, sourceOffset - tag.sourceStart);
43
+ }
44
+ return sourceOffset - removed;
45
+ };
46
+ return { text, markup, visibleOffset };
47
+ }
48
+ function matchingRanges(source, pattern, visibleOffset) {
49
+ if (!pattern) return [];
50
+ return Array.from(source.matchAll(globalRegex(pattern)), (match) => [
51
+ visibleOffset(match.index),
52
+ visibleOffset(match.index + match[0].length)
53
+ ]);
54
+ }
55
+ function replacementBoundaries(source, replacement) {
56
+ const boundaries = Array(source.length + 1).fill(0);
57
+ let sourceOffset = 0;
58
+ let outputOffset = 0;
59
+ while (sourceOffset < source.length) {
60
+ const sourceChar = String.fromCodePoint(source.codePointAt(sourceOffset));
61
+ const sourceEnd = sourceOffset + sourceChar.length;
62
+ const lower = sourceChar.toLowerCase();
63
+ let outputLength;
64
+ if (replacement.startsWith(sourceChar, outputOffset)) outputLength = sourceChar.length;
65
+ else if (replacement.startsWith(lower, outputOffset)) outputLength = lower.length;
66
+ else if (outputOffset < replacement.length) outputLength = String.fromCodePoint(replacement.codePointAt(outputOffset)).length;
67
+ else outputLength = 0;
68
+ for (let offset = sourceOffset + 1; offset < sourceEnd; offset++) boundaries[offset] = outputOffset;
69
+ sourceOffset = sourceEnd;
70
+ outputOffset += outputLength;
71
+ boundaries[sourceOffset] = outputOffset;
72
+ }
73
+ return boundaries;
74
+ }
75
+ function applyEdits(source, edits) {
76
+ let result = "";
77
+ let cursor = 0;
78
+ for (const edit of edits) {
79
+ result += source.slice(cursor, edit.start) + edit.replacement;
80
+ cursor = edit.end;
81
+ }
82
+ return result + source.slice(cursor);
83
+ }
84
+ function outputBoundary(position, edits) {
85
+ let delta = 0;
86
+ for (const edit of edits) {
87
+ if (position < edit.start) break;
88
+ if (position <= edit.end) return edit.start + delta + edit.boundaries[position - edit.start];
89
+ delta += edit.replacement.length - (edit.end - edit.start);
90
+ }
91
+ return position + delta;
92
+ }
93
+ function restorePreservedRanges(source, cased, ranges, edits) {
94
+ const merged = [];
95
+ for (const [start, end] of [...ranges].sort((left, right) => left[0] - right[0] || left[1] - right[1])) {
96
+ const previous = merged[merged.length - 1];
97
+ if (previous && start <= previous[1]) previous[1] = Math.max(previous[1], end);
98
+ else merged.push([start, end]);
99
+ }
100
+ let result = cased;
101
+ for (const [start, end] of merged.reverse()) {
102
+ const outputStart = outputBoundary(start, edits);
103
+ const outputEnd = outputBoundary(end, edits);
104
+ result = result.slice(0, outputStart) + source.slice(start, end) + result.slice(outputEnd);
105
+ }
106
+ return result;
107
+ }
108
+ function restoreMarkup(text, markup, edits) {
109
+ const insertions = markup.map((tag) => ({ position: outputBoundary(tag.position, edits), sourceStart: tag.sourceStart, text: tag.text })).sort((left, right) => left.position - right.position || left.sourceStart - right.sourceStart);
110
+ let result = "";
111
+ let cursor = 0;
112
+ for (const insertion of insertions) {
113
+ result += text.slice(cursor, insertion.position) + insertion.text;
114
+ cursor = insertion.position;
115
+ }
116
+ return result + text.slice(cursor);
117
+ }
23
118
  function toSentenceCase(title, options = {}) {
24
119
  options = merge(options, {
25
120
  preserveQuoted: true,
26
121
  subSentenceCapitalization: true,
27
122
  guess: false
28
123
  });
29
- const allCaps = title === title.toUpperCase();
30
- const allLower = title === title.toLowerCase();
124
+ const view = stripMarkup(title, options.markup);
125
+ const allCaps = view.text === view.text.toUpperCase();
126
+ const allLower = view.text === view.text.toLowerCase();
31
127
  if (options.guess && !allCaps && !allLower) {
32
128
  let $title = title;
33
129
  if (options.nocase) $title = $title.replace(options.nocase, (match) => match.match(/\s/) ? " " : "");
34
- if (options.markup) $title = $title.replace(options.markup, "");
130
+ $title = stripMarkup($title, options.markup).text;
35
131
  const guess = {
36
132
  words: tokenize($title),
37
133
  sc: 0,
@@ -44,34 +140,36 @@ function toSentenceCase(title, options = {}) {
44
140
  });
45
141
  if (guess.sc && guess.sc >= guess.other) return title;
46
142
  }
47
- if (allCaps && !title.match(/\s/)) return title;
48
- const tokens = tokenize(title, options.markup);
49
- let sentenceCased = "";
50
- for (const token of tokens) {
51
- if (token.hyphenated && token.shape[0] === "x") {
52
- sentenceCased += token.text;
53
- } else if (token.hyphenated) {
54
- for (const t of token.hyphenated) {
55
- sentenceCased += wordSC(t, allCaps, options.subSentenceCapitalization, true);
56
- }
57
- } else {
58
- sentenceCased += wordSC(token, allCaps, options.subSentenceCapitalization, false);
143
+ if (allCaps && !view.text.match(/\s/)) return title;
144
+ const tokens = tokenize(view.text);
145
+ const preserved = matchingRanges(title, options.nocase, view.visibleOffset);
146
+ if (options.preserveQuoted) {
147
+ for (const quote of [/“.*?”/g, /‘.*?’/g, /".*?"/g]) {
148
+ preserved.push(...matchingRanges(title, quote, view.visibleOffset));
59
149
  }
60
150
  }
61
- if (options.markup) {
62
- for (const match of title.matchAll(options.markup)) {
63
- sentenceCased = sentenceCased.substring(0, match.index) + match[0] + sentenceCased.substring(match.index + match[0].length);
151
+ const edits = [];
152
+ const addEdit = (token, hyphenatedPart = false) => {
153
+ if (token.type !== "word" && token.type !== "domain") return;
154
+ const start = token.start;
155
+ const end = token.end + 1;
156
+ const replacement = wordSC(token, allCaps, options.subSentenceCapitalization, hyphenatedPart);
157
+ if (replacement !== token.text) {
158
+ edits.push({ start, end, replacement, boundaries: replacementBoundaries(token.text, replacement) });
64
159
  }
65
- }
66
- if (options.nocase) {
67
- sentenceCased = sentenceCased.replace(options.nocase, (match, tag, offset) => title.substring(offset, offset + match.length));
68
- }
69
- if (options.preserveQuoted) {
70
- for (const q of [/“.*?”/g, /‘.*?’/g, /".*?"/g]) {
71
- sentenceCased = sentenceCased.replace(q, (match, offset) => title.substring(offset, offset + match.length));
160
+ };
161
+ for (const token of tokens) {
162
+ if (token.hyphenated) {
163
+ if (token.shape[0] !== "x") {
164
+ for (const part of token.hyphenated) addEdit(part, true);
165
+ }
166
+ } else {
167
+ addEdit(token);
72
168
  }
73
169
  }
74
- return sentenceCased;
170
+ const cased = applyEdits(view.text, edits);
171
+ const restored = restorePreservedRanges(view.text, cased, preserved, edits);
172
+ return restoreMarkup(restored, view.markup, edits);
75
173
  }
76
174
  export {
77
175
  toSentenceCase
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../sentence-case.ts"],
4
- "sourcesContent": ["import { merge } from './merge.js'\nimport type { Token } from './tokenizer.js'\nimport { tokenize } from './tokenizer.js'\n\nconst connectedInnerWord = /-\\p{Lu}\\p{Ll}*(?=-|$)/ug\n\n// const show = (obj: any): string => JSON.stringify(obj, null, 2).replace(/[\\u007F-\\uFFFF]/g, chr => `\\\\u${(`0000${chr.charCodeAt(0).toString(16)}`).substr(-4)}`)\n\nfunction titleCase(s: string): string {\n return s.replace(/^(.)(.+)/, (match, car, cdr) => `${car}${cdr.toLowerCase()}`)\n}\n\nfunction wordSC(token: Token, allCaps: boolean, subSentence: boolean, hyphenated: boolean): string {\n // domains are case-insensitive\n if (token.type === 'domain') return token.text.toLowerCase()\n\n // punctuation needs no handling, and websites can have case-sensitive stuff in the URL\n if (token.type !== 'word') return token.text\n\n // I'll, I'm\n if (token.text.match(/^I'/)) return titleCase(token.text)\n\n // starting 'a'\n if (subSentence && token.subSentenceStart && token.text.match(/^a$/i)) return 'a'\n\n // (sub)sentence start\n if ((subSentence && token.subSentenceStart) || token.sentenceStart) {\n return allCaps ? titleCase(token.text) : token.text.replace(connectedInnerWord, match => match.toLowerCase())\n }\n\n // prepositions should already have been lowercased, but OK\n if (token.subtype === 'preposition') return token.text.toLowerCase()\n\n // keep acronyms\n if (token.subtype === 'acronym') return token.text\n\n // if (!allCaps && token.shape.match(/^[Xxd]+(-[Xxd]+)+/)) return XRegExp.replace(token.text, connectedWord, match => match.toLowerCase())\n\n // single-letter capitals are usually some kind of noun, but not when they're part of an hyphenated word\n if (token.text.match(/^[B-Z]$/)) return hyphenated ? token.text.toLowerCase() : token.text\n\n // ?? if (!allCaps && token.shape.match(/^[-X]+$/)) return token.text\n\n const shape = token.shape.replace(/[^-Xxd]/g, '')\n // ?? if (!allCaps && shape.match(/^[Xd-]+$/)) return token.text\n\n // hyphenated ?? if (shape.match(/^X[xd]*(-[Xxd]*)*$/)) return token.text.toLowerCase()\n\n // all-caps and numbers\n if (shape.match(/^[Xd]+$/)) return allCaps ? token.text.toLowerCase() : token.text\n\n // domain ?? if (token.text.includes('.')) return token.text\n if (shape.match(/x.*X/)) return token.text\n\n return token.text.toLowerCase()\n}\n\nexport type Options = {\n preserveQuoted?: boolean\n subSentenceCapitalization?: boolean\n markup?: RegExp\n nocase?: RegExp\n guess?: boolean\n}\n\nexport function toSentenceCase(title: string, options: Options = {}): string {\n options = merge(options, {\n preserveQuoted: true,\n subSentenceCapitalization: true,\n guess: false,\n })\n\n const allCaps = title === title.toUpperCase()\n const allLower = title === title.toLowerCase()\n if (options.guess && !allCaps && !allLower) {\n let $title = title\n if (options.nocase) $title = $title.replace(options.nocase, match => match.match(/\\s/) ? ' ' : '')\n if (options.markup) $title = $title.replace(options.markup, '')\n\n const guess = {\n words: tokenize($title),\n sc: 0,\n other: 0,\n }\n\n guess.words.forEach((token, i) => {\n if (token.type === 'word' && token.text.length > 1 && !token.subtype.match(/preposition|acronym|ordinal/) && (i === 0 || guess.words[i - 1].type === 'whitespace')) {\n guess[token.shape.match(/^[^X]*x[^X]*$/) ? 'sc' : 'other'] += 1\n }\n })\n if (guess.sc && guess.sc >= guess.other) return title\n }\n\n if (allCaps && !title.match(/\\s/)) return title\n\n const tokens = tokenize(title, options.markup)\n\n let sentenceCased = ''\n for (const token of tokens) {\n if (token.hyphenated && token.shape[0] === 'x') {\n sentenceCased += token.text\n }\n else if (token.hyphenated) {\n for (const t of token.hyphenated) {\n sentenceCased += wordSC(t, allCaps, options.subSentenceCapitalization!, true)\n }\n }\n else {\n sentenceCased += wordSC(token, allCaps, options.subSentenceCapitalization!, false)\n }\n }\n\n if (options.markup) {\n for (const match of title.matchAll(options.markup)) {\n sentenceCased = sentenceCased.substring(0, match.index) + match[0] + sentenceCased.substring(match.index + match[0].length)\n }\n }\n\n if (options.nocase) {\n sentenceCased = sentenceCased\n .replace(options.nocase, (match: string, tag: string, offset: number) => title.substring(offset, offset + match.length))\n }\n\n if (options.preserveQuoted) {\n for (const q of [/\u201C.*?\u201D/g, /\u2018.*?\u2019/g, /\".*?\"/g]) {\n sentenceCased = sentenceCased.replace(q, (match: string, offset: number) => title.substring(offset, offset + match.length))\n }\n }\n\n return sentenceCased\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../js/sentence-case.ts"],
4
+ "sourcesContent": ["import { merge } from './merge.js'\nimport type { Token } from './tokenizer.js'\nimport { tokenize } from './tokenizer.js'\n\nconst connectedInnerWord = /-\\p{Lu}\\p{Ll}*(?=-|$)/ug\n\n// const show = (obj: any): string => JSON.stringify(obj, null, 2).replace(/[\\u007F-\\uFFFF]/g, chr => `\\\\u${(`0000${chr.charCodeAt(0).toString(16)}`).substr(-4)}`)\n\n/** Keep the first character and lowercase the remainder, matching the tokenizer's all-caps rule. */\nfunction titleCase(s: string): string {\n return s.replace(/^(.)(.+)/, (match, car, cdr) => `${car}${cdr.toLowerCase()}`)\n}\n\n/** Apply the established sentence-case priority order to one complete visible-text token. */\nfunction wordSC(token: Token, allCaps: boolean, subSentence: boolean, hyphenatedPart = false): string {\n // Domain labels are case-insensitive even when their source spelling is mixed case.\n if (token.type === 'domain') return token.text.toLowerCase()\n\n // Leave punctuation and URLs untouched; URL paths may be case-sensitive.\n if (token.type !== 'word') return token.text\n\n // I'll, I'm\n if (token.text.match(/^I'/)) return titleCase(token.text)\n\n // A at a colon-started sub-sentence is an article, not a proper-name initial.\n if (subSentence && token.subSentenceStart && token.text.match(/^a$/i)) return 'a'\n\n // Sentence starts retain their spelling, except all-caps input is normalized to title case.\n if ((subSentence && token.subSentenceStart) || token.sentenceStart) {\n return allCaps ? titleCase(token.text) : token.text.replace(connectedInnerWord, match => match.toLowerCase())\n }\n\n // Normalize prepositions defensively, even if their tokenizer classification was already lowercase.\n if (token.subtype === 'preposition') return token.text.toLowerCase()\n\n // Acronym recognition takes priority over the ordinary single-capital rule.\n if (token.subtype === 'acronym') return token.text\n\n // Hyphenated title-case parts are not standalone initials, but all-caps parts remain acronyms.\n if (hyphenatedPart && token.shape.match(/^Xx*$/)) return token.text.toLowerCase()\n\n // Lowercase the standalone article A, but preserve other one-letter capitals as initials.\n if (token.shape === 'X' && Array.from(token.text).length === 1) return token.text === 'A' ? 'a' : token.text\n\n // if (!allCaps && token.shape.match(/^[Xxd]+(-[Xxd]+)+/)) return XRegExp.replace(token.text, connectedWord, match => match.toLowerCase())\n\n // ?? if (!allCaps && token.shape.match(/^[-X]+$/)) return token.text\n\n const shape = token.shape.replace(/[^-Xxd]/g, '')\n // ?? if (!allCaps && shape.match(/^[Xd-]+$/)) return token.text\n\n // hyphenated ?? if (shape.match(/^X[xd]*(-[Xxd]*)*$/)) return token.text.toLowerCase()\n\n // Preserve all-caps tokens in mixed-case titles; lowercase them only in an all-caps title.\n if (shape.match(/^[Xd]+$/)) return allCaps ? token.text.toLowerCase() : token.text\n\n // Keep internal capitalization such as camel case; only ordinary lower/title shapes fall through.\n if (shape.match(/x.*X/)) return token.text\n\n return token.text.toLowerCase()\n}\n\n/** A removed markup match, with its original UTF-16 offset and visible-text insertion position. */\ntype InlineMarkup = { sourceStart: number; position: number; text: string }\n\n/** A token replacement plus a map from every original UTF-16 boundary to its output boundary. */\ntype CasingEdit = { start: number; end: number; replacement: string; boundaries: number[] }\n\n/** Return an independent global matcher so callers' flags and lastIndex are not changed. */\nfunction globalRegex(pattern: RegExp): RegExp {\n return new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`)\n}\n\n/** Remove matching markup while recording both restoration points and source-to-visible offsets. */\nfunction stripMarkup(source: string, pattern?: RegExp) {\n const markup: InlineMarkup[] = []\n if (pattern) {\n let removed = 0\n for (const match of source.matchAll(globalRegex(pattern))) {\n // Empty matches cannot represent markup and could otherwise produce repeated insertion points.\n if (!match[0].length) continue\n markup.push({ sourceStart: match.index!, position: match.index! - removed, text: match[0] })\n removed += match[0].length\n }\n }\n const text = pattern ? source.replace(globalRegex(pattern), '') : source\n const visibleOffset = (sourceOffset: number): number => {\n let removed = 0\n for (const tag of markup) {\n if (sourceOffset <= tag.sourceStart) break\n // A range boundary inside a tag maps to the same zero-width visible position as the tag.\n removed += Math.min(tag.text.length, sourceOffset - tag.sourceStart)\n }\n return sourceOffset - removed\n }\n return { text, markup, visibleOffset }\n}\n\n/** Map source ranges, such as nocase or quoted spans, into the markup-free text coordinates. */\nfunction matchingRanges(source: string, pattern: RegExp | undefined, visibleOffset: (offset: number) => number): [number, number][] {\n if (!pattern) return []\n return Array.from(source.matchAll(globalRegex(pattern)), match => [\n visibleOffset(match.index!),\n visibleOffset(match.index! + match[0].length),\n ])\n}\n\n/** Map each original UTF-16 boundary through a token replacement, including expanding case maps. */\nfunction replacementBoundaries(source: string, replacement: string): number[] {\n const boundaries = Array<number>(source.length + 1).fill(0)\n let sourceOffset = 0\n let outputOffset = 0\n while (sourceOffset < source.length) {\n const sourceChar = String.fromCodePoint(source.codePointAt(sourceOffset)!)\n const sourceEnd = sourceOffset + sourceChar.length\n const lower = sourceChar.toLowerCase()\n let outputLength: number\n // Prefer exact alignment, then the character's lowercase expansion (for example, \u0130 -> i + dot).\n if (replacement.startsWith(sourceChar, outputOffset)) outputLength = sourceChar.length\n else if (replacement.startsWith(lower, outputOffset)) outputLength = lower.length\n // Unusual transformations still need a monotone boundary map for later markup insertions.\n else if (outputOffset < replacement.length) outputLength = String.fromCodePoint(replacement.codePointAt(outputOffset)!).length\n else outputLength = 0\n // Never place a saved position between the UTF-16 halves of one surrogate pair.\n for (let offset = sourceOffset + 1; offset < sourceEnd; offset++) boundaries[offset] = outputOffset\n sourceOffset = sourceEnd\n outputOffset += outputLength\n boundaries[sourceOffset] = outputOffset\n }\n return boundaries\n}\n\n/** Apply sorted, nonoverlapping token edits to the markup-free source. */\nfunction applyEdits(source: string, edits: CasingEdit[]): string {\n let result = ''\n let cursor = 0\n for (const edit of edits) {\n result += source.slice(cursor, edit.start) + edit.replacement\n cursor = edit.end\n }\n return result + source.slice(cursor)\n}\n\n/** Translate a visible-text boundary through all preceding edits and any edit containing it. */\nfunction outputBoundary(position: number, edits: CasingEdit[]): number {\n let delta = 0\n for (const edit of edits) {\n if (position < edit.start) break\n if (position <= edit.end) return edit.start + delta + edit.boundaries[position - edit.start]\n delta += edit.replacement.length - (edit.end - edit.start)\n }\n return position + delta\n}\n\n/** Restore protected source spans over cased text using the token edits' boundary map. */\nfunction restorePreservedRanges(source: string, cased: string, ranges: [number, number][], edits: CasingEdit[]): string {\n const merged: [number, number][] = []\n for (const [start, end] of [...ranges].sort((left, right) => left[0] - right[0] || left[1] - right[1])) {\n const previous = merged[merged.length - 1]\n if (previous && start <= previous[1]) previous[1] = Math.max(previous[1], end)\n else merged.push([start, end])\n }\n\n let result = cased\n for (const [start, end] of merged.reverse()) {\n const outputStart = outputBoundary(start, edits)\n const outputEnd = outputBoundary(end, edits)\n result = result.slice(0, outputStart) + source.slice(start, end) + result.slice(outputEnd)\n }\n return result\n}\n\n/** Reinsert tags at mapped output boundaries, retaining source order for tags sharing a boundary. */\nfunction restoreMarkup(text: string, markup: InlineMarkup[], edits: CasingEdit[]): string {\n const insertions = markup\n .map(tag => ({ position: outputBoundary(tag.position, edits), sourceStart: tag.sourceStart, text: tag.text }))\n .sort((left, right) => left.position - right.position || left.sourceStart - right.sourceStart)\n let result = ''\n let cursor = 0\n for (const insertion of insertions) {\n result += text.slice(cursor, insertion.position) + insertion.text\n cursor = insertion.position\n }\n return result + text.slice(cursor)\n}\n\nexport type Options = {\n preserveQuoted?: boolean\n subSentenceCapitalization?: boolean\n markup?: RegExp\n nocase?: RegExp\n guess?: boolean\n}\n\n/** Sentence-case visible words while preserving markup, protected spans, and source casing options. */\nexport function toSentenceCase(title: string, options: Options = {}): string {\n options = merge(options, {\n preserveQuoted: true,\n subSentenceCapitalization: true,\n guess: false,\n })\n\n // Case classification must see the same whole word regardless of where inline markup falls.\n const view = stripMarkup(title, options.markup)\n const allCaps = view.text === view.text.toUpperCase()\n const allLower = view.text === view.text.toLowerCase()\n if (options.guess && !allCaps && !allLower) {\n let $title = title\n if (options.nocase) $title = $title.replace(options.nocase, match => match.match(/\\s/) ? ' ' : '')\n $title = stripMarkup($title, options.markup).text\n\n const guess = {\n words: tokenize($title),\n sc: 0,\n other: 0,\n }\n\n guess.words.forEach((token, i) => {\n if (token.type === 'word' && token.text.length > 1 && !token.subtype.match(/preposition|acronym|ordinal/) && (i === 0 || guess.words[i - 1].type === 'whitespace')) {\n guess[token.shape.match(/^[^X]*x[^X]*$/) ? 'sc' : 'other'] += 1\n }\n })\n // Guessing preserves the complete original, including markup and any author's casing choices.\n if (guess.sc && guess.sc >= guess.other) return title\n }\n\n // Keep a one-token all-caps identifier intact; whitespace is measured after removing markup.\n if (allCaps && !view.text.match(/\\s/)) return title\n\n const tokens = tokenize(view.text)\n const preserved = matchingRanges(title, options.nocase, view.visibleOffset)\n if (options.preserveQuoted) {\n for (const quote of [/\u201C.*?\u201D/g, /\u2018.*?\u2019/g, /\".*?\"/g]) {\n preserved.push(...matchingRanges(title, quote, view.visibleOffset))\n }\n }\n const edits: CasingEdit[] = []\n const addEdit = (token: Token, hyphenatedPart = false) => {\n // Only lexical tokens are cased; punctuation, markup-free spacing, and URLs remain untouched.\n if (token.type !== 'word' && token.type !== 'domain') return\n const start = token.start\n const end = token.end + 1\n const replacement = wordSC(token, allCaps, options.subSentenceCapitalization!, hyphenatedPart)\n if (replacement !== token.text) {\n edits.push({ start, end, replacement, boundaries: replacementBoundaries(token.text, replacement) })\n }\n }\n for (const token of tokens) {\n if (token.hyphenated) {\n // A lowercase-leading hyphenated word is already sentence-cased; retain the entire form.\n if (token.shape[0] !== 'x') {\n for (const part of token.hyphenated) addEdit(part, true)\n }\n }\n else {\n addEdit(token)\n }\n }\n\n // Apply casing before reinserting tags so tags never split tokenizer input or casing decisions.\n const cased = applyEdits(view.text, edits)\n const restored = restorePreservedRanges(view.text, cased, preserved, edits)\n return restoreMarkup(restored, view.markup, edits)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../js/tokenizer.ts"],"names":[],"mappings":"AA+FA,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAA;IACtC,OAAO,EAAE,EAAE,GAAG,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAA;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,KAAK,EAAE,CAAA;CACrB,CAAA;AAkBD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,CA6EhE"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../tokenizer.ts"],
3
+ "sources": ["../../js/tokenizer.ts"],
4
4
  "sourcesContent": ["import moo from 'moo'\n\nimport prepositions from './prepositions.js'\n\n// const show = (obj: any): string => JSON.stringify(obj, null, 2).replace(/[\\u007F-\\uFFFF]/g, chr => `\\\\u${(`0000${chr.charCodeAt(0).toString(16)}`).substr(-4)}`)\n\nconst RE = new class {\n public Acronym: RegExp\n public Word: RegExp\n public Contraction: RegExp\n public Whitespace = /[ \\t\\n\\r\\u00A0]+/u\n public Ordinal: RegExp\n public Email: RegExp\n public Handle: RegExp\n public IntOrVersion: RegExp\n public Domain: RegExp\n public Website: RegExp\n public Preposition: RegExp\n public ComplexPreposition = /^([^ \\t\\n\\r\\u00A0]+)([ \\t\\n\\r\\u00A0]+)([^ \\t\\n\\r\\u00A0]+)(?:([ \\t\\n\\r\\u00A0]+)([^ \\t\\n\\r\\u00A0]+))?$/u\n public P = /\\p{P}/u\n\n constructor() {\n const B = '(?=(?:[^\\\\p{L}\\\\p{N}\\\\p{M}\\\\u00AD\\\\u2060]|$))'\n const LNM = '[\\\\p{L}\\\\p{N}\\\\p{M}\\u00AD\\u2060]'\n const W = `${LNM}*?\\\\p{L}${LNM}*`\n\n this.Acronym = new RegExp(`(?:(?:(?:[\\\\p{Lu}\\\\p{Lt}][.]){2,}${B})|(?:(?:vs?[.])(?=[ \\t\\n\\r\\u00A0])))`, 'u')\n this.Word = new RegExp(`${W}${B}`, 'u')\n\n this.Contraction = new RegExp(`${W}['\u2019]${W}${B}`, 'u')\n this.Ordinal = new RegExp(`\\\\d+(?:st|nd|rd|th)${B}`, 'u')\n this.Email = new RegExp(`[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:[.][A-Za-z0-9-]+)+${B}`, 'u')\n this.Handle = new RegExp(`@[A-Za-z0-9-]{2,}${B}`, 'u')\n this.IntOrVersion = new RegExp(`v?\\\\d+(?:\\\\.\\\\d+)*${B}`, 'u')\n this.Domain = new RegExp(`${W}(?:[.]${W})+${B}`, 'u')\n this.Website = new RegExp(`https?://${W}(?:[.]${W})+(?:[^.!? \\t\\n\\r\\u00A0]+|[.!?]${LNM})+`, 'u')\n\n const ci = (s: string): string =>\n s\n .replace(/[a-z]/ig, match => `[${match.toUpperCase()}${match.toLowerCase()}]`)\n .replace(' ', this.Whitespace.source)\n\n this.Preposition = new RegExp(`(?:${prepositions.sort().reverse().map(ci).join('|')})${B}`, 'u')\n }\n}()\n\nconst lexer = moo.compile({\n 'word-preposition': RE.Preposition,\n 'word-acronym': RE.Acronym,\n 'word-contraction': RE.Contraction,\n 'word-ordinal': RE.Ordinal,\n email: RE.Email,\n handle: RE.Handle,\n website: RE.Website,\n domain: RE.Domain,\n word: RE.Word,\n number: RE.IntOrVersion, // eslint-disable-line id-blacklist\n 'punctuation-end': /[?.!](?=[ \\t\\n\\r\\u00A0]|$)/u,\n 'punctuation-colon': /:(?=[ \\t\\n\\r\\u00A0])/u,\n 'punctuation-ellipsis': /[.][.][.]/u,\n punctuation: RE.P,\n whitespace: { match: /[ \\t\\n\\r\\u00A0]/u, lineBreaks: true },\n other: { match: /[\\s\\S]/u, lineBreaks: true },\n})\n\nconst Shape = new class {\n private shapes: Map<string, string> = new Map()\n private re: Record<string, RegExp> = {\n X: /[\\p{Lu}\\p{Lt}]/u,\n x: /[\\p{Ll}\\p{Lm}\\p{Lo}]/u,\n d: /\\p{N}/u,\n }\n\n private match(c: string): string {\n if (c.match(this.re.d)) return 'd'\n if (c.toLowerCase() === c.toUpperCase()) return c\n if (c.match(this.re.X)) return 'X'\n if (c.match(this.re.x)) return 'x'\n if (c === '\u2019') return \"'\"\n if (c === '\u2013') return '-'\n if (c === '\\u2060' || c === '\\u00AD') return ''\n return c\n }\n\n private fetch(c: string): string {\n if (!this.shapes.has(c)) this.shapes.set(c, this.match(c))\n return this.shapes.get(c)!\n }\n\n shape(t: string) {\n if (!this.shapes.has(t)) this.shapes.set(t, Array.from(t).map(c => this.fetch(c)).join(''))\n return this.shapes.get(t)!\n }\n}()\n\nexport type Token = {\n type: 'word' | 'domain' | 'whitespace'\n subtype: '' | 'preposition' | 'acronym' | 'whitespace' | 'word' | 'domain'\n text: string\n start: number\n end: number\n shape: string\n sentenceStart: boolean\n subSentenceStart: boolean\n hyphenated?: Token[]\n}\n\nfunction combine(tokens: Token[]) {\n const combined = { ...tokens[0] }\n for (const t of tokens.slice(1)) {\n combined.text += t.text\n combined.end = t.end\n combined.shape += t.shape\n }\n return combined\n}\n\nfunction hyphenate(t: Token) {\n if (t.type === 'word') return 'w'\n if (t.text === '-' || t.text === '\u2013') return '-'\n return ' '\n}\n\nexport function tokenize(title: string, markup?: RegExp): Token[] {\n if (markup) title = title.replace(markup, match => '\\u2060'.repeat(match.length))\n\n lexer.reset(title)\n const tokens: Token[] = []\n let sentenceStart = true\n let subSentenceStart = false\n for (const token of lexer) {\n const [type, subtype] = (token.type.includes('-') ? token.type : `${token.type}-`).split('-')\n\n tokens.push({\n type,\n subtype,\n text: token.text,\n start: token.offset,\n end: token.offset + token.text.length - 1,\n shape: Shape.shape(<string> token.text)!,\n sentenceStart: type === 'word' && sentenceStart,\n subSentenceStart: type === 'word' && subSentenceStart,\n })\n\n switch (token.type) {\n case 'punctuation-end':\n sentenceStart = true\n break\n case 'punctuation-colon':\n subSentenceStart = true\n break\n default:\n if (type.match(/word|number|handle|domain|website/)) {\n sentenceStart = false\n subSentenceStart = false\n }\n break\n }\n }\n\n const stack = tokens.splice(0)\n\n let cpt: RegExpMatchArray | null\n let cps: RegExpMatchArray | null\n while (stack.length) {\n if (stack[0].subtype === 'preposition' && (cpt = stack[0].text.match(RE.ComplexPreposition)) && (cps = stack[0].shape.match(RE.ComplexPreposition))) {\n const complex = stack.shift()!\n\n let start = complex.start\n let end\n for (const i of Array.from({ length: 5 }, (_, n) => n + 1)) {\n if (!cpt[i]) break\n\n end = start + cps[i].length - 1\n tokens.push({\n ...complex,\n text: cpt[i],\n shape: cps[i],\n start,\n end,\n type: i % 2 ? complex.type : 'whitespace',\n subtype: i % 2 ? complex.type : '',\n })\n start = end + 1\n }\n continue\n }\n\n // hyphenated words\n const pat = stack.map(t => hyphenate(t)).join('')\n if (cpt = pat.match(/^w(-w)+(?= |$)/)) {\n const hyphenated = stack.splice(0, cpt[0].length)\n tokens.push({ ...combine(hyphenated), hyphenated })\n continue\n }\n\n tokens.push(stack.shift()!)\n }\n\n return markup ? tokens.map(token => ({ ...token, text: title.substring(token.start, token.end + 1) })) : tokens\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
@@ -0,0 +1,90 @@
1
+ export type Entry = {
2
+ /** Source text for this entry. */
3
+ input: string;
4
+ /** Entry type from the source. */
5
+ type: string;
6
+ /** Citation key identifying this entry. */
7
+ key: string;
8
+ /** Field names mapped to BibTeX text values; LaTeX and creator names are not normalized. */
9
+ fields: Record<string, string>;
10
+ };
11
+ export interface ParseError {
12
+ /** Human-readable diagnostic, with a source location when one is available. */
13
+ error: string;
14
+ /** Source fragment associated with the diagnostic; may be empty when no fragment is available. */
15
+ input?: string;
16
+ }
17
+ export interface ParserOptions {
18
+ /**
19
+ * Stop after this many entries have been parsed; useful for quickly checking whether text is BibTeX.
20
+ */
21
+ max_entries?: number;
22
+ /**
23
+ * Preload `@string` definitions as a name-to-value map or BibTeX source text.
24
+ */
25
+ strings?: string | Record<string, string>;
26
+ }
27
+ /** Parser state and verbatim result for one BibTeX source string. */
28
+ export declare class Library {
29
+ /** The directive currently being parsed, or `null` when no directive is active. */
30
+ parsing: string | null;
31
+ /** Successfully parsed entries, in source order. */
32
+ entries: Entry[];
33
+ /** Resolved values declared by `@string` directives. */
34
+ strings: Record<string, string>;
35
+ /** Text from `@comment` directives. */
36
+ comments: string[];
37
+ /** Diagnostics collected while parsing. */
38
+ errors: ParseError[];
39
+ /** Values from `@preamble` directives, in source order. */
40
+ preambles: string[];
41
+ private default_strings;
42
+ private pos;
43
+ private linebreaks;
44
+ private input;
45
+ private max_entries;
46
+ /** Creates a parser for `input`; call {@link parse} or {@link parseAsync} to populate the result fields. */
47
+ constructor(input: string, options?: ParserOptions);
48
+ /** Parses the complete source synchronously and populates this library's result fields. */
49
+ parse(): void;
50
+ /** Asynchronously parses the complete source, yielding during large inputs. */
51
+ parseAsync(): Promise<void>;
52
+ private isWhitespace;
53
+ private match;
54
+ private tryMatch;
55
+ private skipWhitespace;
56
+ private value_braces;
57
+ private value_quotes;
58
+ private single_value;
59
+ location(): string;
60
+ error(err: ParseError): void;
61
+ private value;
62
+ private key;
63
+ private key_equals_value;
64
+ private entry;
65
+ private directive;
66
+ private string;
67
+ private preamble;
68
+ private comment;
69
+ private hasMore;
70
+ private bibtex;
71
+ private bibtexAsync;
72
+ private matchGuard;
73
+ private parseNext;
74
+ }
75
+ /**
76
+ * Parses BibTeX without interpreting field contents as LaTeX, making the result useful for detection or later reparsing.
77
+ * `@string` references are resolved, but entry fields remain strings and retain their TeX source.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * const library = parse('@article{example, title={A \\LaTeX title}}')
82
+ * const rawTitle = library.entries[0]?.fields.title
83
+ * ```
84
+ */
85
+ export declare function parse(input: string, options?: ParserOptions): Library;
86
+ export declare const promises: {
87
+ /** Asynchronous variant of {@link parse}; resolves after parsing and populating the library. */
88
+ parse(input: string, options?: ParserOptions): Promise<Library>;
89
+ };
90
+ //# sourceMappingURL=verbatim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verbatim.d.ts","sourceRoot":"","sources":["../../js/verbatim.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,KAAK,GAAG;IAClB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAA;IACX,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,kGAAkG;IAClG,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C;AAED,qEAAqE;AACrE,qBAAa,OAAO;IAClB,mFAAmF;IAC5E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAE7B,oDAAoD;IAC7C,OAAO,EAAE,KAAK,EAAE,CAAK;IAC5B,wDAAwD;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAK;IAC3C,uCAAuC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAK;IAC9B,2CAA2C;IACpC,MAAM,EAAE,UAAU,EAAE,CAAK;IAChC,2DAA2D;IACpD,SAAS,EAAE,MAAM,EAAE,CAAK;IAE/B,OAAO,CAAC,eAAe,CA4CtB;IAED,OAAO,CAAC,GAAG,CAAI;IACf,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,KAAK,CAAQ;IAErB,OAAO,CAAC,WAAW,CAAQ;IAE3B,4GAA4G;gBAChG,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB;IAqBtD,2FAA2F;IACpF,KAAK,IAAI,IAAI;IAKpB,+EAA+E;IAClE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxC,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,YAAY;IAwCpB,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,YAAY;IAuBpB,QAAQ,IAAI,MAAM;IAKlB,KAAK,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAK5B,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,GAAG;IAgBX,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,KAAK;IAkCb,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,MAAM;YAMA,WAAW;IAQzB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,SAAS;CAoDlB;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAIzE;AAED,eAAO,MAAM,QAAQ;IACnB,gGAAgG;iBAC7E,MAAM,YAAW,aAAa,GAAQ,OAAO,CAAC,OAAO,CAAC;CAK1E,CAAA"}
@@ -13,12 +13,19 @@ class ParsingError extends Error {
13
13
  }
14
14
  const letter = /[\p{Lu}\p{Lt}\p{Ll}\p{Lm}\p{Lo}]/u;
15
15
  class Library {
16
+ /** Creates a parser for `input`; call {@link parse} or {@link parseAsync} to populate the result fields. */
16
17
  constructor(input, options = {}) {
18
+ /** The directive currently being parsed, or `null` when no directive is active. */
17
19
  __publicField(this, "parsing");
20
+ /** Successfully parsed entries, in source order. */
18
21
  __publicField(this, "entries", []);
22
+ /** Resolved values declared by `@string` directives. */
19
23
  __publicField(this, "strings", {});
24
+ /** Text from `@comment` directives. */
20
25
  __publicField(this, "comments", []);
26
+ /** Diagnostics collected while parsing. */
21
27
  __publicField(this, "errors", []);
28
+ /** Values from `@preamble` directives, in source order. */
22
29
  __publicField(this, "preambles", []);
23
30
  __publicField(this, "default_strings", {
24
31
  JAN: "01",
@@ -85,10 +92,12 @@ class Library {
85
92
  pos = input.indexOf("\n", pos + 1);
86
93
  }
87
94
  }
95
+ /** Parses the complete source synchronously and populates this library's result fields. */
88
96
  parse() {
89
97
  this.bibtex();
90
98
  this.entries.reverse();
91
99
  }
100
+ /** Asynchronously parses the complete source, yielding during large inputs. */
92
101
  async parseAsync() {
93
102
  await this.bibtexAsync();
94
103
  this.entries.reverse();
@@ -373,6 +382,7 @@ function parse(input, options = {}) {
373
382
  return parser;
374
383
  }
375
384
  const promises = {
385
+ /** Asynchronous variant of {@link parse}; resolves after parsing and populating the library. */
376
386
  async parse(input, options = {}) {
377
387
  const parser = new Library(input, options);
378
388
  await parser.parseAsync();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../verbatim.ts"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-argument */\n// Original work by Henrik Muehe (c) 2010\n//\n// CommonJS port by Mikola Lysenko 2013\n//\n// error recovery and unicode handling by Emiliano Heyns (c) 2017-2024\n\nimport { playnice } from './yield.js'\n\nclass ParsingError extends Error {\n public source!: string\n\n constructor(message, parser) {\n message += ` at ${parser.location()}`\n if (parser.parsing) message += ` in ${JSON.stringify(parser.parsing)}`\n super(message)\n this.name = 'ParsingError'\n }\n}\n\nconst letter = /[\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lm}\\p{Lo}]/u\n\nexport type Entry = {\n input: string\n type: string\n key: string\n fields: Record<string, string>\n}\n\nexport interface ParseError {\n error: string\n input?: string\n}\n\nexport interface ParserOptions {\n /**\n * stop parsing after `max_entries` entries have been found. Useful for quick detection if a text file is in fact a bibtex file\n */\n max_entries?: number\n\n /**\n * preload these strings\n */\n strings?: string | Record<string, string>\n}\n\nexport class Library {\n public parsing: string | null\n\n public entries: Entry[] = []\n public strings: Record<string, string> = {}\n public comments: string[] = []\n public errors: ParseError[] = []\n public preambles: string[] = []\n\n private default_strings: Record<string, string> = {\n JAN: '01',\n JANUARY: '01',\n FEB: '02',\n FEBRUARY: '02',\n MAR: '03',\n MARCH: '03',\n APR: '04',\n APRIL: '04',\n MAY: '05',\n JUN: '06',\n JUNE: '06',\n JUL: '07',\n JULY: '07',\n AUG: '08',\n AUGUST: '08',\n SEP: '09',\n SEPTEMBER: '09',\n OCT: '10',\n OCTOBER: '10',\n NOV: '11',\n NOVEMBER: '11',\n DEC: '12',\n DECEMBER: '12',\n ACMCS: 'ACM Computing Surveys',\n ACTA: 'Acta Informatica',\n CACM: 'Communications of the ACM',\n IBMJRD: 'IBM Journal of Research and Development',\n IBMSJ: 'IBM Systems Journal',\n IEEESE: 'IEEE Transactions on Software Engineering',\n IEEETC: 'IEEE Transactions on Computers',\n IEEETCAD: 'IEEE Transactions on Computer-Aided Design of Integrated Circuits',\n IPL: 'Information Processing Letters',\n JACM: 'Journal of the ACM',\n JCSS: 'Journal of Computer and System Sciences',\n SCP: 'Science of Computer Programming',\n SICOMP: 'SIAM Journal on Computing',\n TOCS: 'ACM Transactions on Computer Systems',\n TODS: 'ACM Transactions on Database Systems',\n TOG: 'ACM Transactions on Graphics',\n TOMS: 'ACM Transactions on Mathematical Software',\n TOOIS: 'ACM Transactions on Office Information Systems',\n TOPLAS: 'ACM Transactions on Programming Languages and Systems',\n TCS: 'Theoretical Computer Science',\n }\n\n private pos = 0\n private linebreaks: number[] = []\n private input: string\n\n private max_entries: number\n\n constructor(input: string, options: ParserOptions = {}) {\n this.max_entries = options.max_entries || 0\n this.input = input\n this.parsing = null\n\n if (typeof options.strings === 'string') {\n this.input = options.strings + '\\n' + input // eslint-disable-line prefer-template\n }\n else if (options.strings) {\n for (const [k, v] of Object.entries(options.strings)) {\n this.default_strings[k.toUpperCase()] = v\n }\n }\n\n let pos = input.indexOf('\\n')\n while (pos !== -1) {\n this.linebreaks.push(pos)\n pos = input.indexOf('\\n', pos + 1)\n }\n }\n\n public parse(): void {\n this.bibtex()\n this.entries.reverse()\n }\n\n public async parseAsync(): Promise<void> {\n await this.bibtexAsync()\n this.entries.reverse()\n }\n\n private isWhitespace(s, horizontalOnly = false) {\n return (s === ' ' || s === '\\t' || (!horizontalOnly && (s === '\\r' || s === '\\n')))\n }\n\n private match(s, sws = true) {\n this.skipWhitespace()\n if (this.input.substr(this.pos, s.length) !== s) {\n throw new ParsingError(`Token mismatch, expected ${JSON.stringify(s)}, found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n this.pos += s.length\n if (sws) this.skipWhitespace()\n }\n\n private tryMatch(s, consume = false) {\n this.skipWhitespace()\n const match = this.input.substr(this.pos, s.length) === s\n if (match && consume) this.pos += s.length\n return match\n // this.skipWhitespace()\n }\n\n private skipWhitespace() {\n while (this.pos < this.input.length && this.input[this.pos].match(/[ \\t\\r\\n%]/)) {\n // test for '%' is shady\n if (this.input[this.pos] === '%') {\n while (this.pos < this.input.length && this.input[this.pos] !== '\\n') this.pos++\n }\n else {\n this.pos++\n }\n }\n }\n\n /*\n private fix_path() {\n // workaround for https://github.com/siefkenj/unified-latex/issues/94\n const path = '\\\\path|'\n const verb = path.replace('path', 'verb')\n if (this.input.substring(this.pos).startsWith(path)) {\n // eslint-disable-next-line prefer-template\n this.input = this.input.substring(0, this.pos) + verb + this.input.substring(this.pos + path.length)\n }\n }\n */\n\n private value_braces() {\n let bracecount = 0\n this.match('{', false)\n const start = this.pos\n let math = false\n\n while (true) {\n // this.fix_path()\n\n switch (this.input[this.pos]) {\n case '\\\\':\n this.pos += 1\n break\n\n case '{':\n bracecount++\n break\n\n case '}':\n if (bracecount === 0) {\n if (math) throw new ParsingError('Unclosed math section', this)\n this.pos++\n return this.input.substring(start, this.pos - 1)\n }\n bracecount--\n break\n\n case '$':\n math = !math\n break\n }\n\n this.pos++\n\n if (this.pos >= this.input.length) {\n throw new ParsingError(`Unterminated brace-value ${JSON.stringify(this.input.substr(start, 20))}`, this)\n }\n }\n }\n\n private value_quotes() {\n this.match('\"', false)\n const start = this.pos\n let bracecount = 0\n while (true) {\n // this.fix_path()\n\n switch (this.input[this.pos]) {\n case '\\\\':\n this.pos += 1\n break\n\n case '{':\n bracecount++\n break\n\n case '}':\n bracecount--\n break\n\n case '\"':\n if (bracecount <= 0) {\n this.pos++\n return this.input.substring(start, this.pos - 1)\n }\n }\n\n this.pos++\n\n if (this.pos >= this.input.length) {\n throw new ParsingError(`Unterminated quote-value ${JSON.stringify(this.input.substr(start, 20))}`, this)\n }\n }\n }\n\n private single_value() {\n if (this.tryMatch('{')) {\n return this.value_braces()\n }\n else if (this.tryMatch('\"')) {\n return this.value_quotes()\n }\n else {\n const bare = this.key()\n if (bare.match(/^\\d+$/)) return bare\n\n const u_bare = bare.toUpperCase()\n const resolved = this.strings[u_bare] || this.default_strings[u_bare]\n if (typeof resolved === 'undefined') {\n this.error({\n error: `unresolved @string reference ${JSON.stringify(bare)}`,\n input: '',\n })\n }\n return resolved || `{{${bare}}}`\n }\n }\n\n location(): string {\n const lines = this.input.substring(0, this.pos).split('\\n')\n return `line ${lines.length}, column ${lines[lines.length - 1].length + 1}`\n }\n\n error(err: ParseError): void {\n if (this.errors.find(e => e.error === err.error)) return\n this.errors.push(err)\n }\n\n private value() {\n const values: string[] = []\n values.push(this.single_value())\n while (this.tryMatch('#')) {\n this.match('#')\n values.push(this.single_value())\n }\n return values.join('')\n }\n\n private key(allow = ''): string {\n const start = this.pos\n while (true) {\n if (this.pos === this.input.length) {\n throw new ParsingError('Runaway key', this)\n }\n\n if (this.input[this.pos].match(/[+'a-zA-Z0-9&;_:\\\\./-]/) || this.input[this.pos].match(letter) || allow.includes(this.input[this.pos])) {\n this.pos++\n }\n else {\n return this.input.substring(start, this.pos)\n }\n }\n }\n\n private key_equals_value(key?: string) {\n key = key || this.key()\n if (!key) return // no key found, stray comma\n\n if (!this.tryMatch('=')) {\n throw new ParsingError(`${key} = value expected, equals sign missing: ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n this.match('=')\n const val = this.value()\n\n if (this.parsing === 'string') {\n this.strings[key.toUpperCase()] = val\n }\n else {\n const bare = key.toLowerCase()\n let postfix = 0\n while (typeof this.entries[0].fields[key = postfix ? `${bare}+duplicate-${postfix}` : bare] === 'string') {\n postfix++\n }\n this.entries[0].fields[key] = val\n }\n }\n\n private entry(d: string, guard: string) {\n if (this.tryMatch(guard)) return // empty entry\n\n let key: string\n // mendeley end endnote can output entries without key... sure...\n if (this.tryMatch(',')) {\n // mendeley absurdity which outputs keyless entries, but at least have the decency to put a comma there\n key = ''\n }\n else {\n // so maybe a real key?\n key = this.key('[]*\"')\n }\n\n if (this.tryMatch('=')) {\n // nope; endnote just skips the comma; we can infer we're in this moronic situation because we've been dumped into field-parsing\n this.key_equals_value(key)\n }\n else {\n // so we did find the real key earlier\n this.entries[0].key = key\n }\n this.tryMatch(',', true)\n if (this.tryMatch(guard)) return // no fields\n\n this.key_equals_value()\n while (this.tryMatch(',', true)) {\n // fixes problems with commas at the end of a list\n if (this.tryMatch(guard)) return\n\n this.key_equals_value()\n }\n }\n\n private directive() {\n this.match('@')\n return this.key().toLowerCase()\n }\n\n private string() { // eslint-disable-line id-blacklist\n this.key_equals_value()\n }\n\n private preamble() {\n this.preambles.push(this.value())\n }\n\n private comment() {\n while (this.isWhitespace(this.input[this.pos], true)) this.pos++\n\n if (this.input[this.pos] === '{') {\n this.comments.push(this.value_braces())\n }\n else {\n const start = this.pos\n while (this.input[this.pos] !== '\\n' && this.pos < this.input.length) this.pos++\n this.comments.push(this.input.substring(start, this.pos))\n }\n }\n\n private hasMore() {\n if (this.max_entries && this.entries.length >= this.max_entries) return false\n return (this.pos < this.input.length)\n }\n\n private bibtex() {\n while (this.hasMore()) {\n this.parseNext()\n }\n }\n\n private async bibtexAsync() {\n let n = 1\n while (this.hasMore()) {\n this.parseNext()\n if ((n++ % 1000) === 0) await playnice()\n }\n }\n\n private matchGuard() {\n for (const guard of ['{}', '()']) {\n if (this.tryMatch(guard[0])) {\n this.match(guard[0])\n return guard[1]\n }\n }\n\n throw new ParsingError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n private parseNext() {\n this.skipWhitespace()\n\n while (this.pos < this.input.length && this.input[this.pos] !== '@') {\n if (this.input[this.pos] === '%') {\n while (this.pos < this.input.length && this.input[this.pos] !== '\\n') this.pos++\n }\n else {\n this.pos++\n }\n }\n if (this.pos >= this.input.length) return\n\n let guard = ''\n const start = this.pos\n try {\n const d = this.parsing = this.directive()\n switch (d) {\n case 'string':\n guard = this.matchGuard()\n this.string()\n this.match(guard)\n break\n\n case 'preamble':\n this.preamble()\n break\n\n case 'comment':\n this.comment()\n break\n\n default:\n guard = this.matchGuard()\n this.entries.unshift({ input: '', type: d, key: '', fields: {} })\n this.entry(d, guard)\n this.match(guard)\n this.entries[0].input = this.input.substring(start, this.pos).trim()\n break\n }\n }\n catch (err) {\n if ((err as any).name !== 'ParsingError') throw err\n // skip ahead to the next @ and try again\n this.pos = start + 1\n while (this.pos < this.input.length && this.input[this.pos] !== '@') this.pos++\n this.error({\n error: err.message,\n input: this.input.substring(start, this.pos),\n })\n }\n }\n}\n\n/**\n * Parses a bibtex source in verbatim mode. Good for detection of bibtex and for later LaTeX-reparsing.\n */\nexport function parse(input: string, options: ParserOptions = {}): Library {\n const parser = new Library(input, options)\n parser.parse()\n return parser\n}\n\nexport const promises = {\n async parse(input: string, options: ParserOptions = {}): Promise<Library> {\n const parser = new Library(input, options)\n await parser.parseAsync()\n return parser\n },\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../js/verbatim.ts"],
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-argument */\n// Original work by Henrik Muehe (c) 2010\n//\n// CommonJS port by Mikola Lysenko 2013\n//\n// error recovery and unicode handling by Emiliano Heyns (c) 2017-2024\n\nimport { playnice } from './yield.js'\n\nclass ParsingError extends Error {\n public source!: string\n\n constructor(message, parser) {\n message += ` at ${parser.location()}`\n if (parser.parsing) message += ` in ${JSON.stringify(parser.parsing)}`\n super(message)\n this.name = 'ParsingError'\n }\n}\n\nconst letter = /[\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lm}\\p{Lo}]/u\n\nexport type Entry = {\n /** Source text for this entry. */\n input: string\n /** Entry type from the source. */\n type: string\n /** Citation key identifying this entry. */\n key: string\n /** Field names mapped to BibTeX text values; LaTeX and creator names are not normalized. */\n fields: Record<string, string>\n}\n\nexport interface ParseError {\n /** Human-readable diagnostic, with a source location when one is available. */\n error: string\n /** Source fragment associated with the diagnostic; may be empty when no fragment is available. */\n input?: string\n}\n\nexport interface ParserOptions {\n /**\n * Stop after this many entries have been parsed; useful for quickly checking whether text is BibTeX.\n */\n max_entries?: number\n\n /**\n * Preload `@string` definitions as a name-to-value map or BibTeX source text.\n */\n strings?: string | Record<string, string>\n}\n\n/** Parser state and verbatim result for one BibTeX source string. */\nexport class Library {\n /** The directive currently being parsed, or `null` when no directive is active. */\n public parsing: string | null\n\n /** Successfully parsed entries, in source order. */\n public entries: Entry[] = []\n /** Resolved values declared by `@string` directives. */\n public strings: Record<string, string> = {}\n /** Text from `@comment` directives. */\n public comments: string[] = []\n /** Diagnostics collected while parsing. */\n public errors: ParseError[] = []\n /** Values from `@preamble` directives, in source order. */\n public preambles: string[] = []\n\n private default_strings: Record<string, string> = {\n JAN: '01',\n JANUARY: '01',\n FEB: '02',\n FEBRUARY: '02',\n MAR: '03',\n MARCH: '03',\n APR: '04',\n APRIL: '04',\n MAY: '05',\n JUN: '06',\n JUNE: '06',\n JUL: '07',\n JULY: '07',\n AUG: '08',\n AUGUST: '08',\n SEP: '09',\n SEPTEMBER: '09',\n OCT: '10',\n OCTOBER: '10',\n NOV: '11',\n NOVEMBER: '11',\n DEC: '12',\n DECEMBER: '12',\n ACMCS: 'ACM Computing Surveys',\n ACTA: 'Acta Informatica',\n CACM: 'Communications of the ACM',\n IBMJRD: 'IBM Journal of Research and Development',\n IBMSJ: 'IBM Systems Journal',\n IEEESE: 'IEEE Transactions on Software Engineering',\n IEEETC: 'IEEE Transactions on Computers',\n IEEETCAD: 'IEEE Transactions on Computer-Aided Design of Integrated Circuits',\n IPL: 'Information Processing Letters',\n JACM: 'Journal of the ACM',\n JCSS: 'Journal of Computer and System Sciences',\n SCP: 'Science of Computer Programming',\n SICOMP: 'SIAM Journal on Computing',\n TOCS: 'ACM Transactions on Computer Systems',\n TODS: 'ACM Transactions on Database Systems',\n TOG: 'ACM Transactions on Graphics',\n TOMS: 'ACM Transactions on Mathematical Software',\n TOOIS: 'ACM Transactions on Office Information Systems',\n TOPLAS: 'ACM Transactions on Programming Languages and Systems',\n TCS: 'Theoretical Computer Science',\n }\n\n private pos = 0\n private linebreaks: number[] = []\n private input: string\n\n private max_entries: number\n\n /** Creates a parser for `input`; call {@link parse} or {@link parseAsync} to populate the result fields. */\n constructor(input: string, options: ParserOptions = {}) {\n this.max_entries = options.max_entries || 0\n this.input = input\n this.parsing = null\n\n if (typeof options.strings === 'string') {\n this.input = options.strings + '\\n' + input // eslint-disable-line prefer-template\n }\n else if (options.strings) {\n for (const [k, v] of Object.entries(options.strings)) {\n this.default_strings[k.toUpperCase()] = v\n }\n }\n\n let pos = input.indexOf('\\n')\n while (pos !== -1) {\n this.linebreaks.push(pos)\n pos = input.indexOf('\\n', pos + 1)\n }\n }\n\n /** Parses the complete source synchronously and populates this library's result fields. */\n public parse(): void {\n this.bibtex()\n this.entries.reverse()\n }\n\n /** Asynchronously parses the complete source, yielding during large inputs. */\n public async parseAsync(): Promise<void> {\n await this.bibtexAsync()\n this.entries.reverse()\n }\n\n private isWhitespace(s, horizontalOnly = false) {\n return (s === ' ' || s === '\\t' || (!horizontalOnly && (s === '\\r' || s === '\\n')))\n }\n\n private match(s, sws = true) {\n this.skipWhitespace()\n if (this.input.substr(this.pos, s.length) !== s) {\n throw new ParsingError(`Token mismatch, expected ${JSON.stringify(s)}, found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n this.pos += s.length\n if (sws) this.skipWhitespace()\n }\n\n private tryMatch(s, consume = false) {\n this.skipWhitespace()\n const match = this.input.substr(this.pos, s.length) === s\n if (match && consume) this.pos += s.length\n return match\n // this.skipWhitespace()\n }\n\n private skipWhitespace() {\n while (this.pos < this.input.length && this.input[this.pos].match(/[ \\t\\r\\n%]/)) {\n // test for '%' is shady\n if (this.input[this.pos] === '%') {\n while (this.pos < this.input.length && this.input[this.pos] !== '\\n') this.pos++\n }\n else {\n this.pos++\n }\n }\n }\n\n /*\n private fix_path() {\n // workaround for https://github.com/siefkenj/unified-latex/issues/94\n const path = '\\\\path|'\n const verb = path.replace('path', 'verb')\n if (this.input.substring(this.pos).startsWith(path)) {\n // eslint-disable-next-line prefer-template\n this.input = this.input.substring(0, this.pos) + verb + this.input.substring(this.pos + path.length)\n }\n }\n */\n\n private value_braces() {\n let bracecount = 0\n this.match('{', false)\n const start = this.pos\n let math = false\n\n while (true) {\n // this.fix_path()\n\n switch (this.input[this.pos]) {\n case '\\\\':\n this.pos += 1\n break\n\n case '{':\n bracecount++\n break\n\n case '}':\n if (bracecount === 0) {\n if (math) throw new ParsingError('Unclosed math section', this)\n this.pos++\n return this.input.substring(start, this.pos - 1)\n }\n bracecount--\n break\n\n case '$':\n math = !math\n break\n }\n\n this.pos++\n\n if (this.pos >= this.input.length) {\n throw new ParsingError(`Unterminated brace-value ${JSON.stringify(this.input.substr(start, 20))}`, this)\n }\n }\n }\n\n private value_quotes() {\n this.match('\"', false)\n const start = this.pos\n let bracecount = 0\n while (true) {\n // this.fix_path()\n\n switch (this.input[this.pos]) {\n case '\\\\':\n this.pos += 1\n break\n\n case '{':\n bracecount++\n break\n\n case '}':\n bracecount--\n break\n\n case '\"':\n if (bracecount <= 0) {\n this.pos++\n return this.input.substring(start, this.pos - 1)\n }\n }\n\n this.pos++\n\n if (this.pos >= this.input.length) {\n throw new ParsingError(`Unterminated quote-value ${JSON.stringify(this.input.substr(start, 20))}`, this)\n }\n }\n }\n\n private single_value() {\n if (this.tryMatch('{')) {\n return this.value_braces()\n }\n else if (this.tryMatch('\"')) {\n return this.value_quotes()\n }\n else {\n const bare = this.key()\n if (bare.match(/^\\d+$/)) return bare\n\n const u_bare = bare.toUpperCase()\n const resolved = this.strings[u_bare] || this.default_strings[u_bare]\n if (typeof resolved === 'undefined') {\n this.error({\n error: `unresolved @string reference ${JSON.stringify(bare)}`,\n input: '',\n })\n }\n return resolved || `{{${bare}}}`\n }\n }\n\n location(): string {\n const lines = this.input.substring(0, this.pos).split('\\n')\n return `line ${lines.length}, column ${lines[lines.length - 1].length + 1}`\n }\n\n error(err: ParseError): void {\n if (this.errors.find(e => e.error === err.error)) return\n this.errors.push(err)\n }\n\n private value() {\n const values: string[] = []\n values.push(this.single_value())\n while (this.tryMatch('#')) {\n this.match('#')\n values.push(this.single_value())\n }\n return values.join('')\n }\n\n private key(allow = ''): string {\n const start = this.pos\n while (true) {\n if (this.pos === this.input.length) {\n throw new ParsingError('Runaway key', this)\n }\n\n if (this.input[this.pos].match(/[+'a-zA-Z0-9&;_:\\\\./-]/) || this.input[this.pos].match(letter) || allow.includes(this.input[this.pos])) {\n this.pos++\n }\n else {\n return this.input.substring(start, this.pos)\n }\n }\n }\n\n private key_equals_value(key?: string) {\n key = key || this.key()\n if (!key) return // no key found, stray comma\n\n if (!this.tryMatch('=')) {\n throw new ParsingError(`${key} = value expected, equals sign missing: ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n this.match('=')\n const val = this.value()\n\n if (this.parsing === 'string') {\n this.strings[key.toUpperCase()] = val\n }\n else {\n const bare = key.toLowerCase()\n let postfix = 0\n while (typeof this.entries[0].fields[key = postfix ? `${bare}+duplicate-${postfix}` : bare] === 'string') {\n postfix++\n }\n this.entries[0].fields[key] = val\n }\n }\n\n private entry(d: string, guard: string) {\n if (this.tryMatch(guard)) return // empty entry\n\n let key: string\n // mendeley end endnote can output entries without key... sure...\n if (this.tryMatch(',')) {\n // mendeley absurdity which outputs keyless entries, but at least have the decency to put a comma there\n key = ''\n }\n else {\n // so maybe a real key?\n key = this.key('[]*\"')\n }\n\n if (this.tryMatch('=')) {\n // nope; endnote just skips the comma; we can infer we're in this moronic situation because we've been dumped into field-parsing\n this.key_equals_value(key)\n }\n else {\n // so we did find the real key earlier\n this.entries[0].key = key\n }\n this.tryMatch(',', true)\n if (this.tryMatch(guard)) return // no fields\n\n this.key_equals_value()\n while (this.tryMatch(',', true)) {\n // fixes problems with commas at the end of a list\n if (this.tryMatch(guard)) return\n\n this.key_equals_value()\n }\n }\n\n private directive() {\n this.match('@')\n return this.key().toLowerCase()\n }\n\n private string() { // eslint-disable-line id-blacklist\n this.key_equals_value()\n }\n\n private preamble() {\n this.preambles.push(this.value())\n }\n\n private comment() {\n while (this.isWhitespace(this.input[this.pos], true)) this.pos++\n\n if (this.input[this.pos] === '{') {\n this.comments.push(this.value_braces())\n }\n else {\n const start = this.pos\n while (this.input[this.pos] !== '\\n' && this.pos < this.input.length) this.pos++\n this.comments.push(this.input.substring(start, this.pos))\n }\n }\n\n private hasMore() {\n if (this.max_entries && this.entries.length >= this.max_entries) return false\n return (this.pos < this.input.length)\n }\n\n private bibtex() {\n while (this.hasMore()) {\n this.parseNext()\n }\n }\n\n private async bibtexAsync() {\n let n = 1\n while (this.hasMore()) {\n this.parseNext()\n if ((n++ % 1000) === 0) await playnice()\n }\n }\n\n private matchGuard() {\n for (const guard of ['{}', '()']) {\n if (this.tryMatch(guard[0])) {\n this.match(guard[0])\n return guard[1]\n }\n }\n\n throw new ParsingError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this)\n }\n\n private parseNext() {\n this.skipWhitespace()\n\n while (this.pos < this.input.length && this.input[this.pos] !== '@') {\n if (this.input[this.pos] === '%') {\n while (this.pos < this.input.length && this.input[this.pos] !== '\\n') this.pos++\n }\n else {\n this.pos++\n }\n }\n if (this.pos >= this.input.length) return\n\n let guard = ''\n const start = this.pos\n try {\n const d = this.parsing = this.directive()\n switch (d) {\n case 'string':\n guard = this.matchGuard()\n this.string()\n this.match(guard)\n break\n\n case 'preamble':\n this.preamble()\n break\n\n case 'comment':\n this.comment()\n break\n\n default:\n guard = this.matchGuard()\n this.entries.unshift({ input: '', type: d, key: '', fields: {} })\n this.entry(d, guard)\n this.match(guard)\n this.entries[0].input = this.input.substring(start, this.pos).trim()\n break\n }\n }\n catch (err) {\n if ((err as any).name !== 'ParsingError') throw err\n // skip ahead to the next @ and try again\n this.pos = start + 1\n while (this.pos < this.input.length && this.input[this.pos] !== '@') this.pos++\n this.error({\n error: err.message,\n input: this.input.substring(start, this.pos),\n })\n }\n }\n}\n\n/**\n * Parses BibTeX without interpreting field contents as LaTeX, making the result useful for detection or later reparsing.\n * `@string` references are resolved, but entry fields remain strings and retain their TeX source.\n *\n * @example\n * ```ts\n * const library = parse('@article{example, title={A \\\\LaTeX title}}')\n * const rawTitle = library.entries[0]?.fields.title\n * ```\n */\nexport function parse(input: string, options: ParserOptions = {}): Library {\n const parser = new Library(input, options)\n parser.parse()\n return parser\n}\n\nexport const promises = {\n /** Asynchronous variant of {@link parse}; resolves after parsing and populating the library. */\n async parse(input: string, options: ParserOptions = {}): Promise<Library> {\n const parser = new Library(input, options)\n await parser.parseAsync()\n return parser\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yield.d.ts","sourceRoot":"","sources":["../../js/yield.ts"],"names":[],"mappings":"AAAA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../yield.ts"],
3
+ "sources": ["../../js/yield.ts"],
4
4
  "sourcesContent": ["export async function playnice(): Promise<void> {\n await new Promise(resolve => setTimeout(resolve, 0))\n}\n"],
5
5
  "mappings": "AAAA,eAAsB,WAA0B;AAC9C,QAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,CAAC,CAAC;AACrD;",
6
6
  "names": []
@@ -6,6 +6,9 @@
6
6
  */
7
7
  export function parse(input: string, options?: any | null): any;
8
8
 
9
+ /**
10
+ * Parse and render BibTeX cooperatively, yielding between directive and entry batches.
11
+ */
9
12
  export function parseAsync(input: string, options?: any | null): Promise<any>;
10
13
 
11
14
  /**
@@ -13,19 +16,20 @@ export function parseAsync(input: string, options?: any | null): Promise<any>;
13
16
  */
14
17
  export function parseVerbatim(input: string, options?: any | null): any;
15
18
 
19
+ /**
20
+ * Parse raw BibTeX cooperatively, yielding between directive batches and returning a JS object.
21
+ */
16
22
  export function parseVerbatimAsync(input: string, options?: any | null): Promise<any>;
17
23
 
18
- export function start(): void;
19
-
20
24
  /**
21
- * Apply sentence casing with the standalone TypeScript API's defaulted option object.
25
+ * Install the browser-console panic hook once when the WASM module is initialized.
22
26
  */
23
- export function toSentenceCase(input: string, options?: any | null): string;
27
+ export function start(): void;
24
28
 
25
29
  /**
26
- * Tokenize Unicode title text using the same token record shape as TypeScript.
30
+ * Apply sentence casing using optional `guess`, `subSentenceCapitalization`, and `preserveQuoted` flags.
27
31
  */
28
- export function tokenize(input: string): any;
32
+ export function toSentenceCase(input: string, options?: any | null): string;
29
33
 
30
34
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
31
35
 
@@ -37,9 +41,8 @@ export interface InitOutput {
37
41
  readonly parseVerbatimAsync: (a: number, b: number, c: number) => any;
38
42
  readonly start: () => void;
39
43
  readonly toSentenceCase: (a: number, b: number, c: number) => [number, number, number, number];
40
- readonly tokenize: (a: number, b: number) => [number, number, number];
41
- readonly wasm_bindgen_27e16af4bbf8dd6f___convert__closures_____invoke___js_sys_8149d826ad841366___Function_fn_wasm_bindgen_27e16af4bbf8dd6f___JsValue_____wasm_bindgen_27e16af4bbf8dd6f___sys__Undefined___js_sys_8149d826ad841366___Function_fn_wasm_bindgen_27e16af4bbf8dd6f___JsValue_____wasm_bindgen_27e16af4bbf8dd6f___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
42
- readonly wasm_bindgen_27e16af4bbf8dd6f___convert__closures_____invoke___wasm_bindgen_27e16af4bbf8dd6f___JsValue__core_37ee0ce0c4cd57d5___result__Result_____wasm_bindgen_27e16af4bbf8dd6f___JsError___true_: (a: number, b: number, c: any) => [number, number];
44
+ readonly wasm_bindgen_7c27bb0bb2b93bf___convert__closures_____invoke___js_sys_4854f420e42f68ae___Function_fn_wasm_bindgen_7c27bb0bb2b93bf___JsValue_____wasm_bindgen_7c27bb0bb2b93bf___sys__Undefined___js_sys_4854f420e42f68ae___Function_fn_wasm_bindgen_7c27bb0bb2b93bf___JsValue_____wasm_bindgen_7c27bb0bb2b93bf___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
45
+ readonly wasm_bindgen_7c27bb0bb2b93bf___convert__closures_____invoke___wasm_bindgen_7c27bb0bb2b93bf___JsValue__core_37ee0ce0c4cd57d5___result__Result_____wasm_bindgen_7c27bb0bb2b93bf___JsError___true_: (a: number, b: number, c: any) => [number, number];
43
46
  readonly __wbindgen_malloc: (a: number, b: number) => number;
44
47
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
45
48
  readonly __wbindgen_exn_store: (a: number) => void;