Skip to content

Commit

Permalink
fix: Make sure ${cwd}/ works in globs. (#2315)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S committed Jan 24, 2022
1 parent 30f9c47 commit 2dbe93e
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 23 deletions.
12 changes: 12 additions & 0 deletions packages/cspell-glob/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Validate index loads API 1`] = `
Object {
"GlobMatcher": [Function],
"fileOrGlobToGlob": [Function],
"isGlobPatternNormalized": [Function],
"isGlobPatternWithOptionalRoot": [Function],
"isGlobPatternWithRoot": [Function],
"normalizeGlobPatterns": [Function],
}
`;
37 changes: 35 additions & 2 deletions packages/cspell-glob/src/globHelper.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { fileOrGlobToGlob, normalizeGlobPatterns, normalizeGlobToRoot, __testing__ } from './globHelper';
import {
fileOrGlobToGlob,
normalizeGlobPatterns,
normalizeGlobToRoot,
__testing__,
normalizeGlobPattern,
} from './globHelper';
import { win32, posix } from 'path';
import * as path from 'path';
import { GlobPattern, GlobPatternNormalized, GlobPatternWithOptionalRoot, PathInterface } from './GlobMatcherTypes';
import mm = require('micromatch');
import { NormalizeOptions } from '.';

const { rebaseGlob, trimGlob } = __testing__;

Expand Down Expand Up @@ -210,7 +217,7 @@ describe('Validate Glob Normalization to root', () => {
const glob: GlobPatternNormalized = globPath.glob;
root = path.resolve(root);
const shouldMatch = !file.startsWith('!');
file = file.replace(/!$/, '');
file = file.replace(/^!/, '');
file = path.relative(root, path.resolve(root, file)).replace(/\\/g, '/');

const result = normalizeGlobToRoot(glob, root, path);
Expand Down Expand Up @@ -239,6 +246,7 @@ describe('Validate Glob Normalization to root', () => {
${mg('/node_modules')} | ${'project'} | ${e(mGlob(gg('node_modules', 'node_modules/**'), { rawGlob: '/node_modules' }))} | ${'/node_modules'}
${mg('node_modules/')} | ${'project'} | ${e(mGlob(gg('**/node_modules/**/*'), { rawGlob: 'node_modules/' }))} | ${'node_modules/'}
${mg('/node_modules/')} | ${'project'} | ${e(mGlob(gg('node_modules/**/*'), { rawGlob: '/node_modules/' }))} | ${'/node_modules/'}
${mg({ glob: '/node_modules/' })} | ${'project'} | ${e(mGlob(gg('node_modules/**/*'), { rawGlob: '/node_modules/' }))} | ${'/node_modules/'}
${mg('i18/en_US')} | ${'project'} | ${e(mGlob(gg('i18/en_US', 'i18/en_US/**'), { rawGlob: 'i18/en_US' }))} | ${'i18/en_US'}
`('tests normalization nested "$comment" root: "$root"', ({ globs, root, expectedGlobs }: TestCase) => {
root = path.resolve(root);
Expand Down Expand Up @@ -296,6 +304,31 @@ describe('Validate Glob Normalization to root', () => {
);
expect(r).toEqual(expectedGlobs);
});

function nOpts(opts: Partial<NormalizeOptions> = {}): Required<NormalizeOptions> {
const { nodePath = pathPosix } = opts;
const { root = nodePath.resolve(), cwd = nodePath.resolve(), nested = false } = opts;
return { root, cwd, nested, nodePath };
}

function gN(glob: string, root: string, rawGlob: string, rawRoot: string): GlobPatternNormalized {
return { glob, root, rawGlob, rawRoot };
}

test.each`
glob | options | expected
${'*.ts'} | ${nOpts()} | ${[gN('*.ts', nOpts().root, '*.ts', nOpts().root)]}
${'${cwd}/*.ts'} | ${nOpts()} | ${[gN('*.ts', nOpts().root, '${cwd}/*.ts', nOpts().root)]}
${'${cwd}/*.ts'} | ${nOpts({ nested: true })} | ${[gN('*.ts', nOpts().root, '${cwd}/*.ts', nOpts().root), gN('*.ts/**', nOpts().root, '${cwd}/*.ts', nOpts().root)]}
${{ glob: '*.ts', root: '' }} | ${nOpts()} | ${[gN('*.ts', nOpts().root, '*.ts', '')]}
${{ glob: '*.ts', root: '${cwd}/a' }} | ${nOpts()} | ${[gN('*.ts', nOpts().nodePath.resolve('a'), '*.ts', '${cwd}/a')]}
${{ glob: '*.ts', root: '${cwd}/a' }} | ${nOpts({ root: 'myRoot' })} | ${[gN('*.ts', nOpts().nodePath.resolve('a'), '*.ts', '${cwd}/a')]}
${{ glob: '*.ts', root: '${cwd}/a' }} | ${nOpts({ cwd: 'myCwd' })} | ${[gN('*.ts', nOpts().nodePath.resolve('myCwd/a'), '*.ts', '${cwd}/a')]}
${{ glob: '${cwd}/*.ts', root: 'a' }} | ${nOpts({ cwd: 'myCwd' })} | ${[gN('*.ts', nOpts().nodePath.resolve('myCwd'), '${cwd}/*.ts', 'a')]}
${{ glob: 'a/*.ts', root: '${cwd}/myRoot' }} | ${nOpts({ root: 'otherRoot' })} | ${[gN('a/*.ts', nOpts().nodePath.resolve('myRoot'), 'a/*.ts', '${cwd}/myRoot')]}
`('normalizeGlobPattern glob: "$glob", options: $options', ({ glob, options, expected }) => {
expect(normalizeGlobPattern(glob, options)).toEqual(expected);
});
});

