@codemerge/hunspell 2.4.0
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/LICENSE +21 -0
- package/NOTICE +35 -0
- package/dist/applyRule.d.cts +6 -0
- package/dist/applyRule.d.ts +6 -0
- package/dist/applyRule.d.ts.map +1 -0
- package/dist/check.d.cts +14 -0
- package/dist/check.d.ts +14 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/dictionary.d.cts +7 -0
- package/dist/dictionary.d.ts +7 -0
- package/dist/dictionary.d.ts.map +1 -0
- package/dist/index.cjs +435 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +434 -0
- package/dist/index.mjs.map +1 -0
- package/dist/limits.d.cts +12 -0
- package/dist/limits.d.ts +12 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/parseAff.d.cts +13 -0
- package/dist/parseAff.d.ts +13 -0
- package/dist/parseAff.d.ts.map +1 -0
- package/dist/parseDic.d.cts +13 -0
- package/dist/parseDic.d.ts +13 -0
- package/dist/parseDic.d.ts.map +1 -0
- package/dist/parseRuleCodes.d.cts +7 -0
- package/dist/parseRuleCodes.d.ts +7 -0
- package/dist/parseRuleCodes.d.ts.map +1 -0
- package/dist/suggest.d.cts +6 -0
- package/dist/suggest.d.ts +6 -0
- package/dist/suggest.d.ts.map +1 -0
- package/dist/types.d.cts +45 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
@codemerge/hunspell
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
This package includes a TypeScript Hunspell-style dictionary engine derived from
|
|
5
|
+
Typo.js (https://github.com/cfinke/Typo.js) by Christopher Finke.
|
|
6
|
+
|
|
7
|
+
Typo.js is licensed under the BSD 3-Clause License:
|
|
8
|
+
|
|
9
|
+
Copyright (c) Christopher Finke
|
|
10
|
+
All rights reserved.
|
|
11
|
+
|
|
12
|
+
Redistribution and use in source and binary forms, with or without
|
|
13
|
+
modification, are permitted provided that the following conditions are met:
|
|
14
|
+
|
|
15
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
16
|
+
list of conditions and the following disclaimer.
|
|
17
|
+
|
|
18
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
|
|
22
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
23
|
+
contributors may be used to endorse or promote products derived from
|
|
24
|
+
this software without specific prior written permission.
|
|
25
|
+
|
|
26
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
27
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
28
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
29
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
30
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
31
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
32
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
33
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
34
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
35
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AffixRule, AffixRules } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Apply one affix rule (and any continuation classes) to a stem.
|
|
4
|
+
*/
|
|
5
|
+
export declare function applyRule(word: string, rule: AffixRule, rules: AffixRules, depth?: number): string[];
|
|
6
|
+
//# sourceMappingURL=applyRule.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AffixRule, AffixRules } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Apply one affix rule (and any continuation classes) to a stem.
|
|
4
|
+
*/
|
|
5
|
+
export declare function applyRule(word: string, rule: AffixRule, rules: AffixRules, depth?: number): string[];
|
|
6
|
+
//# sourceMappingURL=applyRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyRule.d.ts","sourceRoot":"","sources":["../src/applyRule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CA+B/F"}
|
package/dist/check.d.cts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HunspellState } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasFlag(state: HunspellState, word: string, flag: string, wordFlags?: string[]): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Exact dictionary lookup (no capitalization folding). Also matches compound rules.
|
|
8
|
+
*/
|
|
9
|
+
export declare function checkExact(state: HunspellState, word: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Spell-check with capitalization variants (all-caps / Capitalized).
|
|
12
|
+
*/
|
|
13
|
+
export declare function check(state: HunspellState, aWord: string): boolean;
|
|
14
|
+
//# sourceMappingURL=check.d.ts.map
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HunspellState } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasFlag(state: HunspellState, word: string, flag: string, wordFlags?: string[]): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Exact dictionary lookup (no capitalization folding). Also matches compound rules.
|
|
8
|
+
*/
|
|
9
|
+
export declare function checkExact(state: HunspellState, word: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Spell-check with capitalization variants (all-caps / Capitalized).
|
|
12
|
+
*/
|
|
13
|
+
export declare function check(state: HunspellState, aWord: string): boolean;
|
|
14
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAc7C;;GAEG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAOT;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiCtE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmClE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HunspellDictionary } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.
|
|
4
|
+
* Dictionaries are not bundled — callers fetch/load the files.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createDictionary(affData: string, dicData: string): HunspellDictionary;
|
|
7
|
+
//# sourceMappingURL=dictionary.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HunspellDictionary } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.
|
|
4
|
+
* Dictionaries are not bundled — callers fetch/load the files.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createDictionary(affData: string, dicData: string): HunspellDictionary;
|
|
7
|
+
//# sourceMappingURL=dictionary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary.d.ts","sourceRoot":"","sources":["../src/dictionary.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAiB,MAAM,SAAS,CAAC;AA8BjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,CA0CrF"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
function escapeRegExp(text) {
|
|
3
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4
|
+
}
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/check.ts
|
|
7
|
+
/** Typographic apostrophe → ASCII (dictionary-en stems use U+0027). */
|
|
8
|
+
function normalizeWord(word) {
|
|
9
|
+
return word.replaceAll("’", "'");
|
|
10
|
+
}
|
|
11
|
+
function flattenFlags(ruleCodes) {
|
|
12
|
+
if (ruleCodes === null || ruleCodes === void 0) return [];
|
|
13
|
+
return ruleCodes.flat();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).
|
|
17
|
+
*/
|
|
18
|
+
function hasFlag(state, word, flag, wordFlags) {
|
|
19
|
+
const flagValue = state.flags[flag];
|
|
20
|
+
if (flagValue === void 0) return false;
|
|
21
|
+
return (wordFlags ?? flattenFlags(state.dictionaryTable.get(word) ?? void 0)).includes(flagValue);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Exact dictionary lookup (no capitalization folding). Also matches compound rules.
|
|
25
|
+
*/
|
|
26
|
+
function checkExact(state, word) {
|
|
27
|
+
if (word.length > 256) return false;
|
|
28
|
+
const ruleCodes = state.dictionaryTable.get(word);
|
|
29
|
+
if (ruleCodes === void 0) {
|
|
30
|
+
const compoundMin = state.flags.COMPOUNDMIN;
|
|
31
|
+
if (compoundMin !== void 0 && word.length >= Number(compoundMin) && word.length <= 256) {
|
|
32
|
+
for (const rule of state.compoundRules) if (rule.test(word)) return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (ruleCodes === null) return true;
|
|
37
|
+
for (const codes of ruleCodes) if (!hasFlag(state, word, "ONLYINCOMPOUND", codes)) return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Spell-check with capitalization variants (all-caps / Capitalized).
|
|
42
|
+
*/
|
|
43
|
+
function check(state, aWord) {
|
|
44
|
+
if (aWord.length === 0) return false;
|
|
45
|
+
const trimmedWord = normalizeWord(aWord).replace(/^\s+/, "").replace(/\s+$/, "");
|
|
46
|
+
if (checkExact(state, trimmedWord)) return true;
|
|
47
|
+
const first = trimmedWord.charAt(0);
|
|
48
|
+
if (trimmedWord.toUpperCase() === trimmedWord) {
|
|
49
|
+
const capitalizedWord = first + trimmedWord.slice(1).toLowerCase();
|
|
50
|
+
if (hasFlag(state, capitalizedWord, "KEEPCASE")) return false;
|
|
51
|
+
if (checkExact(state, capitalizedWord)) return true;
|
|
52
|
+
if (checkExact(state, trimmedWord.toLowerCase())) return true;
|
|
53
|
+
}
|
|
54
|
+
const uncapitalizedWord = first.toLowerCase() + trimmedWord.slice(1);
|
|
55
|
+
if (uncapitalizedWord !== trimmedWord) {
|
|
56
|
+
if (hasFlag(state, uncapitalizedWord, "KEEPCASE")) return false;
|
|
57
|
+
if (checkExact(state, uncapitalizedWord)) return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/parseRuleCodes.ts
|
|
63
|
+
/**
|
|
64
|
+
* Split a Hunspell flag string according to the `.aff` `FLAG` mode.
|
|
65
|
+
* Default: one character per flag.
|
|
66
|
+
*/
|
|
67
|
+
function parseRuleCodes(textCodes, flags) {
|
|
68
|
+
if (!textCodes) return [];
|
|
69
|
+
if (!("FLAG" in flags)) return textCodes.split("");
|
|
70
|
+
if (flags.FLAG === "long") {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (let i = 0; i < textCodes.length; i += 2) out.push(textCodes.slice(i, i + 2));
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
if (flags.FLAG === "num") return textCodes.split(",");
|
|
76
|
+
if (flags.FLAG === "UTF-8") return Array.from(textCodes);
|
|
77
|
+
return textCodes.split("");
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/parseAff.ts
|
|
81
|
+
function removeAffixComments(line) {
|
|
82
|
+
if (/^\s*#/.test(line)) return "";
|
|
83
|
+
return line;
|
|
84
|
+
}
|
|
85
|
+
function tryCompileAffixRegex(source) {
|
|
86
|
+
if (source.length > 80) return;
|
|
87
|
+
try {
|
|
88
|
+
return new RegExp(source);
|
|
89
|
+
} catch {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.
|
|
95
|
+
*/
|
|
96
|
+
function parseAff(data) {
|
|
97
|
+
const rules = {};
|
|
98
|
+
const flags = {};
|
|
99
|
+
const compoundRulePatterns = [];
|
|
100
|
+
const replacementTable = [];
|
|
101
|
+
const lines = data.split(/\r?\n/);
|
|
102
|
+
for (let i = 0; i < lines.length; i++) {
|
|
103
|
+
let line = removeAffixComments(lines[i] ?? "").trim();
|
|
104
|
+
if (!line) continue;
|
|
105
|
+
const definitionParts = line.split(/\s+/);
|
|
106
|
+
const ruleType = definitionParts[0];
|
|
107
|
+
if (!ruleType) continue;
|
|
108
|
+
if (ruleType === "PFX" || ruleType === "SFX") {
|
|
109
|
+
const ruleCode = definitionParts[1];
|
|
110
|
+
const combineable = definitionParts[2];
|
|
111
|
+
const numEntries = Math.trunc(Number(definitionParts[3] ?? "0"));
|
|
112
|
+
if (!ruleCode || Number.isNaN(numEntries) || numEntries < 0 || numEntries > 1e4) {
|
|
113
|
+
if (!Number.isNaN(numEntries) && numEntries > 0) i += Math.min(numEntries, lines.length);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const entries = [];
|
|
117
|
+
for (let j = i + 1; j < i + 1 + numEntries; j++) {
|
|
118
|
+
const lineParts = (lines[j] ?? "").split(/\s+/);
|
|
119
|
+
const charactersToRemove = lineParts[2] ?? "0";
|
|
120
|
+
const additionParts = (lineParts[3] ?? "0").split("/");
|
|
121
|
+
let charactersToAdd = additionParts[0] ?? "";
|
|
122
|
+
if (charactersToAdd === "0") charactersToAdd = "";
|
|
123
|
+
const continuationClasses = parseRuleCodes(additionParts[1], flags);
|
|
124
|
+
const regexToMatch = lineParts[4] ?? ".";
|
|
125
|
+
const entry = { add: charactersToAdd };
|
|
126
|
+
if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
|
|
127
|
+
if (regexToMatch !== ".") {
|
|
128
|
+
const match = ruleType === "SFX" ? tryCompileAffixRegex(`${regexToMatch}$`) : tryCompileAffixRegex(`^${regexToMatch}`);
|
|
129
|
+
if (match === void 0) continue;
|
|
130
|
+
entry.match = match;
|
|
131
|
+
}
|
|
132
|
+
if (charactersToRemove !== "0") {
|
|
133
|
+
if (ruleType === "SFX") {
|
|
134
|
+
const remove = tryCompileAffixRegex(`${charactersToRemove}$`);
|
|
135
|
+
if (remove === void 0) continue;
|
|
136
|
+
entry.remove = remove;
|
|
137
|
+
} else if (charactersToRemove.length <= 80) entry.remove = charactersToRemove;
|
|
138
|
+
else continue;
|
|
139
|
+
}
|
|
140
|
+
entries.push(entry);
|
|
141
|
+
}
|
|
142
|
+
rules[ruleCode] = {
|
|
143
|
+
type: ruleType,
|
|
144
|
+
combineable: combineable === "Y",
|
|
145
|
+
entries
|
|
146
|
+
};
|
|
147
|
+
i += numEntries;
|
|
148
|
+
} else if (ruleType === "COMPOUNDRULE") {
|
|
149
|
+
const numEntries = Math.trunc(Number(definitionParts[1] ?? "0"));
|
|
150
|
+
if (Number.isNaN(numEntries) || numEntries < 0 || numEntries > 1e4) {
|
|
151
|
+
if (!Number.isNaN(numEntries) && numEntries > 0) i += Math.min(numEntries, lines.length);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
for (let j = i + 1; j < i + 1 + numEntries; j++) {
|
|
155
|
+
const pattern = (lines[j] ?? "").split(/\s+/)[1];
|
|
156
|
+
if (pattern) compoundRulePatterns.push(pattern);
|
|
157
|
+
}
|
|
158
|
+
i += numEntries;
|
|
159
|
+
} else if (ruleType === "REP") {
|
|
160
|
+
const from = definitionParts[1];
|
|
161
|
+
const to = definitionParts[2];
|
|
162
|
+
if (from !== void 0 && to !== void 0 && definitionParts.length === 3) replacementTable.push([from, to]);
|
|
163
|
+
} else {
|
|
164
|
+
const value = definitionParts[1];
|
|
165
|
+
if (value !== void 0) flags[ruleType] = value;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
rules,
|
|
170
|
+
flags,
|
|
171
|
+
compoundRulePatterns,
|
|
172
|
+
replacementTable
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/applyRule.ts
|
|
177
|
+
/**
|
|
178
|
+
* Apply one affix rule (and any continuation classes) to a stem.
|
|
179
|
+
*/
|
|
180
|
+
function applyRule(word, rule, rules, depth = 0) {
|
|
181
|
+
const newWords = [];
|
|
182
|
+
if (depth > 12) return newWords;
|
|
183
|
+
for (const entry of rule.entries) {
|
|
184
|
+
if (entry.match !== void 0 && !entry.match.test(word)) continue;
|
|
185
|
+
let newWord = word;
|
|
186
|
+
if (entry.remove !== void 0) newWord = newWord.replace(entry.remove, "");
|
|
187
|
+
newWord = rule.type === "SFX" ? newWord + entry.add : entry.add + newWord;
|
|
188
|
+
newWords.push(newWord);
|
|
189
|
+
const cont = entry.continuationClasses;
|
|
190
|
+
if (cont === void 0) continue;
|
|
191
|
+
for (const code of cont) {
|
|
192
|
+
const continuationRule = rules[code];
|
|
193
|
+
if (continuationRule !== void 0) newWords.push(...applyRule(newWord, continuationRule, rules, depth + 1));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return newWords;
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/parseDic.ts
|
|
200
|
+
function removeDicComments(data) {
|
|
201
|
+
return data.replace(/^\t.*$/gm, "");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Parse Hunspell `.dic` content into a word→flags lookup table.
|
|
205
|
+
* Also expands affix forms into the table (Typo.js approach).
|
|
206
|
+
*/
|
|
207
|
+
function parseDic(data, options) {
|
|
208
|
+
const { rules, flags, compoundRuleCodes } = options;
|
|
209
|
+
const lines = removeDicComments(data).split(/\r?\n/);
|
|
210
|
+
const dictionaryTable = /* @__PURE__ */ new Map();
|
|
211
|
+
function addWord(word, ruleCodes) {
|
|
212
|
+
if (!dictionaryTable.has(word)) dictionaryTable.set(word, null);
|
|
213
|
+
if (ruleCodes.length === 0) return;
|
|
214
|
+
if (dictionaryTable.get(word) === null) dictionaryTable.set(word, []);
|
|
215
|
+
const existing = dictionaryTable.get(word);
|
|
216
|
+
if (Array.isArray(existing)) existing.push(ruleCodes);
|
|
217
|
+
}
|
|
218
|
+
for (let i = 1; i < lines.length; i++) {
|
|
219
|
+
const line = lines[i];
|
|
220
|
+
if (line === void 0 || line.length === 0) continue;
|
|
221
|
+
const parts = line.replace(/\s.*$/, "").split("/", 2);
|
|
222
|
+
const word = parts[0] ?? "";
|
|
223
|
+
if (word.length === 0) continue;
|
|
224
|
+
if (parts.length > 1) {
|
|
225
|
+
const ruleCodesArray = parseRuleCodes(parts[1], flags);
|
|
226
|
+
const needAffix = flags.NEEDAFFIX;
|
|
227
|
+
if (needAffix === void 0 || !ruleCodesArray.includes(needAffix)) addWord(word, ruleCodesArray);
|
|
228
|
+
for (let j = 0; j < ruleCodesArray.length; j++) {
|
|
229
|
+
const code = ruleCodesArray[j];
|
|
230
|
+
if (code === void 0) continue;
|
|
231
|
+
const rule = rules[code];
|
|
232
|
+
if (rule !== void 0) {
|
|
233
|
+
const newWords = applyRule(word, rule, rules);
|
|
234
|
+
for (const newWord of newWords) {
|
|
235
|
+
addWord(newWord, []);
|
|
236
|
+
if (rule.combineable) for (let k = j + 1; k < ruleCodesArray.length; k++) {
|
|
237
|
+
const combineCode = ruleCodesArray[k];
|
|
238
|
+
if (combineCode === void 0) continue;
|
|
239
|
+
const combineRule = rules[combineCode];
|
|
240
|
+
if (combineRule !== void 0 && combineRule.combineable && rule.type !== combineRule.type) for (const other of applyRule(newWord, combineRule, rules)) addWord(other, []);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const compoundStems = compoundRuleCodes[code];
|
|
245
|
+
if (compoundStems !== void 0) compoundStems.push(word);
|
|
246
|
+
}
|
|
247
|
+
} else addWord(word.trim(), []);
|
|
248
|
+
}
|
|
249
|
+
return dictionaryTable;
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/suggest.ts
|
|
253
|
+
function ensureAlphabet(state) {
|
|
254
|
+
if (state.alphabet.length > 0) return state.alphabet;
|
|
255
|
+
const LATIN_FALLBACK = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
256
|
+
let alphabet = "";
|
|
257
|
+
if (state.flags.TRY !== void 0 && state.flags.TRY.length > 0) alphabet = state.flags.TRY;
|
|
258
|
+
else alphabet = LATIN_FALLBACK;
|
|
259
|
+
if (state.flags.WORDCHARS !== void 0) alphabet += state.flags.WORDCHARS;
|
|
260
|
+
const seen = /* @__PURE__ */ new Set();
|
|
261
|
+
let unique = "";
|
|
262
|
+
for (const ch of alphabet) if (!seen.has(ch)) {
|
|
263
|
+
seen.add(ch);
|
|
264
|
+
unique += ch;
|
|
265
|
+
}
|
|
266
|
+
state.alphabet = unique;
|
|
267
|
+
return unique;
|
|
268
|
+
}
|
|
269
|
+
function edits1(state, words, knownOnly) {
|
|
270
|
+
const alphabet = ensureAlphabet(state);
|
|
271
|
+
const rv = {};
|
|
272
|
+
const bump = (edit) => {
|
|
273
|
+
if (knownOnly && !check(state, edit)) return;
|
|
274
|
+
rv[edit] = (rv[edit] ?? 0) + 1;
|
|
275
|
+
};
|
|
276
|
+
for (const word of Object.keys(words)) for (let i = 0; i <= word.length; i++) {
|
|
277
|
+
const left = word.slice(0, i);
|
|
278
|
+
const right = word.slice(i);
|
|
279
|
+
if (right.length > 0) bump(left + right.slice(1));
|
|
280
|
+
const right0 = right[0];
|
|
281
|
+
const right1 = right[1];
|
|
282
|
+
if (right.length > 1 && right1 !== void 0 && right0 !== void 0 && right1 !== right0) bump(left + right1 + right0 + right.slice(2));
|
|
283
|
+
if (right.length > 0) {
|
|
284
|
+
const lettercase = right.slice(0, 1).toUpperCase() === right.slice(0, 1) ? "uppercase" : "lowercase";
|
|
285
|
+
for (const ch of alphabet) {
|
|
286
|
+
let replacementLetter = ch;
|
|
287
|
+
if (lettercase === "uppercase") replacementLetter = replacementLetter.toUpperCase();
|
|
288
|
+
if (replacementLetter !== right.slice(0, 1)) bump(left + replacementLetter + right.slice(1));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (right.length > 0) {
|
|
292
|
+
const leftTail = left.slice(-1);
|
|
293
|
+
const rightHead = right.slice(0, 1);
|
|
294
|
+
const lettercase = leftTail.toUpperCase() === leftTail && rightHead.toUpperCase() === rightHead ? "uppercase" : "lowercase";
|
|
295
|
+
for (const ch of alphabet) {
|
|
296
|
+
let replacementLetter = ch;
|
|
297
|
+
if (lettercase === "uppercase") replacementLetter = replacementLetter.toUpperCase();
|
|
298
|
+
bump(left + replacementLetter + right);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return rv;
|
|
303
|
+
}
|
|
304
|
+
function rankCorrections(state, word, limit) {
|
|
305
|
+
const tier1 = edits1(state, { [word]: true }, true);
|
|
306
|
+
let weighted = tier1;
|
|
307
|
+
if (Object.keys(tier1).length === 0) {
|
|
308
|
+
const ed1 = edits1(state, { [word]: true }, false);
|
|
309
|
+
const unknownSeeds = {};
|
|
310
|
+
let seedCount = 0;
|
|
311
|
+
const MAX_ED2_SEEDS = 64;
|
|
312
|
+
for (const [candidate, weight] of Object.entries(ed1)) {
|
|
313
|
+
if (check(state, candidate)) {
|
|
314
|
+
weighted[candidate] = (weighted[candidate] ?? 0) + weight;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (seedCount >= MAX_ED2_SEEDS) continue;
|
|
318
|
+
unknownSeeds[candidate] = weight;
|
|
319
|
+
seedCount += 1;
|
|
320
|
+
}
|
|
321
|
+
if (Object.keys(unknownSeeds).length > 0) {
|
|
322
|
+
const ed2 = edits1(state, unknownSeeds, true);
|
|
323
|
+
for (const [candidate, weight] of Object.entries(ed2)) weighted[candidate] = (weighted[candidate] ?? 0) + weight;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const sorted = [];
|
|
327
|
+
for (const [candidate, weight] of Object.entries(weighted)) {
|
|
328
|
+
let w = weight;
|
|
329
|
+
if (hasFlag(state, candidate, "PRIORITYSUGGEST")) w += 1e3;
|
|
330
|
+
sorted.push([candidate, w]);
|
|
331
|
+
}
|
|
332
|
+
sorted.sort((a, b) => {
|
|
333
|
+
if (a[1] !== b[1]) return b[1] - a[1];
|
|
334
|
+
return a[0].localeCompare(b[0]);
|
|
335
|
+
});
|
|
336
|
+
let capitalizationScheme = "lowercase";
|
|
337
|
+
if (word.toUpperCase() === word) capitalizationScheme = "uppercase";
|
|
338
|
+
else if (word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() === word) capitalizationScheme = "capitalized";
|
|
339
|
+
const rv = [];
|
|
340
|
+
let workingLimit = limit;
|
|
341
|
+
for (let i = 0; i < Math.min(workingLimit, sorted.length); i++) {
|
|
342
|
+
const entry = sorted[i];
|
|
343
|
+
if (entry === void 0) break;
|
|
344
|
+
let suggestion = entry[0];
|
|
345
|
+
if (capitalizationScheme === "uppercase") suggestion = suggestion.toUpperCase();
|
|
346
|
+
else if (capitalizationScheme === "capitalized") suggestion = suggestion.slice(0, 1).toUpperCase() + suggestion.slice(1);
|
|
347
|
+
if (!hasFlag(state, suggestion, "NOSUGGEST") && !rv.includes(suggestion)) rv.push(suggestion);
|
|
348
|
+
else workingLimit++;
|
|
349
|
+
}
|
|
350
|
+
return rv;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Suggest corrections for a misspelling (REP table + edit-distance ranking).
|
|
354
|
+
*/
|
|
355
|
+
function suggest(state, word, limit = 5) {
|
|
356
|
+
if (word.length === 0 || word.length > 64) return [];
|
|
357
|
+
const normalized = word.replaceAll("’", "'");
|
|
358
|
+
const memo = state.memoized[normalized];
|
|
359
|
+
if (memo !== void 0 && (limit <= memo.limit || memo.suggestions.length < memo.limit)) return memo.suggestions.slice(0, limit);
|
|
360
|
+
if (check(state, normalized)) return [];
|
|
361
|
+
for (const [from, to] of state.replacementTable) if (normalized.includes(from)) {
|
|
362
|
+
const corrected = normalized.replace(from, to);
|
|
363
|
+
if (check(state, corrected)) {
|
|
364
|
+
const suggestions = [corrected];
|
|
365
|
+
remember(state, normalized, suggestions, limit);
|
|
366
|
+
return suggestions;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const suggestions = rankCorrections(state, normalized, limit);
|
|
370
|
+
remember(state, normalized, suggestions, limit);
|
|
371
|
+
return suggestions;
|
|
372
|
+
}
|
|
373
|
+
function remember(state, word, suggestions, limit) {
|
|
374
|
+
const keys = Object.keys(state.memoized);
|
|
375
|
+
if (keys.length >= 512) {
|
|
376
|
+
const drop = keys[0];
|
|
377
|
+
if (drop !== void 0) delete state.memoized[drop];
|
|
378
|
+
}
|
|
379
|
+
state.memoized[word] = {
|
|
380
|
+
suggestions,
|
|
381
|
+
limit
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region src/dictionary.ts
|
|
386
|
+
function buildCompoundRegexes(patterns, compoundRuleCodes) {
|
|
387
|
+
const out = [];
|
|
388
|
+
for (const ruleText of patterns) {
|
|
389
|
+
let expressionText = "";
|
|
390
|
+
for (const character of ruleText) if (Object.hasOwn(compoundRuleCodes, character)) {
|
|
391
|
+
const stems = compoundRuleCodes[character] ?? [];
|
|
392
|
+
expressionText += `(${stems.map(escapeRegExp).join("|")})`;
|
|
393
|
+
} else expressionText += character;
|
|
394
|
+
if (expressionText.length > 4096) continue;
|
|
395
|
+
try {
|
|
396
|
+
out.push(new RegExp(`^${expressionText}$`, "i"));
|
|
397
|
+
} catch {}
|
|
398
|
+
}
|
|
399
|
+
return out;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.
|
|
403
|
+
* Dictionaries are not bundled — callers fetch/load the files.
|
|
404
|
+
*/
|
|
405
|
+
function createDictionary(affData, dicData) {
|
|
406
|
+
if (affData.length > 8388608 || dicData.length > 8388608) throw new Error("dictionary too large");
|
|
407
|
+
const { rules, flags, compoundRulePatterns, replacementTable } = parseAff(affData);
|
|
408
|
+
const compoundRuleCodes = {};
|
|
409
|
+
for (const pattern of compoundRulePatterns) for (const ch of pattern) compoundRuleCodes[ch] = [];
|
|
410
|
+
if (flags.ONLYINCOMPOUND !== void 0) compoundRuleCodes[flags.ONLYINCOMPOUND] = [];
|
|
411
|
+
const dictionaryTable = parseDic(dicData, {
|
|
412
|
+
rules,
|
|
413
|
+
flags,
|
|
414
|
+
compoundRuleCodes
|
|
415
|
+
});
|
|
416
|
+
for (const code of Object.keys(compoundRuleCodes)) if ((compoundRuleCodes[code] ?? []).length === 0) delete compoundRuleCodes[code];
|
|
417
|
+
const state = {
|
|
418
|
+
dictionaryTable,
|
|
419
|
+
compoundRules: buildCompoundRegexes(compoundRulePatterns, compoundRuleCodes),
|
|
420
|
+
replacementTable,
|
|
421
|
+
flags,
|
|
422
|
+
memoized: {},
|
|
423
|
+
alphabet: ""
|
|
424
|
+
};
|
|
425
|
+
return {
|
|
426
|
+
loaded: true,
|
|
427
|
+
check: (word) => check(state, word),
|
|
428
|
+
checkExact: (word) => checkExact(state, word),
|
|
429
|
+
suggest: (word, limit) => suggest(state, word, limit)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
exports.createDictionary = createDictionary;
|
|
434
|
+
|
|
435
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/limits.ts","../src/check.ts","../src/parseRuleCodes.ts","../src/parseAff.ts","../src/applyRule.ts","../src/parseDic.ts","../src/suggest.ts","../src/dictionary.ts"],"sourcesContent":["/** Resource bounds for untrusted / oversized Hunspell inputs. */\n\nexport const MAX_AFF_BYTES = 8_388_608;\nexport const MAX_DIC_BYTES = 8_388_608;\nexport const MAX_AFFIX_ENTRIES = 10_000;\nexport const MAX_APPLY_RULE_DEPTH = 12;\nexport const MAX_COMPOUND_REGEX_SOURCE = 4096;\nexport const MAX_AFFIX_PATTERN_LEN = 80;\nexport const MAX_CHECK_WORD_LEN = 256;\nexport const MAX_SUGGEST_WORD_LEN = 64;\nexport const MAX_SUGGEST_MEMO = 512;\n\nexport function escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","import { MAX_CHECK_WORD_LEN } from './limits';\nimport type { HunspellState } from './types';\n\n/** Typographic apostrophe → ASCII (dictionary-en stems use U+0027). */\nfunction normalizeWord(word: string): string {\n return word.replaceAll('\\u2019', \"'\");\n}\n\nfunction flattenFlags(ruleCodes: string[][] | null | undefined): string[] {\n if (ruleCodes === null || ruleCodes === undefined) {\n return [];\n }\n return ruleCodes.flat();\n}\n\n/**\n * Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).\n */\nexport function hasFlag(\n state: HunspellState,\n word: string,\n flag: string,\n wordFlags?: string[]\n): boolean {\n const flagValue = state.flags[flag];\n if (flagValue === undefined) {\n return false;\n }\n const codes = wordFlags ?? flattenFlags(state.dictionaryTable.get(word) ?? undefined);\n return codes.includes(flagValue);\n}\n\n/**\n * Exact dictionary lookup (no capitalization folding). Also matches compound rules.\n */\nexport function checkExact(state: HunspellState, word: string): boolean {\n if (word.length > MAX_CHECK_WORD_LEN) {\n return false;\n }\n\n const ruleCodes = state.dictionaryTable.get(word);\n\n if (ruleCodes === undefined) {\n const compoundMin = state.flags.COMPOUNDMIN;\n if (\n compoundMin !== undefined &&\n word.length >= Number(compoundMin) &&\n word.length <= MAX_CHECK_WORD_LEN\n ) {\n for (const rule of state.compoundRules) {\n if (rule.test(word)) {\n return true;\n }\n }\n }\n return false;\n }\n\n if (ruleCodes === null) {\n return true;\n }\n\n for (const codes of ruleCodes) {\n if (!hasFlag(state, word, 'ONLYINCOMPOUND', codes)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Spell-check with capitalization variants (all-caps / Capitalized).\n */\nexport function check(state: HunspellState, aWord: string): boolean {\n if (aWord.length === 0) {\n return false;\n }\n\n const trimmedWord = normalizeWord(aWord).replace(/^\\s+/, '').replace(/\\s+$/, '');\n if (checkExact(state, trimmedWord)) {\n return true;\n }\n\n const first = trimmedWord.charAt(0);\n if (trimmedWord.toUpperCase() === trimmedWord) {\n const capitalizedWord = first + trimmedWord.slice(1).toLowerCase();\n if (hasFlag(state, capitalizedWord, 'KEEPCASE')) {\n return false;\n }\n if (checkExact(state, capitalizedWord)) {\n return true;\n }\n if (checkExact(state, trimmedWord.toLowerCase())) {\n return true;\n }\n }\n\n const uncapitalizedWord = first.toLowerCase() + trimmedWord.slice(1);\n if (uncapitalizedWord !== trimmedWord) {\n if (hasFlag(state, uncapitalizedWord, 'KEEPCASE')) {\n return false;\n }\n if (checkExact(state, uncapitalizedWord)) {\n return true;\n }\n }\n\n return false;\n}\n","import type { AffFlags } from './types';\n\n/**\n * Split a Hunspell flag string according to the `.aff` `FLAG` mode.\n * Default: one character per flag.\n */\nexport function parseRuleCodes(textCodes: string | undefined, flags: AffFlags): string[] {\n if (!textCodes) {\n return [];\n }\n if (!('FLAG' in flags)) {\n return textCodes.split('');\n }\n if (flags.FLAG === 'long') {\n const out: string[] = [];\n for (let i = 0; i < textCodes.length; i += 2) {\n out.push(textCodes.slice(i, i + 2));\n }\n return out;\n }\n if (flags.FLAG === 'num') {\n return textCodes.split(',');\n }\n if (flags.FLAG === 'UTF-8') {\n return Array.from(textCodes);\n }\n return textCodes.split('');\n}\n","import type { AffFlags, AffixEntry, AffixRules, ReplacementPair } from './types';\nimport { MAX_AFFIX_ENTRIES, MAX_AFFIX_PATTERN_LEN } from './limits';\nimport { parseRuleCodes } from './parseRuleCodes';\n\nexport type AffParseResult = {\n rules: AffixRules;\n flags: AffFlags;\n /** Raw COMPOUNDRULE pattern strings (not yet RegExp). */\n compoundRulePatterns: string[];\n replacementTable: ReplacementPair[];\n};\n\nfunction removeAffixComments(line: string): string {\n // Only strip full-line comments — COMPOUNDRULE may use `#` as a flag char.\n if (/^\\s*#/.test(line)) {\n return '';\n }\n return line;\n}\n\nfunction tryCompileAffixRegex(source: string): RegExp | undefined {\n if (source.length > MAX_AFFIX_PATTERN_LEN) {\n return undefined;\n }\n try {\n return new RegExp(source);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.\n */\nexport function parseAff(data: string): AffParseResult {\n const rules: AffixRules = {};\n const flags: AffFlags = {};\n const compoundRulePatterns: string[] = [];\n const replacementTable: ReplacementPair[] = [];\n\n const lines = data.split(/\\r?\\n/);\n\n for (let i = 0; i < lines.length; i++) {\n let line = removeAffixComments(lines[i] ?? '').trim();\n if (!line) {\n continue;\n }\n\n const definitionParts = line.split(/\\s+/);\n const ruleType = definitionParts[0];\n if (!ruleType) {\n continue;\n }\n\n if (ruleType === 'PFX' || ruleType === 'SFX') {\n const ruleCode = definitionParts[1];\n const combineable = definitionParts[2];\n const numEntries = Math.trunc(Number(definitionParts[3] ?? '0'));\n if (\n !ruleCode ||\n Number.isNaN(numEntries) ||\n numEntries < 0 ||\n numEntries > MAX_AFFIX_ENTRIES\n ) {\n if (!Number.isNaN(numEntries) && numEntries > 0) {\n i += Math.min(numEntries, lines.length);\n }\n continue;\n }\n\n const entries: AffixEntry[] = [];\n for (let j = i + 1; j < i + 1 + numEntries; j++) {\n const subline = lines[j] ?? '';\n const lineParts = subline.split(/\\s+/);\n const charactersToRemove = lineParts[2] ?? '0';\n const additionParts = (lineParts[3] ?? '0').split('/');\n let charactersToAdd = additionParts[0] ?? '';\n if (charactersToAdd === '0') {\n charactersToAdd = '';\n }\n const continuationClasses = parseRuleCodes(additionParts[1], flags);\n const regexToMatch = lineParts[4] ?? '.';\n\n const entry: AffixEntry = { add: charactersToAdd };\n if (continuationClasses.length > 0) {\n entry.continuationClasses = continuationClasses;\n }\n if (regexToMatch !== '.') {\n const match =\n ruleType === 'SFX'\n ? tryCompileAffixRegex(`${regexToMatch}$`)\n : tryCompileAffixRegex(`^${regexToMatch}`);\n if (match === undefined) {\n continue;\n }\n entry.match = match;\n }\n if (charactersToRemove !== '0') {\n if (ruleType === 'SFX') {\n const remove = tryCompileAffixRegex(`${charactersToRemove}$`);\n if (remove === undefined) {\n continue;\n }\n entry.remove = remove;\n } else if (charactersToRemove.length <= MAX_AFFIX_PATTERN_LEN) {\n entry.remove = charactersToRemove;\n } else {\n continue;\n }\n }\n entries.push(entry);\n }\n\n rules[ruleCode] = {\n type: ruleType,\n combineable: combineable === 'Y',\n entries,\n };\n i += numEntries;\n } else if (ruleType === 'COMPOUNDRULE') {\n const numEntries = Math.trunc(Number(definitionParts[1] ?? '0'));\n if (Number.isNaN(numEntries) || numEntries < 0 || numEntries > MAX_AFFIX_ENTRIES) {\n if (!Number.isNaN(numEntries) && numEntries > 0) {\n i += Math.min(numEntries, lines.length);\n }\n continue;\n }\n for (let j = i + 1; j < i + 1 + numEntries; j++) {\n const parts = (lines[j] ?? '').split(/\\s+/);\n const pattern = parts[1];\n if (pattern) {\n compoundRulePatterns.push(pattern);\n }\n }\n i += numEntries;\n } else if (ruleType === 'REP') {\n const from = definitionParts[1];\n const to = definitionParts[2];\n if (from !== undefined && to !== undefined && definitionParts.length === 3) {\n replacementTable.push([from, to]);\n }\n } else {\n // ONLYINCOMPOUND, COMPOUNDMIN, FLAG, KEEPCASE, NEEDAFFIX, TRY, WORDCHARS, NOSUGGEST, …\n const value = definitionParts[1];\n if (value !== undefined) {\n flags[ruleType] = value;\n }\n }\n }\n\n return { rules, flags, compoundRulePatterns, replacementTable };\n}\n","import { MAX_APPLY_RULE_DEPTH } from './limits';\nimport type { AffixRule, AffixRules } from './types';\n\n/**\n * Apply one affix rule (and any continuation classes) to a stem.\n */\nexport function applyRule(word: string, rule: AffixRule, rules: AffixRules, depth = 0): string[] {\n const newWords: string[] = [];\n if (depth > MAX_APPLY_RULE_DEPTH) {\n return newWords;\n }\n\n for (const entry of rule.entries) {\n if (entry.match !== undefined && !entry.match.test(word)) {\n continue;\n }\n\n let newWord = word;\n if (entry.remove !== undefined) {\n newWord = newWord.replace(entry.remove, '');\n }\n newWord = rule.type === 'SFX' ? newWord + entry.add : entry.add + newWord;\n newWords.push(newWord);\n\n const cont = entry.continuationClasses;\n if (cont === undefined) {\n continue;\n }\n for (const code of cont) {\n const continuationRule = rules[code];\n if (continuationRule !== undefined) {\n newWords.push(...applyRule(newWord, continuationRule, rules, depth + 1));\n }\n }\n }\n\n return newWords;\n}\n","import { applyRule } from './applyRule';\nimport { parseRuleCodes } from './parseRuleCodes';\nimport type { AffFlags, AffixRules, DictionaryTable } from './types';\n\nfunction removeDicComments(data: string): string {\n // de_DE and some others use tab-indented comment lines.\n return data.replace(/^\\t.*$/gm, '');\n}\n\nexport type ParseDicOptions = {\n rules: AffixRules;\n flags: AffFlags;\n /** Mutated: flag code → stems that carry that compound flag. */\n compoundRuleCodes: Record<string, string[]>;\n};\n\n/**\n * Parse Hunspell `.dic` content into a word→flags lookup table.\n * Also expands affix forms into the table (Typo.js approach).\n */\nexport function parseDic(data: string, options: ParseDicOptions): DictionaryTable {\n const { rules, flags, compoundRuleCodes } = options;\n const cleaned = removeDicComments(data);\n const lines = cleaned.split(/\\r?\\n/);\n const dictionaryTable: DictionaryTable = new Map();\n\n function addWord(word: string, ruleCodes: string[]): void {\n if (!dictionaryTable.has(word)) {\n dictionaryTable.set(word, null);\n }\n if (ruleCodes.length === 0) {\n return;\n }\n if (dictionaryTable.get(word) === null) {\n dictionaryTable.set(word, []);\n }\n const existing = dictionaryTable.get(word);\n if (Array.isArray(existing)) {\n existing.push(ruleCodes);\n }\n }\n\n // First line is the word count — skip it.\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined || line.length === 0) {\n continue;\n }\n\n // Drop morphological fields (`xx:abc`); keep `word` or `word/flags`.\n const justWordAndFlags = line.replace(/\\s.*$/, '');\n const parts = justWordAndFlags.split('/', 2);\n const word = parts[0] ?? '';\n if (word.length === 0) {\n continue;\n }\n\n if (parts.length > 1) {\n const ruleCodesArray = parseRuleCodes(parts[1], flags);\n const needAffix = flags.NEEDAFFIX;\n if (needAffix === undefined || !ruleCodesArray.includes(needAffix)) {\n addWord(word, ruleCodesArray);\n }\n\n for (let j = 0; j < ruleCodesArray.length; j++) {\n const code = ruleCodesArray[j];\n if (code === undefined) {\n continue;\n }\n const rule = rules[code];\n if (rule !== undefined) {\n const newWords = applyRule(word, rule, rules);\n for (const newWord of newWords) {\n addWord(newWord, []);\n if (rule.combineable) {\n for (let k = j + 1; k < ruleCodesArray.length; k++) {\n const combineCode = ruleCodesArray[k];\n if (combineCode === undefined) {\n continue;\n }\n const combineRule = rules[combineCode];\n if (\n combineRule !== undefined &&\n combineRule.combineable &&\n rule.type !== combineRule.type\n ) {\n for (const other of applyRule(newWord, combineRule, rules)) {\n addWord(other, []);\n }\n }\n }\n }\n }\n }\n const compoundStems = compoundRuleCodes[code];\n if (compoundStems !== undefined) {\n compoundStems.push(word);\n }\n }\n } else {\n addWord(word.trim(), []);\n }\n }\n\n return dictionaryTable;\n}\n","import { check, hasFlag } from './check';\nimport { MAX_SUGGEST_MEMO, MAX_SUGGEST_WORD_LEN } from './limits';\nimport type { HunspellState } from './types';\n\nfunction ensureAlphabet(state: HunspellState): string {\n if (state.alphabet.length > 0) {\n return state.alphabet;\n }\n\n // Hunspell: suggestion edits come from TRY (+ WORDCHARS). Latin fallback is only\n // for incomplete .aff files — do not force English letters onto every locale.\n const LATIN_FALLBACK = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n let alphabet = '';\n if (state.flags.TRY !== undefined && state.flags.TRY.length > 0) {\n alphabet = state.flags.TRY;\n } else {\n alphabet = LATIN_FALLBACK;\n }\n if (state.flags.WORDCHARS !== undefined) {\n alphabet += state.flags.WORDCHARS;\n }\n\n const seen = new Set<string>();\n let unique = '';\n for (const ch of alphabet) {\n if (!seen.has(ch)) {\n seen.add(ch);\n unique += ch;\n }\n }\n state.alphabet = unique;\n return unique;\n}\n\ntype EditCounts = Record<string, number>;\n\nfunction edits1(\n state: HunspellState,\n words: Record<string, true | number>,\n knownOnly: boolean\n): EditCounts {\n const alphabet = ensureAlphabet(state);\n const rv: EditCounts = {};\n\n const bump = (edit: string): void => {\n if (knownOnly && !check(state, edit)) {\n return;\n }\n rv[edit] = (rv[edit] ?? 0) + 1;\n };\n\n for (const word of Object.keys(words)) {\n for (let i = 0; i <= word.length; i++) {\n const left = word.slice(0, i);\n const right = word.slice(i);\n\n if (right.length > 0) {\n bump(left + right.slice(1));\n }\n\n const right0 = right[0];\n const right1 = right[1];\n if (right.length > 1 && right1 !== undefined && right0 !== undefined && right1 !== right0) {\n bump(left + right1 + right0 + right.slice(2));\n }\n\n if (right.length > 0) {\n const lettercase =\n right.slice(0, 1).toUpperCase() === right.slice(0, 1) ? 'uppercase' : 'lowercase';\n for (const ch of alphabet) {\n let replacementLetter = ch;\n if (lettercase === 'uppercase') {\n replacementLetter = replacementLetter.toUpperCase();\n }\n if (replacementLetter !== right.slice(0, 1)) {\n bump(left + replacementLetter + right.slice(1));\n }\n }\n }\n\n if (right.length > 0) {\n // Capitalize insert when both neighbors are uppercase.\n const leftTail = left.slice(-1);\n const rightHead = right.slice(0, 1);\n const lettercase =\n leftTail.toUpperCase() === leftTail && rightHead.toUpperCase() === rightHead\n ? 'uppercase'\n : 'lowercase';\n for (const ch of alphabet) {\n let replacementLetter = ch;\n if (lettercase === 'uppercase') {\n replacementLetter = replacementLetter.toUpperCase();\n }\n bump(left + replacementLetter + right);\n }\n }\n }\n }\n\n return rv;\n}\n\nfunction rankCorrections(state: HunspellState, word: string, limit: number): string[] {\n // Norvig tier-1: known edit-distance-1 first (fast path for near-misses).\n const tier1 = edits1(state, { [word]: true }, true);\n let weighted: EditCounts = tier1;\n\n if (Object.keys(tier1).length === 0) {\n // Tier-2: expand unknown ed1 seeds, then keep only known ed2 forms.\n const ed1 = edits1(state, { [word]: true }, false);\n const unknownSeeds: Record<string, true | number> = {};\n let seedCount = 0;\n const MAX_ED2_SEEDS = 64;\n for (const [candidate, weight] of Object.entries(ed1)) {\n if (check(state, candidate)) {\n weighted[candidate] = (weighted[candidate] ?? 0) + weight;\n continue;\n }\n if (seedCount >= MAX_ED2_SEEDS) {\n continue;\n }\n unknownSeeds[candidate] = weight;\n seedCount += 1;\n }\n if (Object.keys(unknownSeeds).length > 0) {\n const ed2 = edits1(state, unknownSeeds, true);\n for (const [candidate, weight] of Object.entries(ed2)) {\n weighted[candidate] = (weighted[candidate] ?? 0) + weight;\n }\n }\n }\n\n const sorted: Array<[string, number]> = [];\n for (const [candidate, weight] of Object.entries(weighted)) {\n let w = weight;\n if (hasFlag(state, candidate, 'PRIORITYSUGGEST')) {\n w += 1000;\n }\n sorted.push([candidate, w]);\n }\n\n sorted.sort((a, b) => {\n if (a[1] !== b[1]) {\n return b[1] - a[1];\n }\n return a[0].localeCompare(b[0]);\n });\n\n let capitalizationScheme: 'lowercase' | 'uppercase' | 'capitalized' = 'lowercase';\n if (word.toUpperCase() === word) {\n capitalizationScheme = 'uppercase';\n } else if (word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() === word) {\n capitalizationScheme = 'capitalized';\n }\n\n const rv: string[] = [];\n let workingLimit = limit;\n for (let i = 0; i < Math.min(workingLimit, sorted.length); i++) {\n const entry = sorted[i];\n if (entry === undefined) {\n break;\n }\n let suggestion = entry[0];\n if (capitalizationScheme === 'uppercase') {\n suggestion = suggestion.toUpperCase();\n } else if (capitalizationScheme === 'capitalized') {\n suggestion = suggestion.slice(0, 1).toUpperCase() + suggestion.slice(1);\n }\n if (!hasFlag(state, suggestion, 'NOSUGGEST') && !rv.includes(suggestion)) {\n rv.push(suggestion);\n } else {\n workingLimit++;\n }\n }\n return rv;\n}\n\n/**\n * Suggest corrections for a misspelling (REP table + edit-distance ranking).\n */\nexport function suggest(state: HunspellState, word: string, limit = 5): string[] {\n if (word.length === 0 || word.length > MAX_SUGGEST_WORD_LEN) {\n return [];\n }\n\n const normalized = word.replaceAll('\\u2019', \"'\");\n const memo = state.memoized[normalized];\n if (memo !== undefined && (limit <= memo.limit || memo.suggestions.length < memo.limit)) {\n return memo.suggestions.slice(0, limit);\n }\n\n if (check(state, normalized)) {\n return [];\n }\n\n for (const [from, to] of state.replacementTable) {\n if (normalized.includes(from)) {\n const corrected = normalized.replace(from, to);\n if (check(state, corrected)) {\n const suggestions = [corrected];\n remember(state, normalized, suggestions, limit);\n return suggestions;\n }\n }\n }\n\n const suggestions = rankCorrections(state, normalized, limit);\n remember(state, normalized, suggestions, limit);\n return suggestions;\n}\n\nfunction remember(state: HunspellState, word: string, suggestions: string[], limit: number): void {\n const keys = Object.keys(state.memoized);\n if (keys.length >= MAX_SUGGEST_MEMO) {\n const drop = keys[0];\n if (drop !== undefined) {\n delete state.memoized[drop];\n }\n }\n state.memoized[word] = { suggestions, limit };\n}\n","import { check, checkExact } from './check';\nimport { escapeRegExp, MAX_AFF_BYTES, MAX_COMPOUND_REGEX_SOURCE, MAX_DIC_BYTES } from './limits';\nimport { parseAff } from './parseAff';\nimport { parseDic } from './parseDic';\nimport { suggest } from './suggest';\nimport type { HunspellDictionary, HunspellState } from './types';\n\nfunction buildCompoundRegexes(\n patterns: string[],\n compoundRuleCodes: Record<string, string[]>\n): RegExp[] {\n const out: RegExp[] = [];\n for (const ruleText of patterns) {\n let expressionText = '';\n for (const character of ruleText) {\n if (Object.hasOwn(compoundRuleCodes, character)) {\n const stems = compoundRuleCodes[character] ?? [];\n expressionText += `(${stems.map(escapeRegExp).join('|')})`;\n } else {\n // Hunspell COMPOUNDRULE operators (e.g. `*` in `n*1t`) must stay unescaped.\n expressionText += character;\n }\n }\n if (expressionText.length > MAX_COMPOUND_REGEX_SOURCE) {\n continue;\n }\n try {\n out.push(new RegExp(`^${expressionText}$`, 'i'));\n } catch {\n // Malformed pattern after escaping — skip this compound rule.\n }\n }\n return out;\n}\n\n/**\n * Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.\n * Dictionaries are not bundled — callers fetch/load the files.\n */\nexport function createDictionary(affData: string, dicData: string): HunspellDictionary {\n if (affData.length > MAX_AFF_BYTES || dicData.length > MAX_DIC_BYTES) {\n throw new Error('dictionary too large');\n }\n\n const { rules, flags, compoundRulePatterns, replacementTable } = parseAff(affData);\n\n const compoundRuleCodes: Record<string, string[]> = {};\n for (const pattern of compoundRulePatterns) {\n for (const ch of pattern) {\n compoundRuleCodes[ch] = [];\n }\n }\n if (flags.ONLYINCOMPOUND !== undefined) {\n compoundRuleCodes[flags.ONLYINCOMPOUND] = [];\n }\n\n const dictionaryTable = parseDic(dicData, { rules, flags, compoundRuleCodes });\n\n for (const code of Object.keys(compoundRuleCodes)) {\n if ((compoundRuleCodes[code] ?? []).length === 0) {\n delete compoundRuleCodes[code];\n }\n }\n\n const compoundRules = buildCompoundRegexes(compoundRulePatterns, compoundRuleCodes);\n\n const state: HunspellState = {\n dictionaryTable,\n compoundRules,\n replacementTable,\n flags,\n memoized: {},\n alphabet: '',\n };\n\n return {\n loaded: true,\n check: (word: string) => check(state, word),\n checkExact: (word: string) => checkExact(state, word),\n suggest: (word: string, limit?: number) => suggest(state, word, limit),\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,GAChB,MAAM,SAAS,CAAC"}
|