• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

streetsidesoftware / cspell / 21238860898

22 Jan 2026 06:44AM UTC coverage: 92.823% (-0.006%) from 92.829%
21238860898

push

github

web-flow
ci: Workflow Bot -- Update PNPM (main) (#8350)

Signed-off-by: Jason Dent <Jason3S@users.noreply.github.com>
Co-authored-by: street-side-software-automation[bot] <74785433+street-side-software-automation[bot]@users.noreply.github.com>
Co-authored-by: Jason Dent <Jason3S@users.noreply.github.com>

8868 of 10616 branches covered (83.53%)

17668 of 19034 relevant lines covered (92.82%)

31650.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.0
/packages/cspell/src/lint/processFile.ts
1
import { formatWithOptions } from 'node:util';
2

3
import type { ChalkInstance } from 'chalk';
4
import type {
5
    CSpellSettings,
6
    CSpellSettingsWithSourceTrace,
7
    Document,
8
    ImportFileRefWithError,
9
    Issue,
10
    SpellCheckFileResult,
11
    TextDocumentOffset,
12
    ValidationIssue,
13
} from 'cspell-lib';
14
import {
15
    extractDependencies,
16
    extractImportErrors,
17
    MessageTypes,
18
    spellCheckDocument,
19
    Text as cspellText,
20
} from 'cspell-lib';
21

22
import type { CSpellLintResultCache } from '../util/cache/CSpellLintResultCache.js';
23
import type { ConfigInfo } from '../util/configFileHelper.js';
24
import { toError } from '../util/errors.js';
25
import { extractContext } from '../util/extractContext.js';
26
import { fileInfoToDocument, readFileInfo, relativeToCwd } from '../util/fileHelper.js';
27
import type { LintFileResult } from '../util/LintFileResult.js';
28
import type { LintFileReporter } from '../util/reporters.js';
29
import { mergeReportIssueOptions } from '../util/reporters.js';
30
import { getTimeMeasurer } from '../util/timer.js';
31
import { indent, unindent } from '../util/unindent.js';
32
import * as util from '../util/util.js';
33
import { wordWrapAnsiText } from '../util/wrap.js';
34
import { LinterError } from './LinterError.js';
35
import type { LintRequest } from './LintRequest.js';
36
import type { PrefetchResult } from './types.js';
37

38
export interface ProcessFileOptions {
39
    readonly reporter: LintFileReporter;
40
    readonly configInfo: ConfigInfo;
41
    readonly verboseLevel: number;
42
    readonly useColor: boolean;
43
    readonly cfg: LintRequest;
44
    readonly configErrors: Set<string>;
45
    readonly chalk: ChalkInstance;
46
    readonly userSettings: CSpellSettingsWithSourceTrace;
47
}
48

49
export async function processFile(
50
    filename: string,
51
    cache: CSpellLintResultCache,
52
    prefetch: PrefetchResult | undefined,
53
    processFileOptions: ProcessFileOptions,
54
): Promise<LintFileResult> {
55
    if (prefetch?.fileResult) return prefetch.fileResult;
256✔
56

57
    const { reporter, cfg, configInfo, userSettings } = processFileOptions;
236✔
58

59
    const getElapsedTimeMs = getTimeMeasurer();
236✔
60
    const reportIssueOptions = prefetch?.reportIssueOptions;
236✔
61
    const cachedResult = await cache.getCachedLintResults(filename);
236✔
62
    if (cachedResult) {
236!
63
        reporter.debug(`Filename: ${filename}, using cache`);
×
64
        return {
×
65
            ...cachedResult,
66
            elapsedTimeMs: getElapsedTimeMs(),
67
            reportIssueOptions: { ...cachedResult.reportIssueOptions, ...reportIssueOptions },
68
        };
69
    }
70

71
    const result: LintFileResult = {
236✔
72
        fileInfo: {
73
            filename,
74
        },
75
        issues: [],
76
        processed: false,
77
        errors: 0,
78
        configErrors: 0,
79
        elapsedTimeMs: 0,
80
        reportIssueOptions,
81
    };
82

83
    const fileInfo = prefetch?.fileInfo || (await readFileInfo(filename, undefined, true));
236!
84
    if (fileInfo.errorCode) {
236✔
85
        if (fileInfo.errorCode !== 'EISDIR' && cfg.options.mustFindFiles) {
6✔
86
            const err = new LinterError(`File not found: "${filename}"`);
3✔
87
            reporter.error('Linter:', err);
3✔
88
            result.errors += 1;
3✔
89
        }
90
        return result;
6✔
91
    }
92

93
    const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
230✔
94
    const { text } = fileInfo;
230✔
95
    result.fileInfo = fileInfo;
230✔
96

97
    let spellResult: Partial<SpellCheckFileResult> = {};
230✔
98
    try {
230✔
99
        const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
230✔
100
        const numSuggestions = configInfo.config.numSuggestions ?? 5;
230✔
101
        const validateOptions = util.clean({
230✔
102
            generateSuggestions,
103
            numSuggestions,
104
            validateDirectives,
105
            skipValidation,
106
        });
107
        const r = await spellCheckDocument(doc, validateOptions, userSettings);
230✔
108
        // console.warn('filename: %o %o', path.relative(process.cwd(), filename), r.perf);
109
        spellResult = r;
230✔
110
        result.processed = r.checked;
230✔
111
        result.perf = r.perf ? { ...r.perf } : undefined;
230!
112
        result.issues = cspellText.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
230✔
113
    } catch (e) {
114
        reporter.error(`Failed to process "${filename}"`, toError(e));
×
115
        result.errors += 1;
×
116
    }
117
    result.elapsedTimeMs = getElapsedTimeMs();
230✔
118

119
    const config = spellResult.settingsUsed ?? {};
230!
120
    result.reportIssueOptions = mergeReportIssueOptions(
230✔
121
        spellResult.settingsUsed || configInfo.config,
230!
122
        reportIssueOptions,
123
    );
124

125
    result.configErrors += reportSpellingResultConfigErrors(spellResult, processFileOptions);
230✔
126

127
    reportCheckResult(result, doc, spellResult, config, processFileOptions);
230✔
128

129
    const dep = calcDependencies(config);
230✔
130

131
    await cache.setCachedLintResults(result, dep.files);
230✔
132
    return result;
230✔
133

134
    function mapIssue({ doc: _, ...tdo }: TextDocumentOffset & ValidationIssue): Issue {
135
        const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : undefined;
1,364✔
136
        return util.clean({ ...tdo, context });
1,364✔
137
    }
138
}
139

140
export function reportCheckResult(
141
    result: LintFileResult,
142
    _doc: Document,
143
    spellResult: Partial<SpellCheckFileResult>,
144
    config: CSpellSettingsWithSourceTrace,
145
    processFileOptions: ProcessFileOptions,
146
): void {
147
    const { configInfo, reporter, verboseLevel, useColor, cfg, chalk } = processFileOptions;
230✔
148
    const elapsed = result.elapsedTimeMs || 0;
230!
149
    const dictionaries = config.dictionaries || [];
230✔
150

151
    if (verboseLevel > 1) {
230✔
152
        const dictsUsed = [...dictionaries]
19✔
153
            .sort()
154
            .map((name) => chalk.green(name))
261✔
155
            .join(', ');
156
        const msg = unindent`
19✔
157
                    File type: ${config.languageId}, Language: ${config.language}, Issues: ${
158
                        result.issues.length
159
                    } ${elapsed.toFixed(2)}ms
160
                    Config file Used: ${relativeToCwd(spellResult.localConfigFilepath || configInfo.source, cfg.root)}
23✔
161
                    Dictionaries Used:
162
                      ${wordWrapAnsiText(dictsUsed, 70)}`;
163
        reporter.info(indent(msg, '  '), MessageTypes.Info);
19✔
164
    }
165

166
    if (cfg.options.debug) {
230✔
167
        const { enabled, language, languageId, dictionaries } = config;
2✔
168
        const useConfig = { languageId, enabled, language, dictionaries };
2✔
169
        const msg = unindent`\
2✔
170
                Debug Config: ${formatWithOptions({ depth: 2, colors: useColor }, useConfig)}`;
171
        reporter.debug(msg);
2✔
172
    }
173
}
174

175
interface ConfigDependencies {
176
    files: string[];
177
}
178

179
function calcDependencies(config: CSpellSettings): ConfigDependencies {
180
    const { configFiles, dictionaryFiles } = extractDependencies(config);
230✔
181

182
    return { files: [...configFiles, ...dictionaryFiles] };
230✔
183
}
184

185
function reportConfigurationErrors(config: CSpellSettings, processFileOptions: ProcessFileOptions): number {
186
    const errors = extractImportErrors(config);
155✔
187
    return reportImportErrors(errors, processFileOptions);
155✔
188
}
189

190
function reportImportErrors(errors: ImportFileRefWithError[], processFileOptions: ProcessFileOptions): number {
191
    const { reporter, configErrors } = processFileOptions;
385✔
192
    let count = 0;
385✔
193
    errors.forEach((ref) => {
385✔
194
        const key = ref.error.toString();
14✔
195
        if (configErrors.has(key)) return;
14✔
196
        configErrors.add(key);
10✔
197
        count += 1;
10✔
198
        reporter.error('Configuration', ref.error);
10✔
199
    });
200

201
    return count;
385✔
202
}
203

204
function reportSpellingResultConfigErrors(
205
    spellResult: Partial<SpellCheckFileResult>,
206
    processFileOptions: ProcessFileOptions,
207
): number {
208
    const { reporter, configErrors } = processFileOptions;
230✔
209
    let count = reportImportErrors(spellResult.configErrors || [], processFileOptions);
230✔
210

211
    const dictionaryErrors = [...(spellResult.dictionaryErrors || [])];
230✔
212
    for (const [dictName, dictErrors] of dictionaryErrors) {
230✔
213
        const msg = `Dictionary Error with (${dictName})`;
1✔
214
        dictErrors.forEach((error) => {
1✔
215
            const key = msg + error.toString();
1✔
216
            if (configErrors.has(key)) return;
1!
217
            configErrors.add(key);
1✔
218
            count += 1;
1✔
219
            reporter.error(msg, error);
1✔
220
        });
221
    }
222

223
    return count;
230✔
224
}
225

226
export function countConfigErrors(configInfo: ConfigInfo, processFileOptions: ProcessFileOptions): number {
227
    return reportConfigurationErrors(configInfo.config, processFileOptions);
155✔
228
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc