@@ -3,34 +3,21 @@ import { formatWithOptions } from 'node:util';
33
44import { isAsyncIterable , operators , opFilter , pipeAsync } from '@cspell/cspell-pipe' ;
55import { opMap , pipe } from '@cspell/cspell-pipe/sync' ;
6- import type {
7- CSpellSettings ,
8- CSpellSettingsWithSourceTrace ,
9- Glob ,
10- Issue ,
11- ReportIssueOptions ,
12- RunResult ,
13- TextDocumentOffset ,
14- } from '@cspell/cspell-types' ;
6+ import type { Glob , RunResult } from '@cspell/cspell-types' ;
157import { MessageTypes } from '@cspell/cspell-types' ;
168import { toFileURL } from '@cspell/url' ;
179import chalk from 'chalk' ;
1810import { dictionaryCacheEnableLogging , dictionaryCacheGetLog } from 'cspell-dictionary' ;
1911import { findRepoRoot , GitIgnore } from 'cspell-gitignore' ;
2012import { GlobMatcher , type GlobMatchOptions , type GlobPatternNormalized , type GlobPatternWithRoot } from 'cspell-glob' ;
21- import type { Document , Logger , SpellCheckFileResult , ValidationIssue } from 'cspell-lib' ;
13+ import type { Logger } from 'cspell-lib' ;
2214import {
2315 ENV_CSPELL_GLOB_ROOT ,
24- extractDependencies ,
25- extractImportErrors ,
2616 getDefaultConfigLoader ,
27- getDictionary ,
2817 isBinaryFile as cspellIsBinaryFile ,
2918 mergeSettings ,
3019 setLogger ,
3120 shouldCheckDocument ,
32- spellCheckDocument ,
33- Text as cspellText ,
3421} from 'cspell-lib' ;
3522
3623import { console } from '../console.js' ;
@@ -41,11 +28,8 @@ import { npmPackage } from '../pkgInfo.js';
4128import type { CreateCacheSettings , CSpellLintResultCache } from '../util/cache/index.js' ;
4229import { calcCacheSettings , createCache } from '../util/cache/index.js' ;
4330import { type ConfigInfo , readConfig } from '../util/configFileHelper.js' ;
44- import { ApplicationError , CheckFailed , toApplicationError , toError } from '../util/errors.js' ;
45- import { extractContext } from '../util/extractContext.js' ;
46- import type { ReadFileInfoResult } from '../util/fileHelper.js' ;
31+ import { ApplicationError , CheckFailed , toApplicationError } from '../util/errors.js' ;
4732import {
48- fileInfoToDocument ,
4933 filenameToUri ,
5034 findFiles ,
5135 getFileSize ,
@@ -68,14 +52,16 @@ import {
6852import type { LintFileResult } from '../util/LintFileResult.js' ;
6953import { prefetchIterable } from '../util/prefetch.js' ;
7054import type { FinalizedReporter } from '../util/reporters.js' ;
71- import { extractReporterIssueOptions , LintReporter , mergeReportIssueOptions } from '../util/reporters.js' ;
55+ import { extractReporterIssueOptions , LintReporter } from '../util/reporters.js' ;
7256import { getTimeMeasurer } from '../util/timer.js' ;
73- import { indent , unindent } from '../util/unindent.js' ;
57+ import { unindent } from '../util/unindent.js' ;
7458import { sizeToNumber } from '../util/unitNumbers.js' ;
7559import * as util from '../util/util.js' ;
7660import { wordWrapAnsiText } from '../util/wrap.js' ;
7761import { writeFileOrStream } from '../util/writeFile.js' ;
7862import type { LintRequest } from './LintRequest.js' ;
63+ import { countConfigErrors , processFile , type ProcessFileOptions } from './processFile.js' ;
64+ import type { PFCached , PFFile , PFSkipped , PrefetchFileResult } from './types.js' ;
7965
8066const version = npmPackage . version ;
8167
@@ -111,42 +97,6 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
11197 }
11298 return lintResult ;
11399
114- interface PrefetchResult {
115- fileResult ?: LintFileResult | undefined ;
116- fileInfo ?: ReadFileInfoResult | undefined ;
117- skip ?: boolean | undefined ;
118- skipReason ?: string | undefined ;
119- reportIssueOptions ?: ReportIssueOptions | undefined ;
120- }
121-
122- interface PFCached extends PrefetchResult {
123- fileResult : LintFileResult ;
124- fileInfo ?: undefined ;
125- skipReason ?: undefined ;
126- skip ?: undefined ;
127- }
128-
129- interface PFFile extends PrefetchResult {
130- fileResult ?: undefined ;
131- fileInfo : ReadFileInfoResult ;
132- skip ?: undefined ;
133- skipReason ?: undefined ;
134- reportIssueOptions : ReportIssueOptions | undefined ;
135- }
136-
137- interface PFSkipped extends PrefetchResult {
138- fileResult ?: undefined ;
139- fileInfo ?: undefined ;
140- skip : true ;
141- skipReason ?: string | undefined ;
142- reportIssueOptions ?: undefined ;
143- }
144-
145- interface PrefetchFileResult {
146- filename : string ;
147- result ?: Promise < PFCached | PFFile | PFSkipped | Error > ;
148- }
149-
150100 function prefetch ( filename : string , configInfo : ConfigInfo , cache : CSpellLintResultCache ) : PrefetchFileResult {
151101 if ( isBinaryFile ( filename , cfg . root ) ) {
152102 return { filename, result : Promise . resolve ( { skip : true , skipReason : 'Binary file.' } ) } ;
@@ -184,128 +134,6 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
184134 return { filename, result } ;
185135 }
186136
187- async function processFile (
188- filename : string ,
189- configInfo : ConfigInfo ,
190- cache : CSpellLintResultCache ,
191- prefetch : PrefetchResult | undefined ,
192- ) : Promise < LintFileResult > {
193- if ( prefetch ?. fileResult ) return prefetch . fileResult ;
194-
195- const getElapsedTimeMs = getTimeMeasurer ( ) ;
196- const reportIssueOptions = prefetch ?. reportIssueOptions ;
197- const cachedResult = await cache . getCachedLintResults ( filename ) ;
198- if ( cachedResult ) {
199- reporter . debug ( `Filename: ${ filename } , using cache` ) ;
200- return {
201- ...cachedResult ,
202- elapsedTimeMs : getElapsedTimeMs ( ) ,
203- reportIssueOptions : { ...cachedResult . reportIssueOptions , ...reportIssueOptions } ,
204- } ;
205- }
206-
207- const result : LintFileResult = {
208- fileInfo : {
209- filename,
210- } ,
211- issues : [ ] ,
212- processed : false ,
213- errors : 0 ,
214- configErrors : 0 ,
215- elapsedTimeMs : 0 ,
216- reportIssueOptions,
217- } ;
218-
219- const fileInfo = prefetch ?. fileInfo || ( await readFileInfo ( filename , undefined , true ) ) ;
220- if ( fileInfo . errorCode ) {
221- if ( fileInfo . errorCode !== 'EISDIR' && cfg . options . mustFindFiles ) {
222- const err = new LinterError ( `File not found: "${ filename } "` ) ;
223- reporter . error ( 'Linter:' , err ) ;
224- result . errors += 1 ;
225- }
226- return result ;
227- }
228-
229- const doc = fileInfoToDocument ( fileInfo , cfg . options . languageId , cfg . locale ) ;
230- const { text } = fileInfo ;
231- result . fileInfo = fileInfo ;
232-
233- let spellResult : Partial < SpellCheckFileResult > = { } ;
234- try {
235- const { showSuggestions : generateSuggestions , validateDirectives, skipValidation } = cfg . options ;
236- const numSuggestions = configInfo . config . numSuggestions ?? 5 ;
237- const validateOptions = util . clean ( {
238- generateSuggestions,
239- numSuggestions,
240- validateDirectives,
241- skipValidation,
242- } ) ;
243- const r = await spellCheckDocument ( doc , validateOptions , configInfo . config ) ;
244- // console.warn('filename: %o %o', path.relative(process.cwd(), filename), r.perf);
245- spellResult = r ;
246- result . processed = r . checked ;
247- result . perf = r . perf ? { ...r . perf } : undefined ;
248- result . issues = cspellText . calculateTextDocumentOffsets ( doc . uri , text , r . issues ) . map ( mapIssue ) ;
249- } catch ( e ) {
250- reporter . error ( `Failed to process "${ filename } "` , toError ( e ) ) ;
251- result . errors += 1 ;
252- }
253- result . elapsedTimeMs = getElapsedTimeMs ( ) ;
254-
255- const config = spellResult . settingsUsed ?? { } ;
256- result . reportIssueOptions = mergeReportIssueOptions (
257- spellResult . settingsUsed || configInfo . config ,
258- reportIssueOptions ,
259- ) ;
260- result . configErrors += await reportConfigurationErrors ( config ) ;
261-
262- reportCheckResult ( result , doc , spellResult , configInfo , config ) ;
263-
264- const dep = calcDependencies ( config ) ;
265-
266- await cache . setCachedLintResults ( result , dep . files ) ;
267- return result ;
268- }
269-
270- function reportCheckResult (
271- result : LintFileResult ,
272- _doc : Document ,
273- spellResult : Partial < SpellCheckFileResult > ,
274- configInfo : ConfigInfo ,
275- config : CSpellSettingsWithSourceTrace ,
276- ) {
277- const elapsed = result . elapsedTimeMs || 0 ;
278- const dictionaries = config . dictionaries || [ ] ;
279-
280- if ( verboseLevel > 1 ) {
281- const dictsUsed = [ ...dictionaries ]
282- . sort ( )
283- . map ( ( name ) => chalk . green ( name ) )
284- . join ( ', ' ) ;
285- const msg = unindent `
286- File type: ${ config . languageId } , Language: ${ config . language } , Issues: ${
287- result . issues . length
288- } ${ elapsed . toFixed ( 2 ) } ms
289- Config file Used: ${ relativeToCwd ( spellResult . localConfigFilepath || configInfo . source , cfg . root ) }
290- Dictionaries Used:
291- ${ wordWrapAnsiText ( dictsUsed , 70 ) } ` ;
292- reporter . info ( indent ( msg , ' ' ) , MessageTypes . Info ) ;
293- }
294-
295- if ( cfg . options . debug ) {
296- const { enabled, language, languageId, dictionaries } = config ;
297- const useConfig = { languageId, enabled, language, dictionaries } ;
298- const msg = unindent `\
299- Debug Config: ${ formatWithOptions ( { depth : 2 , colors : useColor } , useConfig ) } ` ;
300- reporter . debug ( msg ) ;
301- }
302- }
303-
304- function mapIssue ( { doc : _ , ...tdo } : TextDocumentOffset & ValidationIssue ) : Issue {
305- const context = cfg . showContext ? extractContext ( tdo , cfg . showContext ) : undefined ;
306- return util . clean ( { ...tdo , context } ) ;
307- }
308-
309137 async function processFiles (
310138 files : string [ ] | AsyncIterable < string > ,
311139 configInfo : ConfigInfo ,
@@ -366,7 +194,7 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
366194 result,
367195 } ;
368196 }
369- const result = await processFile ( filename , configInfo , cache , fetchResult ) ;
197+ const result = await processFile ( filename , cache , fetchResult , getProcessFileOptions ( configInfo ) ) ;
370198 return { filename, fileNum : index , result } ;
371199 }
372200
@@ -405,53 +233,15 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
405233 return status ;
406234 }
407235
408- interface ConfigDependencies {
409- files : string [ ] ;
410- }
411-
412- function calcDependencies ( config : CSpellSettings ) : ConfigDependencies {
413- const { configFiles, dictionaryFiles } = extractDependencies ( config ) ;
414-
415- return { files : [ ...configFiles , ...dictionaryFiles ] } ;
416- }
417-
418- async function reportConfigurationErrors ( config : CSpellSettings ) : Promise < number > {
419- const errors = extractImportErrors ( config ) ;
420- let count = 0 ;
421- errors . forEach ( ( ref ) => {
422- const key = ref . error . toString ( ) ;
423- if ( configErrors . has ( key ) ) return ;
424- configErrors . add ( key ) ;
425- count += 1 ;
426- reporter . error ( 'Configuration' , ref . error ) ;
427- } ) ;
428-
429- const dictCollection = await getDictionary ( config ) ;
430- dictCollection . dictionaries . forEach ( ( dict ) => {
431- const dictErrors = dict . getErrors ?.( ) || [ ] ;
432- const msg = `Dictionary Error with (${ dict . name } )` ;
433- dictErrors . forEach ( ( error ) => {
434- const key = msg + error . toString ( ) ;
435- if ( configErrors . has ( key ) ) return ;
436- configErrors . add ( key ) ;
437- count += 1 ;
438- reporter . error ( msg , error ) ;
439- } ) ;
440- } ) ;
441-
442- return count ;
443- }
444-
445- function countConfigErrors ( configInfo : ConfigInfo ) : Promise < number > {
446- return reportConfigurationErrors ( configInfo . config ) ;
447- }
448-
449236 async function run ( ) : Promise < RunResult > {
450237 if ( cfg . options . root ) {
451238 setEnvironmentVariable ( ENV_CSPELL_GLOB_ROOT , cfg . root ) ;
452239 }
453240
454241 const configInfo : ConfigInfo = await readConfig ( cfg . configFile , cfg . root , cfg . options . stopConfigSearchAt ) ;
242+
243+ const processFileOptions = getProcessFileOptions ( configInfo ) ;
244+
455245 if ( cfg . options . defaultConfiguration !== undefined ) {
456246 configInfo . config . loadDefaultConfiguration = cfg . options . defaultConfiguration ;
457247 }
@@ -484,7 +274,7 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
484274 reporter . info ( `Config Files Found:\n ${ relativeToCwd ( configInfo . source ) } \n` , MessageTypes . Info ) ;
485275 }
486276
487- const configErrors = await countConfigErrors ( configInfo ) ;
277+ const configErrors = await countConfigErrors ( configInfo , processFileOptions ) ;
488278 if ( configErrors && cfg . options . exitCode !== false && ! cfg . options . continueOnError ) {
489279 return runResult ( { errors : configErrors } ) ;
490280 }
@@ -528,6 +318,19 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
528318 MessageTypes . Info ,
529319 ) ;
530320 }
321+
322+ function getProcessFileOptions ( configInfo : ConfigInfo ) : ProcessFileOptions {
323+ const processFileOptionsGeneral : ProcessFileOptions = {
324+ reporter,
325+ chalk,
326+ configInfo,
327+ cfg,
328+ verboseLevel,
329+ useColor,
330+ configErrors,
331+ } ;
332+ return processFileOptionsGeneral ;
333+ }
531334}
532335
533336interface AppGlobInfo {
@@ -765,16 +568,6 @@ function globPattern(g: Glob) {
765568 return typeof g === 'string' ? g : g . glob ;
766569}
767570
768- export class LinterError extends Error {
769- constructor ( message : string ) {
770- super ( message ) ;
771- }
772-
773- toString ( ) : string {
774- return this . message ;
775- }
776- }
777-
778571function calcVerboseLevel ( options : LintRequest [ 'options' ] ) : number {
779572 return options . verboseLevel ?? ( options . verbose ? 1 : 0 ) ;
780573}
0 commit comments