cspell 7.1.1 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/esm/app.d.ts +5 -0
- package/dist/esm/app.js +45 -0
- package/dist/esm/app.mjs +1 -0
- package/dist/esm/application.d.ts +16 -0
- package/dist/esm/application.js +88 -0
- package/dist/esm/application.mjs +1 -0
- package/dist/esm/cli-reporter.d.ts +16 -0
- package/dist/esm/cli-reporter.js +218 -0
- package/dist/esm/cli-reporter.mjs +1 -0
- package/dist/esm/commandCheck.d.ts +3 -0
- package/dist/esm/commandCheck.js +48 -0
- package/dist/esm/commandCheck.mjs +1 -0
- package/dist/esm/commandLink.d.ts +3 -0
- package/dist/esm/commandLink.js +47 -0
- package/dist/esm/commandLink.mjs +1 -0
- package/dist/esm/commandLint.d.ts +3 -0
- package/dist/esm/commandLint.js +128 -0
- package/dist/esm/commandLint.mjs +7 -2
- package/dist/esm/commandSuggestion.d.ts +3 -0
- package/dist/esm/commandSuggestion.js +61 -0
- package/dist/esm/commandSuggestion.mjs +1 -0
- package/dist/esm/commandTrace.d.ts +3 -0
- package/dist/esm/commandTrace.js +60 -0
- package/dist/esm/commandTrace.mjs +1 -0
- package/dist/esm/emitters/DictionaryPathFormat.d.ts +3 -0
- package/dist/esm/emitters/DictionaryPathFormat.js +12 -0
- package/dist/esm/emitters/DictionaryPathFormat.mjs +1 -0
- package/dist/esm/emitters/suggestionsEmitter.d.ts +13 -0
- package/dist/esm/emitters/suggestionsEmitter.js +77 -0
- package/dist/esm/emitters/suggestionsEmitter.mjs +1 -0
- package/dist/esm/emitters/traceEmitter.d.ts +21 -0
- package/dist/esm/emitters/traceEmitter.js +141 -0
- package/dist/esm/emitters/traceEmitter.mjs +1 -0
- package/dist/esm/featureFlags/featureFlags.d.ts +4 -0
- package/dist/esm/featureFlags/featureFlags.js +20 -0
- package/dist/esm/featureFlags/featureFlags.mjs +1 -0
- package/dist/esm/featureFlags/index.d.ts +2 -0
- package/dist/esm/featureFlags/index.js +2 -0
- package/dist/esm/featureFlags/index.mjs +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/link.d.ts +8 -0
- package/dist/esm/link.js +39 -0
- package/dist/esm/link.mjs +1 -0
- package/dist/esm/lint/LintRequest.d.ts +23 -0
- package/dist/esm/lint/LintRequest.js +21 -0
- package/dist/esm/lint/LintRequest.mjs +1 -0
- package/dist/esm/lint/index.d.ts +3 -0
- package/dist/esm/lint/index.js +3 -0
- package/dist/esm/lint/index.mjs +1 -0
- package/dist/esm/lint/lint.d.ts +4 -0
- package/dist/esm/lint/lint.js +469 -0
- package/dist/esm/lint/lint.mjs +1 -0
- package/dist/esm/options.d.ts +178 -0
- package/dist/esm/options.js +8 -0
- package/dist/esm/options.mjs +1 -0
- package/dist/esm/repl/index.d.ts +18 -0
- package/dist/esm/repl/index.js +47 -0
- package/dist/esm/repl/index.mjs +1 -0
- package/dist/esm/util/InMemoryReporter.d.ts +28 -0
- package/dist/esm/util/InMemoryReporter.js +43 -0
- package/dist/esm/util/InMemoryReporter.mjs +1 -0
- package/dist/esm/util/async.d.ts +3 -0
- package/dist/esm/util/async.js +4 -0
- package/dist/esm/util/async.mjs +1 -0
- package/dist/esm/util/cache/CSpellLintResultCache.d.ts +20 -0
- package/dist/esm/util/cache/CSpellLintResultCache.js +2 -0
- package/dist/esm/util/cache/CSpellLintResultCache.mjs +1 -0
- package/dist/esm/util/cache/CacheOptions.d.ts +34 -0
- package/dist/esm/util/cache/CacheOptions.js +2 -0
- package/dist/esm/util/cache/CacheOptions.mjs +1 -0
- package/dist/esm/util/cache/DiskCache.d.ts +63 -0
- package/dist/esm/util/cache/DiskCache.js +207 -0
- package/dist/esm/util/cache/DiskCache.mjs +1 -0
- package/dist/esm/util/cache/DummyCache.d.ts +11 -0
- package/dist/esm/util/cache/DummyCache.js +18 -0
- package/dist/esm/util/cache/DummyCache.mjs +1 -0
- package/dist/esm/util/cache/ObjectCollection.d.ts +17 -0
- package/dist/esm/util/cache/ObjectCollection.js +131 -0
- package/dist/esm/util/cache/ObjectCollection.mjs +1 -0
- package/dist/esm/util/cache/createCache.d.ts +31 -0
- package/dist/esm/util/cache/createCache.js +69 -0
- package/dist/esm/util/cache/createCache.mjs +1 -0
- package/dist/esm/util/cache/fileEntryCache.d.ts +9 -0
- package/dist/esm/util/cache/fileEntryCache.js +79 -0
- package/dist/esm/util/cache/fileEntryCache.mjs +1 -0
- package/dist/esm/util/cache/index.d.ts +4 -0
- package/dist/esm/util/cache/index.js +2 -0
- package/dist/esm/util/cache/index.mjs +1 -0
- package/dist/esm/util/constants.d.ts +6 -0
- package/dist/esm/util/constants.js +5 -0
- package/dist/esm/util/constants.mjs +1 -0
- package/dist/esm/util/errors.d.ts +23 -0
- package/dist/esm/util/errors.js +51 -0
- package/dist/esm/util/errors.mjs +1 -0
- package/dist/esm/util/fileHelper.d.ts +63 -0
- package/dist/esm/util/fileHelper.js +182 -0
- package/dist/esm/util/fileHelper.mjs +1 -0
- package/dist/esm/util/glob.d.ts +45 -0
- package/dist/esm/util/glob.js +137 -0
- package/dist/esm/util/glob.mjs +1 -0
- package/dist/esm/util/prefetch.d.ts +2 -0
- package/dist/esm/util/prefetch.js +16 -0
- package/dist/esm/util/prefetch.mjs +1 -0
- package/dist/esm/util/reporters.d.ts +14 -0
- package/dist/esm/util/reporters.js +62 -0
- package/dist/esm/util/reporters.mjs +1 -0
- package/dist/esm/util/stdin.d.ts +2 -0
- package/dist/esm/util/stdin.js +5 -0
- package/dist/esm/util/stdin.mjs +1 -0
- package/dist/esm/util/table.d.ts +10 -0
- package/dist/esm/util/table.js +32 -0
- package/dist/esm/util/table.mjs +1 -0
- package/dist/esm/util/timer.d.ts +8 -0
- package/dist/esm/util/timer.js +11 -0
- package/dist/esm/util/timer.mjs +1 -0
- package/dist/esm/util/types.d.ts +7 -0
- package/dist/esm/util/types.js +5 -0
- package/dist/esm/util/types.mjs +1 -0
- package/dist/esm/util/util.d.ts +13 -0
- package/dist/esm/util/util.js +45 -0
- package/dist/esm/util/util.mjs +1 -0
- package/package.json +11 -13
package/dist/esm/index.mjs
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Link } from 'cspell-lib';
|
|
2
|
+
import type { Table } from './util/table.js';
|
|
3
|
+
export declare const listGlobalImports: typeof Link.listGlobalImports;
|
|
4
|
+
export declare const addPathsToGlobalImports: typeof Link.addPathsToGlobalImports;
|
|
5
|
+
export declare const removePathsFromGlobalImports: typeof Link.removePathsFromGlobalImports;
|
|
6
|
+
export declare function listGlobalImportsResultToTable(results: Link.ListGlobalImportsResult[]): Table;
|
|
7
|
+
export declare function addPathsToGlobalImportsResultToTable(results: Link.AddPathsToGlobalImportsResults): Table;
|
|
8
|
+
//# sourceMappingURL=link.d.ts.map
|
package/dist/esm/link.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Link } from 'cspell-lib';
|
|
3
|
+
export const listGlobalImports = Link.listGlobalImports;
|
|
4
|
+
export const addPathsToGlobalImports = Link.addPathsToGlobalImports;
|
|
5
|
+
export const removePathsFromGlobalImports = Link.removePathsFromGlobalImports;
|
|
6
|
+
export function listGlobalImportsResultToTable(results) {
|
|
7
|
+
const header = ['id', 'package', 'name', 'filename', 'dictionaries', 'errors'];
|
|
8
|
+
const decorate = (isError) => (isError ? (s) => chalk.red(s) : (s) => s);
|
|
9
|
+
function toColumns(r) {
|
|
10
|
+
return [
|
|
11
|
+
r.id,
|
|
12
|
+
r.package?.name,
|
|
13
|
+
r.name,
|
|
14
|
+
r.filename,
|
|
15
|
+
r.dictionaryDefinitions?.map((def) => def.name).join(', '),
|
|
16
|
+
r.error ? 'Failed to read file.' : '',
|
|
17
|
+
]
|
|
18
|
+
.map((c) => c || '')
|
|
19
|
+
.map(decorate(!!r.error));
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
header,
|
|
23
|
+
rows: results.map(toColumns),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function addPathsToGlobalImportsResultToTable(results) {
|
|
27
|
+
const header = ['filename', 'errors'];
|
|
28
|
+
const decorate = (isError) => (isError ? (s) => chalk.red(s) : (s) => s);
|
|
29
|
+
function toColumns(r) {
|
|
30
|
+
return [r.resolvedToFilename || r.filename, r.error ? 'Failed to read file.' : '']
|
|
31
|
+
.map((c) => c || '')
|
|
32
|
+
.map(decorate(!!r.error));
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
header,
|
|
36
|
+
rows: results.resolvedSettings.map(toColumns),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=link.js.map
|
package/dist/esm/link.mjs
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Issue } from '@cspell/cspell-types';
|
|
2
|
+
import type { LinterOptions } from '../options.js';
|
|
3
|
+
import type { GlobSrcInfo } from '../util/glob.js';
|
|
4
|
+
import type { FinalizedReporter } from '../util/reporters.js';
|
|
5
|
+
interface Deprecated {
|
|
6
|
+
fileLists?: LinterOptions['fileList'];
|
|
7
|
+
}
|
|
8
|
+
export declare class LintRequest {
|
|
9
|
+
readonly fileGlobs: string[];
|
|
10
|
+
readonly options: LinterOptions & Deprecated;
|
|
11
|
+
readonly reporter: FinalizedReporter;
|
|
12
|
+
readonly uniqueFilter: (issue: Issue) => boolean;
|
|
13
|
+
readonly locale: string;
|
|
14
|
+
readonly configFile: string | undefined;
|
|
15
|
+
readonly excludes: GlobSrcInfo[];
|
|
16
|
+
readonly root: string;
|
|
17
|
+
readonly showContext: number;
|
|
18
|
+
readonly enableGlobDot: boolean | undefined;
|
|
19
|
+
readonly fileLists: string[];
|
|
20
|
+
constructor(fileGlobs: string[], options: LinterOptions & Deprecated, reporter: FinalizedReporter);
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=LintRequest.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { calcExcludeGlobInfo } from '../util/glob.js';
|
|
3
|
+
import * as util from '../util/util.js';
|
|
4
|
+
const defaultContextRange = 20;
|
|
5
|
+
export class LintRequest {
|
|
6
|
+
constructor(fileGlobs, options, reporter) {
|
|
7
|
+
this.fileGlobs = fileGlobs;
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.reporter = reporter;
|
|
10
|
+
this.root = path.resolve(options.root || process.cwd());
|
|
11
|
+
this.configFile = options.config;
|
|
12
|
+
this.excludes = calcExcludeGlobInfo(this.root, options.exclude);
|
|
13
|
+
this.locale = options.locale || '';
|
|
14
|
+
this.enableGlobDot = options.dot;
|
|
15
|
+
this.uniqueFilter = options.unique ? util.uniqueFilterFnGenerator((issue) => issue.text) : () => true;
|
|
16
|
+
this.showContext =
|
|
17
|
+
options.showContext === true ? defaultContextRange : options.showContext ? options.showContext : 0;
|
|
18
|
+
this.fileLists = (options.fileList ?? options.fileLists) || [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=LintRequest.js.map
|
package/dist/esm/lint/index.mjs
CHANGED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { isAsyncIterable, operators, opFilter, pipeAsync, pipeSync } from '@cspell/cspell-pipe';
|
|
2
|
+
import { opMap, pipe } from '@cspell/cspell-pipe/sync';
|
|
3
|
+
import { MessageTypes } from '@cspell/cspell-types';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { findRepoRoot, GitIgnore } from 'cspell-gitignore';
|
|
6
|
+
import { GlobMatcher } from 'cspell-glob';
|
|
7
|
+
import * as cspell from 'cspell-lib';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { format } from 'util';
|
|
10
|
+
import { npmPackage } from '../../lib/pkgInfo.cjs';
|
|
11
|
+
import { URI } from '../../lib/uri.cjs';
|
|
12
|
+
import { getFeatureFlags } from '../featureFlags/index.js';
|
|
13
|
+
import { calcCacheSettings, createCache } from '../util/cache/index.js';
|
|
14
|
+
import { CheckFailed, toApplicationError, toError } from '../util/errors.js';
|
|
15
|
+
import { fileInfoToDocument, filenameToUri, findFiles, isBinaryFile, isNotDir, readConfig, readFileInfo, readFileListFiles, } from '../util/fileHelper.js';
|
|
16
|
+
import { buildGlobMatcher, extractGlobsFromMatcher, extractPatterns, normalizeFileOrGlobsToRoot, normalizeGlobsToRoot, } from '../util/glob.js';
|
|
17
|
+
import { prefetchIterable } from '../util/prefetch.js';
|
|
18
|
+
import { loadReporters, mergeReporters } from '../util/reporters.js';
|
|
19
|
+
import { getTimeMeasurer } from '../util/timer.js';
|
|
20
|
+
import * as util from '../util/util.js';
|
|
21
|
+
const version = npmPackage.version;
|
|
22
|
+
const BATCH_SIZE = 8;
|
|
23
|
+
const { opFilterAsync } = operators;
|
|
24
|
+
export async function runLint(cfg) {
|
|
25
|
+
let { reporter } = cfg;
|
|
26
|
+
cspell.setLogger(getLoggerFromReporter(reporter));
|
|
27
|
+
const configErrors = new Set();
|
|
28
|
+
const timer = getTimeMeasurer();
|
|
29
|
+
const lintResult = await run();
|
|
30
|
+
await reporter.result(lintResult);
|
|
31
|
+
const elapsed = timer();
|
|
32
|
+
if (getFeatureFlags().getFlag('timer')) {
|
|
33
|
+
console.log(`Elapsed Time: ${elapsed.toFixed(2)}ms`);
|
|
34
|
+
}
|
|
35
|
+
return lintResult;
|
|
36
|
+
function prefetch(filename, configInfo, cache) {
|
|
37
|
+
if (isBinaryFile(filename, cfg.root))
|
|
38
|
+
return { filename, result: Promise.resolve({ skip: true }) };
|
|
39
|
+
async function fetch() {
|
|
40
|
+
const getElapsedTimeMs = getTimeMeasurer();
|
|
41
|
+
const cachedResult = await cache.getCachedLintResults(filename);
|
|
42
|
+
if (cachedResult) {
|
|
43
|
+
reporter.debug(`Filename: ${filename}, using cache`);
|
|
44
|
+
const fileResult = { ...cachedResult, elapsedTimeMs: getElapsedTimeMs() };
|
|
45
|
+
return { fileResult };
|
|
46
|
+
}
|
|
47
|
+
const uri = filenameToUri(filename, cfg.root);
|
|
48
|
+
const checkResult = await cspell.shouldCheckDocument({ uri }, {}, configInfo.config);
|
|
49
|
+
if (!checkResult.shouldCheck)
|
|
50
|
+
return { skip: true };
|
|
51
|
+
const fileInfo = await readFileInfo(filename, undefined, true);
|
|
52
|
+
return { fileInfo };
|
|
53
|
+
}
|
|
54
|
+
const result = fetch();
|
|
55
|
+
return { filename, result };
|
|
56
|
+
}
|
|
57
|
+
async function processFile(filename, configInfo, cache, prefetch) {
|
|
58
|
+
if (prefetch?.fileResult)
|
|
59
|
+
return prefetch.fileResult;
|
|
60
|
+
const getElapsedTimeMs = getTimeMeasurer();
|
|
61
|
+
const cachedResult = await cache.getCachedLintResults(filename);
|
|
62
|
+
if (cachedResult) {
|
|
63
|
+
reporter.debug(`Filename: ${filename}, using cache`);
|
|
64
|
+
return { ...cachedResult, elapsedTimeMs: getElapsedTimeMs() };
|
|
65
|
+
}
|
|
66
|
+
const result = {
|
|
67
|
+
fileInfo: {
|
|
68
|
+
filename,
|
|
69
|
+
},
|
|
70
|
+
issues: [],
|
|
71
|
+
processed: false,
|
|
72
|
+
errors: 0,
|
|
73
|
+
configErrors: 0,
|
|
74
|
+
elapsedTimeMs: 0,
|
|
75
|
+
};
|
|
76
|
+
const fileInfo = prefetch?.fileInfo || (await readFileInfo(filename, undefined, true));
|
|
77
|
+
if (fileInfo.errorCode) {
|
|
78
|
+
if (fileInfo.errorCode !== 'EISDIR' && cfg.options.mustFindFiles) {
|
|
79
|
+
const err = toError(`File not found: "${filename}"`);
|
|
80
|
+
reporter.error('Linter:', err);
|
|
81
|
+
result.errors += 1;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
|
|
86
|
+
const { text } = fileInfo;
|
|
87
|
+
result.fileInfo = fileInfo;
|
|
88
|
+
let spellResult = {};
|
|
89
|
+
reporter.info(`Checking: ${filename}, File type: ${doc.languageId ?? 'auto'}, Language: ${doc.locale ?? 'default'}`, MessageTypes.Info);
|
|
90
|
+
try {
|
|
91
|
+
const { showSuggestions: generateSuggestions, validateDirectives } = cfg.options;
|
|
92
|
+
const numSuggestions = configInfo.config.numSuggestions ?? 5;
|
|
93
|
+
const validateOptions = util.clean({ generateSuggestions, numSuggestions, validateDirectives });
|
|
94
|
+
const r = await cspell.spellCheckDocument(doc, validateOptions, configInfo.config);
|
|
95
|
+
spellResult = r;
|
|
96
|
+
result.processed = r.checked;
|
|
97
|
+
result.issues = cspell.Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
reporter.error(`Failed to process "${filename}"`, toError(e));
|
|
101
|
+
result.errors += 1;
|
|
102
|
+
}
|
|
103
|
+
result.elapsedTimeMs = getElapsedTimeMs();
|
|
104
|
+
const config = spellResult.settingsUsed ?? {};
|
|
105
|
+
result.configErrors += await reportConfigurationErrors(config);
|
|
106
|
+
const elapsed = result.elapsedTimeMs / 1000.0;
|
|
107
|
+
const dictionaries = config.dictionaries || [];
|
|
108
|
+
reporter.info(`Checked: ${filename}, File type: ${config.languageId}, Language: ${config.language} ... Issues: ${result.issues.length} ${elapsed}S`, MessageTypes.Info);
|
|
109
|
+
reporter.info(`Config file Used: ${spellResult.localConfigFilepath || configInfo.source}`, MessageTypes.Info);
|
|
110
|
+
reporter.info(`Dictionaries Used: ${dictionaries.join(', ')}`, MessageTypes.Info);
|
|
111
|
+
if (cfg.options.debug) {
|
|
112
|
+
const { id: _id, name: _name, __imports, __importRef, ...cfg } = config;
|
|
113
|
+
const debugCfg = {
|
|
114
|
+
filename,
|
|
115
|
+
languageId: doc.languageId ?? cfg.languageId ?? 'default',
|
|
116
|
+
config: { ...cfg, source: null },
|
|
117
|
+
source: spellResult.localConfigFilepath,
|
|
118
|
+
};
|
|
119
|
+
reporter.debug(JSON.stringify(debugCfg, undefined, 2));
|
|
120
|
+
}
|
|
121
|
+
const dep = calcDependencies(config);
|
|
122
|
+
cache.setCachedLintResults(result, dep.files);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function mapIssue({ doc: _, ...tdo }) {
|
|
126
|
+
const context = cfg.showContext
|
|
127
|
+
? extractContext(tdo, cfg.showContext)
|
|
128
|
+
: { text: tdo.line.text.trimEnd(), offset: tdo.line.offset };
|
|
129
|
+
return util.clean({ ...tdo, context });
|
|
130
|
+
}
|
|
131
|
+
async function processFiles(files, configInfo, cacheSettings) {
|
|
132
|
+
const fileCount = files instanceof Array ? files.length : undefined;
|
|
133
|
+
const status = runResult();
|
|
134
|
+
const cache = createCache(cacheSettings);
|
|
135
|
+
const failFast = cfg.options.failFast ?? configInfo.config.failFast ?? false;
|
|
136
|
+
const emitProgressBegin = (filename, fileNum, fileCount) => reporter.progress({
|
|
137
|
+
type: 'ProgressFileBegin',
|
|
138
|
+
fileNum,
|
|
139
|
+
fileCount,
|
|
140
|
+
filename,
|
|
141
|
+
});
|
|
142
|
+
const emitProgressComplete = (filename, fileNum, fileCount, result) => reporter.progress(util.clean({
|
|
143
|
+
type: 'ProgressFileComplete',
|
|
144
|
+
fileNum,
|
|
145
|
+
fileCount,
|
|
146
|
+
filename,
|
|
147
|
+
elapsedTimeMs: result?.elapsedTimeMs,
|
|
148
|
+
processed: result?.processed,
|
|
149
|
+
numErrors: result?.issues.length || result?.errors,
|
|
150
|
+
cached: result?.cached,
|
|
151
|
+
}));
|
|
152
|
+
function* prefetchFiles(files) {
|
|
153
|
+
const iter = prefetchIterable(pipe(files, opMap((filename) => prefetch(filename, configInfo, cache))), BATCH_SIZE);
|
|
154
|
+
for (const v of iter) {
|
|
155
|
+
yield v;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function* prefetchFilesAsync(files) {
|
|
159
|
+
for await (const filename of files) {
|
|
160
|
+
yield prefetch(filename, configInfo, cache);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const emptyResult = {
|
|
164
|
+
fileInfo: { filename: '' },
|
|
165
|
+
issues: [],
|
|
166
|
+
processed: false,
|
|
167
|
+
errors: 0,
|
|
168
|
+
configErrors: 0,
|
|
169
|
+
elapsedTimeMs: 1,
|
|
170
|
+
};
|
|
171
|
+
async function processPrefetchFileResult(pf, index) {
|
|
172
|
+
const { filename, result: pFetchResult } = pf;
|
|
173
|
+
const getElapsedTimeMs = getTimeMeasurer();
|
|
174
|
+
const fetchResult = await pFetchResult;
|
|
175
|
+
emitProgressBegin(filename, index, fileCount ?? index);
|
|
176
|
+
if (fetchResult?.skip) {
|
|
177
|
+
return {
|
|
178
|
+
filename,
|
|
179
|
+
fileNum: index,
|
|
180
|
+
result: { ...emptyResult, fileInfo: { filename }, elapsedTimeMs: getElapsedTimeMs() },
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const result = await processFile(filename, configInfo, cache, fetchResult);
|
|
184
|
+
return { filename, fileNum: index, result };
|
|
185
|
+
}
|
|
186
|
+
async function* loadAndProcessFiles() {
|
|
187
|
+
let i = 0;
|
|
188
|
+
if (isAsyncIterable(files)) {
|
|
189
|
+
for await (const pf of prefetchFilesAsync(files)) {
|
|
190
|
+
yield processPrefetchFileResult(pf, ++i);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
for (const pf of prefetchFiles(files)) {
|
|
195
|
+
await pf.result;
|
|
196
|
+
yield await processPrefetchFileResult(pf, ++i);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for await (const fileP of loadAndProcessFiles()) {
|
|
201
|
+
const { filename, fileNum, result } = await fileP;
|
|
202
|
+
status.files += 1;
|
|
203
|
+
status.cachedFiles = (status.cachedFiles || 0) + (result.cached ? 1 : 0);
|
|
204
|
+
emitProgressComplete(filename, fileNum, fileCount ?? fileNum, result);
|
|
205
|
+
// Show the spelling errors after emitting the progress.
|
|
206
|
+
result.issues.filter(cfg.uniqueFilter).forEach((issue) => reporter.issue(issue));
|
|
207
|
+
if (result.issues.length || result.errors) {
|
|
208
|
+
status.filesWithIssues.add(filename);
|
|
209
|
+
status.issues += result.issues.length;
|
|
210
|
+
status.errors += result.errors;
|
|
211
|
+
if (failFast) {
|
|
212
|
+
return status;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
status.errors += result.configErrors;
|
|
216
|
+
}
|
|
217
|
+
cache.reconcile();
|
|
218
|
+
return status;
|
|
219
|
+
}
|
|
220
|
+
function calcDependencies(config) {
|
|
221
|
+
const { configFiles, dictionaryFiles } = cspell.extractDependencies(config);
|
|
222
|
+
return { files: configFiles.concat(dictionaryFiles) };
|
|
223
|
+
}
|
|
224
|
+
async function reportConfigurationErrors(config) {
|
|
225
|
+
const errors = cspell.extractImportErrors(config);
|
|
226
|
+
let count = 0;
|
|
227
|
+
errors.forEach((ref) => {
|
|
228
|
+
const key = ref.error.toString();
|
|
229
|
+
if (configErrors.has(key))
|
|
230
|
+
return;
|
|
231
|
+
configErrors.add(key);
|
|
232
|
+
count += 1;
|
|
233
|
+
reporter.error('Configuration', ref.error);
|
|
234
|
+
});
|
|
235
|
+
const dictCollection = await cspell.getDictionary(config);
|
|
236
|
+
dictCollection.dictionaries.forEach((dict) => {
|
|
237
|
+
const dictErrors = dict.getErrors?.() || [];
|
|
238
|
+
const msg = `Dictionary Error with (${dict.name})`;
|
|
239
|
+
dictErrors.forEach((error) => {
|
|
240
|
+
const key = msg + error.toString();
|
|
241
|
+
if (configErrors.has(key))
|
|
242
|
+
return;
|
|
243
|
+
configErrors.add(key);
|
|
244
|
+
count += 1;
|
|
245
|
+
reporter.error(msg, error);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
return count;
|
|
249
|
+
}
|
|
250
|
+
function countConfigErrors(configInfo) {
|
|
251
|
+
return reportConfigurationErrors(configInfo.config);
|
|
252
|
+
}
|
|
253
|
+
async function run() {
|
|
254
|
+
if (cfg.options.root) {
|
|
255
|
+
process.env[cspell.ENV_CSPELL_GLOB_ROOT] = cfg.root;
|
|
256
|
+
}
|
|
257
|
+
const configInfo = await readConfig(cfg.configFile, cfg.root);
|
|
258
|
+
if (cfg.options.defaultConfiguration !== undefined) {
|
|
259
|
+
configInfo.config.loadDefaultConfiguration = cfg.options.defaultConfiguration;
|
|
260
|
+
}
|
|
261
|
+
const reporterConfig = util.clean({
|
|
262
|
+
maxNumberOfProblems: configInfo.config.maxNumberOfProblems,
|
|
263
|
+
maxDuplicateProblems: configInfo.config.maxDuplicateProblems,
|
|
264
|
+
minWordLength: configInfo.config.minWordLength,
|
|
265
|
+
...cfg.options,
|
|
266
|
+
});
|
|
267
|
+
const reporters = cfg.options.reporter ?? configInfo.config.reporters;
|
|
268
|
+
reporter = mergeReporters(...(await loadReporters(reporters, cfg.reporter, reporterConfig)));
|
|
269
|
+
cspell.setLogger(getLoggerFromReporter(reporter));
|
|
270
|
+
const globInfo = await determineGlobs(configInfo, cfg);
|
|
271
|
+
const { fileGlobs, excludeGlobs } = globInfo;
|
|
272
|
+
const hasFileLists = !!cfg.fileLists.length;
|
|
273
|
+
if (!fileGlobs.length && !hasFileLists) {
|
|
274
|
+
// Nothing to do.
|
|
275
|
+
return runResult();
|
|
276
|
+
}
|
|
277
|
+
header(fileGlobs, excludeGlobs);
|
|
278
|
+
checkGlobs(fileGlobs, reporter);
|
|
279
|
+
reporter.info(`Config Files Found:\n ${configInfo.source}\n`, MessageTypes.Info);
|
|
280
|
+
const configErrors = await countConfigErrors(configInfo);
|
|
281
|
+
if (configErrors)
|
|
282
|
+
return runResult({ errors: configErrors });
|
|
283
|
+
// Get Exclusions from the config files.
|
|
284
|
+
const { root } = cfg;
|
|
285
|
+
try {
|
|
286
|
+
const cacheSettings = await calcCacheSettings(configInfo.config, { ...cfg.options, version }, root);
|
|
287
|
+
const files = await determineFilesToCheck(configInfo, cfg, reporter, globInfo);
|
|
288
|
+
return await processFiles(files, configInfo, cacheSettings);
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
const err = toApplicationError(e);
|
|
292
|
+
reporter.error('Linter', err);
|
|
293
|
+
return runResult({ errors: 1 });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function header(files, cliExcludes) {
|
|
297
|
+
const formattedFiles = files.length > 100 ? files.slice(0, 100).concat(['...']) : files;
|
|
298
|
+
reporter.info(`
|
|
299
|
+
cspell;
|
|
300
|
+
Date: ${new Date().toUTCString()}
|
|
301
|
+
Options:
|
|
302
|
+
verbose: ${yesNo(!!cfg.options.verbose)}
|
|
303
|
+
config: ${cfg.configFile || 'default'}
|
|
304
|
+
exclude: ${cliExcludes.join('\n ')}
|
|
305
|
+
files: ${formattedFiles}
|
|
306
|
+
wordsOnly: ${yesNo(!!cfg.options.wordsOnly)}
|
|
307
|
+
unique: ${yesNo(!!cfg.options.unique)}
|
|
308
|
+
`, MessageTypes.Info);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function checkGlobs(globs, reporter) {
|
|
312
|
+
globs
|
|
313
|
+
.filter((g) => g.startsWith("'") || g.endsWith("'"))
|
|
314
|
+
.map((glob) => chalk.yellow(glob))
|
|
315
|
+
.forEach((glob) => reporter.error('Linter', new CheckFailed(`Glob starting or ending with ' (single quote) is not likely to match any files: ${glob}.`)));
|
|
316
|
+
}
|
|
317
|
+
async function determineGlobs(configInfo, cfg) {
|
|
318
|
+
const useGitignore = cfg.options.gitignore ?? configInfo.config.useGitignore ?? false;
|
|
319
|
+
const gitignoreRoots = cfg.options.gitignoreRoot ?? configInfo.config.gitignoreRoot;
|
|
320
|
+
const gitIgnore = useGitignore ? await generateGitIgnore(gitignoreRoots) : undefined;
|
|
321
|
+
const cliGlobs = cfg.fileGlobs;
|
|
322
|
+
const allGlobs = cliGlobs.length ? cliGlobs : configInfo.config.files || [];
|
|
323
|
+
const combinedGlobs = await normalizeFileOrGlobsToRoot(allGlobs, cfg.root);
|
|
324
|
+
const cliExcludeGlobs = extractPatterns(cfg.excludes).map((p) => p.glob);
|
|
325
|
+
const normalizedExcludes = normalizeGlobsToRoot(cliExcludeGlobs, cfg.root, true);
|
|
326
|
+
const includeGlobs = combinedGlobs.filter((g) => !g.startsWith('!'));
|
|
327
|
+
const excludeGlobs = combinedGlobs.filter((g) => g.startsWith('!')).concat(normalizedExcludes);
|
|
328
|
+
const fileGlobs = includeGlobs;
|
|
329
|
+
const appGlobs = { allGlobs, gitIgnore, fileGlobs, excludeGlobs, normalizedExcludes };
|
|
330
|
+
return appGlobs;
|
|
331
|
+
}
|
|
332
|
+
async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
|
|
333
|
+
async function _determineFilesToCheck() {
|
|
334
|
+
const { fileLists } = cfg;
|
|
335
|
+
const hasFileLists = !!fileLists.length;
|
|
336
|
+
const { allGlobs, gitIgnore, fileGlobs, excludeGlobs, normalizedExcludes } = globInfo;
|
|
337
|
+
// Get Exclusions from the config files.
|
|
338
|
+
const { root } = cfg;
|
|
339
|
+
const globsToExclude = (configInfo.config.ignorePaths || []).concat(excludeGlobs);
|
|
340
|
+
const globMatcher = buildGlobMatcher(globsToExclude, root, true);
|
|
341
|
+
const ignoreGlobs = extractGlobsFromMatcher(globMatcher);
|
|
342
|
+
// cspell:word nodir
|
|
343
|
+
const globOptions = {
|
|
344
|
+
root,
|
|
345
|
+
cwd: root,
|
|
346
|
+
ignore: ignoreGlobs.concat(normalizedExcludes),
|
|
347
|
+
nodir: true,
|
|
348
|
+
};
|
|
349
|
+
const enableGlobDot = cfg.enableGlobDot ?? configInfo.config.enableGlobDot;
|
|
350
|
+
if (enableGlobDot !== undefined) {
|
|
351
|
+
globOptions.dot = enableGlobDot;
|
|
352
|
+
}
|
|
353
|
+
const filterFiles = opFilter(filterFilesFn(globMatcher));
|
|
354
|
+
const foundFiles = await (hasFileLists
|
|
355
|
+
? useFileLists(fileLists, allGlobs, root, enableGlobDot)
|
|
356
|
+
: findFiles(fileGlobs, globOptions));
|
|
357
|
+
const filtered = gitIgnore ? await gitIgnore.filterOutIgnored(foundFiles) : foundFiles;
|
|
358
|
+
const files = isAsyncIterable(filtered)
|
|
359
|
+
? pipeAsync(filtered, filterFiles)
|
|
360
|
+
: [...pipeSync(filtered, filterFiles)];
|
|
361
|
+
return files;
|
|
362
|
+
}
|
|
363
|
+
function isExcluded(filename, globMatcherExclude) {
|
|
364
|
+
if (cspell.isBinaryFile(URI.file(filename))) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
const { root } = cfg;
|
|
368
|
+
const absFilename = path.resolve(root, filename);
|
|
369
|
+
const r = globMatcherExclude.matchEx(absFilename);
|
|
370
|
+
if (r.matched) {
|
|
371
|
+
const { glob, source } = extractGlobSource(r.pattern);
|
|
372
|
+
reporter.info(`Excluded File: ${path.relative(root, absFilename)}; Excluded by ${glob} from ${source}`, MessageTypes.Info);
|
|
373
|
+
}
|
|
374
|
+
return r.matched;
|
|
375
|
+
}
|
|
376
|
+
function filterFilesFn(globMatcherExclude) {
|
|
377
|
+
const patterns = globMatcherExclude.patterns;
|
|
378
|
+
const excludeInfo = patterns
|
|
379
|
+
.map(extractGlobSource)
|
|
380
|
+
.map(({ glob, source }) => `Glob: ${glob} from ${source}`)
|
|
381
|
+
.filter(util.uniqueFn());
|
|
382
|
+
reporter.info(`Exclusion Globs: \n ${excludeInfo.join('\n ')}\n`, MessageTypes.Info);
|
|
383
|
+
return (filename) => !isExcluded(filename, globMatcherExclude);
|
|
384
|
+
}
|
|
385
|
+
return _determineFilesToCheck();
|
|
386
|
+
}
|
|
387
|
+
function extractContext(tdo, contextRange) {
|
|
388
|
+
const { line, offset } = tdo;
|
|
389
|
+
const textOffsetInLine = offset - line.offset;
|
|
390
|
+
let left = Math.max(textOffsetInLine - contextRange, 0);
|
|
391
|
+
let right = Math.min(line.text.length, textOffsetInLine + contextRange + tdo.text.length);
|
|
392
|
+
const lineText = line.text;
|
|
393
|
+
const isLetter = /^[a-z]$/i;
|
|
394
|
+
const isSpace = /^\s$/;
|
|
395
|
+
for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
|
|
396
|
+
if (!isLetter.test(lineText[left - 1])) {
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
|
|
401
|
+
if (!isLetter.test(lineText[right])) {
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// remove leading space
|
|
406
|
+
for (; left < textOffsetInLine && isSpace.test(lineText[left]); left++) {
|
|
407
|
+
/* do nothing */
|
|
408
|
+
}
|
|
409
|
+
const context = {
|
|
410
|
+
text: line.text.slice(left, right).trimEnd(),
|
|
411
|
+
offset: left + line.offset,
|
|
412
|
+
};
|
|
413
|
+
return context;
|
|
414
|
+
}
|
|
415
|
+
function extractGlobSource(g) {
|
|
416
|
+
const { glob, rawGlob, source } = g;
|
|
417
|
+
return {
|
|
418
|
+
glob: rawGlob || glob,
|
|
419
|
+
source,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function runResult(init = {}) {
|
|
423
|
+
const { files = 0, filesWithIssues = new Set(), issues = 0, errors = 0, cachedFiles = 0 } = init;
|
|
424
|
+
return { files, filesWithIssues, issues, errors, cachedFiles };
|
|
425
|
+
}
|
|
426
|
+
function yesNo(value) {
|
|
427
|
+
return value ? 'Yes' : 'No';
|
|
428
|
+
}
|
|
429
|
+
function getLoggerFromReporter(reporter) {
|
|
430
|
+
const log = (...params) => {
|
|
431
|
+
const msg = format(...params);
|
|
432
|
+
reporter.info(msg, 'Info');
|
|
433
|
+
};
|
|
434
|
+
const error = (...params) => {
|
|
435
|
+
const msg = format(...params);
|
|
436
|
+
const err = { message: '', name: 'error', toString: () => '' };
|
|
437
|
+
reporter.error(msg, err);
|
|
438
|
+
};
|
|
439
|
+
const warn = (...params) => {
|
|
440
|
+
const msg = format(...params);
|
|
441
|
+
reporter.info(msg, 'Warning');
|
|
442
|
+
};
|
|
443
|
+
return {
|
|
444
|
+
log,
|
|
445
|
+
warn,
|
|
446
|
+
error,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
async function generateGitIgnore(roots) {
|
|
450
|
+
const root = (typeof roots === 'string' ? [roots].filter((r) => !!r) : roots) || [];
|
|
451
|
+
if (!root?.length) {
|
|
452
|
+
const cwd = process.cwd();
|
|
453
|
+
const repo = (await findRepoRoot(cwd)) || cwd;
|
|
454
|
+
root.push(repo);
|
|
455
|
+
}
|
|
456
|
+
return new GitIgnore(root?.map((p) => path.resolve(p)));
|
|
457
|
+
}
|
|
458
|
+
async function useFileLists(fileListFiles, includeGlobPatterns, root, dot) {
|
|
459
|
+
includeGlobPatterns = includeGlobPatterns.length ? includeGlobPatterns : ['**'];
|
|
460
|
+
const options = { root, mode: 'include' };
|
|
461
|
+
if (dot !== undefined) {
|
|
462
|
+
options.dot = dot;
|
|
463
|
+
}
|
|
464
|
+
const globMatcher = new GlobMatcher(includeGlobPatterns, options);
|
|
465
|
+
const filterFiles = (file) => globMatcher.match(file);
|
|
466
|
+
const files = readFileListFiles(fileListFiles);
|
|
467
|
+
return pipeAsync(files, opFilter(filterFiles), opFilterAsync(isNotDir));
|
|
468
|
+
}
|
|
469
|
+
//# sourceMappingURL=lint.js.map
|
package/dist/esm/lint/lint.mjs
CHANGED