Skip to content

Commit

Permalink
dev: read fileLists when checking files.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S committed Dec 28, 2021
1 parent 4fc5f38 commit e3733f5
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
../../../src/app.ts
../../../README.md
3 changes: 3 additions & 0 deletions packages/cspell/fixtures/fileHelper/file-list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
file1
../file2
dir/file3
1 change: 1 addition & 0 deletions packages/cspell/fixtures/fileHelper/nested/file-list-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file2.txt
35 changes: 35 additions & 0 deletions packages/cspell/src/fileHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { readFileListFile, readFileListFiles } from './fileHelper';
import * as path from 'path';

const fixtures = path.join(__dirname, '../fixtures/fileHelper');
const fileListFile = path.join(fixtures, 'file-list.txt');
const fileListFile2 = path.join(fixtures, 'nested/file-list-2.txt');

const oc = expect.objectContaining;

describe('fileHelper', () => {
test('readFileListFile', async () => {
try {
const files = ['a', 'b', 'c'];
process.stdin.isTTY = false;
const pResult = readFileListFile('stdin');
process.stdin.push(files.join('\n'));
process.stdin.emit('end');
const r = await pResult;
expect(r).toEqual(files.map((f) => path.resolve(f)));
} finally {
process.stdin.isTTY = true;
}
});

test('readFileListFiles', async () => {
const files = ['file1', '../file2', 'dir/file3', 'nested/file2.txt'];
const r = await readFileListFiles([fileListFile, fileListFile2]);
expect(r).toEqual(files.map((f) => path.resolve(fixtures, f)));
});

test('readFileListFiles Error', () => {
const r = readFileListFiles(['not-found.txt']);
return expect(r).rejects.toEqual(oc({ message: 'Error reading file: "not-found.txt"' }));
});
});
37 changes: 37 additions & 0 deletions packages/cspell/src/fileHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,40 @@ export function calcFinalConfigInfo(
languageIds,
};
}

/**
* Read
* @param listFiles - array of file paths to read that will contain a list of files. Paths contained in each
* file will be resolved relative to the containing file.
* @returns - a list of files to be processed.
*/
export async function readFileListFiles(listFiles: string[]): Promise<string[]> {
return flatten(await Promise.all(listFiles.map(readFileListFile)));
}

/**
* Read a `listFile` and return the containing file paths resolved relative to the `listFile`.
* @param listFiles - array of file paths to read that will contain a list of files. Paths contained in each
* file will be resolved relative to the containing file.
* @returns - a list of files to be processed.
*/
export async function readFileListFile(listFile: string): Promise<string[]> {
const relTo = path.resolve(path.dirname(listFile));
const content = await readFile(listFile);
const lines = content
.split('\n')
.map((a) => a.trim())
.filter((a) => !!a)
.map((file) => path.resolve(relTo, file));
return lines;
}

function flatten(fileLists: string[][]): string[] {
function* f() {
for (const list of fileLists) {
yield* list;
}
}

return [...f()];
}
7 changes: 6 additions & 1 deletion packages/cspell/src/lint/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { LintRequest } from './LintRequest';
import { InMemoryReporter } from '../util/InMemoryReporter';
import { runLint } from './lint';

const samples = path.resolve(__dirname, '../../samples');
const root = path.resolve(__dirname, '../..');
const samples = path.resolve(root, 'samples');
const latexSamples = path.resolve(samples, 'latex');
const hiddenSamples = path.resolve(samples, 'hidden-test');
const filesToCheck = path.resolve(root, 'fixtures/features/file-list/files-to-check.txt');

