hunspell-reader 5.14.0 → 5.15.3

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.
@@ -1,5 +1,6 @@
1
- import { Aff, AffWord } from './aff';
2
1
  import { Sequence } from 'gensequence';
2
+ import { Aff } from './aff';
3
+ import type { AffWord } from './affDef';
3
4
  import { WordInfo } from './types';
4
5
  export { WordInfo } from './types';
5
6
  export interface HunspellSrcData {
@@ -37,14 +38,14 @@ export declare class IterableHunspellReader implements Iterable<string> {
37
38
  * @param tapPreApplyRules -- optional function to be called before rules are applied to a word.
38
39
  * It is mostly used for monitoring progress in combination with `size`.
39
40
  */
40
- seqAffWords(tapPreApplyRules?: (dicEntry: string, index: number) => any, maxDepth?: number): Sequence<AffWord>;
41
+ seqAffWords(tapPreApplyRules?: (dicEntry: string, index: number) => void, maxDepth?: number): Sequence<AffWord>;
41
42
  /**
42
43
  * create an iterable sequence of the words in the dictionary.
43
44
  *
44
45
  * @param tapPreApplyRules -- optional function to be called before rules are applied to a word.
45
46
  * It is mostly used for monitoring progress in combination with `size`.
46
47
  */
47
- seqTransformDictionaryEntries(tapPreApplyRules?: (dicEntry: string, index: number) => any, maxDepth?: number): Sequence<AffWord[]>;
48
+ seqTransformDictionaryEntries(tapPreApplyRules?: (dicEntry: string, index: number) => void, maxDepth?: number): Sequence<AffWord[]>;
48
49
  /**
49
50
  * Iterator for all the words in the dictionary. The words are in the order found in the .dic after the
50
51
  * transformations have been applied. Forbidden and CompoundOnly ARE INCLUDED.
@@ -20,10 +20,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.createMatchingWordsFilter = exports.IterableHunspellReader = void 0;
23
- const affReader_1 = require("./affReader");
24
- const gensequence_1 = require("gensequence");
25
23
  const fs = __importStar(require("fs-extra"));
24
+ const gensequence_1 = require("gensequence");
26
25
  const iconv_lite_1 = require("iconv-lite");
26
+ const affReader_1 = require("./affReader");
27
27
  const util_1 = require("./util");
28
28
  const defaultEncoding = 'UTF-8';
29
29
  class IterableHunspellReader {
package/dist/aff.d.ts CHANGED
@@ -1,183 +1,5 @@
1
+ import type { AffInfo, AffWord, AffWordFlags, Fx, Rule, Substitution } from './affDef';
1
2
  import { Converter } from './converter';
2
- export interface Fx {
3
- type: string;
4
- id: string;
5
- combinable: boolean;
6
- substitutionSets: Substitutions;
7
- count?: string;
8
- extra?: string[];
9
- }
10
- export declare type Substitutions = Map<string, SubstitutionSet>;
11
- export interface Substitution {
12
- remove: string;
13
- attach: string;
14
- attachRules?: string;
15
- replace: RegExp;
16
- extra?: string;
17
- }
18
- export interface SubstitutionSet {
19
- match: RegExp;
20
- substitutions: Substitution[];
21
- }
22
- export interface Rep {
23
- match: string;
24
- replaceWith: string;
25
- }
26
- export interface Conv {
27
- from: string;
28
- to: string;
29
- }
30
- export interface AffTransformFlags {
31
- KEEPCASE?: string;
32
- WARN?: string;
33
- NEEDAFFIX?: string;
34
- FORCEUCASE?: string;
35
- FORBIDDENWORD?: string;
36
- NOSUGGEST?: string;
37
- COMPOUNDBEGIN?: string;
38
- COMPOUNDEND?: string;
39
- COMPOUNDFLAG?: string;
40
- COMPOUNDFORBIDFLAG?: string;
41
- COMPOUNDMIDDLE?: string;
42
- COMPOUNDPERMITFLAG?: string;
43
- ONLYINCOMPOUND?: string;
44
- }
45
- export interface AffInfo extends AffTransformFlags {
46
- SET: string;
47
- TRY?: string;
48
- KEY?: string;
49
- WORDCHARS?: string;
50
- NOSPLITSUGS?: boolean;
51
- MAXCPDSUGS?: number;
52
- ONLYMAXDIFF?: boolean;
53
- MAXDIFF?: number;
54
- BREAK?: number;
55
- FLAG?: string;
56
- MAP?: string[];
57
- ICONV?: Conv[];
58
- OCONV?: Conv[];
59
- REP?: Rep[];
60
- AF?: string[];
61
- COMPOUNDMIN?: number;
62
- COMPOUNDRULE?: string[];
63
- CHECKCOMPOUNDCASE?: boolean;
64
- CHECKCOMPOUNDDUP?: boolean;
65
- CHECKCOMPOUNDREP?: boolean;
66
- CHECKCOMPOUNDPATTERN?: string[][];
67
- PFX?: Map<string, Fx>;
68
- SFX?: Map<string, Fx>;
69
- }
70
- export interface Rule {
71
- id: string;
72
- type: string;
73
- flags?: AffWordFlags;
74
- pfx?: Fx;
75
- sfx?: Fx;
76
- }
77
- /**
78
- * AffWordFlags are the flags applied to a word after the hunspell rules have been applied.
79
- * They are either `true` or `undefined`.
80
- */
81
- export interface AffWordFlags {
82
- /**
83
- * COMPOUNDFLAG flag
84
- *
85
- * Words signed with COMPOUNDFLAG may be in compound words (except when word shorter than COMPOUNDMIN).
86
- * Affixes with COMPOUNDFLAG also permits compounding of affixed words.
87
- *
88
- */
89
- isCompoundPermitted?: true;
90
- /**
91
- * COMPOUNDBEGIN flag
92
- *
93
- * Words signed with COMPOUNDBEGIN (or with a signed affix) may be first elements in compound words.
94
- *
95
- */
96
- canBeCompoundBegin?: true;
97
- /**
98
- * COMPOUNDMIDDLE flag
99
- *
100
- * Words signed with COMPOUNDMIDDLE (or with a signed affix) may be middle elements in compound words.
101
- *
102
- */
103
- canBeCompoundMiddle?: true;
104
- /**
105
- * COMPOUNDLAST flag
106
- *
107
- * Words signed with COMPOUNDLAST (or with a signed affix) may be last elements in compound words.
108
- *
109
- */
110
- canBeCompoundEnd?: true;
111
- /**
112
- * COMPOUNDPERMITFLAG flag
113
- *
114
- * Prefixes are allowed at the beginning of compounds, suffixes are allowed at the end of compounds by default.
115
- * Affixes with COMPOUNDPERMITFLAG may be inside of compounds.
116
- *
117
- */
118
- isOnlyAllowedInCompound?: true;
119
- /**
120
- * COMPOUNDFORBIDFLAG flag
121
- *
122
- * Suffixes with this flag forbid compounding of the affixed word.
123
- *
124
- */
125
- isCompoundForbidden?: true;
126
- /**
127
- * WARN flag
128
- *
129
- * This flag is for rare words, which are also often spelling mistakes, see option -r of command line Hunspell and FORBIDWARN.
130
- */
131
- isWarning?: true;
132
- /**
133
- * KEEPCASE flag
134
- *
135
- * Forbid uppercased and capitalized forms of words signed with KEEPCASE flags. Useful for special orthographies (measurements and
136
- * currency often keep their case in uppercased texts) and writing systems (e.g. keeping lower case of IPA characters). Also valuable
137
- * for words erroneously written in the wrong case.
138
- */
139
- isKeepCase?: true;
140
- /**
141
- * FORCEUCASE flag
142
- *
143
- * Last word part of a compound with flag FORCEUCASE forces capitalization of the whole compound word.
144
- * Eg. Dutch word "straat" (street) with FORCEUCASE flags will allowed only in capitalized compound forms,
145
- * according to the Dutch spelling rules for proper names.
146
- */
147
- isForceUCase?: true;
148
- /**
149
- * FORBIDDENWORD flag
150
- *
151
- * This flag signs forbidden word form. Because affixed forms are also forbidden, we can subtract a subset from set of the
152
- * accepted affixed and compound words. Note: useful to forbid erroneous words, generated by the compounding mechanism.
153
- */
154
- isForbiddenWord?: true;
155
- /**
156
- * NOSUGGEST flag
157
- *
158
- * Words signed with NOSUGGEST flag are not suggested (but still accepted when typed correctly). Proposed flag for vulgar
159
- * and obscene words (see also SUBSTANDARD).
160
- */
161
- isNoSuggest?: true;
162
- /**
163
- * NEEDAFFIX flag
164
- *
165
- * This flag signs virtual stems in the dictionary, words only valid when affixed. Except, if the dictionary word has a homonym
166
- * or a zero affix. NEEDAFFIX works also with prefixes and prefix + suffix combinations (see tests/pseudoroot5.*).
167
- */
168
- isNeedAffix?: true;
169
- }
170
- export interface AffWord {
171
- word: string;
172
- rules: string;
173
- flags: AffWordFlags;
174
- rulesApplied: string;
175
- /** prefix + base + suffix == word */
176
- base: string;
177
- suffix: string;
178
- prefix: string;
179
- dic: string;
180
- }
181
3
  /** The `word` field in a Converted AffWord has been converted using the OCONV mapping */
182
4
  export declare type ConvertedAffWord = AffWord;
183
5
  export declare class Aff {
package/dist/aff.js CHANGED
@@ -20,10 +20,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.debug = exports.filterAff = exports.compareAff = exports.asAffWord = exports.flagsToString = exports.affWordToColoredString = exports.logAffWord = exports.processRules = exports.Aff = void 0;
23
+ const GS = __importStar(require("gensequence"));
24
+ const gensequence_1 = require("gensequence");
23
25
  const util = __importStar(require("util"));
24
26
  const converter_1 = require("./converter");
25
- const gensequence_1 = require("gensequence");
26
- const GS = __importStar(require("gensequence"));
27
27
  const util_1 = require("./util");
28
28
  const log = false;
29
29
  const DefaultMaxDepth = 5;
@@ -73,7 +73,7 @@ class Aff {
73
73
  flags: { ...acc.flags, ...rule.flags },
74
74
  }), { rulesApplied: affWord.rulesApplied, flags: affWord.flags });
75
75
  const rules = this.joinRules(allRules.filter((rule) => !rule.flags).map((rule) => rule.id));
76
- const affixRules = allRules.map((rule) => rule.sfx || rule.pfx).filter((a) => !!a);
76
+ const affixRules = allRules.map((rule) => rule.sfx || rule.pfx).filter(util_1.isDefined);
77
77
  const wordWithFlags = { word, flags, rulesApplied, rules: '', base, suffix, prefix, dic };
78
78
  return [wordWithFlags, ...this.applyAffixesToWord(affixRules, { ...wordWithFlags, rules }, remainingDepth)]
79
79
  .filter(({ flags }) => !flags.isNeedAffix)
@@ -141,10 +141,11 @@ class Aff {
141
141
  }
142
142
  getMatchingRules(rules) {
143
143
  const { AF = [] } = this.affInfo;
144
- const rulesToSplit = AF[rules] || rules;
144
+ const idx = parseInt(rules, 10);
145
+ const rulesToSplit = AF[idx] || rules;
145
146
  return this.separateRules(rulesToSplit)
146
- .map((key) => this.rules[key])
147
- .filter((a) => !!a);
147
+ .map((key) => this.rules.get(key))
148
+ .filter(util_1.isDefined);
148
149
  }
149
150
  joinRules(rules) {
150
151
  switch (this.affInfo.FLAG) {
@@ -175,7 +176,7 @@ exports.Aff = Aff;
175
176
  function signature(aff) {
176
177
  const { word, flags } = aff;
177
178
  const sig = Object.entries(flags)
178
- .filter((e) => e[1])
179
+ .filter((e) => !!e[1])
179
180
  .map((f) => flagToStringMap[f[0]])
180
181
  .sort()
181
182
  .join('');
@@ -190,12 +191,13 @@ function processRules(affInfo) {
190
191
  .map((pfx) => ({ id: pfx.id, type: 'pfx', pfx }));
191
192
  const flagRules = GS.sequenceFromObject(affInfo)
192
193
  .filter(([key, value]) => !!affFlag[key] && !!value)
194
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
193
195
  .map(([key, value]) => ({ id: value, type: 'flag', flags: affFlag[key] }));
194
196
  const rules = sfxRules
195
197
  .concat(pfxRules)
196
198
  .concat(flagRules)
197
199
  .reduce((acc, rule) => {
198
- acc[rule.id] = rule;
200
+ acc.set(rule.id, rule);
199
201
  return acc;
200
202
  }, new Map());
201
203
  return rules;
@@ -216,7 +218,7 @@ const affFlag = {
216
218
  COMPOUNDFORBIDFLAG: { isCompoundForbidden: true },
217
219
  ONLYINCOMPOUND: { isOnlyAllowedInCompound: true },
218
220
  };
219
- const flagToStringMap = {
221
+ const _FlagToStringMap = {
220
222
  isCompoundPermitted: 'C',
221
223
  canBeCompoundBegin: 'B',
222
224
  canBeCompoundMiddle: 'M',
@@ -230,6 +232,22 @@ const flagToStringMap = {
230
232
  isNeedAffix: 'A',
231
233
  isCompoundForbidden: '-',
232
234
  };
235
+ const _FlagToLongStringMap = {
236
+ isCompoundPermitted: 'CompoundPermitted',
237
+ canBeCompoundBegin: 'CompoundBegin',
238
+ canBeCompoundMiddle: 'CompoundMiddle',
239
+ canBeCompoundEnd: 'CompoundEnd',
240
+ isOnlyAllowedInCompound: 'OnlyInCompound',
241
+ isWarning: 'Warning',
242
+ isKeepCase: 'KeepCase',
243
+ isForceUCase: 'ForceUpperCase',
244
+ isForbiddenWord: 'Forbidden',
245
+ isNoSuggest: 'NoSuggest',
246
+ isNeedAffix: 'NeedAffix',
247
+ isCompoundForbidden: 'CompoundForbidden',
248
+ };
249
+ const flagToStringMap = _FlagToStringMap;
250
+ const flagToLongStringMap = _FlagToLongStringMap;
233
251
  function logAffWord(affWord, message) {
234
252
  /* istanbul ignore if */
235
253
  if (log) {
@@ -248,13 +266,11 @@ function affWordToColoredString(affWord) {
248
266
  exports.affWordToColoredString = affWordToColoredString;
249
267
  /* istanbul ignore next */
250
268
  function flagsToString(flags) {
251
- return (GS.sequenceFromObject(flags)
269
+ return [...Object.entries(flags)]
252
270
  .filter(([, v]) => !!v)
253
- // convert the key to a string
254
- .map(([k]) => flagToStringMap[k])
255
- .toArray()
271
+ .map(([k]) => flagToLongStringMap[k])
256
272
  .sort()
257
- .join('_'));
273
+ .join(':');
258
274
  }
259
275
  exports.flagsToString = flagsToString;
260
276
  function asAffWord(word, rules = '', flags = {}) {
@@ -299,7 +315,7 @@ function adjustCompounding(affWord, minLength) {
299
315
  if (!affWord.flags.isCompoundPermitted || affWord.word.length >= minLength) {
300
316
  return affWord;
301
317
  }
302
- const { isCompoundPermitted, ...flags } = affWord.flags;
318
+ const { isCompoundPermitted: _, ...flags } = affWord.flags;
303
319
  affWord.flags = flags;
304
320
  return affWord;
305
321
  }
@@ -0,0 +1,180 @@
1
+ export interface Fx {
2
+ type: string;
3
+ id: string;
4
+ combinable: boolean;
5
+ substitutionSets: Substitutions;
6
+ count?: string;
7
+ extra?: string[];
8
+ }
9
+ export declare type Substitutions = Map<string, SubstitutionSet>;
10
+ export interface Substitution {
11
+ remove: string;
12
+ attach: string;
13
+ attachRules?: string;
14
+ replace: RegExp;
15
+ extra?: string;
16
+ }
17
+ export interface SubstitutionSet {
18
+ match: RegExp;
19
+ substitutions: Substitution[];
20
+ }
21
+ export interface Rep {
22
+ match: string;
23
+ replaceWith: string;
24
+ }
25
+ export interface Conv {
26
+ from: string;
27
+ to: string;
28
+ }
29
+ export interface AffTransformFlags {
30
+ KEEPCASE?: string;
31
+ WARN?: string;
32
+ NEEDAFFIX?: string;
33
+ FORCEUCASE?: string;
34
+ FORBIDDENWORD?: string;
35
+ NOSUGGEST?: string;
36
+ COMPOUNDBEGIN?: string;
37
+ COMPOUNDEND?: string;
38
+ COMPOUNDFLAG?: string;
39
+ COMPOUNDFORBIDFLAG?: string;
40
+ COMPOUNDMIDDLE?: string;
41
+ COMPOUNDPERMITFLAG?: string;
42
+ ONLYINCOMPOUND?: string;
43
+ }
44
+ export interface AffInfo extends AffTransformFlags {
45
+ SET: string;
46
+ TRY?: string;
47
+ KEY?: string;
48
+ WORDCHARS?: string;
49
+ NOSPLITSUGS?: boolean;
50
+ MAXCPDSUGS?: number;
51
+ ONLYMAXDIFF?: boolean;
52
+ MAXDIFF?: number;
53
+ BREAK?: string[];
54
+ FLAG?: string;
55
+ MAP?: string[];
56
+ ICONV?: Conv[];
57
+ OCONV?: Conv[];
58
+ REP?: Rep[];
59
+ AF?: string[];
60
+ COMPOUNDMIN?: number;
61
+ COMPOUNDRULE?: string[];
62
+ CHECKCOMPOUNDCASE?: boolean;
63
+ CHECKCOMPOUNDDUP?: boolean;
64
+ CHECKCOMPOUNDREP?: boolean;
65
+ CHECKCOMPOUNDPATTERN?: string[][];
66
+ PFX?: Map<string, Fx>;
67
+ SFX?: Map<string, Fx>;
68
+ }
69
+ export interface Rule {
70
+ id: string;
71
+ type: string;
72
+ flags?: AffWordFlags;
73
+ pfx?: Fx;
74
+ sfx?: Fx;
75
+ }
76
+ /**
77
+ * AffWordFlags are the flags applied to a word after the hunspell rules have been applied.
78
+ * They are either `true` or `undefined`.
79
+ */
80
+ export interface AffWordFlags {
81
+ /**
82
+ * COMPOUNDFLAG flag
83
+ *
84
+ * Words signed with COMPOUNDFLAG may be in compound words (except when word shorter than COMPOUNDMIN).
85
+ * Affixes with COMPOUNDFLAG also permits compounding of affixed words.
86
+ *
87
+ */
88
+ isCompoundPermitted?: true;
89
+ /**
90
+ * COMPOUNDBEGIN flag
91
+ *
92
+ * Words signed with COMPOUNDBEGIN (or with a signed affix) may be first elements in compound words.
93
+ *
94
+ */
95
+ canBeCompoundBegin?: true;
96
+ /**
97
+ * COMPOUNDMIDDLE flag
98
+ *
99
+ * Words signed with COMPOUNDMIDDLE (or with a signed affix) may be middle elements in compound words.
100
+ *
101
+ */
102
+ canBeCompoundMiddle?: true;
103
+ /**
104
+ * COMPOUNDLAST flag
105
+ *
106
+ * Words signed with COMPOUNDLAST (or with a signed affix) may be last elements in compound words.
107
+ *
108
+ */
109
+ canBeCompoundEnd?: true;
110
+ /**
111
+ * COMPOUNDPERMITFLAG flag
112
+ *
113
+ * Prefixes are allowed at the beginning of compounds, suffixes are allowed at the end of compounds by default.
114
+ * Affixes with COMPOUNDPERMITFLAG may be inside of compounds.
115
+ *
116
+ */
117
+ isOnlyAllowedInCompound?: true;
118
+ /**
119
+ * COMPOUNDFORBIDFLAG flag
120
+ *
121
+ * Suffixes with this flag forbid compounding of the affixed word.
122
+ *
123
+ */
124
+ isCompoundForbidden?: true;
125
+ /**
126
+ * WARN flag
127
+ *
128
+ * This flag is for rare words, which are also often spelling mistakes, see option -r of command line Hunspell and FORBIDWARN.
129
+ */
130
+ isWarning?: true;
131
+ /**
132
+ * KEEPCASE flag
133
+ *
134
+ * Forbid uppercased and capitalized forms of words signed with KEEPCASE flags. Useful for special orthographies (measurements and
135
+ * currency often keep their case in uppercased texts) and writing systems (e.g. keeping lower case of IPA characters). Also valuable
136
+ * for words erroneously written in the wrong case.
137
+ */
138
+ isKeepCase?: true;
139
+ /**
140
+ * FORCEUCASE flag
141
+ *
142
+ * Last word part of a compound with flag FORCEUCASE forces capitalization of the whole compound word.
143
+ * Eg. Dutch word "straat" (street) with FORCEUCASE flags will allowed only in capitalized compound forms,
144
+ * according to the Dutch spelling rules for proper names.
145
+ */
146
+ isForceUCase?: true;
147
+ /**
148
+ * FORBIDDENWORD flag
149
+ *
150
+ * This flag signs forbidden word form. Because affixed forms are also forbidden, we can subtract a subset from set of the
151
+ * accepted affixed and compound words. Note: useful to forbid erroneous words, generated by the compounding mechanism.
152
+ */
153
+ isForbiddenWord?: true;
154
+ /**
155
+ * NOSUGGEST flag
156
+ *
157
+ * Words signed with NOSUGGEST flag are not suggested (but still accepted when typed correctly). Proposed flag for vulgar
158
+ * and obscene words (see also SUBSTANDARD).
159
+ */
160
+ isNoSuggest?: true;
161
+ /**
162
+ * NEEDAFFIX flag
163
+ *
164
+ * This flag signs virtual stems in the dictionary, words only valid when affixed. Except, if the dictionary word has a homonym
165
+ * or a zero affix. NEEDAFFIX works also with prefixes and prefix + suffix combinations (see tests/pseudoroot5.*).
166
+ */
167
+ isNeedAffix?: true;
168
+ }
169
+ export interface AffWord {
170
+ word: string;
171
+ rules: string;
172
+ flags: AffWordFlags;
173
+ rulesApplied: string;
174
+ /** prefix + base + suffix == word */
175
+ base: string;
176
+ suffix: string;
177
+ prefix: string;
178
+ dic: string;
179
+ }
180
+ //# sourceMappingURL=affDef.d.ts.map
package/dist/affDef.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ // cspell:words uppercased
3
+ // cspell:words KEEPCASE WARN NEEDAFFIX FORCEUCASE FORBIDDENWORD NOSUGGEST WORDCHARS
4
+ // cspell:words COMPOUNDBEGIN COMPOUNDMIDDLE COMPOUNDEND COMPOUNDPERMITFLAG COMPOUNDFORBIDFLAG
5
+ // cspell:words MAXDIFF COMPOUNDMIN COMPOUNDRULE COMPOUNDFLAG COMPOUNDLAST FORBIDWARN
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=affDef.js.map
@@ -1,4 +1,5 @@
1
- import { AffInfo, Aff, Fx } from './aff';
1
+ import { Aff } from './aff';
2
+ import type { AffInfo, Fx } from './affDef';
2
3
  export interface ConvEntry {
3
4
  from: string;
4
5
  to: string;
package/dist/affReader.js CHANGED
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.testing = exports.parseAffFileToAff = exports.parseAff = exports.parseAffFile = void 0;
4
- const aff_1 = require("./aff");
7
+ const assert_1 = __importDefault(require("assert"));
5
8
  const fs_extra_1 = require("fs-extra");
6
9
  const iconv_lite_1 = require("iconv-lite");
10
+ const aff_1 = require("./aff");
11
+ const util_1 = require("./util");
7
12
  const fixRegex = {
8
13
  SFX: { m: /$/, r: '$' },
9
14
  PFX: { m: /^/, r: '^' },
@@ -13,32 +18,55 @@ const spaceRegex = /\s+/;
13
18
  const commentRegex = /(?:^\s*#.*)|(?:\s+#.*)/;
14
19
  const affixLine = /^\s*([^\s]+)\s+(.*)?$/;
15
20
  const UTF8 = 'UTF-8';
16
- function convEntry(fieldValue, line) {
17
- if (fieldValue === undefined) {
18
- return [];
19
- }
20
- const args = (line.value || '').split(spaceRegex);
21
- fieldValue.push({ from: args[0], to: args[1] });
22
- return fieldValue;
21
+ function convEntry() {
22
+ let fieldValue;
23
+ return {
24
+ addLine: (line) => {
25
+ if (fieldValue === undefined) {
26
+ fieldValue = [];
27
+ return;
28
+ }
29
+ const args = (line.value || '').split(spaceRegex);
30
+ fieldValue.push({ from: args[0], to: args[1] });
31
+ },
32
+ getValue: () => fieldValue,
33
+ };
23
34
  }
24
- function afEntry(fieldValue, line) {
25
- if (fieldValue === undefined) {
26
- return [''];
27
- }
28
- if (line.value) {
29
- fieldValue.push(line.value);
30
- }
31
- return fieldValue;
35
+ function afEntry() {
36
+ let fieldValue;
37
+ return {
38
+ addLine: (line) => {
39
+ if (fieldValue === undefined) {
40
+ // Add empty entry because rules start at 1
41
+ fieldValue = [''];
42
+ return;
43
+ }
44
+ if (line.value) {
45
+ fieldValue.push(line.value);
46
+ }
47
+ },
48
+ getValue: () => fieldValue,
49
+ };
32
50
  }
33
- function simpleTable(fieldValue, line) {
34
- const args = (line.value || '').split(spaceRegex);
35
- if (fieldValue === undefined) {
36
- const [count, ...extraValues] = args;
37
- const extra = extraValues.length ? extraValues : undefined;
38
- return { count, extra, values: [] };
51
+ function simpleTable(map) {
52
+ let data;
53
+ function getValue() {
54
+ if (data === null || data === void 0 ? void 0 : data.values)
55
+ return map(data.values);
56
+ return undefined;
39
57
  }
40
- fieldValue.values.push(args);
41
- return fieldValue;
58
+ function addLine(line) {
59
+ const args = (line.value || '').split(spaceRegex);
60
+ if (data === undefined) {
61
+ const [count, ...extraValues] = args;
62
+ const extra = extraValues.length ? extraValues : undefined;
63
+ const values = [];
64
+ data = { count, extra, values };
65
+ return;
66
+ }
67
+ data.values.push(args);
68
+ }
69
+ return { addLine, getValue };
42
70
  }
43
71
  function tablePfxOrSfx(fieldValue, line) {
44
72
  /*
@@ -69,6 +97,7 @@ function tablePfxOrSfx(fieldValue, line) {
69
97
  return fieldValue;
70
98
  }
71
99
  const fixRuleSet = fieldValue.get(subField);
100
+ (0, assert_1.default)(fixRuleSet);
72
101
  const substitutionSets = fixRuleSet.substitutionSets;
73
102
  const ruleAsString = rule.condition.source;
74
103
  if (!substitutionSets.has(ruleAsString)) {
@@ -78,6 +107,7 @@ function tablePfxOrSfx(fieldValue, line) {
78
107
  });
79
108
  }
80
109
  const substitutionSet = substitutionSets.get(ruleAsString);
110
+ (0, assert_1.default)(substitutionSet);
81
111
  const [attachText, attachRules] = rule.affix.split('/', 2);
82
112
  substitutionSet.substitutions.push({
83
113
  remove: rule.stripping,
@@ -143,23 +173,46 @@ function affixMatchToRegExpString(match) {
143
173
  return '';
144
174
  return match.replace(/([\\\-?*])/g, '\\$1');
145
175
  }
146
- function asPfx(fieldValue, line) {
147
- return tablePfxOrSfx(fieldValue, line);
176
+ function collectFx() {
177
+ let value;
178
+ function addLine(line) {
179
+ value = tablePfxOrSfx(value, line);
180
+ }
181
+ return {
182
+ addLine,
183
+ getValue: () => value,
184
+ };
185
+ }
186
+ const asPfx = collectFx;
187
+ const asSfx = collectFx;
188
+ const asString = () => collectPrimitive((v) => v, '');
189
+ const asBoolean = () => collectPrimitive((v) => !!parseInt(v), '1');
190
+ const asNumber = () => collectPrimitive(parseInt, '0');
191
+ function collectPrimitive(map, defaultValue = '') {
192
+ let primitive;
193
+ function getValue() {
194
+ return primitive;
195
+ }
196
+ function addLine(line) {
197
+ const { value = defaultValue } = line;
198
+ primitive = map(value);
199
+ }
200
+ return { addLine, getValue };
201
+ }
202
+ function toRep(values) {
203
+ return values.map((v) => ({ match: v[0], replaceWith: v[1] }));
148
204
  }
149
- function asSfx(fieldValue, line) {
150
- return tablePfxOrSfx(fieldValue, line);
205
+ function toSingleStrings(values) {
206
+ return values.map((v) => v[0]).filter(util_1.isDefined);
151
207
  }
152
- function asString(_fieldValue, line) {
153
- return line.value || '';
208
+ function toAffMap(values) {
209
+ return toSingleStrings(values);
154
210
  }
155
- function asBoolean(_fieldValue, line) {
156
- const { value = '1' } = line;
157
- const iValue = parseInt(value);
158
- return !!iValue;
211
+ function toCompoundRule(values) {
212
+ return toSingleStrings(values);
159
213
  }
160
- function asNumber(_fieldValue, line) {
161
- const { value = '0' } = line;
162
- return parseInt(value);
214
+ function toCheckCompoundPattern(values) {
215
+ return values;
163
216
  }
164
217
  /*
165
218
  cspell:ignore COMPOUNDBEGIN COMPOUNDEND COMPOUNDMIDDLE COMPOUNDMIN COMPOUNDPERMITFLAG COMPOUNDRULE COMPOUNDFORBIDFLAG COMPOUNDFLAG
@@ -167,44 +220,86 @@ cspell:ignore FORBIDDENWORD KEEPCASE
167
220
  cspell:ignore MAXDIFF NEEDAFFIX WORDCHARS
168
221
  */
169
222
  // prettier-ignore
170
- const affTableField = {
171
- AF: afEntry,
172
- BREAK: asNumber,
173
- CHECKCOMPOUNDCASE: asBoolean,
174
- CHECKCOMPOUNDDUP: asBoolean,
175
- CHECKCOMPOUNDPATTERN: simpleTable,
176
- CHECKCOMPOUNDREP: asBoolean,
177
- COMPOUNDBEGIN: asString,
178
- COMPOUNDEND: asString,
179
- COMPOUNDMIDDLE: asString,
180
- COMPOUNDMIN: asNumber,
181
- COMPOUNDFLAG: asString,
182
- COMPOUNDPERMITFLAG: asString,
183
- COMPOUNDFORBIDFLAG: asString,
184
- COMPOUNDRULE: simpleTable,
185
- FLAG: asString,
186
- FORBIDDENWORD: asString,
187
- FORCEUCASE: asString,
188
- ICONV: convEntry,
189
- KEEPCASE: asString,
190
- KEY: asString,
191
- MAP: simpleTable,
192
- MAXCPDSUGS: asNumber,
193
- MAXDIFF: asNumber,
194
- NEEDAFFIX: asString,
195
- NOSPLITSUGS: asBoolean,
196
- NOSUGGEST: asString,
197
- OCONV: convEntry,
198
- ONLYINCOMPOUND: asString,
199
- ONLYMAXDIFF: asBoolean,
200
- PFX: asPfx,
201
- REP: simpleTable,
202
- SET: asString,
203
- SFX: asSfx,
204
- TRY: asString,
205
- WARN: asString,
206
- WORDCHARS: asString,
207
- };
223
+ const createAffFieldTable = () => ({
224
+ AF: afEntry(),
225
+ BREAK: simpleTable(toSingleStrings),
226
+ CHECKCOMPOUNDCASE: asBoolean(),
227
+ CHECKCOMPOUNDDUP: asBoolean(),
228
+ CHECKCOMPOUNDPATTERN: simpleTable(toCheckCompoundPattern),
229
+ CHECKCOMPOUNDREP: asBoolean(),
230
+ COMPOUNDBEGIN: asString(),
231
+ COMPOUNDEND: asString(),
232
+ COMPOUNDMIDDLE: asString(),
233
+ COMPOUNDMIN: asNumber(),
234
+ COMPOUNDFLAG: asString(),
235
+ COMPOUNDPERMITFLAG: asString(),
236
+ COMPOUNDFORBIDFLAG: asString(),
237
+ COMPOUNDRULE: simpleTable(toCompoundRule),
238
+ FLAG: asString(),
239
+ FORBIDDENWORD: asString(),
240
+ FORCEUCASE: asString(),
241
+ ICONV: convEntry(),
242
+ KEEPCASE: asString(),
243
+ KEY: asString(),
244
+ MAP: simpleTable(toAffMap),
245
+ MAXCPDSUGS: asNumber(),
246
+ MAXDIFF: asNumber(),
247
+ NEEDAFFIX: asString(),
248
+ NOSPLITSUGS: asBoolean(),
249
+ NOSUGGEST: asString(),
250
+ OCONV: convEntry(),
251
+ ONLYINCOMPOUND: asString(),
252
+ ONLYMAXDIFF: asBoolean(),
253
+ PFX: asPfx(),
254
+ REP: simpleTable(toRep),
255
+ SET: asString(),
256
+ SFX: asSfx(),
257
+ TRY: asString(),
258
+ WARN: asString(),
259
+ WORDCHARS: asString(),
260
+ });
261
+ function collectionToAffInfo(affFieldCollectionTable, encoding) {
262
+ // prettier-ignore
263
+ const result = {
264
+ AF: affFieldCollectionTable.AF.getValue(),
265
+ BREAK: affFieldCollectionTable.BREAK.getValue(),
266
+ CHECKCOMPOUNDCASE: affFieldCollectionTable.CHECKCOMPOUNDCASE.getValue(),
267
+ CHECKCOMPOUNDDUP: affFieldCollectionTable.CHECKCOMPOUNDDUP.getValue(),
268
+ CHECKCOMPOUNDPATTERN: affFieldCollectionTable.CHECKCOMPOUNDPATTERN.getValue(),
269
+ CHECKCOMPOUNDREP: affFieldCollectionTable.CHECKCOMPOUNDREP.getValue(),
270
+ COMPOUNDBEGIN: affFieldCollectionTable.COMPOUNDBEGIN.getValue(),
271
+ COMPOUNDEND: affFieldCollectionTable.COMPOUNDEND.getValue(),
272
+ COMPOUNDMIDDLE: affFieldCollectionTable.COMPOUNDMIDDLE.getValue(),
273
+ COMPOUNDMIN: affFieldCollectionTable.COMPOUNDMIN.getValue(),
274
+ COMPOUNDFLAG: affFieldCollectionTable.COMPOUNDFLAG.getValue(),
275
+ COMPOUNDPERMITFLAG: affFieldCollectionTable.COMPOUNDPERMITFLAG.getValue(),
276
+ COMPOUNDFORBIDFLAG: affFieldCollectionTable.COMPOUNDFORBIDFLAG.getValue(),
277
+ COMPOUNDRULE: affFieldCollectionTable.COMPOUNDRULE.getValue(),
278
+ FLAG: affFieldCollectionTable.FLAG.getValue(),
279
+ FORBIDDENWORD: affFieldCollectionTable.FORBIDDENWORD.getValue(),
280
+ FORCEUCASE: affFieldCollectionTable.FORCEUCASE.getValue(),
281
+ ICONV: affFieldCollectionTable.ICONV.getValue(),
282
+ KEEPCASE: affFieldCollectionTable.KEEPCASE.getValue(),
283
+ KEY: affFieldCollectionTable.KEY.getValue(),
284
+ MAP: affFieldCollectionTable.MAP.getValue(),
285
+ MAXCPDSUGS: affFieldCollectionTable.MAXCPDSUGS.getValue(),
286
+ MAXDIFF: affFieldCollectionTable.MAXDIFF.getValue(),
287
+ NEEDAFFIX: affFieldCollectionTable.NEEDAFFIX.getValue(),
288
+ NOSPLITSUGS: affFieldCollectionTable.NOSPLITSUGS.getValue(),
289
+ NOSUGGEST: affFieldCollectionTable.NOSUGGEST.getValue(),
290
+ OCONV: affFieldCollectionTable.OCONV.getValue(),
291
+ ONLYINCOMPOUND: affFieldCollectionTable.ONLYINCOMPOUND.getValue(),
292
+ ONLYMAXDIFF: affFieldCollectionTable.ONLYMAXDIFF.getValue(),
293
+ PFX: affFieldCollectionTable.PFX.getValue(),
294
+ REP: affFieldCollectionTable.REP.getValue(),
295
+ SET: affFieldCollectionTable.SET.getValue() || encoding,
296
+ SFX: affFieldCollectionTable.SFX.getValue(),
297
+ TRY: affFieldCollectionTable.TRY.getValue(),
298
+ WARN: affFieldCollectionTable.WARN.getValue(),
299
+ WORDCHARS: affFieldCollectionTable.WORDCHARS.getValue(),
300
+ };
301
+ return (0, util_1.cleanObject)(result);
302
+ }
208
303
  async function parseAffFile(filename, encoding = UTF8) {
209
304
  const buffer = await (0, fs_extra_1.readFile)(filename);
210
305
  const file = (0, iconv_lite_1.decode)(buffer, encoding);
@@ -217,22 +312,19 @@ async function parseAffFile(filename, encoding = UTF8) {
217
312
  exports.parseAffFile = parseAffFile;
218
313
  function parseAff(affFileContent, encoding = UTF8) {
219
314
  const lines = affFileContent.split(/\r?\n/g);
220
- return lines
221
- .map((line) => line.trimLeft())
315
+ const affFieldCollectionTable = createAffFieldTable();
316
+ affFieldCollectionTable.SET.addLine({ option: 'SET', value: encoding });
317
+ lines
318
+ .map((line) => line.trimStart())
222
319
  .map((line) => line.replace(commentRegex, ''))
223
320
  .filter((line) => line.trim() !== '')
224
321
  .map(parseLine)
225
- .reduce((aff, line) => {
322
+ .forEach((line) => {
323
+ var _a;
226
324
  const field = line.option;
227
- const fn = affTableField[field];
228
- if (fn) {
229
- aff[field] = fn(aff[field], line);
230
- }
231
- else {
232
- aff[field] = line.value;
233
- }
234
- return aff;
235
- }, { SET: encoding });
325
+ (_a = affFieldCollectionTable[field]) === null || _a === void 0 ? void 0 : _a.addLine(line);
326
+ });
327
+ return collectionToAffInfo(affFieldCollectionTable, encoding);
236
328
  }
237
329
  exports.parseAff = parseAff;
238
330
  function parseAffFileToAff(filename, encoding) {
package/dist/app.js CHANGED
@@ -28,6 +28,7 @@ const gensequence_1 = require("gensequence");
28
28
  const aff_1 = require("./aff");
29
29
  const iterableToStream_1 = require("./iterableToStream");
30
30
  const uniqueHistorySize = 500000;
31
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
31
32
  const packageInfo = require('../package.json');
32
33
  const version = packageInfo['version'];
33
34
  let displayHelp = true;
@@ -159,7 +160,9 @@ async function actionPrime(hunspellDicFilename, options) {
159
160
  current++;
160
161
  !(current % reportProgressRate) && process.stderr.write(calcProgress(), 'utf-8');
161
162
  }
162
- : () => { };
163
+ : () => {
164
+ /* void */
165
+ };
163
166
  const seqWords = transform ? reader.seqAffWords(callback) : reader.seqRootWords().map(aff_1.asAffWord);
164
167
  const filterUnique = unique ? (0, util_1.uniqueFilter)(uniqueHistorySize) : (_) => true;
165
168
  const applyTransformers = (aff) => transformers.reduce((aff, fn) => fn(aff), aff);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './IterableHunspellReader';
2
2
  export { IterableHunspellReader as HunspellReader } from './IterableHunspellReader';
3
+ export { parseAffFile as readAffFile, parseAff } from './affReader';
4
+ export type { AffInfo, AffWord } from './affDef';
3
5
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -10,8 +10,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
10
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.HunspellReader = void 0;
13
+ exports.parseAff = exports.readAffFile = exports.HunspellReader = void 0;
14
14
  __exportStar(require("./IterableHunspellReader"), exports);
15
15
  var IterableHunspellReader_1 = require("./IterableHunspellReader");
16
16
  Object.defineProperty(exports, "HunspellReader", { enumerable: true, get: function () { return IterableHunspellReader_1.IterableHunspellReader; } });
17
+ var affReader_1 = require("./affReader");
18
+ Object.defineProperty(exports, "readAffFile", { enumerable: true, get: function () { return affReader_1.parseAffFile; } });
19
+ Object.defineProperty(exports, "parseAff", { enumerable: true, get: function () { return affReader_1.parseAff; } });
17
20
  //# sourceMappingURL=index.js.map
package/dist/util.d.ts CHANGED
@@ -7,4 +7,11 @@ export declare function batch<T>(i: Iterable<T>, size: number): Iterable<T[]>;
7
7
  * @param compare function to evaluate if two values are considered the same.
8
8
  */
9
9
  export declare function filterOrderedList<T>(compare: (a: T, b: T) => boolean | number): (t: T) => boolean;
10
+ export declare function isDefined<T>(v: T | undefined): v is T;
11
+ /**
12
+ * Remove all `undefined` values from an Object.
13
+ * @param obj
14
+ * @returns the same object.
15
+ */
16
+ export declare function cleanObject<T>(obj: T): T;
10
17
  //# sourceMappingURL=util.d.ts.map
package/dist/util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.filterOrderedList = exports.batch = exports.uniqueFilter = exports.hrTimeToSeconds = void 0;
3
+ exports.cleanObject = exports.isDefined = exports.filterOrderedList = exports.batch = exports.uniqueFilter = exports.hrTimeToSeconds = void 0;
4
4
  function hrTimeToSeconds([seconds, nanoseconds]) {
5
5
  return seconds + nanoseconds / 1000000000;
6
6
  }
@@ -53,4 +53,25 @@ function filterOrderedList(compare) {
53
53
  };
54
54
  }
55
55
  exports.filterOrderedList = filterOrderedList;
56
+ function isDefined(v) {
57
+ return v !== undefined;
58
+ }
59
+ exports.isDefined = isDefined;
60
+ /**
61
+ * Remove all `undefined` values from an Object.
62
+ * @param obj
63
+ * @returns the same object.
64
+ */
65
+ function cleanObject(obj) {
66
+ if (typeof obj != 'object')
67
+ return obj;
68
+ const r = obj;
69
+ for (const [k, v] of Object.entries(r)) {
70
+ if (v === undefined) {
71
+ delete r[k];
72
+ }
73
+ }
74
+ return obj;
75
+ }
76
+ exports.cleanObject = cleanObject;
56
77
  //# sourceMappingURL=util.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunspell-reader",
3
- "version": "5.14.0",
3
+ "version": "5.15.3",
4
4
  "description": "A library for reading Hunspell Dictionary Files",
5
5
  "bin": "bin.js",
6
6
  "main": "dist/index.js",
@@ -39,12 +39,11 @@
39
39
  "homepage": "https://github.com/Jason-Rev/hunspell-reader#readme",
40
40
  "devDependencies": {
41
41
  "@types/fs-extra": "^9.0.13",
42
- "@types/jest": "^27.0.3",
43
- "@types/node": "^17.0.5",
44
- "jest": "^27.4.5",
45
- "prettier": "^2.5.1",
42
+ "@types/jest": "^27.4.0",
43
+ "@types/node": "^17.0.10",
44
+ "jest": "^27.4.7",
46
45
  "rimraf": "^3.0.2",
47
- "ts-jest": "^27.1.2",
46
+ "ts-jest": "^27.1.3",
48
47
  "typescript": "^4.5.4"
49
48
  },
50
49
  "dependencies": {
@@ -53,24 +52,8 @@
53
52
  "gensequence": "^3.1.1",
54
53
  "iconv-lite": "^0.6.3"
55
54
  },
56
- "eslintConfig": {
57
- "root": true,
58
- "parserOptions": {
59
- "ecmaVersion": 6,
60
- "sourceType": "module"
61
- },
62
- "env": {
63
- "node": true,
64
- "mocha": true
65
- },
66
- "ignorePatterns": [
67
- "dist/**",
68
- "node_modules/**"
69
- ],
70
- "rules": {}
71
- },
72
55
  "engines": {
73
56
  "node": ">=12.13.0"
74
57
  },
75
- "gitHead": "8c8dfb70479584839e8035094771762f77118667"
58
+ "gitHead": "8a7c55b7d0b340d3c4e964ba91390a405f2cda2d"
76
59
  }