@retorquere/bibtex-parser 6.4.18 → 6.4.19

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.js CHANGED
@@ -1357,7 +1357,7 @@ class Parser {
1357
1357
  convertToSentenceCase(text) {
1358
1358
  if (!this.field.preserveRanges)
1359
1359
  return text;
1360
- const sentenceCased = (0, sentence_case_2.restore)((0, sentence_case_2.toSentenceCase)(text, true), text, this.field.preserveRanges);
1360
+ const sentenceCased = (0, sentence_case_2.restore)((0, sentence_case_2.toSentenceCase)(text), text, this.field.preserveRanges);
1361
1361
  if (text !== sentenceCased)
1362
1362
  this.entry.sentenceCased = true;
1363
1363
  return sentenceCased;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retorquere/bibtex-parser",
3
- "version": "6.4.18",
3
+ "version": "6.4.19",
4
4
  "description": "BibTeX parser",
5
5
  "main": "index.js",
6
6
  "typings": "./index.d.ts",
@@ -8,7 +8,8 @@
8
8
  "lint": "eslint . --ext .ts --cache --cache-location .eslintcache/",
9
9
  "prestart": "npm run lint && peggy grammar.peggy",
10
10
  "start": "tsc -d",
11
- "poststart": "npm run docs",
11
+ "tsc": "tsc -d",
12
+ "poststart": "echo npm run docs",
12
13
  "pretest": "npm start",
13
14
  "test": "tap test/tap --no-timeout --no-coverage",
14
15
  "snap": "npm test --snapshot",
@@ -4,5 +4,5 @@ export type TextRange = {
4
4
  description?: string;
5
5
  };
6
6
  export declare function restore(text: string, orig: string, preserve: TextRange[]): string;
7
- export declare function toSentenceCase(text: string, ignoreHTML?: boolean): string;
7
+ export declare function toSentenceCase(text: string): string;
8
8
  //# sourceMappingURL=sentence-case.d.ts.map
package/sentence-case.js CHANGED
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.toSentenceCase = exports.restore = void 0;
7
4
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
8
- const xregexp_1 = __importDefault(require("xregexp"));
5
+ const categories = require("xregexp/tools/output/categories");
6
+ const cat = name => categories.find(c => c.name === name).bmp;
7
+ const L = cat('L');
8
+ const Lu = cat('Lu');
9
+ const Ll = cat('Ll');
10
+ const N = cat('N');
11
+ const No = cat('No');
12
+ const Pc = cat('Pc');
9
13
  function restore(text, orig, preserve) {
10
14
  for (const { start, end } of preserve) {
11
15
  text = text.substring(0, start) + orig.substring(start, end) + text.substring(end);
@@ -13,84 +17,73 @@ function restore(text, orig, preserve) {
13
17
  return text;
14
18
  }
15
19
  exports.restore = restore;
16
- class SentenceCaser {
17
- constructor() {
18
- this.acronym = (0, xregexp_1.default)('^(\\p{Lu}[.])+(?=$|[\\P{L}])');
19
- this.quoted = (0, xregexp_1.default)('^"[^"]+"(?=$|[\\P{L}])');
20
- this.innerCaps = (0, xregexp_1.default)('\\p{Ll}\\p{Lu}');
21
- this.allCaps = (0, xregexp_1.default)('^\\p{Lu}+$');
22
- this.aint = (0, xregexp_1.default)("^\\p{L}n't(?=$|[\\P{L}])"); // isn't
23
- this.word = (0, xregexp_1.default)('^\\p{L}+(-\\p{L}+)*'); // also match gallium-nitride as one word
24
- this.and = (0, xregexp_1.default)('^\\p{Lu}&\\p{Lu}(?=$|[\\P{L}])'); // Q&A
25
- }
26
- convert(text, ignoreHTML = false) {
27
- this.input = text;
28
- this.result = '';
29
- this.sentenceStart = true;
30
- const preserve = [];
31
- if (ignoreHTML) {
32
- this.input.replace(/[^<>]<[^>]+>/g, (match, i) => {
33
- preserve.push({ start: i + 1, end: i + match.length });
34
- // replace markup by the preceding char
35
- return match[0].repeat(match.length);
36
- });
37
- this.input = this.input.replace(/<[^>]+>[^<>]/g, (match, i) => {
38
- preserve.push({ start: i, end: i + match.length - 1 });
39
- // replace markup by the following char
40
- return match[match.length - 1].repeat(match.length);
41
- });
42
- }
43
- this.input = this.input.replace(/[;:]\s+A\s/g, match => match.toLowerCase());
44
- this.input = this.input.replace(/[–—]\s*A\s/g, match => match.toLowerCase());
45
- let m;
46
- while (this.input) {
47
- if (m = xregexp_1.default.exec(this.input, this.quoted)) { // "Hello There"
48
- this.add(m[0], undefined, true);
49
- }
50
- else if (m = xregexp_1.default.exec(this.input, this.acronym)) { // U.S.
51
- this.add(m[0], undefined, true);
52
- }
53
- else if (m = xregexp_1.default.exec(this.input, this.aint)) { // isn't
54
- this.add(m[0], undefined, false);
55
- }
56
- else if (m = xregexp_1.default.exec(this.input, this.word)) {
57
- this.add(m[0], '-', false);
58
- }
59
- else if (m = xregexp_1.default.exec(this.input, this.and)) {
60
- this.add(m[0], undefined, true);
61
- }
62
- else {
63
- this.add(this.input[0], undefined, false);
64
- }
65
- }
66
- return restore(this.result, text, preserve);
67
- }
68
- add(word, splitter, keep) {
69
- if (splitter) {
70
- word = word.split(splitter).map((part, i) => {
71
- if ((keep || this.sentenceStart) && i === 0)
72
- return part;
73
- if (xregexp_1.default.exec(part, this.innerCaps))
74
- return part;
75
- if (xregexp_1.default.exec(part, this.allCaps))
76
- return part;
77
- return part.toLowerCase();
78
- }).join(splitter);
79
- }
80
- else {
81
- if (!keep)
82
- word = word.toLowerCase();
83
- }
84
- this.result += word;
85
- this.input = this.input.substr(word.length);
86
- if (!word.match(/^\s+$/)) {
87
- this.sentenceStart = !!word.match(/^[.?!]$/) || (word.length === 2 && word[1] === '.'); // Vitamin A. Vitamin B.
88
- }
89
- }
20
+ const re = {
21
+ acronym: new RegExp(`([${Lu}][.])+$`),
22
+ innerCaps: new RegExp(`.[${Lu}]`),
23
+ ident: new RegExp(`^[$p{L}]+[${N}${No}][${L}${N}${No}]*$`),
24
+ allCaps: new RegExp(`^[${Lu}${N}${No}]+$`),
25
+ skipWords: /^(but|or|yet|so|for|and|nor|a|an|the|at|by|from|in|into|of|on|to|with|updown|as)$/i,
26
+ // chemElements: /^(H|He|Li|Be|B|C|N|O|F|Ne|Na|Mg|Al|Si|P|S|Cl|Ar|K|Ca|Sc|Ti|V|Cr|Mn|Fe|Co|Ni|Cu|Zn|Ga|Ge|As|Se|Br|Kr|Rb|Sr|Y|Zr|Nb|Mo|Tc|Ru|Rh|Pb|Ag|Cd|In|Sn|Sb|Te|I|Xe|Cs|Ba|La|Hf|Ta|W|Re|Os|Ir|Pt|Au|Hg|Tl|Pb|Bi|Po|At|Rn|Fr|Ra|Ac|Rf|Db|Sg|Bh|Hs|Mt|Ds|Rg|Cn|Nh|Fl|Mc|Lv|Ts|Og|La|Ce|Pr|Nd|Pm|Sm|Eu|Gd|Tb|Dy|Ho|Er|Tm|Yb|Lu|Ac|Th|Pa|U|Np|Pu|Am|Cm|Bk|Cf|Es|Fm|Md|No|Lr)$/,
27
+ words: new RegExp(`([\uFFFD${L}${N}${No}]+([\uFFFD${Pc}${L}${N}${No}]*))|(\\s([\uFFFD${Lu}]+[.]){2,})?`, 'g'),
28
+ titleCase: new RegExp(`^[${Lu}][${Ll}${N}${No}]+$`),
29
+ // private aint = XRegExp("^\\p{L}n't(?=$|[\\P{L}])") // isn't
30
+ // private word = XRegExp('^\\p{L}+([-.]\\p{L}+)*') // also match gallium-nitride as one word
31
+ // private and = XRegExp('^\\p{Lu}&\\p{Lu}(?=$|[\\P{L}])') // Q&A
32
+ };
33
+ function lowercase(word) {
34
+ if (!word)
35
+ return word;
36
+ const unmasked = word.replace(/\uFFFD/g, '');
37
+ if (unmasked.match(re.skipWords))
38
+ return word.toLowerCase();
39
+ if (unmasked.match(re.titleCase))
40
+ return word.toLowerCase();
41
+ // if (unmasked.match(re.chemElements)) return word
42
+ if (unmasked.length === 1)
43
+ return unmasked === 'A' ? word.toLowerCase() : word;
44
+ if (unmasked.match(re.innerCaps))
45
+ return word;
46
+ if (unmasked.match(re.ident) || unmasked.match(re.allCaps))
47
+ return word;
48
+ return word.toLowerCase();
90
49
  }
91
- const sentenceCaser = new SentenceCaser;
92
- function toSentenceCase(text, ignoreHTML = false) {
93
- return sentenceCaser.convert(text, ignoreHTML);
50
+ function toSentenceCase(text) {
51
+ const preserve = [];
52
+ text.replace(/“.*?”/g, (match, i) => {
53
+ preserve.push({ start: i, end: i + match.length, description: 'quoted' });
54
+ return '';
55
+ });
56
+ text.replace(/‘.*?’/g, (match, i) => {
57
+ preserve.push({ start: i, end: i + match.length, description: 'quoted' });
58
+ return '';
59
+ });
60
+ text.replace(/(["]).*?\1/g, (match, _quote, i) => {
61
+ preserve.push({ start: i, end: i + match.length, description: 'quoted' });
62
+ return '';
63
+ });
64
+ text.replace(/([.?!][\s]+)(<[^>]+>)?([A-Z])/g, (match, end, markup, char, i) => {
65
+ if (!text.substring(0, i + 1).match(re.acronym)) {
66
+ preserve.push({ start: i + end.length + ((markup === null || markup === void 0 ? void 0 : markup.length) || 0), end: i + end.length + ((markup === null || markup === void 0 ? void 0 : markup.length) || 0) + char.length, description: 'sub-sentence-start' });
67
+ }
68
+ return '';
69
+ });
70
+ text.replace(/^(<[^>]+>)?([A-Z])/, (match, markup, char) => {
71
+ preserve.push({ start: ((markup === null || markup === void 0 ? void 0 : markup.length) || 0), end: ((markup === null || markup === void 0 ? void 0 : markup.length) || 0) + char.length, description: 'sentence-start' });
72
+ return '';
73
+ });
74
+ text.replace(/<span class="nocase">.*?<\/span>|<nc>.*?<\/nc>/gi, (match, i) => {
75
+ preserve.push({ start: i, end: i + match.length, description: 'nocase' });
76
+ return '';
77
+ });
78
+ let masked = text.replace(/<[^>]+>/g, (match, i) => {
79
+ preserve.push({ start: i, end: i + match.length, description: 'markup' });
80
+ return '\uFFFD'.repeat(match.length);
81
+ });
82
+ masked = masked
83
+ .replace(/[;:]\uFFFD*\s+\uFFFD*A\s/g, match => match.toLowerCase())
84
+ .replace(/[–—]\uFFFD*\s*\uFFFD*A\s/g, match => match.toLowerCase())
85
+ .replace(re.words, word => lowercase(word));
86
+ return restore(masked, text, preserve);
94
87
  }
95
88
  exports.toSentenceCase = toSentenceCase;
96
89
  //# sourceMappingURL=sentence-case.js.map