const oc = expect.objectContaining;
const j = path.join;
Expand Down Expand Up @@ -35,6 +37,9 @@ describe('Linter Validation Tests', () => {
${['**']} | ${{ root: samples, config: j(samples, 'linked/cspell-import-missing.json') }} | ${oc({ errors: 1, files: 0 })} | ${oc({ errorCount: 1, errors: [expect.any(Error)], issues: [] })}
${['**/ebook.tex']} | ${{ root: samples, config: j(samples, 'cspell-missing-dict.json') }} | ${oc({ errors: 0, files: 0 })} | ${oc({ errorCount: 0, errors: [], issues: [] })}
${['**/ebook.tex']} | ${{ root: samples, config: j(samples, 'linked/cspell-import.json') }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, issues: [] })}
${[]} | ${{ root, config: j(root, 'cspell.json'), fileLists: [filesToCheck] }} | ${oc({ errors: 0, files: 2 })} | ${oc({ errorCount: 0, issues: [] })}
${['**/*.md']} | ${{ root, config: j(root, 'cspell.json'), fileLists: [filesToCheck] }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, issues: [] })}
${['**/*.ts']} | ${{ root, config: j(root, 'cspell.json'), fileLists: [filesToCheck] }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, issues: [] })}
`('runLint $files $options', async ({ files, options, expectedRunResult, expectedReport }) => {
const reporter = new InMemoryReporter();
const runResult = await runLint(new LintRequest(files, options, reporter));
Expand Down
47 changes: 38 additions & 9 deletions packages/cspell/src/lint/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ import type { CSpellReporter, CSpellSettings, Glob, Issue, RunResult, TextDocume
import { MessageTypes } from '@cspell/cspell-types';
import * as commentJson from 'comment-json';
import { findRepoRoot, GitIgnore } from 'cspell-gitignore';
import type { GlobMatcher, GlobPatternNormalized, GlobPatternWithRoot } from 'cspell-glob';
import { GlobMatcher, type GlobMatchOptions, type GlobPatternNormalized, type GlobPatternWithRoot } from 'cspell-glob';
import type { ValidationIssue } from 'cspell-lib';
import * as cspell from 'cspell-lib';
import { Logger } from 'cspell-lib';
import * as path from 'path';
import { format } from 'util';
import { URI } from 'vscode-uri';
import { ConfigInfo, fileInfoToDocument, FileResult, findFiles, readConfig, readFileInfo } from '../fileHelper';
import {
ConfigInfo,
fileInfoToDocument,
FileResult,
findFiles,
readConfig,
readFileInfo,
readFileListFiles,
} from '../fileHelper';
import type { CSpellLintResultCache } from '../util/cache';
import { createCache } from '../util/cache';
import { toError } from '../util/errors';
Expand Down Expand Up @@ -198,7 +206,8 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
const includeGlobs = combinedGlobs.filter((g) => !g.startsWith('!'));
const excludeGlobs = combinedGlobs.filter((g) => g.startsWith('!')).concat(normalizedExcludes);
const fileGlobs: string[] = includeGlobs;
if (!fileGlobs.length && !fileLists.length) {
const hasFileLists = !!fileLists.length;
if (!fileGlobs.length && !hasFileLists) {
// Nothing to do.
return runResult();
}
Expand Down Expand Up @@ -226,7 +235,9 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
globOptions.dot = enableGlobDot;
}

const foundFiles = await findFiles(fileGlobs, globOptions);
const foundFiles = await (hasFileLists
? useFileLists(fileLists, allGlobs, root, enableGlobDot)
: findFiles(fileGlobs, globOptions));
const filtered = gitIgnore ? await gitIgnore.filterOutIgnored(foundFiles) : foundFiles;
const files = filterFiles(filtered, globMatcher);

Expand All @@ -252,13 +263,13 @@ Options:
);
}

function isExcluded(filename: string, globMatcher: GlobMatcher) {
function isExcluded(filename: string, globMatcherExclude: GlobMatcher) {
if (cspell.isBinaryFile(URI.file(filename))) {
return true;
}
const { root } = cfg;
const absFilename = path.resolve(root, filename);
const r = globMatcher.matchEx(absFilename);
const r = globMatcherExclude.matchEx(absFilename);

if (r.matched) {
const { glob, source } = extractGlobSource(r.pattern);
Expand All @@ -279,14 +290,14 @@ Options:
};
}

function filterFiles(files: string[], globMatcher: GlobMatcher): string[] {
const patterns = globMatcher.patterns;
function filterFiles(files: string[], globMatcherExclude: GlobMatcher): string[] {
const patterns = globMatcherExclude.patterns;
const excludeInfo = patterns
.map(extractGlobSource)
.map(({ glob, source }) => `Glob: ${glob} from ${source}`)
.filter(util.uniqueFn());
reporter.info(`Exclusion Globs: \n ${excludeInfo.join('\n ')}\n`, MessageTypes.Info);
const result = files.filter(util.uniqueFn()).filter((filename) => !isExcluded(filename, globMatcher));
const result = files.filter(util.uniqueFn()).filter((filename) => !isExcluded(filename, globMatcherExclude));
return result;
}
}
Expand Down Expand Up @@ -369,3 +380,21 @@ async function generateGitIgnore(roots: string | string[] | undefined) {
}
return new GitIgnore(root?.map((p) => path.resolve(p)));
}

async function useFileLists(
fileListFiles: string[],
includeGlobPatterns: Glob[],
root: string,
dot: boolean | undefined
): Promise<string[]> {
includeGlobPatterns = includeGlobPatterns.length ? includeGlobPatterns : ['**'];
const options: GlobMatchOptions = { root, mode: 'include' };
if (dot !== undefined) {
options.dot = dot;
}
const globMatcher = new GlobMatcher(includeGlobPatterns, options);

const files = await readFileListFiles(fileListFiles);

return files.filter((file) => globMatcher.match(file));
}

0 comments on commit e3733f5

Please sign in to comment.