@retorquere/bibtex-parser 8.0.6 → 8.0.7

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.
package/index.d.ts CHANGED
@@ -3,6 +3,41 @@ import * as bibtex from './verbatim';
3
3
  import * as JabRef from './jabref';
4
4
  export { JabRefMetadata } from './jabref';
5
5
  export { toSentenceCase } from './sentence-case';
6
+ type CreatorFields = {
7
+ author?: Creator[];
8
+ bookauthor?: Creator[];
9
+ collaborator?: Creator[];
10
+ commentator?: Creator[];
11
+ director?: Creator[];
12
+ editor?: Creator[];
13
+ editora?: Creator[];
14
+ editorb?: Creator[];
15
+ editors?: Creator[];
16
+ holder?: Creator[];
17
+ scriptwriter?: Creator[];
18
+ translator?: Creator[];
19
+ };
20
+ type ArrayFields = {
21
+ keywords?: string[];
22
+ institution?: string[];
23
+ publisher?: string[];
24
+ origpublisher?: string[];
25
+ organization?: string[];
26
+ location?: string[];
27
+ origlocation?: string[];
28
+ };
29
+ type NumberFields = {
30
+ year?: number | string;
31
+ month?: number | string;
32
+ };
33
+ type TypedFields = CreatorFields & ArrayFields & NumberFields;
34
+ type Fields = TypedFields & Omit<Record<string, string>, keyof TypedFields>;
35
+ export type Entry = {
36
+ type: string;
37
+ key: string;
38
+ fields: Fields;
39
+ input: string;
40
+ };
6
41
  export interface Bibliography {
7
42
  /**
8
43
  * errors found while parsing
@@ -135,38 +170,11 @@ export interface Creator {
135
170
  useprefix?: boolean;
136
171
  juniorcomma?: boolean;
137
172
  }
138
- export interface Entry {
139
- type: string;
140
- key: string;
141
- fields: {
142
- author?: Creator[];
143
- bookauthor?: Creator[];
144
- collaborator?: Creator[];
145
- commentator?: Creator[];
146
- director?: Creator[];
147
- editor?: Creator[];
148
- editora?: Creator[];
149
- editorb?: Creator[];
150
- editors?: Creator[];
151
- holder?: Creator[];
152
- scriptwriter?: Creator[];
153
- translator?: Creator[];
154
- keywords?: string[];
155
- institution?: string[];
156
- publisher?: string[];
157
- origpublisher?: string[];
158
- organization?: string[];
159
- location?: string[];
160
- origlocation?: string[];
161
- [key: string]: number | string | string[] | Creator[];
162
- };
163
- }
164
173
  export declare const FieldMode: {
165
174
  creator: string[];
166
175
  title: string[];
167
176
  verbatim: (string | RegExp)[];
168
177
  richtext: string[];
169
- commalist: string[];
170
178
  literallist: string[];
171
179
  };
172
180
  type ParseMode = keyof typeof FieldMode | 'literal';
package/index.js CHANGED
@@ -76,6 +76,31 @@ for (const tag of ['i', 'b', 'sc', 'nc', 'ncx', 'br', 'p', 'li', 'code']) {
76
76
  close[tag] = `\x0E/${tag}\x0F`;
77
77
  }
78
78
  const collapsable = /\x0E\/([a-z]+)\x0F(\s*)\x0E\1\x0F/ig;
79
+ const Month = {
80
+ jan: 1,
81
+ january: 1,
82
+ feb: 2,
83
+ february: 2,
84
+ mar: 3,
85
+ march: 3,
86
+ apr: 4,
87
+ april: 4,
88
+ may: 5,
89
+ jun: 6,
90
+ june: 6,
91
+ jul: 7,
92
+ july: 7,
93
+ aug: 8,
94
+ august: 8,
95
+ sep: 9,
96
+ september: 9,
97
+ oct: 10,
98
+ october: 10,
99
+ nov: 11,
100
+ november: 11,
101
+ dec: 12,
102
+ december: 12,
103
+ };
79
104
  exports.FieldMode = {
80
105
  creator: [
81
106
  'author',
@@ -114,6 +139,7 @@ exports.FieldMode = {
114
139
  'verba',
115
140
  'verbb',
116
141
  'verbc',
142
+ /^keywords([+]duplicate-\d+)?$/,
117
143
  /^citeulike-linkout-[0-9]+$/,
118
144
  /^bdsk-url-[0-9]+$/,
119
145
  ],
@@ -125,9 +151,6 @@ exports.FieldMode = {
125
151
  'notes',
126
152
  'note',
127
153
  ],
128
- commalist: [
129
- 'keywords',
130
- ],
131
154
  literallist: [
132
155
  'institution',
133
156
  'publisher',
@@ -250,6 +273,7 @@ function asyncGenerator(array) {
250
273
  class BibTeXParser {
251
274
  constructor() {
252
275
  this.newcommands = {};
276
+ this.unhandled = new Set;
253
277
  }
254
278
  split(ast, sep, split) {
255
279
  const roots = [];
@@ -412,19 +436,30 @@ class BibTeXParser {
412
436
  }
413
437
  unsupported(node) {
414
438
  var _a;
439
+ const tex = (0, unified_latex_util_print_raw_1.printRaw)(node);
415
440
  if (this.fallback)
416
- return (_a = this.fallback(node, (0, unified_latex_util_print_raw_1.printRaw)(node), this.current)) !== null && _a !== void 0 ? _a : '';
441
+ return (_a = this.fallback(node, tex, this.current)) !== null && _a !== void 0 ? _a : '';
442
+ let id;
417
443
  switch (node.type) {
418
444
  case 'macro':
419
- this.bib.errors.push({ error: `unhandled ${node.type} ${node.content} (${(0, unified_latex_util_print_raw_1.printRaw)(node)})`, input: (0, unified_latex_util_print_raw_1.printRaw)(node) });
445
+ id = `${node.type}.${node.content}`;
446
+ if (!this.unhandled.has(id)) {
447
+ this.unhandled.add(id);
448
+ this.bib.errors.push({ error: `unhandled ${node.type} ${(0, unified_latex_util_print_raw_1.printRaw)(node)}`, input: (0, unified_latex_util_print_raw_1.printRaw)(node) });
449
+ }
420
450
  break;
421
451
  case 'environment':
422
- this.bib.errors.push({ error: `unhandled ${node.type} ${node.env} (${(0, unified_latex_util_print_raw_1.printRaw)(node)})`, input: (0, unified_latex_util_print_raw_1.printRaw)(node) });
452
+ id = `${node.type}.${node.env}`;
453
+ if (!this.unhandled.has(id)) {
454
+ this.unhandled.add(id);
455
+ this.bib.errors.push({ error: `unhandled ${node.type} ${node.env} (${(0, unified_latex_util_print_raw_1.printRaw)(node)})`, input: (0, unified_latex_util_print_raw_1.printRaw)(node) });
456
+ }
423
457
  break;
424
458
  default:
425
459
  this.bib.errors.push({ error: `unhandled ${node.type} (${(0, unified_latex_util_print_raw_1.printRaw)(node)})`, input: (0, unified_latex_util_print_raw_1.printRaw)(node) });
426
460
  break;
427
461
  }
462
+ return tex;
428
463
  }
429
464
  wrap(text, tag, wrap = true) {
430
465
  if (!text || !wrap)
@@ -878,15 +913,16 @@ class BibTeXParser {
878
913
  });
879
914
  switch (mode) {
880
915
  case 'creator':
881
- entry.fields[field] = this.split(ast, /^and$/i, /(^& | & | &$)/).map(cr => this.parseCreator(cr)).filter(cr => Object.keys(cr).length);
882
- break;
883
- case 'commalist':
884
- entry.fields[field] = this.split(ast, /^[;,]$/, /(&)/).map(elt => this.stringify(elt, { mode: 'literal' }).trim());
916
+ entry.fields[field] = this.split(ast, /^and$/i, /(^& | & | &$)/)
917
+ .map(cr => this.parseCreator(cr))
918
+ .filter(cr => Object.keys(cr).length); // pacify typescript
885
919
  break;
886
920
  case 'literallist':
887
- entry.fields[field] = this.split(ast, /^and$/i, /(^& | & | &$)/).map(elt => this.stringify(elt, { mode: 'literal' }).trim());
921
+ entry.fields[field] = this.split(ast, /^and$/i, /(^& | & | &$)/)
922
+ .map(elt => this.stringify(elt, { mode: 'literal' }).trim()); // pacify typescript
888
923
  break;
889
924
  default:
925
+ // can be number or string, but typescript cannot know this
890
926
  entry.fields[field] = this.stringField(field, this.stringify(ast, { mode }), sentenceCase, mode);
891
927
  break;
892
928
  }
@@ -937,31 +973,41 @@ class BibTeXParser {
937
973
  jabref: null,
938
974
  };
939
975
  }
940
- reparse(entry) {
941
- let langid = (entry.fields.langid || entry.fields.hyphenation || '').toLowerCase();
942
- if (!langid && this.options.sentenceCase.language && entry.fields.language)
943
- langid = entry.fields.language.toLowerCase();
976
+ reparse(verbatim) {
977
+ let langid = (verbatim.fields.langid || verbatim.fields.hyphenation || '').toLowerCase();
978
+ if (!langid && this.options.sentenceCase.language && verbatim.fields.language)
979
+ langid = verbatim.fields.language.toLowerCase();
944
980
  const sentenceCase = this.options.sentenceCase.langids.includes(langid);
945
- this.current = entry;
981
+ const entry = this.current = {
982
+ type: verbatim.type,
983
+ key: verbatim.key,
984
+ fields: {},
985
+ input: verbatim.input,
986
+ };
946
987
  let keywords = []; // OMG #783
947
988
  try {
948
- for (const [field, value] of Object.entries(entry.fields)) {
949
- if (!value.trim()) {
950
- delete entry.fields[field];
989
+ for (let [field, value] of Object.entries(verbatim.fields)) {
990
+ if (!value.trim())
951
991
  continue;
952
- }
953
- if (this.options.raw)
992
+ if (this.options.raw) {
993
+ entry.fields[field] = value.trim();
954
994
  continue;
995
+ }
996
+ if (field.match(/^keywords([+]duplicate-\d+)?$/))
997
+ field = 'keywords'; // #873
955
998
  this.field(entry, field, value, sentenceCase);
956
- if (field.match(/^keywords\[\d+\]$/)) {
957
- keywords = [...keywords, entry.fields[field]];
958
- delete entry.fields[field];
999
+ if (field === 'keywords') { // #873
1000
+ keywords = [...keywords, ...entry.fields.keywords.split(/\s*[,;]\s*/)].map((k) => k.trim()).filter(k => k);
1001
+ delete entry.fields.keywords;
959
1002
  }
