@cspell/cspell-tools 10.4.0-alpha.1 → 10.4.1

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.
@@ -53,12 +53,6 @@
53
53
  "description": "Words in the `allowedSplitWords` are considered correct and can be used as a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed, then only the individual words are added, otherwise the entire entry is added. This is to prevent misspellings in CamelCase words from being introduced into the dictionary.",
54
54
  "markdownDescription": "Words in the `allowedSplitWords` are considered correct and can be used\nas a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed,\nthen only the individual words are added, otherwise the entire entry is added.\nThis is to prevent misspellings in CamelCase words from being introduced into the\ndictionary."
55
55
  },
56
- "keepRawCase": {
57
- "default": false,
58
- "description": "Do not generate lower case / accent free versions of words.",
59
- "markdownDescription": "Do not generate lower case / accent free versions of words.",
60
- "type": "boolean"
61
- },
62
56
  "listFile": {
63
57
  "$ref": "#/definitions/FilePath"
64
58
  },
@@ -128,12 +122,6 @@
128
122
  "filename": {
129
123
  "$ref": "#/definitions/FilePath"
130
124
  },
131
- "keepRawCase": {
132
- "default": false,
133
- "description": "Do not generate lower case / accent free versions of words.",
134
- "markdownDescription": "Do not generate lower case / accent free versions of words.",
135
- "type": "boolean"
136
- },
137
125
  "maxDepth": {
138
126
  "description": "Maximum number of nested Hunspell Rules to apply. This is needed for recursive dictionaries like Hebrew.",
139
127
  "markdownDescription": "Maximum number of nested Hunspell Rules to apply.\nThis is needed for recursive dictionaries like Hebrew.",
@@ -189,21 +177,6 @@
189
177
  "Target": {
190
178
  "additionalProperties": false,
191
179
  "properties": {
192
- "allowedSplitWords": {
193
- "anyOf": [
194
- {
195
- "$ref": "#/definitions/FilePath"
196
- },
197
- {
198
- "items": {
199
- "$ref": "#/definitions/FilePath"
200
- },
201
- "type": "array"
202
- }
203
- ],
204
- "description": "Words in the `allowedSplitWords` are considered correct and can be used as a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed, then only the individual words are added, otherwise the entire entry is added. This is to prevent misspellings in CamelCase words from being introduced into the dictionary.",
205
- "markdownDescription": "Words in the `allowedSplitWords` are considered correct and can be used\nas a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed,\nthen only the individual words are added, otherwise the entire entry is added.\nThis is to prevent misspellings in CamelCase words from being introduced into the\ndictionary."
206
- },
207
180
  "bTrie": {
208
181
  "anyOf": [
209
182
  {
@@ -270,6 +243,11 @@
270
243
  "markdownDescription": "If `compress` is true, setting this value to true will also keep an uncompressed version of the dictionary.",
271
244
  "type": "boolean"
272
245
  },
246
+ "locale": {
247
+ "description": "The locale of the dictionary's words: a language and an optional region, such as `tr` or `tr-TR`. Words are lower-cased with this locale's case rules, for example, when generating the case insensitive forms with `generateNonStrict`. Without it, the default case rules are used.\n\nSet `dictionaryInformation.locale` in the dictionary definition to the same locale, so cspell searches the dictionary with the case rules it was built with. If the definition lists more than one locale, cspell uses the first valid one for case rules, so put this locale first.",
248
+ "markdownDescription": "The locale of the dictionary's words: a language and an optional region, such as `tr` or `tr-TR`.\nWords are lower-cased with this locale's case rules, for example, when generating the case insensitive\nforms with `generateNonStrict`. Without it, the default case rules are used.\n\nSet `dictionaryInformation.locale` in the dictionary definition to the same locale, so cspell searches the\ndictionary with the case rules it was built with. If the definition lists more than one locale, cspell uses the\nfirst valid one for case rules, so put this locale first.",
249
+ "type": "string"
250
+ },
273
251
  "name": {
274
252
  "description": "Name of target, used as the basis of target file name.",
275
253
  "markdownDescription": "Name of target, used as the basis of target file name.",
@@ -327,21 +305,6 @@
327
305
  "markdownDescription": "Url to JSON Schema",
328
306
  "type": "string"
329
307
  },
330
- "allowedSplitWords": {
331
- "anyOf": [
332
- {
333
- "$ref": "#/definitions/FilePath"
334
- },
335
- {
336
- "items": {
337
- "$ref": "#/definitions/FilePath"
338
- },
339
- "type": "array"
340
- }
341
- ],
342
- "description": "Words in the `allowedSplitWords` are considered correct and can be used as a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed, then only the individual words are added, otherwise the entire entry is added. This is to prevent misspellings in CamelCase words from being introduced into the dictionary.",
343
- "markdownDescription": "Words in the `allowedSplitWords` are considered correct and can be used\nas a basis for splitting compound words.\n\nIf entries can be split so that all the words in the entry are allowed,\nthen only the individual words are added, otherwise the entire entry is added.\nThis is to prevent misspellings in CamelCase words from being introduced into the\ndictionary."
344
- },
345
308
  "checksumFile": {
346
309
  "description": "Path to checksum file. `true` - defaults to `./checksum.txt`.",
347
310
  "markdownDescription": "Path to checksum file. `true` - defaults to `./checksum.txt`.",
@@ -364,22 +327,10 @@
364
327
  "markdownDescription": "Generate lower case / accent free versions of words.",
365
328
  "type": "boolean"
366
329
  },
367
- "keepRawCase": {
368
- "default": false,
369
- "description": "Do not generate lower case / accent free versions of words.",
370
- "markdownDescription": "Do not generate lower case / accent free versions of words.",
371
- "type": "boolean"
372
- },
373
- "maxDepth": {
374
- "description": "Maximum number of nested Hunspell Rules to apply. This is needed for recursive dictionaries like Hebrew.",
375
- "markdownDescription": "Maximum number of nested Hunspell Rules to apply.\nThis is needed for recursive dictionaries like Hebrew.",
376
- "type": "number"
377
- },
378
- "minCompoundLength": {
379
- "default": 4,
380
- "description": "Controls the minimum length of a compound word when storing words using `storeSplitWordsAsCompounds`. The compound words are prefixed / suffixed with `*`, to allow them to be combined with other compound words. If the length is too low, then the dictionary will consider many misspelled words as correct.",
381
- "markdownDescription": "Controls the minimum length of a compound word when storing words using `storeSplitWordsAsCompounds`.\nThe compound words are prefixed / suffixed with `*`, to allow them to be combined with other compound words.\nIf the length is too low, then the dictionary will consider many misspelled words as correct.",
382
- "type": "number"
330
+ "locale": {
331
+ "description": "The locale of the dictionary's words: a language and an optional region, such as `tr` or `tr-TR`. Words are lower-cased with this locale's case rules, for example, when generating the case insensitive forms with `generateNonStrict`. Without it, the default case rules are used.\n\nSet `dictionaryInformation.locale` in the dictionary definition to the same locale, so cspell searches the dictionary with the case rules it was built with. If the definition lists more than one locale, cspell uses the first valid one for case rules, so put this locale first.",
332
+ "markdownDescription": "The locale of the dictionary's words: a language and an optional region, such as `tr` or `tr-TR`.\nWords are lower-cased with this locale's case rules, for example, when generating the case insensitive\nforms with `generateNonStrict`. Without it, the default case rules are used.\n\nSet `dictionaryInformation.locale` in the dictionary definition to the same locale, so cspell searches the\ndictionary with the case rules it was built with. If the definition lists more than one locale, cspell uses the\nfirst valid one for case rules, so put this locale first.",
333
+ "type": "string"
383
334
  },
384
335
  "removeDuplicates": {
385
336
  "default": false,
@@ -398,26 +349,6 @@
398
349
  "markdownDescription": "Sort the words in the resulting dictionary.\nDoes not apply to `trie` based formats.",
399
350
  "type": "boolean"
400
351
  },
401
- "split": {
402
- "anyOf": [
403
- {
404
- "type": "boolean"
405
- },
406
- {
407
- "const": "legacy",
408
- "type": "string"
409
- }
410
- ],
411
- "default": false,
412
- "description": "Split lines into words.",
413
- "markdownDescription": "Split lines into words."
414
- },
415
- "storeSplitWordsAsCompounds": {
416
- "default": false,
417
- "description": "Camel case words that have been split using the `allowedSplitWords` are added to the dictionary as compoundable words. These words are prefixed / suffixed with `*`.",
418
- "markdownDescription": "Camel case words that have been split using the `allowedSplitWords` are added to the dictionary as compoundable words.\nThese words are prefixed / suffixed with `*`.",
419
- "type": "boolean"
420
- },
421
352
  "targets": {
422
353
  "description": "Optional Target Dictionaries to create.",
423
354
  "items": {
package/dist/app.d.mts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Command } from "commander";
2
- //#region src/FeatureFlags/FeatureFlags.d.ts
3
2
  interface FeatureFlag {
4
3
  name: string;
5
4
  description: string;
@@ -26,9 +25,6 @@ declare class FeatureFlags {
26
25
  help(): string;
27
26
  fork(): FeatureFlags;
28
27
  }
29
- //#endregion
30
- //#region src/app.d.ts
31
- declare function run(program: Command, argv: string[], flags?: FeatureFlags): Promise<void>;
32
- //#endregion
33
- export { run };
28
+ export declare function run(program: Command, argv: string[], flags?: FeatureFlags): Promise<void>;
29
+ export {};
34
30
  //# sourceMappingURL=app.d.mts.map
package/dist/app.mjs CHANGED
@@ -1,18 +1,27 @@
1
- import { a as reportCheckChecksumFile, c as toError, i as logWithTimestamp, l as generateBTrie, n as compile, o as reportChecksumForFiles, r as setLogger, s as updateChecksumForFiles, t as build, u as compressFile } from "./build-Fod7lDHd.mjs";
1
+ import { a as reportCheckChecksumFile, c as toError, i as logWithTimestamp, l as generateBTrie, n as compile, o as reportChecksumForFiles, r as setLogger, s as updateChecksumForFiles, t as build, u as compressFile } from "./build-CkIhBjXk.mjs";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { CommanderError, Option } from "commander";
4
- import { writeFile } from "node:fs/promises";
4
+ import { glob, writeFile } from "node:fs/promises";
5
5
  import * as path from "node:path";
6
- import { glob } from "glob";
6
+ import { join, relative, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { toArray } from "@cspell/cspell-pipe";
7
9
  import { opConcatMap, pipe } from "@cspell/cspell-pipe/sync";
8
10
  import YAML from "yaml";
9
- //#region src/util/globP.ts
11
+ // src/util/globP.ts
10
12
  function globP(pattern, options) {
11
13
  const globs = (Array.isArray(pattern) ? pattern : [pattern]).map((pattern) => pattern.replaceAll("\\", "/"));
12
- return glob(globs, options);
14
+ const cwd = options?.cwd ?? ".";
15
+ const root = resolve(cwd instanceof URL ? fileURLToPath(cwd) : cwd);
16
+ return options?.nodir ? globNonDirectories(globs, root) : toArray(glob(globs, { cwd: root }));
13
17
  }
14
- //#endregion
15
- //#region src/gzip/gzip.ts
18
+ async function globNonDirectories(globs, root) {
19
+ return (await toArray(glob(globs, {
20
+ cwd: root,
21
+ withFileTypes: true
22
+ }))).filter((entry) => !entry.isDirectory()).map((entry) => relative(root, join(entry.parentPath, entry.name)));
23
+ }
24
+ // src/gzip/gzip.ts
16
25
  /**
17
26
  * GZip files matching the given globs.
18
27
  * @param globs - array of globs to gzip
@@ -22,24 +31,20 @@ async function gzipFiles(globs, os) {
22
31
  const files = await globP(globs, { nodir: true });
23
32
  for (const fileName of files) await compressFile(fileName, os);
24
33
  }
25
- //#endregion
26
- //#region src/compiler/createCompileRequest.ts
34
+ // src/compiler/createCompileRequest.ts
27
35
  function createCompileRequest(sourceFiles, options) {
28
36
  options = { ...options };
29
37
  options.maxDepth ??= options.max_depth;
30
- const { maxDepth, split, keepRawCase, useLegacySplitter } = options;
31
- return {
32
- targets: calcTargets([...sourceFiles, ...(options.listFile || []).map((listFile) => ({ listFile }))], options),
33
- maxDepth: parseNumber(maxDepth),
34
- split: useLegacySplitter ? "legacy" : split,
35
- /**
36
- * Do not generate lower case / accent free versions of words.
37
- * @default false
38
- */
39
- keepRawCase
38
+ return { targets: calcTargets([...sourceFiles, ...(options.listFile || []).map((listFile) => ({ listFile }))], options, calcSourceOptions(options)) };
39
+ }
40
+ function calcSourceOptions(options) {
41
+ const sourceOptions = {
42
+ maxDepth: parseNumber(options.maxDepth),
43
+ split: options.useLegacySplitter ? "legacy" : options.split
40
44
  };
45
+ return Object.fromEntries(Object.entries(sourceOptions).filter(([, v]) => v !== void 0));
41
46
  }
42
- function calcTargets(sources, options) {
47
+ function calcTargets(sources, options, sourceOptions) {
43
48
  const { merge, output = ".", experimental = [] } = options;
44
49
  const generateNonStrict = experimental.includes("compound") || void 0;
45
50
  const format = calcFormat(options);
@@ -49,7 +54,7 @@ function calcTargets(sources, options) {
49
54
  targetDirectory: output,
50
55
  compress: options.compress,
51
56
  format,
52
- sources: sources.map(normalizeSource),
57
+ sources: sources.map((source) => normalizeSource(source, sourceOptions)),
53
58
  sort,
54
59
  trieBase: parseNumber(options.trieBase),
55
60
  generateNonStrict,
@@ -61,7 +66,7 @@ function calcTargets(sources, options) {
61
66
  targetDirectory: output,
62
67
  compress: options.compress,
63
68
  format,
64
- sources: [normalizeSource(source)],
69
+ sources: [normalizeSource(source, sourceOptions)],
65
70
  sort: options.sort,
66
71
  trieBase: parseNumber(options.trieBase),
67
72
  generateNonStrict,
@@ -85,26 +90,37 @@ function baseNameOfSource(source) {
85
90
  function isFileSource(source) {
86
91
  return typeof source !== "string" && source.filename !== void 0;
87
92
  }
88
- function normalizeSource(source) {
89
- if (typeof source === "string") return normalizeSourcePath(source);
90
- if (isFileSource(source)) return {
91
- ...source,
92
- filename: normalizeSourcePath(source.filename)
93
- };
93
+ function normalizeSource(source, sourceOptions) {
94
+ const hasOptions = Object.keys(sourceOptions).length > 0;
95
+ if (typeof source === "string") {
96
+ const filename = normalizeSourcePath(source);
97
+ return hasOptions ? {
98
+ filename,
99
+ ...sourceOptions
100
+ } : filename;
101
+ }
102
+ if (isFileSource(source)) {
103
+ const { filename, ...rest } = source;
104
+ return {
105
+ filename: normalizeSourcePath(filename),
106
+ ...sourceOptions,
107
+ ...rest
108
+ };
109
+ }
110
+ const { listFile, ...rest } = source;
94
111
  return {
95
- ...source,
96
- listFile: normalizeSourcePath(source.listFile)
112
+ listFile: normalizeSourcePath(listFile),
113
+ ...sourceOptions,
114
+ ...rest
97
115
  };
98
116
  }
99
117
  function normalizeSourcePath(source) {
100
118
  const cwd = process.cwd();
101
119
  return path.relative(cwd, source).replaceAll("\\", "/");
102
120
  }
103
- //#endregion
104
- //#region src/config/config.ts
121
+ // src/config/config.ts
105
122
  const configFileSchemaURL = "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/packages/cspell-tools/cspell-tools.config.schema.json";
106
- //#endregion
107
- //#region src/FeatureFlags/FeatureFlags.ts
123
+ // src/FeatureFlags/FeatureFlags.ts
108
124
  let systemFeatureFlags;
109
125
  /**
110
126
  * Feature Flags are used to turn on/off features.
@@ -195,8 +211,7 @@ function toBool(value) {
195
211
  if (typeof value !== "string") return value;
196
212
  return boolValues[value.toLowerCase()];
197
213
  }
198
- //#endregion
199
- //#region src/FeatureFlags/parseFlags.ts
214
+ // src/FeatureFlags/parseFlags.ts
200
215
  const splitFlag = /[:=]/;
201
216
  const leadingEql = /^=/;
202
217
  function parseFlags(ff, flags) {
@@ -214,8 +229,7 @@ function parseFlags(ff, flags) {
214
229
  }
215
230
  return ff;
216
231
  }
217
- //#endregion
218
- //#region src/compile.ts
232
+ // src/compile.ts
219
233
  getSystemFeatureFlags().register("compound", "Enable compound dictionary sources.");
220
234
  const defaultConfigFile = "cspell-tools.config.yaml";
221
235
  const configFileHeader = `# yaml-language-server: $schema=${configFileSchemaURL}\n\n`;
@@ -245,8 +259,7 @@ To build, use:
245
259
  cspell-tools-cli build
246
260
  `);
247
261
  }
248
- //#endregion
249
- //#region src/app.ts
262
+ // src/app.ts
250
263
  const npmPackageRaw = readFileSync(new URL("../package.json", import.meta.url), "utf8");
251
264
  const npmPackage = JSON.parse(npmPackageRaw);
252
265
  setLogger(logWithTimestamp);
@@ -254,7 +267,9 @@ function collect(value, previous) {
254
267
  return [...previous, value];
255
268
  }
256
269
  function addCompileOptions(compileCommand) {
257
- return compileCommand.option("-o, --output <path>", "Specify the output directory, otherwise files are written back to the same location.").option("-n, --no-compress", "By default the files are GZipped, this will turn off GZ compression.").option("-m, --max_depth <limit>", "Maximum depth to apply suffix rules.").option("-M, --merge <target>", "Merge all files into a single target file (extensions are applied)").option("--split", "Split each line", void 0).option("--no-split", "Treat each line as a dictionary entry, do not split").option("--use-legacy-splitter", "Do not use legacy line splitter logic.").option("--keep-raw-case", "Do not normalize words before adding them to dictionary.").option("-x, --experimental <flag>", "Experimental flags, used for testing new concepts. Flags: compound", collect, []).option("--trie3", "Use file format trie3", false).option("--trie4", "Use file format trie4", false).option("--trie-base <number>", "Advanced: Set the trie base number. A value between 10 and 36").option("--list-file <filename...>", "Path to a file that contains the list of files to compile. A list file contains one file per line.").option("--init", "Create a build command `cspell-tools.config.yaml` file based upon the options given instead of building.");
270
+ return compileCommand.option("-o, --output <path>", "Specify the output directory, otherwise files are written back to the same location.").option("-n, --no-compress", "By default the files are GZipped, this will turn off GZ compression.").option("-m, --max_depth <limit>", "Maximum depth to apply suffix rules.").option("-M, --merge <target>", "Merge all files into a single target file (extensions are applied)").option("--split", "Split each line", void 0).option("--no-split", "Treat each line as a dictionary entry, do not split").option("--use-legacy-splitter", "Do not use legacy line splitter logic.").addOption(new Option("--keep-raw-case", "Has no effect.").hideHelp()).on("option:keep-raw-case", () => {
271
+ console.warn("--keep-raw-case has no effect and will be removed in future versions.");
272
+ }).option("-x, --experimental <flag>", "Experimental flags, used for testing new concepts. Flags: compound", collect, []).option("--trie3", "Use file format trie3", false).option("--trie4", "Use file format trie4", false).option("--trie-base <number>", "Advanced: Set the trie base number. A value between 10 and 36").option("--list-file <filename...>", "Path to a file that contains the list of files to compile. A list file contains one file per line.").option("--init", "Create a build command `cspell-tools.config.yaml` file based upon the options given instead of building.");
258
273
  }
259
274
  async function run(program, argv, flags) {
260
275
  async function handleGzip(files) {
@@ -287,7 +302,6 @@ async function run(program, argv, flags) {
287
302
  program.command("shasum [files...]").description("Calculate the checksum for files.").option("--list-file <list-file.txt...>", "Specify one or more files that contain paths of files to check.").option("-c, --check <checksum.txt>", "Verify the checksum of files against those stored in the checksum.txt file.").addOption(new Option("-u, --update <checksum.txt>", "Update checksums found in the file.").conflicts("--check")).option("-r, --root <root>", "Specify the root to use for relative paths. The current working directory is used by default.").action(shasum);
288
303
  await program.parseAsync(argv);
289
304
  }
290
- //#endregion
291
305
  export { run };
292
306
 
293
307
  //# sourceMappingURL=app.mjs.map
@@ -6,15 +6,15 @@ import * as Trie from "cspell-trie-lib";
6
6
  import { COMPOUND_FIX, FORBID_PREFIX, createDictionaryLineParser, decodeTrie, encodeTrieDataToBTrie, parseDictionary } from "cspell-trie-lib";
7
7
  import { promisify } from "node:util";
8
8
  import { gunzip, gunzipSync, gzip } from "node:zlib";
9
+ import { pipeAsync, toArray } from "@cspell/cspell-pipe";
9
10
  import { opAppend, opCombine, opConcatMap, opFilter, opMap, pipe } from "@cspell/cspell-pipe/sync";
10
11
  import * as HR from "hunspell-reader";
11
12
  import { uniqueFilter } from "hunspell-reader";
12
13
  import { cosmiconfig } from "cosmiconfig";
13
- import { pipeAsync, toArray } from "@cspell/cspell-pipe";
14
14
  import { opAwaitAsync, opMapAsync } from "@cspell/cspell-pipe/operators";
15
15
  import { createHash } from "node:crypto";
16
16
  import { Buffer } from "node:buffer";
17
- //#region src/gzip/compressFiles.ts
17
+ // src/gzip/compressFiles.ts
18
18
  const gzip$1 = promisify(gzip);
19
19
  const gunzip$1 = promisify(gunzip);
20
20
  const OSSystemIDOffset = 9;
@@ -55,8 +55,7 @@ async function decompress(buf, encoding) {
55
55
  if (!encoding) return dBuf;
56
56
  return (await dBuf).toString(encoding);
57
57
  }
58
- //#endregion
59
- //#region src/util/writeFile.ts
58
+ // src/util/writeFile.ts
60
59
  /**
61
60
  * Writes a file to disk and compress as needed.
62
61
  *
@@ -74,11 +73,9 @@ async function writeFile$1(filePath, data) {
74
73
  const toWrite = filePath.endsWith(".gz") ? await compressIfNeeded(buf) : buf;
75
74
  return fs.writeFile(filePath, toWrite);
76
75
  }
77
- //#endregion
78
- //#region src/compiler/readers/regHunspellFile.ts
76
+ // src/compiler/readers/regHunspellFile.ts
79
77
  const regHunspellFile = /\.(dic|aff)$/i;
80
- //#endregion
81
- //#region src/compiler/readers/readHunspellFiles.ts
78
+ // src/compiler/readers/readHunspellFiles.ts
82
79
  const DEDUPE_SIZE = 1e3;
83
80
  async function readHunspellFiles(filename, options) {
84
81
  const dicFile = filename.replace(regHunspellFile, ".dic");
@@ -134,8 +131,7 @@ function* _mapAffWords(affWords) {
134
131
  } else yield forbid + word;
135
132
  }
136
133
  }
137
- //#endregion
138
- //#region src/compiler/readers/readTextFile.ts
134
+ // src/compiler/readers/readTextFile.ts
139
135
  const isGzFile$1 = /\.gz$/;
140
136
  function readTextFile(filename) {
141
137
  return readFile$2(filename).then((buffer) => {
@@ -145,8 +141,7 @@ function readTextFile(filename) {
145
141
  function readFile$2(filename) {
146
142
  return promises.readFile(filename).then(async (buffer) => isGzFile$1.test(filename) ? decompress(buffer) : buffer);
147
143
  }
148
- //#endregion
149
- //#region src/compiler/readers/textFileReader.ts
144
+ // src/compiler/readers/textFileReader.ts
150
145
  async function textFileReader$1(filename) {
151
146
  const words = (await readTextFile(filename)).split("\n").map((s) => s.trim());
152
147
  let trie;
@@ -165,8 +160,7 @@ async function textFileReader$1(filename) {
165
160
  }
166
161
  };
167
162
  }
168
- //#endregion
169
- //#region src/compiler/readers/trieFileReader.ts
163
+ // src/compiler/readers/trieFileReader.ts
170
164
  async function trieFileReader(filename) {
171
165
  return TrieFileReader.create(filename);
172
166
  }
@@ -197,8 +191,7 @@ var TrieFileReader = class TrieFileReader {
197
191
  return new TrieFileReader(filename, trie);
198
192
  }
199
193
  };
200
- //#endregion
201
- //#region src/compiler/Reader.ts
194
+ // src/compiler/Reader.ts
202
195
  const readers = [
203
196
  {
204
197
  test: /\.trie\b/,
@@ -221,8 +214,7 @@ async function createReader(filename, options) {
221
214
  const baseReader = await findMatchingReader(filename, options);
222
215
  return Object.assign(baseReader, { [Symbol.iterator]: () => baseReader.lines[Symbol.iterator]() });
223
216
  }
224
- //#endregion
225
- //#region src/compiler/bTrie.ts
217
+ // src/compiler/bTrie.ts
226
218
  async function generateBTrieFromFile(file, options) {
227
219
  const log = options.logger || console.log.bind(console);
228
220
  log(`Processing file: ${file}`);
@@ -253,13 +245,11 @@ async function createBTrieFromFile(file, buildOptions) {
253
245
  const trie = (await createReader(file, {})).toTrie();
254
246
  return encodeTrieDataToBTrie(trie.data, buildOptions);
255
247
  }
256
- //#endregion
257
- //#region src/bTrie.ts
248
+ // src/bTrie.ts
258
249
  function generateBTrie(files, options) {
259
250
  return generateBTrieFromFiles(files, options);
260
251
  }
261
- //#endregion
262
- //#region src/config/configUtils.ts
252
+ // src/config/configUtils.ts
263
253
  function isFilePath(source) {
264
254
  return typeof source === "string";
265
255
  }
@@ -271,8 +261,7 @@ function isFileListSource(source) {
271
261
  if (!source || isFilePath(source)) return false;
272
262
  return source.listFile !== void 0;
273
263
  }
274
- //#endregion
275
- //#region src/config/normalizeConfig.ts
264
+ // src/config/normalizeConfig.ts
276
265
  function normalizeConfig(cfg) {
277
266
  if (!cfg) return cfg;
278
267
  const dir = path.dirname(cfg.filepath);
@@ -285,8 +274,7 @@ function normalizeConfig(cfg) {
285
274
  filepath: cfg.filepath
286
275
  };
287
276
  }
288
- //#endregion
289
- //#region src/shasum/checksum.ts
277
+ // src/shasum/checksum.ts
290
278
  function calcChecksum(buf, alg = "SHA1") {
291
279
  const hash = createHash(alg);
292
280
  hash.update(buf);
@@ -298,8 +286,7 @@ async function calcFileChecksum(filename, alg) {
298
286
  async function checkFile(checksum, filename, alg) {
299
287
  return await calcFileChecksum(filename, alg) === checksum;
300
288
  }
301
- //#endregion
302
- //#region src/util/errors.ts
289
+ // src/util/errors.ts
303
290
  function toError(err) {
304
291
  if (isError(err)) return err;
305
292
  return /* @__PURE__ */ new Error(`${err}`);
@@ -308,13 +295,11 @@ function isError(err) {
308
295
  if (err instanceof Error) return true;
309
296
  return false;
310
297
  }
311
- //#endregion
312
- //#region src/util/index.ts
298
+ // src/util/index.ts
313
299
  function isDefined$1(a) {
314
300
  return !!a || a !== null && a !== void 0;
315
301
  }
316
- //#endregion
317
- //#region src/shasum/shasum.ts
302
+ // src/shasum/shasum.ts
318
303
  async function shasumFile(filename, root) {
319
304
  try {
320
305
  return `${await calcFileChecksum(resolve(root || ".", filename))} ${filename}`;
@@ -453,8 +438,7 @@ async function updateChecksumForFiles(filename, files, options) {
453
438
  function normalizeFilename(filename) {
454
439
  return filename.split(sep).join("/");
455
440
  }
456
- //#endregion
457
- //#region src/util/textRegex.ts
441
+ // src/util/textRegex.ts
458
442
  const regExMatchRegExParts = /^\s*\/([\s\S]*?)\/([gimuxy]*)\s*$/;
459
443
  function stringToRegExp(pattern, defaultFlags = "", forceFlags = "") {
460
444
  if (pattern instanceof RegExp) return pattern;
@@ -549,8 +533,29 @@ function removeVerboseFromRegExp(pattern) {
549
533
  while (result.idx < pattern.length) for (const r of reducers) if (r(result)) break;
550
534
  return result.result;
551
535
  }
552
- //#endregion
553
- //#region src/compiler/WordsCollection.ts
536
+ // src/compiler/text.ts
537
+ const regExSplitWords = /([\p{Ll}])([\p{Lu}])/gu;
538
+ const regExSplitWords2 = /(\p{Lu})(\p{Lu}\p{Ll})/gu;
539
+ const regExpIsLetter = /^\p{L}\p{M}{0,2}$/u;
540
+ /**
541
+ * Split camelCase words into an array of strings.
542
+ */
543
+ function splitCamelCaseWord(word) {
544
+ return word.replaceAll(regExSplitWords, "$1|$2").replaceAll(regExSplitWords2, "$1|$2").replaceAll(/[\d_]+/g, "|").split("|").filter((a) => !!a);
545
+ }
546
+ /**
547
+ * Lower-case a word.
548
+ * @param word - the word to lower-case.
549
+ * @param locale - the locale whose case rules are used. Default rules are used if not given.
550
+ * @returns the lower-case word.
551
+ */
552
+ function lowerCase(word, locale) {
553
+ return locale ? word.toLocaleLowerCase(locale) : word.toLowerCase();
554
+ }
555
+ function isSingleLetter(c) {
556
+ return regExpIsLetter.test(c);
557
+ }
558
+ // src/compiler/WordsCollection.ts
554
559
  const defaultAllowedSplitWords = Object.freeze({
555
560
  size: 0,
556
561
  has: () => true
@@ -559,8 +564,7 @@ Object.freeze({
559
564
  size: 0,
560
565
  has: () => false
561
566
  });
562
- //#endregion
563
- //#region src/compiler/createWordsCollection.ts
567
+ // src/compiler/createWordsCollection.ts
564
568
  var AllowedSplitWordsImpl = class {
565
569
  collection;
566
570
  size;
@@ -572,15 +576,15 @@ var AllowedSplitWordsImpl = class {
572
576
  return !this.size || this.collection.has(word, caseSensitive);
573
577
  }
574
578
  };
575
- async function createAllowedSplitWordsFromFiles(files) {
579
+ async function createAllowedSplitWordsFromFiles(files, locale) {
576
580
  if (!files || !files.length) return defaultAllowedSplitWords;
577
- return new AllowedSplitWordsImpl(await createWordsCollectionFromFiles(files));
581
+ return new AllowedSplitWordsImpl(await createWordsCollectionFromFiles(files, locale));
578
582
  }
579
- function buildHasFn(dict) {
583
+ function buildHasFn(dict, locale) {
580
584
  function has(word, caseSensitive) {
581
585
  const r = dict.hasWord(word, true);
582
586
  if (r || caseSensitive) return r;
583
- const lc = word.toLowerCase();
587
+ const lc = lowerCase(word, locale);
584
588
  if (lc === word) return false;
585
589
  return dict.hasWord(lc, true);
586
590
  }
@@ -589,9 +593,9 @@ function buildHasFn(dict) {
589
593
  async function readFile$1(filename) {
590
594
  return await createReader(filename, {});
591
595
  }
592
- function readersToCollection(readers) {
593
- const dictReaders = readers.filter(isDictionaryReader).map(dictReaderToCollection);
594
- const nonDictCollection = lineReadersToCollection(readers.filter((a) => !isDictionaryReader(a)));
596
+ function readersToCollection(readers, locale) {
597
+ const dictReaders = readers.filter(isDictionaryReader).map((reader) => dictReaderToCollection(reader, locale));
598
+ const nonDictCollection = lineReadersToCollection(readers.filter((a) => !isDictionaryReader(a)), locale);
595
599
  const collections = [...dictReaders, nonDictCollection];
596
600
  return {
597
601
  size: collections.reduce((s, a) => s + a.size, 0),
@@ -599,94 +603,90 @@ function readersToCollection(readers) {
599
603
  };
600
604
  }
601
605
  const cache = /* @__PURE__ */ new WeakMap();
602
- async function createWordsCollectionFromFiles(files) {
606
+ const filePaths = /* @__PURE__ */ new Map();
607
+ function createWordsCollectionFromFiles(files, locale) {
603
608
  files = toFilePathArray(files);
604
- const cached = cache.get(files);
609
+ let byLocale = cache.get(files);
610
+ if (!byLocale) {
611
+ byLocale = /* @__PURE__ */ new Map();
612
+ cache.set(files, byLocale);
613
+ }
614
+ const cached = byLocale.get(locale);
605
615
  if (cached) return cached;
606
- const collection = readersToCollection(await Promise.all(files.map((file) => readFile$1(file))));
607
- cache.set(files, collection);
616
+ const collection = _createWordsCollectionFromFiles(files, locale);
617
+ byLocale.set(locale, collection);
608
618
  return collection;
609
619
  }
620
+ async function _createWordsCollectionFromFiles(files, locale) {
621
+ return readersToCollection(await Promise.all(files.map((file) => readFile$1(file))), locale);
622
+ }
610
623
  function isDictionaryReader(reader) {
611
624
  return "hasWord" in reader && !!reader.hasWord;
612
625
  }
613
- function dictReaderToCollection(reader) {
626
+ function dictReaderToCollection(reader, locale) {
614
627
  return {
615
628
  size: reader.size,
616
- has: buildHasFn(reader)
629
+ has: buildHasFn(reader, locale)
617
630
  };
618
631
  }
619
- function lineReadersToCollection(readers) {
632
+ function lineReadersToCollection(readers, locale) {
620
633
  function* words() {
621
634
  for (const reader of readers) yield* reader.lines;
622
635
  }
623
636
  const dict = parseDictionary(words(), { stripCaseAndAccents: false });
624
637
  return {
625
638
  size: dict.size,
626
- has: buildHasFn(dict)
639
+ has: buildHasFn(dict, locale)
627
640
  };
628
641
  }
629
642
  function toFilePathArray(filePathOrArray) {
630
- return Array.isArray(filePathOrArray) ? filePathOrArray : [filePathOrArray];
631
- }
632
- //#endregion
633
- //#region src/compiler/logWithTimestamp.ts
643
+ if (Array.isArray(filePathOrArray)) return filePathOrArray;
644
+ const found = filePaths.get(filePathOrArray);
645
+ if (found) return found;
646
+ const paths = [filePathOrArray];
647
+ filePaths.set(filePathOrArray, paths);
648
+ return paths;
649
+ }
650
+ // src/compiler/logWithTimestamp.ts
634
651
  const logWithTimestamp = (message, ...optionalParams) => {
635
652
  console.log(`${(/* @__PURE__ */ new Date()).toISOString()} ${message}`, ...optionalParams);
636
653
  };
637
- //#endregion
638
- //#region src/config/configDefaults.ts
654
+ // src/config/configDefaults.ts
639
655
  const defaultCompileSourceOptions = {
640
656
  maxDepth: void 0,
641
657
  split: false,
642
- keepRawCase: false,
643
658
  allowedSplitWords: void 0,
644
659
  storeSplitWordsAsCompounds: false,
645
660
  minCompoundLength: 4
646
661
  };
647
- //#endregion
648
- //#region src/compiler/text.ts
649
- const regExSplitWords = /([\p{Ll}])([\p{Lu}])/gu;
650
- const regExSplitWords2 = /(\p{Lu})(\p{Lu}\p{Ll})/gu;
651
- const regExpIsLetter = /^\p{L}\p{M}{0,2}$/u;
652
- /**
653
- * Split camelCase words into an array of strings.
654
- */
655
- function splitCamelCaseWord(word) {
656
- return word.replaceAll(regExSplitWords, "$1|$2").replaceAll(regExSplitWords2, "$1|$2").replaceAll(/[\d_]+/g, "|").split("|").filter((a) => !!a);
657
- }
658
- function isSingleLetter(c) {
659
- return regExpIsLetter.test(c);
660
- }
661
- //#endregion
662
- //#region src/compiler/splitCamelCaseIfAllowed.ts
662
+ // src/compiler/splitCamelCaseIfAllowed.ts
663
663
  const regExpSpaceOrDash = /[- ]+/g;
664
664
  const regExpIsNumber = /^\d+$/;
665
- function splitCamelCaseIfAllowed(word, allowedWords, keepCase, compoundPrefix, minCompoundLength) {
665
+ function splitCamelCaseIfAllowed(word, allowedWords, keepCase, compoundPrefix, minCompoundLength, locale) {
666
666
  const split = [...splitCamelCase(word)];
667
- if (split.length === 1) return adjustCases(split, allowedWords, keepCase);
667
+ if (split.length === 1) return lowerCaseUnlessAcronyms(split, allowedWords, keepCase, locale);
668
668
  if (split.some((w) => isUnknown(w, allowedWords))) return [word];
669
669
  const wordIndexes = calcWordIndex(word, split);
670
- const adjusted = adjustCases(split, allowedWords, keepCase);
670
+ const adjusted = lowerCaseUnlessAcronyms(split, allowedWords, keepCase, locale);
671
671
  return !compoundPrefix ? adjusted : adjusted.map((w, i) => {
672
672
  const { px, sx } = wordIndexes[i];
673
673
  const canCompound = w.length >= minCompoundLength;
674
- const lc = w.toLowerCase();
674
+ const lc = lowerCase(w, locale);
675
675
  const p = canCompound && isSingleLetter(px) ? compoundPrefix : "";
676
676
  const s = canCompound && isSingleLetter(sx) ? compoundPrefix : "";
677
677
  if (lc.length < 4 || allowedWords.has(w, true)) return p + w + s;
678
678
  return p + lc + s;
679
679
  });
680
680
  }
681
- function adjustCases(words, allowedWords, keepCase) {
682
- return words.map((w) => adjustCase(w, allowedWords, keepCase));
681
+ function lowerCaseUnlessAcronyms(words, allowedWords, keepCase, locale) {
682
+ return words.map((w) => lowerCaseUnlessAcronym(w, allowedWords, keepCase, locale));
683
683
  }
684
- function adjustCase(word, allowedWords, keepCase) {
685
- const lc = word.toLowerCase();
684
+ function lowerCaseUnlessAcronym(word, allowedWords, keepCase, locale) {
685
+ const lc = lowerCase(word, locale);
686
686
  if (!allowedWords.has(lc, true)) return word;
687
687
  if (lc === word) return word;
688
688
  if (word.slice(1).toLowerCase() === word.slice(1)) return lc;
689
- if (!keepCase && word.toUpperCase() === word) return word.toLowerCase();
689
+ if (!keepCase && word.toUpperCase() === word) return lc;
690
690
  return word;
691
691
  }
692
692
  function isUnknown(word, allowedWords) {
@@ -713,21 +713,20 @@ function calcWordIndex(word, words) {
713
713
  return wIndex;
714
714
  });
715
715
  }
716
- //#endregion
717
- //#region src/compiler/legacyLineToWords.ts
716
+ // src/compiler/legacyLineToWords.ts
718
717
  const regNonWord = /[^\p{L}\p{M}' _\d]+/giu;
719
718
  const regExpRepeatChars = /(.)\1{5}/i;
720
719
  const minCompoundLength = defaultCompileSourceOptions.minCompoundLength;
721
- function legacyLineToWords(line, keepCase, allowedSplitWords) {
720
+ function legacyLineToWords(line, keepCase, allowedSplitWords, locale) {
722
721
  const wordGroups = line.replaceAll(regNonWord, "|").split("|");
723
722
  const _minCompoundLength = minCompoundLength;
724
- return pipe(wordGroups, opConcatMap((a) => a.split(regExpSpaceOrDash)), opConcatMap((a) => splitCamelCaseIfAllowed(a, allowedSplitWords, keepCase, "", _minCompoundLength)), opMap((a) => a.trim()), opFilter((a) => !!a), opFilter((s) => !regExpRepeatChars.test(s)));
723
+ return pipe(wordGroups, opConcatMap((a) => a.split(regExpSpaceOrDash)), opConcatMap((a) => splitCamelCaseIfAllowed(a, allowedSplitWords, keepCase, "", _minCompoundLength, locale)), opMap((a) => a.trim()), opFilter((a) => !!a), opFilter((s) => !regExpRepeatChars.test(s)));
725
724
  }
726
- //#endregion
727
- //#region src/compiler/wordListParser.ts
725
+ // src/compiler/wordListParser.ts
728
726
  function normalizeTargetWords(options) {
729
727
  const lineParser = createDictionaryLineParser({
730
728
  stripCaseAndAccents: options.generateNonStrict,
729
+ locale: options.locale,
731
730
  stripCaseAndAccentsOnForbidden: true,
732
731
  keepOptionalCompoundCharacter: true
733
732
  });
@@ -771,7 +770,8 @@ const _defaultOptions = {
771
770
  size: 0
772
771
  },
773
772
  storeSplitWordsAsCompounds: defaultCompileSourceOptions.storeSplitWordsAsCompounds,
774
- minCompoundLength: defaultCompileSourceOptions.minCompoundLength
773
+ minCompoundLength: defaultCompileSourceOptions.minCompoundLength,
774
+ locale: void 0
775
775
  };
776
776
  Object.freeze(_defaultOptions);
777
777
  const cSpellToolDirective = "cspell-tools:";
@@ -783,7 +783,7 @@ const cSpellToolDirective = "cspell-tools:";
783
783
  */
784
784
  function createParseFileLineMapper(options) {
785
785
  const _options = options || _defaultOptions;
786
- const { splitKeepBoth = _defaultOptions.splitKeepBoth, allowedSplitWords = _defaultOptions.allowedSplitWords, storeSplitWordsAsCompounds, minCompoundLength = _defaultOptions.minCompoundLength } = _options;
786
+ const { splitKeepBoth = _defaultOptions.splitKeepBoth, allowedSplitWords = _defaultOptions.allowedSplitWords, storeSplitWordsAsCompounds, minCompoundLength = _defaultOptions.minCompoundLength, locale } = _options;
787
787
  let { legacy = _defaultOptions.legacy } = _options;
788
788
  let { split = _defaultOptions.split, keepCase = legacy ? false : _defaultOptions.keepCase } = _options;
789
789
  const compoundFix = storeSplitWordsAsCompounds ? "+" : "";
@@ -841,12 +841,12 @@ function createParseFileLineMapper(options) {
841
841
  return line.split("|").map((a) => a.trim()).filter((a) => !!a).filter((a) => !/^[0-9_-]+$/.test(a)).filter((a) => !/^0[xo][0-9A-F]+$/i.test(a));
842
842
  }
843
843
  function splitWordIntoWords(word) {
844
- return splitCamelCaseIfAllowed(word, allowedSplitWords, keepCase, compoundFix, minCompoundLength);
844
+ return splitCamelCaseIfAllowed(word, allowedSplitWords, keepCase, compoundFix, minCompoundLength, locale);
845
845
  }
846
846
  function* splitWords(lines) {
847
847
  for (const line of lines) {
848
848
  if (legacy) {
849
- yield* legacyLineToWords(line, keepCase, allowedSplitWords);
849
+ yield* legacyLineToWords(line, keepCase, allowedSplitWords, locale);
850
850
  continue;
851
851
  }
852
852
  if (split) {
@@ -890,8 +890,7 @@ function replacementsToMapOperator(replacements) {
890
890
  return word;
891
891
  });
892
892
  }
893
- //#endregion
894
- //#region src/compiler/SourceReader.ts
893
+ // src/compiler/SourceReader.ts
895
894
  async function createSourceReader(filename, options) {
896
895
  const reader = await createReader(filename, options);
897
896
  if (reader.type !== "TextFile") return {
@@ -911,7 +910,7 @@ function splitLines(lines, options) {
911
910
  return split();
912
911
  }
913
912
  async function textFileReader(reader, options) {
914
- const { legacy, splitWords: split, allowedSplitWords, storeSplitWordsAsCompounds, minCompoundLength } = options;
913
+ const { legacy, splitWords: split, allowedSplitWords, storeSplitWordsAsCompounds, minCompoundLength, locale } = options;
915
914
  const parseOptions = {
916
915
  legacy,
917
916
  split,
@@ -919,7 +918,8 @@ async function textFileReader(reader, options) {
919
918
  keepCase: void 0,
920
919
  allowedSplitWords,
921
920
  storeSplitWordsAsCompounds,
922
- minCompoundLength
921
+ minCompoundLength,
922
+ locale
923
923
  };
924
924
  const words = [...parseFileLines(reader.lines, parseOptions)];
925
925
  return {
@@ -927,13 +927,11 @@ async function textFileReader(reader, options) {
927
927
  words
928
928
  };
929
929
  }
930
- //#endregion
931
- //#region src/compiler/streamSourceWordsFromFile.ts
930
+ // src/compiler/streamSourceWordsFromFile.ts
932
931
  async function streamSourceWordsFromFile(filename, options) {
933
932
  return (await createSourceReader(filename, options)).words;
934
933
  }
935
- //#endregion
936
- //#region src/compiler/logger.ts
934
+ // src/compiler/logger.ts
937
935
  const defaultLogger = console.log;
938
936
  let log = defaultLogger;
939
937
  function setLogger(logger) {
@@ -942,8 +940,7 @@ function setLogger(logger) {
942
940
  function getLogger() {
943
941
  return log;
944
942
  }
945
- //#endregion
946
- //#region src/compiler/writeTextToFile.ts
943
+ // src/compiler/writeTextToFile.ts
947
944
  const isGzFile = /\.gz$/;
948
945
  async function writeTextToFile(filename, data, useGzCompress) {
949
946
  const dataStr = typeof data === "string" ? data : Array.isArray(data) ? data.join("") : [...data].join("");
@@ -954,8 +951,7 @@ async function writeTextToFile(filename, data, useGzCompress) {
954
951
  const buffer = useGz ? await compress(buf) : buf;
955
952
  await promises.writeFile(filename, buffer);
956
953
  }
957
- //#endregion
958
- //#region src/compiler/wordListCompiler.ts
954
+ // src/compiler/wordListCompiler.ts
959
955
  const mkdirp = async (p) => {
960
956
  await mkdir(p, { recursive: true });
961
957
  };
@@ -974,7 +970,7 @@ function createWordListCompiler(options) {
974
970
  }
975
971
  function normalize(lines, options) {
976
972
  const filter = normalizeTargetWords(options);
977
- const cleanLines = options.removeDuplicates ? removeDuplicates(lines) : lines;
973
+ const cleanLines = options.removeDuplicates ? removeDuplicates(lines, options.locale) : lines;
978
974
  const iter = pipe(cleanLines, filter);
979
975
  if (!options.sort) return iter;
980
976
  return [...new Set(iter)].sort();
@@ -982,11 +978,11 @@ function normalize(lines, options) {
982
978
  function stripCompoundAFix(word) {
983
979
  return word.replaceAll("*", "").replaceAll("+", "");
984
980
  }
985
- function* removeDuplicates(words) {
981
+ function* removeDuplicates(words, locale) {
986
982
  const wordSet = new Set(words);
987
983
  const wordForms = /* @__PURE__ */ new Map();
988
984
  for (const word of wordSet) {
989
- const lc = stripCompoundAFix(word.toLowerCase());
985
+ const lc = stripCompoundAFix(lowerCase(word, locale));
990
986
  const forms = wordForms.get(lc) ?? [];
991
987
  forms.push(word);
992
988
  wordForms.set(lc, forms);
@@ -1001,7 +997,7 @@ function* removeDuplicates(words) {
1001
997
  for (const form of mForms.values()) yield* form;
1002
998
  continue;
1003
999
  }
1004
- const lc = [...mForms.keys()][0].toLowerCase();
1000
+ const lc = lowerCase([...mForms.keys()][0], locale);
1005
1001
  const lcForm = mForms.get(lc);
1006
1002
  if (!lcForm) {
1007
1003
  for (const form of mForms.values()) yield* form;
@@ -1012,7 +1008,7 @@ function* removeDuplicates(words) {
1012
1008
  yield* lcForm;
1013
1009
  if (sLcForms.has("*" + lc + "*")) continue;
1014
1010
  for (const forms of mForms.values()) for (const form of forms) {
1015
- if (sLcForms.has(form.toLowerCase())) continue;
1011
+ if (sLcForms.has(lowerCase(form, locale))) continue;
1016
1012
  yield form;
1017
1013
  }
1018
1014
  }
@@ -1084,8 +1080,7 @@ function createTrieCompiler(options) {
1084
1080
  });
1085
1081
  };
1086
1082
  }
1087
- //#endregion
1088
- //#region src/compiler/compile.ts
1083
+ // src/compiler/compile.ts
1089
1084
  async function compile(request, options) {
1090
1085
  const { targets } = request;
1091
1086
  const rootDir = path.resolve(request.rootDir || ".");
@@ -1093,6 +1088,7 @@ async function compile(request, options) {
1093
1088
  const targetOptions = {
1094
1089
  sort: request.sort,
1095
1090
  generateNonStrict: request.generateNonStrict,
1091
+ locale: request.locale,
1096
1092
  removeDuplicates: request.removeDuplicates,
1097
1093
  replacements: void 0
1098
1094
  };
@@ -1105,7 +1101,7 @@ async function compile(request, options) {
1105
1101
  (await compileTarget({
1106
1102
  ...targetOptions,
1107
1103
  ...target
1108
- }, request, {
1104
+ }, {
1109
1105
  rootDir,
1110
1106
  cwd,
1111
1107
  conditional,
@@ -1123,11 +1119,11 @@ function resolveChecksumFile(checksumFile, root) {
1123
1119
  const cFilename = typeof checksumFile === "string" && checksumFile || checksumFile && "./checksum.txt" || void 0;
1124
1120
  return cFilename && path.resolve(root, cFilename);
1125
1121
  }
1126
- async function compileTarget(target, options, compileOptions) {
1122
+ async function compileTarget(target, compileOptions) {
1127
1123
  logWithTimestamp(`Start compile: ${target.name}`);
1128
1124
  const buildOptions = genBuildTargetDictionaryOptions(target, compileOptions);
1129
1125
  const deps = /* @__PURE__ */ new Set();
1130
- addToSet(deps, await buildTargetDictionary(target, options, compileOptions, buildOptions), await compressDictionaryFileIfNeeded(buildOptions), await genBTrieForTarget(target, buildOptions));
1126
+ addToSet(deps, await buildTargetDictionary(target, compileOptions, buildOptions), await compressDictionaryFileIfNeeded(buildOptions), await genBTrieForTarget(target, buildOptions));
1131
1127
  logWithTimestamp(`Done compile: ${target.name}`);
1132
1128
  const checksumRoot = buildOptions.checksumRoot;
1133
1129
  return [...deps].map((d) => path.relative(checksumRoot, d));
@@ -1151,24 +1147,26 @@ function genBuildTargetDictionaryOptions(target, compileOptions) {
1151
1147
  checksumRoot: checksumFile && path.dirname(checksumFile) || rootDir
1152
1148
  };
1153
1149
  }
1154
- async function buildTargetDictionary(target, options, compileOptions, buildOptions) {
1150
+ async function buildTargetDictionary(target, compileOptions, buildOptions) {
1155
1151
  const { rootDir, checksumFile, conditional } = compileOptions;
1156
1152
  const { format, sources, trieBase, sort = true, generateNonStrict = false, excludeWordsFrom = [], excludeWordsNotFoundIn = [], excludeWordsMatchingRegex, replacements } = target;
1157
1153
  const { filename, useTrie, generateOnlyCompressedDictionary, checksumRoot } = buildOptions;
1158
1154
  const dictionaryDirectives = target.dictionaryDirectives ?? compileOptions.dictionaryDirectives;
1159
1155
  const removeDuplicates = target.removeDuplicates ?? false;
1160
- const excludeFromFilter = await createExcludeFilter(excludeWordsFrom);
1161
- const includeFromFilter = await createIncludeFilter(excludeWordsNotFoundIn);
1156
+ const locale = canonicalLocale(target);
1157
+ const excludeFromFilter = await createExcludeFilter(excludeWordsFrom, locale);
1158
+ const includeFromFilter = await createIncludeFilter(excludeWordsNotFoundIn, locale);
1162
1159
  const excludeRegexFilter = createExcludeRegexFilter(excludeWordsMatchingRegex);
1163
1160
  const excludeFilter = (word) => {
1164
1161
  return excludeFromFilter(word) && includeFromFilter(word) && excludeRegexFilter(word);
1165
1162
  };
1166
1163
  const compress = generateOnlyCompressedDictionary;
1167
- const filesToProcessAsync = pipeAsync(readSourceList(sources, rootDir), opMapAsync((src) => readFileSource(src, options)), opAwaitAsync());
1164
+ const filesToProcessAsync = pipeAsync(readSourceList(sources, rootDir), opMapAsync((src) => readFileSource(src, locale)), opAwaitAsync());
1168
1165
  const filesToProcess = await toArray(filesToProcessAsync);
1169
1166
  const normalizer = normalizeTargetWords({
1170
1167
  sort: useTrie || sort,
1171
1168
  generateNonStrict,
1169
+ locale,
1172
1170
  filter: excludeFilter,
1173
1171
  dictionaryDirectives,
1174
1172
  replacements
@@ -1188,6 +1186,7 @@ async function buildTargetDictionary(target, options, compileOptions, buildOptio
1188
1186
  }) : createWordListCompiler({
1189
1187
  sort,
1190
1188
  generateNonStrict,
1189
+ locale,
1191
1190
  dictionaryDirectives,
1192
1191
  removeDuplicates,
1193
1192
  replacements
@@ -1284,17 +1283,17 @@ function readSourceList(sources, rootDir) {
1284
1283
  async function readFileList(fileList) {
1285
1284
  return (await readTextFile(fileList)).split("\n").map((a) => a.trim()).filter((a) => !!a);
1286
1285
  }
1287
- async function readFileSource(fileSource, sourceOptions) {
1288
- const { filename, keepRawCase = sourceOptions.keepRawCase || false, split = sourceOptions.split || false, maxDepth, storeSplitWordsAsCompounds, minCompoundLength } = fileSource;
1286
+ async function readFileSource(fileSource, locale) {
1287
+ const { filename, split = false, maxDepth, storeSplitWordsAsCompounds, minCompoundLength } = fileSource;
1289
1288
  const legacy = split === "legacy";
1290
1289
  const readerOptions = {
1291
1290
  maxDepth,
1292
1291
  legacy,
1293
1292
  splitWords: legacy ? false : split,
1294
- keepCase: keepRawCase,
1295
- allowedSplitWords: await createAllowedSplitWordsFromFiles(fileSource.allowedSplitWords || sourceOptions.allowedSplitWords),
1293
+ allowedSplitWords: await createAllowedSplitWordsFromFiles(fileSource.allowedSplitWords, locale),
1296
1294
  storeSplitWordsAsCompounds,
1297
- minCompoundLength
1295
+ minCompoundLength,
1296
+ locale
1298
1297
  };
1299
1298
  logWithTimestamp(`Reading ${path.basename(filename)}`);
1300
1299
  const stream = await streamSourceWordsFromFile(filename, readerOptions);
@@ -1304,6 +1303,19 @@ async function readFileSource(fileSource, sourceOptions) {
1304
1303
  words: stream
1305
1304
  };
1306
1305
  }
1306
+ const regExpLocale = /^([a-z]{2})(?:[-_]([a-z]{2}))?$/i;
1307
+ /**
1308
+ * @returns the target's locale in its canonical form, such as `tr-TR` for `tr_tr`.
1309
+ * @throws if the locale isn't a language and an optional region.
1310
+ */
1311
+ function canonicalLocale(target) {
1312
+ const { locale } = target;
1313
+ if (locale === void 0) return void 0;
1314
+ const m = regExpLocale.exec(locale);
1315
+ if (!m) throw new Error(`Invalid locale "${locale}" in target "${target.name}". Use a language and an optional region, such as "tr" or "tr-TR".`);
1316
+ const [, language, region] = m;
1317
+ return region ? `${language.toLowerCase()}-${region.toUpperCase()}` : language.toLowerCase();
1318
+ }
1307
1319
  function normalizeTargetName(name) {
1308
1320
  return name.replace(/((\.txt|\.dic|\.aff|\.trie)(\.gz)?)?$/, "").replaceAll(/[^\p{L}\p{M}.\w\\/-]/gu, "_");
1309
1321
  }
@@ -1321,20 +1333,22 @@ function logProgress(freq = 1e5) {
1321
1333
  }
1322
1334
  /**
1323
1335
  * @param excludeWordsFrom - List of files to read words from.
1336
+ * @param locale - the locale used to lower-case words when matching them.
1324
1337
  * @returns a function that returns true if the word is not in the exclude list.
1325
1338
  */
1326
- async function createExcludeFilter(excludeWordsFrom) {
1339
+ async function createExcludeFilter(excludeWordsFrom, locale) {
1327
1340
  if (!excludeWordsFrom || !excludeWordsFrom.length) return () => true;
1328
- const excludeWords = await createWordsCollectionFromFiles(excludeWordsFrom);
1341
+ const excludeWords = await createWordsCollectionFromFiles(excludeWordsFrom, locale);
1329
1342
  return (word) => !excludeWords.has(word, word.toUpperCase() !== word);
1330
1343
  }
1331
1344
  /**
1332
1345
  * @param includeWordsFrom - List of files to read words from.
1346
+ * @param locale - the locale used to lower-case words when matching them.
1333
1347
  * @returns a function that returns true if the word is in the include list.
1334
1348
  */
1335
- async function createIncludeFilter(includeWordsFrom) {
1349
+ async function createIncludeFilter(includeWordsFrom, locale) {
1336
1350
  if (!includeWordsFrom || !includeWordsFrom.length) return () => true;
1337
- const excludeWords = await createWordsCollectionFromFiles(includeWordsFrom);
1351
+ const excludeWords = await createWordsCollectionFromFiles(includeWordsFrom, locale);
1338
1352
  return (word) => excludeWords.has(word, word.toUpperCase() !== word);
1339
1353
  }
1340
1354
  /**
@@ -1356,8 +1370,7 @@ function iterableToString(iter) {
1356
1370
  function addToSet(set, ...sources) {
1357
1371
  for (const items of sources) for (const item of items) set.add(item);
1358
1372
  }
1359
- //#endregion
1360
- //#region src/build.ts
1373
+ // src/build.ts
1361
1374
  const moduleName = "cspell-tools";
1362
1375
  const searchPlaces = [
1363
1376
  `${moduleName}.config.json`,
@@ -1389,14 +1402,13 @@ async function build(targets, options) {
1389
1402
  });
1390
1403
  }
1391
1404
  function normalizeRequest(buildInfo, root) {
1392
- const { rootDir = root, targets = [], checksumFile } = buildInfo;
1405
+ const { rootDir = root, targets = [], ...rest } = buildInfo;
1393
1406
  return {
1407
+ ...rest,
1394
1408
  rootDir: path.resolve(rootDir),
1395
- targets,
1396
- checksumFile
1409
+ targets
1397
1410
  };
1398
1411
  }
1399
- //#endregion
1400
1412
  export { reportCheckChecksumFile as a, toError as c, logWithTimestamp as i, generateBTrie as l, compile as n, reportChecksumForFiles as o, setLogger as r, updateChecksumForFiles as s, build as t, compressFile as u };
1401
1413
 
1402
- //# sourceMappingURL=build-Fod7lDHd.mjs.map
1414
+ //# sourceMappingURL=build-CkIhBjXk.mjs.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/config/config.d.ts
2
1
  interface BTrieOptions {
3
2
  /** compress the resulting file */
4
3
  compress?: boolean | undefined;
@@ -7,21 +6,13 @@ interface BTrieOptions {
7
6
  /** use a string table to reduce size */
8
7
  useStringTable?: boolean | undefined;
9
8
  }
10
- //#endregion
11
- //#region src/compiler/logger.d.ts
12
9
  type Logger = (message?: any, ...optionalParams: any[]) => void;
13
- //#endregion
14
- //#region src/compiler/bTrie.d.ts
15
10
  interface GenerateBTrieOptions extends BTrieOptions {
16
11
  /** output directory */
17
12
  output?: string;
18
13
  logger?: Logger;
19
14
  }
20
- //#endregion
21
- //#region src/bTrie.d.ts
22
- declare function generateBTrie(files: string[], options: GenerateBTrieOptions): Promise<void>;
23
- //#endregion
24
- //#region src/build.d.ts
15
+ export declare function generateBTrie(files: string[], options: GenerateBTrieOptions): Promise<void>;
25
16
  interface BuildOptions {
26
17
  /** Optional path to config file */
27
18
  config?: string | undefined;
@@ -32,7 +23,6 @@ interface BuildOptions {
32
23
  /** Conditional build based upon the targets matching the `checksum.txt` file. */
33
24
  conditional?: boolean;
34
25
  }
35
- declare function build(targets: string[] | undefined, options: BuildOptions): Promise<void>;
36
- //#endregion
37
- export { build, generateBTrie };
26
+ export declare function build(targets: string[] | undefined, options: BuildOptions): Promise<void>;
27
+ export {};
38
28
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,2 +1 @@
1
- import { l as generateBTrie, t as build } from "./build-Fod7lDHd.mjs";
2
- export { build, generateBTrie };
1
+ export { t as build, l as generateBTrie } from "./build-CkIhBjXk.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cspell/cspell-tools",
3
- "version": "10.4.0-alpha.1",
3
+ "version": "10.4.1",
4
4
  "description": "Tools to assist with the development of cSpell",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -65,12 +65,11 @@
65
65
  "watch": "tsdown -w"
66
66
  },
67
67
  "dependencies": {
68
- "@cspell/cspell-pipe": "10.4.0-alpha.1",
68
+ "@cspell/cspell-pipe": "10.4.1",
69
69
  "commander": "^15.0.0",
70
70
  "cosmiconfig": "10.0.1",
71
- "cspell-trie-lib": "10.4.0-alpha.1",
72
- "glob": "^13.0.6",
73
- "hunspell-reader": "10.4.0-alpha.1",
71
+ "cspell-trie-lib": "10.4.1",
72
+ "hunspell-reader": "10.4.1",
74
73
  "yaml": "^2.9.1"
75
74
  },
76
75
  "devDependencies": {
@@ -80,5 +79,5 @@
80
79
  "engines": {
81
80
  "node": ">=22.18.0"
82
81
  },
83
- "gitHead": "628768a6e62b792e628189b41e63e12e5d3d8fe3"
82
+ "gitHead": "ddb1699a4bfc2766675c2c97412a813d775772eb"
84
83
  }