@cspell/cspell-tools 9.3.2 → 9.5.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.
@@ -28,17 +28,7 @@
28
28
  "additionalProperties": false,
29
29
  "properties": {
30
30
  "allowedSplitWords": {
31
- "anyOf": [
32
- {
33
- "$ref": "#/definitions/FilePath"
34
- },
35
- {
36
- "items": {
37
- "$ref": "#/definitions/FilePath"
38
- },
39
- "type": "array"
40
- }
41
- ],
31
+ "$ref": "#/definitions/FilePathOrFilePathArray",
42
32
  "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."
43
33
  },
44
34
  "keepRawCase": {
@@ -86,21 +76,24 @@
86
76
  "description": "Note: All relative paths are relative to the config file location.",
87
77
  "type": "string"
88
78
  },
79
+ "FilePathOrFilePathArray": {
80
+ "anyOf": [
81
+ {
82
+ "$ref": "#/definitions/FilePath"
83
+ },
84
+ {
85
+ "items": {
86
+ "$ref": "#/definitions/FilePath"
87
+ },
88
+ "type": "array"
89
+ }
90
+ ]
91
+ },
89
92
  "FileSource": {
90
93
  "additionalProperties": false,
91
94
  "properties": {
92
95
  "allowedSplitWords": {
93
- "anyOf": [
94
- {
95
- "$ref": "#/definitions/FilePath"
96
- },
97
- {
98
- "items": {
99
- "$ref": "#/definitions/FilePath"
100
- },
101
- "type": "array"
102
- }
103
- ],
96
+ "$ref": "#/definitions/FilePathOrFilePathArray",
104
97
  "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."
105
98
  },
106
99
  "filename": {
@@ -162,8 +155,8 @@
162
155
  "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."
163
156
  },
164
157
  "compress": {
165
- "default": ": false",
166
- "description": "gzip the file?",
158
+ "default": false,
159
+ "description": "Setting this value to true will create a `.gz` dictionary file. Use `keepUncompressed` to also keep an uncompressed version.",
167
160
  "type": "boolean"
168
161
  },
169
162
  "dictionaryDirectives": {
@@ -203,6 +196,10 @@
203
196
  "description": "Generate lower case / accent free versions of words.",
204
197
  "type": "boolean"
205
198
  },
199
+ "keepUncompressed": {
200
+ "description": "If `compress` is true, setting this value to true will also keep an uncompressed version of the dictionary.",
201
+ "type": "boolean"
202
+ },
206
203
  "name": {
207
204
  "description": "Name of target, used as the basis of target file name.",
208
205
  "type": "string"
@@ -0,0 +1,2 @@
1
+ export type WordListCompiler = (srcWords: Iterable<string>) => Iterable<string>;
2
+ //# sourceMappingURL=CompilerDefinitions.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CompilerDefinitions.js.map
@@ -9,7 +9,7 @@ import { createAllowedSplitWordsFromFiles, createWordsCollectionFromFiles } from
9
9
  import { logWithTimestamp } from './logWithTimestamp.js';
10
10
  import { readTextFile } from './readers/readTextFile.js';
11
11
  import { streamSourceWordsFromFile } from './streamSourceWordsFromFile.js';
12
- import { compileTrie, compileWordList } from './wordListCompiler.js';
12
+ import { createTargetFile, createTrieCompiler, createWordListCompiler } from './wordListCompiler.js';
13
13
  import { normalizeTargetWords } from './wordListParser.js';
14
14
  export async function compile(request, options) {
15
15
  const { targets } = request;
@@ -66,10 +66,16 @@ export async function compileTarget(target, options, compileOptions) {
66
66
  const excludeFilter = (word) => {
67
67
  return excludeFromFilter(word) && includeFromFilter(word) && excludeRegexFilter(word);
68
68
  };
69
- const generateNonStrictTrie = target.generateNonStrict ?? true;
70
69
  const name = normalizeTargetName(target.name);
71
70
  const useTrie = format.startsWith('trie');
72
- const filename = resolveTarget(name, targetDirectory, useTrie, target.compress ?? false);
71
+ const generateCompressed = target.compress ?? false;
72
+ const generateUncompressed = target.keepUncompressed ?? false;
73
+ const genSet = new Set();
74
+ genSet.add(generateCompressed);
75
+ if (generateUncompressed) {
76
+ genSet.add(false);
77
+ }
78
+ const filename = resolveTarget(name, targetDirectory, useTrie);
73
79
  const filesToProcessAsync = pipeAsync(readSourceList(sources, rootDir), opMapAsync((src) => readFileSource(src, options)), opAwaitAsync());
74
80
  const filesToProcess = await toArray(filesToProcessAsync);
75
81
  const normalizer = normalizeTargetWords({
@@ -81,7 +87,7 @@ export async function compileTarget(target, options, compileOptions) {
81
87
  });
82
88
  const checksumRoot = (checksumFile && path.dirname(checksumFile)) || rootDir;
83
89
  const deps = [
84
- ...calculateDependencies(filename, filesToProcess, [...excludeWordsFrom, ...excludeWordsNotFoundIn], checksumRoot),
90
+ ...calculateDependencies(filename + (generateCompressed ? '.gz' : ''), filesToProcess, [...excludeWordsFrom, ...excludeWordsNotFoundIn], checksumRoot),
85
91
  ];
86
92
  if (conditional && checksumFile) {
87
93
  const check = await checkShasumFile(checksumFile, deps, checksumRoot).catch(() => undefined);
@@ -90,34 +96,32 @@ export async function compileTarget(target, options, compileOptions) {
90
96
  return [];
91
97
  }
92
98
  }
93
- const action = useTrie
94
- ? async (words, dst) => {
95
- return compileTrie(pipe(words, normalizer), dst, {
99
+ async function action(words, dst) {
100
+ const compiler = useTrie
101
+ ? createTrieCompiler({
96
102
  base: trieBase,
97
- sort: false,
98
103
  trie3: format === 'trie3',
99
104
  trie4: format === 'trie4',
100
- generateNonStrict: generateNonStrictTrie,
101
- dictionaryDirectives: undefined,
102
- // removeDuplicates, // Add this in if we use it.
103
- });
104
- }
105
- : async (words, dst) => {
106
- return compileWordList(pipe(words, normalizer), dst, {
105
+ })
106
+ : createWordListCompiler({
107
107
  sort,
108
108
  generateNonStrict,
109
109
  dictionaryDirectives,
110
110
  removeDuplicates,
111
111
  });
112
- };
113
- await processFiles(action, filesToProcess, filename);
112
+ const data = iterableToString(pipe(words, normalizer, compiler));
113
+ for (const compress of genSet) {
114
+ await createTargetFile(dst, data, compress);
115
+ }
116
+ }
117
+ await processFiles({ action, filesToProcess, mergeTarget: filename });
114
118
  logWithTimestamp(`Done compile: ${target.name}`);
115
119
  return deps;
116
120
  }
117
- function calculateDependencies(targetFile, filesToProcess, excludeFiles, rootDir) {
121
+ function calculateDependencies(targetFile, filesToProcess, extraDependencyFiles, rootDir) {
118
122
  const dependencies = new Set();
119
123
  addDependency(targetFile);
120
- excludeFiles?.forEach((f) => addDependency(f));
124
+ extraDependencyFiles?.forEach((f) => addDependency(f));
121
125
  filesToProcess.forEach((f) => addDependency(f.src));
122
126
  return dependencies;
123
127
  function addDependency(filename) {
@@ -130,7 +134,7 @@ function calculateDependencies(targetFile, filesToProcess, excludeFiles, rootDir
130
134
  function rel(filePath) {
131
135
  return path.relative(process.cwd(), filePath);
132
136
  }
133
- async function processFiles(action, filesToProcess, mergeTarget) {
137
+ async function processFiles({ action, filesToProcess, mergeTarget }) {
134
138
  const toProcess = filesToProcess;
135
139
  const dst = mergeTarget;
136
140
  const words = pipe(toProcess, opMap((ftp) => {
@@ -146,8 +150,8 @@ async function processFiles(action, filesToProcess, mergeTarget) {
146
150
  await action(words, dst);
147
151
  logWithTimestamp('Done "%s"', rel(dst));
148
152
  }
149
- function resolveTarget(name, directory, useTrie, useGzCompress) {
150
- const ext = ((useTrie && '.trie') || '.txt') + ((useGzCompress && '.gz') || '');
153
+ function resolveTarget(name, directory, useTrie) {
154
+ const ext = (useTrie && '.trie') || '.txt';
151
155
  const filename = name + ext;
152
156
  return path.resolve(directory, filename);
153
157
  }
@@ -260,4 +264,7 @@ function createExcludeRegexFilter(excludeWordsMatchingRegex) {
260
264
  });
261
265
  return (word) => !regexes.some((r) => r.test(word));
262
266
  }
267
+ function iterableToString(iter) {
268
+ return Array.isArray(iter) ? iter.join('') : [...iter].join('');
269
+ }
263
270
  //# sourceMappingURL=compile.js.map
@@ -1,9 +1,10 @@
1
- import type { FilePath } from '../config/config.js';
1
+ import type { FilePath, FilePathOrFilePathArray } from '../config/config.js';
2
2
  import type { AllowedSplitWordsCollection, ExcludeWordsCollection, WordsCollection } from './WordsCollection.js';
3
- export declare function createAllowedSplitWordsFromFiles(files: FilePath | FilePath[] | undefined): Promise<AllowedSplitWordsCollection>;
3
+ export declare function createAllowedSplitWordsFromFiles(files: FilePathOrFilePathArray | undefined): Promise<AllowedSplitWordsCollection>;
4
4
  export declare function createAllowedSplitWords(words: Iterable<string> | undefined): AllowedSplitWordsCollection;
5
- export declare function createWordsCollectionFromFiles(files: FilePath | FilePath[]): Promise<WordsCollection>;
5
+ export declare function createWordsCollectionFromFiles(files: FilePathOrFilePathArray): Promise<WordsCollection>;
6
6
  export declare function createWordsCollection(words: Iterable<string>): WordsCollection;
7
- export declare function createExcludeWordsCollectionFromFiles(files: FilePath | FilePath[] | undefined): Promise<ExcludeWordsCollection>;
7
+ export declare function createExcludeWordsCollectionFromFiles(files: FilePathOrFilePathArray | undefined): Promise<ExcludeWordsCollection>;
8
8
  export declare function createExcludeWordsCollection(words: Iterable<string> | undefined): ExcludeWordsCollection;
9
+ export declare function toFilePathArray(filePathOrArray: FilePathOrFilePathArray): FilePath[];
9
10
  //# sourceMappingURL=createWordsCollection.d.ts.map
@@ -50,7 +50,7 @@ function readersToCollection(readers) {
50
50
  }
51
51
  const cache = new WeakMap();
52
52
  export async function createWordsCollectionFromFiles(files) {
53
- files = Array.isArray(files) ? files : [files];
53
+ files = toFilePathArray(files);
54
54
  const cached = cache.get(files);
55
55
  if (cached)
56
56
  return cached;
@@ -105,4 +105,7 @@ function lineReadersToCollection(readers) {
105
105
  const dict = parseDictionary(words(), { stripCaseAndAccents: false });
106
106
  return { size: dict.size, has: buildHasFn(dict) };
107
107
  }
108
+ export function toFilePathArray(filePathOrArray) {
109
+ return Array.isArray(filePathOrArray) ? filePathOrArray : [filePathOrArray];
110
+ }
108
111
  //# sourceMappingURL=createWordsCollection.js.map
@@ -1,5 +1,4 @@
1
1
  export type { CompileRequest, CompileTargetOptions, RunConfig } from '../config/index.js';
2
2
  export { compile, compileTarget } from './compile.js';
3
3
  export { type Logger, setLogger } from './logger.js';
4
- export { compileTrie, compileWordList } from './wordListCompiler.js';
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,3 @@
1
1
  export { compile, compileTarget } from './compile.js';
2
2
  export { setLogger } from './logger.js';
3
- export { compileTrie, compileWordList } from './wordListCompiler.js';
4
3
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,9 @@
1
1
  import type { CompileOptions } from './CompileOptions.js';
2
- export declare function compileWordList(lines: Iterable<string>, destFilename: string, options: CompileOptions): Promise<void>;
2
+ import { WordListCompiler } from './CompilerDefinitions.js';
3
+ export declare function compileWordListToTarget(lines: Iterable<string>, destFilename: string, options: CompileOptions): Promise<void>;
4
+ export declare function createWordListCompiler(options: CompileOptions): WordListCompiler;
3
5
  declare function removeDuplicates(words: Iterable<string>): Iterable<string>;
6
+ export declare function createTargetFile(destFilename: string, seq: Iterable<string> | string, compress?: boolean): Promise<void>;
4
7
  export interface TrieOptions {
5
8
  base?: number | undefined;
6
9
  trie3?: boolean | undefined;
@@ -8,7 +11,8 @@ export interface TrieOptions {
8
11
  }
9
12
  export interface CompileTrieOptions extends CompileOptions, TrieOptions {
10
13
  }
11
- export declare function compileTrie(words: Iterable<string>, destFilename: string, options: CompileTrieOptions): Promise<void>;
14
+ export declare function compileTrieToTarget(words: Iterable<string>, destFilename: string, options: CompileTrieOptions): Promise<void>;
15
+ export declare function createTrieCompiler(options: TrieOptions): WordListCompiler;
12
16
  export declare const __testing__: {
13
17
  wordListHeader: string;
14
18
  removeDuplicates: typeof removeDuplicates;
@@ -2,9 +2,9 @@ import { mkdir } from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
3
  import { opAppend, opMap, pipe } from '@cspell/cspell-pipe/sync';
4
4
  import * as Trie from 'cspell-trie-lib';
5
- import { writeSeqToFile } from './fileWriter.js';
6
5
  import { getLogger } from './logger.js';
7
6
  import { normalizeTargetWords } from './wordListParser.js';
7
+ import { writeTextToFile } from './writeTextToFile.js';
8
8
  const mkdirp = async (p) => {
9
9
  await mkdir(p, { recursive: true });
10
10
  };
@@ -12,12 +12,17 @@ const mkdirp = async (p) => {
12
12
  const wordListHeader = `
13
13
  # cspell-tools: keep-case no-split`;
14
14
  const wordListHeaderLines = wordListHeader.split('\n').map((a) => a.trim());
15
- export async function compileWordList(lines, destFilename, options) {
16
- const finalLines = normalize(lines, options);
17
- const directives = options.dictionaryDirectives ?? [];
18
- const directivesLines = directives.map((a) => `# cspell-dictionary: ${a}`);
19
- const finalSeq = pipe([...wordListHeaderLines, ...directivesLines, ''], opAppend(finalLines));
20
- return createWordListTarget(destFilename)(finalSeq);
15
+ export async function compileWordListToTarget(lines, destFilename, options) {
16
+ const compiler = createWordListCompiler(options);
17
+ return createTargetFile(destFilename, compiler(lines));
18
+ }
19
+ export function createWordListCompiler(options) {
20
+ return (lines) => {
21
+ const finalLines = normalize(lines, options);
22
+ const directives = options.dictionaryDirectives ?? [];
23
+ const directivesLines = directives.map((a) => `# cspell-dictionary: ${a}`);
24
+ return pipe([...wordListHeaderLines, ...directivesLines, ''], opAppend(finalLines), opMap((a) => a + '\n'));
25
+ };
21
26
  }
22
27
  function normalize(lines, options) {
23
28
  const filter = normalizeTargetWords(options);
@@ -158,24 +163,26 @@ function removeDuplicateForms(forms) {
158
163
  return [form, applyFlags(form, flag)];
159
164
  }));
160
165
  }
161
- function createWordListTarget(destFilename) {
162
- const target = createTarget(destFilename);
163
- return (seq) => target(pipe(seq, opMap((a) => a + '\n')));
164
- }
165
- function createTarget(destFilename) {
166
+ export async function createTargetFile(destFilename, seq, compress) {
167
+ const rel = path.relative(process.cwd(), destFilename).replaceAll(path.sep, '/');
168
+ const log = getLogger();
169
+ log(`Writing to file ${rel}${compress ? '.gz' : ''}`);
166
170
  const destDir = path.dirname(destFilename);
167
- const pDir = mkdirp(destDir);
168
- return async (seq) => {
169
- await pDir;
170
- await writeSeqToFile(seq, destFilename);
171
- };
171
+ await mkdirp(destDir);
172
+ await writeTextToFile(destFilename, seq, compress);
172
173
  }
173
- export async function compileTrie(words, destFilename, options) {
174
+ export async function compileTrieToTarget(words, destFilename, options) {
174
175
  await createTrieTarget(destFilename, options)(words);
175
176
  }
176
177
  function createTrieTarget(destFilename, options) {
177
- const target = createTarget(destFilename);
178
178
  return async (words) => {
179
+ await createTargetFile(destFilename, createTrieCompiler(options)(words));
180
+ const log = getLogger();
181
+ log(`Done writing to file ${path.basename(destFilename)}`);
182
+ };
183
+ }
184
+ export function createTrieCompiler(options) {
185
+ return (words) => {
179
186
  const log = getLogger();
180
187
  log('Reading Words into Trie');
181
188
  const base = options.base ?? 32;
@@ -183,13 +190,12 @@ function createTrieTarget(destFilename, options) {
183
190
  const root = Trie.buildTrie(words).root;
184
191
  log('Reduce duplicate word endings');
185
192
  const trie = Trie.consolidate(root);
186
- log(`Writing to file ${path.basename(destFilename)}`);
187
- await target(Trie.serializeTrie(trie, {
193
+ log('Trie compilation complete');
194
+ return Trie.serializeTrie(trie, {
188
195
  base,
189
196
  comment: 'Built by cspell-tools.',
190
197
  version,
191
- }));
192
- log(`Done writing to file ${path.basename(destFilename)}`);
198
+ });
193
199
  };
194
200
  }
195
201
  export const __testing__ = {
@@ -1,3 +1,2 @@
1
- export declare function writeTextToFile(filename: string, data: string): Promise<void>;
2
- export declare function writeTextLinesToFile(filename: string, lines: Iterable<string>): Promise<void>;
1
+ export declare function writeTextToFile(filename: string, data: string | Iterable<string>, useGzCompress?: boolean): Promise<void>;
3
2
  //# sourceMappingURL=writeTextToFile.d.ts.map
@@ -2,14 +2,15 @@ import { Buffer } from 'node:buffer';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import { compress } from '../gzip/index.js';
4
4
  const isGzFile = /\.gz$/;
5
- export async function writeTextToFile(filename, data) {
6
- const useGz = isGzFile.test(filename);
7
- const buf = Buffer.from(data, 'utf8');
5
+ export async function writeTextToFile(filename, data, useGzCompress) {
6
+ const dataStr = typeof data === 'string' ? data : Array.isArray(data) ? data.join('') : [...data].join('');
7
+ const hasGzExt = isGzFile.test(filename);
8
+ const useGz = useGzCompress ?? hasGzExt;
9
+ if (useGz && !hasGzExt) {
10
+ filename += '.gz';
11
+ }
12
+ const buf = Buffer.from(dataStr, 'utf8');
8
13
  const buffer = useGz ? await compress(buf) : buf;
9
14
  await fs.writeFile(filename, buffer);
10
15
  }
11
- export function writeTextLinesToFile(filename, lines) {
12
- const data = Array.isArray(lines) ? lines.join('') : [...lines].join('');
13
- return writeTextToFile(filename, data);
14
- }
15
16
  //# sourceMappingURL=writeTextToFile.js.map
@@ -94,10 +94,15 @@ export interface Target extends CompileTargetOptions {
94
94
  */
95
95
  targetDirectory?: FilePath | undefined;
96
96
  /**
97
- * gzip the file?
98
- * @default: false
97
+ * Setting this value to true will create a `.gz` dictionary file.
98
+ * Use `keepUncompressed` to also keep an uncompressed version.
99
+ * @default false
99
100
  */
100
101
  compress?: boolean | undefined;
102
+ /**
103
+ * If `compress` is true, setting this value to true will also keep an uncompressed version of the dictionary.
104
+ */
105
+ keepUncompressed?: boolean | undefined;
101
106
  /**
102
107
  * Format of the dictionary.
103
108
  */
@@ -143,6 +148,7 @@ export type DictionaryFormats = 'plaintext' | 'trie' | 'trie3' | 'trie4';
143
148
  * Note: All relative paths are relative to the config file location.
144
149
  */
145
150
  export type FilePath = string;
151
+ export type FilePathOrFilePathArray = FilePath | FilePath[];
146
152
  export type DictionarySource = FilePath | FileSource | FileListSource;
147
153
  export interface FileSource extends CompileSourceOptions {
148
154
  filename: FilePath;
@@ -175,7 +181,7 @@ export interface CompileSourceOptions {
175
181
  * This is to prevent misspellings in CamelCase words from being introduced into the
176
182
  * dictionary.
177
183
  */
178
- allowedSplitWords?: FilePath | FilePath[] | undefined;
184
+ allowedSplitWords?: FilePathOrFilePathArray | undefined;
179
185
  /**
180
186
  * Camel case words that have been split using the `allowedSplitWords` are added to the dictionary as compoundable words.
181
187
  * These words are prefixed / suffixed with `*`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cspell/cspell-tools",
3
- "version": "9.3.2",
3
+ "version": "9.5.0",
4
4
  "description": "Tools to assist with the development of cSpell",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -63,13 +63,13 @@
63
63
  },
64
64
  "homepage": "https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-tools#readme",
65
65
  "dependencies": {
66
- "@cspell/cspell-pipe": "9.3.2",
66
+ "@cspell/cspell-pipe": "9.5.0",
67
67
  "commander": "^14.0.2",
68
68
  "cosmiconfig": "9.0.0",
69
- "cspell-trie-lib": "9.3.2",
70
- "glob": "^10.4.5",
71
- "hunspell-reader": "9.3.2",
72
- "yaml": "^2.8.1"
69
+ "cspell-trie-lib": "9.5.0",
70
+ "glob": "^13.0.0",
71
+ "hunspell-reader": "9.5.0",
72
+ "yaml": "^2.8.2"
73
73
  },
74
74
  "engines": {
75
75
  "node": ">=20"
@@ -79,5 +79,5 @@
79
79
  "ts-json-schema-generator": "^2.4.0"
80
80
  },
81
81
  "module": "bin.mjs",
82
- "gitHead": "595bde79b4a5abf3256b71129995ec3601454b02"
82
+ "gitHead": "4407eed11650481d9037e5a1d53488a93ff4b66d"
83
83
  }
@@ -1,3 +0,0 @@
1
- export { writeTextLinesToFile as writeToFileIterableP } from './writeTextToFile.js';
2
- export declare function writeSeqToFile(seq: Iterable<string>, outFile: string): Promise<void>;
3
- //# sourceMappingURL=fileWriter.d.ts.map
@@ -1,6 +0,0 @@
1
- import { writeTextLinesToFile } from './writeTextToFile.js';
2
- export { writeTextLinesToFile as writeToFileIterableP } from './writeTextToFile.js';
3
- export function writeSeqToFile(seq, outFile) {
4
- return writeTextLinesToFile(outFile, seq);
5
- }
6
- //# sourceMappingURL=fileWriter.js.map