960
- if (typeof entry.fields[field] === 'string')
1003
+ if (typeof entry.fields[field] === 'string') {
961
1004
  entry.fields[field] = entry.fields[field].trim();
1005
+ if (field === 'month')
1006
+ entry.fields[field] = Month[entry.fields[field].toLowerCase()] || entry.fields[field];
1007
+ }
962
1008
  }
963
1009
  if (keywords.length)
964
- entry.fields.keywords = [...(new Set([...(entry.fields.keywords || []), ...keywords]))].sort();
1010
+ entry.fields.keywords = [...(new Set(keywords))].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
965
1011
  this.bib.entries.push(entry);
966
1012
  }
967
1013
  catch (err) {
@@ -969,7 +1015,7 @@ class BibTeXParser {
969
1015
  }
970
1016
  }
971
1017
  content(tex) {
972
- const entry = { key: '', type: '', fields: {} };
1018
+ const entry = { key: '', type: '', fields: {}, input: tex };
973
1019
  this.field(entry, 'tex', tex, false);
974
1020
  return entry.fields.tex;
975
1021
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retorquere/bibtex-parser",
3
- "version": "8.0.6",
3
+ "version": "8.0.7",
4
4
  "description": "BibTeX parser",
5
5
  "main": "index.js",
6
6
  "typings": "./index.d.ts",
package/verbatim.d.ts CHANGED
@@ -34,7 +34,6 @@ declare class Parser {
34
34
  private linebreaks;
35
35
  private input;
36
36
  private max_entries;
37
- private keywords;
38
37
  constructor(input: string, options?: ParserOptions);
39
38
  parse(): void;
40
39
  parseAsync(): Promise<void>;
package/verbatim.js CHANGED
@@ -85,7 +85,6 @@ class Parser {
85
85
  this.max_entries = options.max_entries || 0;
86
86
  this.input = input;
87
87
  this.parsing = null;
88
- this.keywords = 0;
89
88
  if (typeof options.strings === 'string') {
90
89
  this.input = options.strings + '\n' + input; // eslint-disable-line prefer-template
91
90
  }
@@ -271,10 +270,10 @@ class Parser {
271
270
  this.strings[key.toUpperCase()] = val;
272
271
  }
273
272
  else {
274
- key = key.toLowerCase();
275
- if (key.toLowerCase() === 'keywords' && this.entries[0].fields.keywords) { // #873... the things people do with bibtex
276
- this.keywords++;
277
- key = `keywords[${this.keywords}]`;
273
+ const bare = key.toLowerCase();
274
+ let postfix = 0;
275
+ while (typeof this.entries[0].fields[key = (postfix ? `${bare}+duplicate-${postfix}` : bare)] === 'string') {
276
+ postfix++;
278
277
  }
279
278
  this.entries[0].fields[key] = val;
280
279
  }