cspell
Version:
A Spelling Checker for Code!
138 lines • 5.18 kB
JavaScript
import { promises as fs } from 'node:fs';
import * as path from 'node:path';
import { posix } from 'node:path';
import { fileOrGlobToGlob, GlobMatcher } from 'cspell-glob';
import { glob } from 'tinyglobby';
import { clean } from './util.js';
const defaultExcludeGlobs = ['node_modules/**'];
/**
*
* @param pattern - glob patterns and NOT file paths. It can be a file path turned into a glob.
* @param options - search options.
*/
export async function globP(pattern, options) {
const cwd = options?.root || options?.cwd || process.cwd();
const ignore = typeof options?.ignore === 'string' ? [options.ignore] : options?.ignore;
const onlyFiles = options?.nodir;
const dot = options?.dot;
const patterns = typeof pattern === 'string' ? [pattern] : pattern;
const useOptions = clean({
cwd,
onlyFiles,
dot,
ignore,
absolute: true,
followSymbolicLinks: false,
expandDirectories: false,
});
const compare = new Intl.Collator('en').compare;
const absolutePaths = (await glob(patterns, useOptions)).sort(compare);
const relativePaths = absolutePaths.map((absFilename) => path.relative(cwd, absFilename));
return relativePaths;
}
export function calcGlobs(commandLineExclude) {
const globs = new Set((commandLineExclude || []).flatMap((glob) => glob.split(/(?<!\\)\s+/g)).map((g) => g.replaceAll('\\ ', ' ')));
const commandLineExcludes = {
globs: [...globs],
source: 'arguments',
};
const defaultExcludes = {
globs: defaultExcludeGlobs,
source: 'default',
};
return commandLineExcludes.globs.length ? commandLineExcludes : defaultExcludes;
}
export function extractPatterns(globs) {
const r = globs.reduce((info, g) => {
const source = g.source;
const patterns = g.matcher.patternsNormalizedToRoot;
return [...info, ...patterns.map((glob) => ({ glob, source }))];
}, []);
return r;
}
export function calcExcludeGlobInfo(root, commandLineExclude) {
commandLineExclude = typeof commandLineExclude === 'string' ? [commandLineExclude] : commandLineExclude;
const choice = calcGlobs(commandLineExclude);
const matcher = new GlobMatcher(choice.globs, { root, dot: true });
return [
{
matcher,
source: choice.source,
},
];
}
export function extractGlobExcludesFromConfig(root, source, config) {
if (!config.ignorePaths || !config.ignorePaths.length) {
return [];
}
const matcher = new GlobMatcher(config.ignorePaths, { root, dot: true });
return [{ source, matcher }];
}
/**
* Build GlobMatcher from command line or config file globs.
* @param globs Glob patterns or file paths
* @param root - directory to use as the root
*/
export function buildGlobMatcher(globs, root, isExclude) {
const withRoots = globs.map((g) => {
const source = typeof g === 'string' ? 'command line' : undefined;
return { source, ...fileOrGlobToGlob(g, root) };
});
return new GlobMatcher(withRoots, { root, mode: isExclude ? 'exclude' : 'include' });
}
export function extractGlobsFromMatcher(globMatcher) {
return globMatcher.patternsNormalizedToRoot.map((g) => g.glob);
}
export function normalizeGlobsToRoot(globs, root, isExclude) {
const urls = globs.filter((g) => typeof g === 'string' && isPossibleUrlRegExp.test(g));
const onlyGlobs = globs.filter((g) => typeof g !== 'string' || !isPossibleUrlRegExp.test(g));
return [urls, extractGlobsFromMatcher(buildGlobMatcher(onlyGlobs, root, isExclude))].flat();
}
const isPossibleGlobRegExp = /[()*?[{}]/;
const isPossibleUrlRegExp = /^[\d_a-z-]{3,}:\/\//;
/**
* If a 'glob' is a path to a directory, then append `**` so that
* directory searches work.
* @param glob - a glob, file, or directory
* @param root - root to use.
* @returns `**` is appended directories.
*/
async function adjustPossibleDirectory(glob, root) {
const g = typeof glob === 'string'
? {
glob,
root,
}
: {
glob: glob.glob,
root: glob.root ?? root,
};
// Do not ask the file system to look up obvious glob patterns.
if (isPossibleGlobRegExp.test(g.glob)) {
return glob;
}
if (isPossibleUrlRegExp.test(g.glob)) {
return glob;
}
const dirPath = path.resolve(g.root, g.glob);
try {
const stat = await fs.stat(dirPath);
if (stat.isDirectory()) {
const useGlob = posix.join(posixPath(g.glob), '**');
return typeof glob === 'string' ? useGlob : { ...glob, glob: useGlob };
}
}
catch {
// it was not possible to access the dirPath, no problem, just let the file glob search look for it.
return glob;
}
return glob;
}
function posixPath(p) {
return path.sep === '\\' ? p.replaceAll('\\', '/') : p;
}
export async function normalizeFileOrGlobsToRoot(globs, root) {
const adjustedGlobs = await Promise.all(globs.map((g) => adjustPossibleDirectory(g, root)));
return normalizeGlobsToRoot(adjustedGlobs, root, false);
}
//# sourceMappingURL=glob.js.map