describe('Validate minimatch assumptions', () => {
Expand Down
37 changes: 32 additions & 5 deletions packages/cspell-glob/src/globHelper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-irregular-whitespace */
import * as Path from 'path';
import {
GlobPattern,
Expand Down Expand Up @@ -68,6 +69,11 @@ export function isGlobPatternNormalized(g: GlobPattern | GlobPatternNormalized):
return 'rawGlob' in gr && 'rawRoot' in gr && typeof gr.rawGlob === 'string';
}

/**
* @param pattern glob pattern
* @param nested when true add `**​/<glob>/​**`
* @returns the set of matching globs.
*/
function normalizePattern(pattern: string, nested: boolean): string[] {
pattern = pattern.replace(/^(!!)+/, '');
const isNeg = pattern.startsWith('!');
Expand Down Expand Up @@ -105,9 +111,26 @@ function normalizePatternGeneral(pattern: string): [string] {
}

export interface NormalizeOptions {
/**
* Indicates that the glob should be modified to match nested patterns.
*
* Example: `node_modules` becomes `**​/node_modules/​**`, `**​/node_modules`, and `node_modules/​**`
*/
nested: boolean;
/**
* This is the root to use for the glob if the glob does not already contain one.
*/
root: string;
nodePath: PathInterface;

/**
* This is the replacement for `${cwd}` in either the root or in the glob.
*/
cwd?: string;

/**
* Optional path interface for working with paths.
*/
nodePath?: PathInterface;
}

/**
Expand All @@ -122,15 +145,15 @@ export function normalizeGlobPatterns(patterns: GlobPattern[], options: Normaliz
yield glob;
continue;
}
yield* normalizeGlobPatternWithRoot(glob, options);
yield* normalizeGlobPattern(glob, options);
}
}

return [...normalize()];
}

function normalizeGlobPatternWithRoot(g: GlobPattern, options: NormalizeOptions): GlobPatternNormalized[] {
const { root, nodePath: path, nested } = options;
export function normalizeGlobPattern(g: GlobPattern, options: NormalizeOptions): GlobPatternNormalized[] {
const { root, nodePath: path = Path, nested, cwd = Path.resolve() } = options;

g = !isGlobPatternWithOptionalRoot(g) ? { glob: g } : g;

Expand All @@ -140,8 +163,12 @@ function normalizeGlobPatternWithRoot(g: GlobPattern, options: NormalizeOptions)
const rawGlob = g.glob;

gr.glob = gr.glob.trim(); // trimGlob(g.glob);
if (gr.glob.startsWith('${cwd}')) {
gr.glob = gr.glob.replace('${cwd}', '');
gr.root = '${cwd}';
}
if (gr.root.startsWith('${cwd}')) {
gr.root = path.join(path.resolve(), gr.root.replace('${cwd}', ''));
gr.root = path.resolve(gr.root.replace('${cwd}', cwd));
}
gr.root = path.resolve(root, path.normalize(gr.root));

Expand Down
4 changes: 4 additions & 0 deletions packages/cspell-glob/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ describe('Validate index loads', () => {
expect(index.GlobMatcher).toBeDefined();
expect(typeof index.fileOrGlobToGlob).toBe('function');
});

test('API', () => {
expect(index).toMatchSnapshot();
});
});
12 changes: 12 additions & 0 deletions packages/cspell-lib/api/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,22 @@ declare function readSettingsFiles(filenames: string[]): CSpellSettingsI;
declare function mergeSettings(left: CSpellSettingsWST | CSpellSettingsI, ...settings: (CSpellSettingsWST | CSpellSettingsI)[]): CSpellSettingsI;
declare function mergeInDocSettings(left: CSpellSettingsWST, right: CSpellSettingsWST): CSpellSettingsWST;
declare function calcOverrideSettings(settings: CSpellSettingsWST, filename: string): CSpellSettingsI;
/**
*
* @param settings - settings to finalize
* @returns settings where all globs and file paths have been resolved.
*/
declare function finalizeSettings(settings: CSpellSettingsWST | CSpellSettingsI): CSpellSettingsI;
declare function getGlobalSettings(): CSpellSettingsI;
declare function getCachedFileSize(): number;
declare function clearCachedSettingsFiles(): void;
/**
* @param filename - filename
* @param globs - globs
* @returns true if it matches
* @deprecated true
* @deprecationMessage No longer actively supported. Use package: `cspell-glob`.
*/
declare function checkFilenameMatchesGlob(filename: string, globs: Glob | Glob[]): boolean;
/**
* Return a list of Setting Sources used to create this Setting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ describe('Validate Glob resolution', () => {
expect(sampleSettings).not.toEqual(sampleSettingsV1);
expect(sampleSettings.globRoot).not.toEqual(sampleSettingsV1.globRoot);
expect(sampleSettings.globRoot).toBe(__dirname);
expect(sampleSettingsV1.globRoot).toContain(process.cwd());
expect(sampleSettingsV1.globRoot).toEqual('${cwd}');
expect(sampleSettings.ignorePaths).toEqual(
expect.arrayContaining([
{ glob: 'node_modules', root: sampleSettings.globRoot, source: sampleSettingsFilename },
Expand Down
30 changes: 15 additions & 15 deletions packages/cspell-lib/src/Settings/CSpellSettingsServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ function merge(left: CSpellSettingsWST | CSpellSettingsI, right: CSpellSettingsW
overrides: versionBasedMergeList(_left.overrides, _right.overrides, version),
features: mergeObjects(_left.features, _right.features),
source: mergeSources(_left, _right),
description: undefined,
globRoot: undefined,
import: undefined,
__imports: mergeImportRefs(_left, _right),
Expand Down Expand Up @@ -574,6 +575,11 @@ export function calcOverrideSettings(settings: CSpellSettingsWST, filename: stri
return result;
}

/**
*
* @param settings - settings to finalize
* @returns settings where all globs and file paths have been resolved.
*/
export function finalizeSettings(settings: CSpellSettingsWST | CSpellSettingsI): CSpellSettingsI {
return _finalizeSettings(toInternalSettings(settings));
}
Expand Down Expand Up @@ -646,21 +652,15 @@ export function clearCachedSettingsFiles(): void {
cspellConfigExplorerSync.clearCaches();
}

const globMatcherCache = new Map<Glob | Glob[], GlobMatcher>();
const globMatcherCacheMaxSize = 1000;

/**
* @param filename - filename
* @param globs - globs
* @returns true if it matches
* @deprecated true
* @deprecationMessage No longer actively supported. Use package: `cspell-glob`.
*/
export function checkFilenameMatchesGlob(filename: string, globs: Glob | Glob[]): boolean {
const matcher = globMatcherCache.get(globs);
if (matcher) {
return matcher.match(filename);
}

if (globMatcherCache.size >= globMatcherCacheMaxSize) {
globMatcherCache.clear();
}

const m = new GlobMatcher(globs);
globMatcherCache.set(globs, m);
return m.match(filename);
}

Expand Down Expand Up @@ -763,7 +763,6 @@ function resolveGlobRoot(settings: CSpellSettingsWST, pathToSettingsFile: string
const isVSCode = path.basename(settingsFileDirRaw) === '.vscode';
const settingsFileDir = isVSCode ? path.dirname(settingsFileDirRaw) : settingsFileDirRaw;
const envGlobRoot = process.env[ENV_CSPELL_GLOB_ROOT];
const cwd = resolveCwd();
const defaultGlobRoot = envGlobRoot ?? '${cwd}';
const rawRoot =
settings.globRoot ??
Expand All @@ -772,7 +771,8 @@ function resolveGlobRoot(settings: CSpellSettingsWST, pathToSettingsFile: string
(isVSCode && !settings.version)
? defaultGlobRoot
: settingsFileDir);
const globRoot = path.resolve(settingsFileDir, rawRoot.replace('${cwd}', cwd));

const globRoot = rawRoot.startsWith('${cwd}') ? rawRoot : path.resolve(settingsFileDir, rawRoot);
return globRoot;
}

Expand Down

0 comments on commit 2dbe93e

Please sign in to comment.