Skip to content

Commit

Permalink
chore: Remove dependency upon find-up. (#5019)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S authored Nov 28, 2023
1 parent bde10cb commit c00f2aa
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 96 deletions.
2 changes: 1 addition & 1 deletion packages/cspell-gitignore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@
},
"dependencies": {
"cspell-glob": "workspace:*",
"find-up": "^6.3.0"
"find-up-simple": "^1.0.0"
}
}
2 changes: 1 addition & 1 deletion packages/cspell-gitignore/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { findUp } from 'find-up';
import { findUp } from 'find-up-simple';
import * as path from 'path';

interface ParsedPath {
Expand Down
3 changes: 1 addition & 2 deletions packages/cspell-lib/api/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,6 @@ type CSpellSettingsI = CSpellSettingsInternal;
declare const sectionCSpell = "cSpell";
declare const defaultFileName = "cspell.json";
declare function loadPnP(pnpSettings: PnPSettingsOptional, searchFrom: URL): Promise<LoaderResult>;
declare function loadPnPSync(pnpSettings: PnPSettingsOptional, searchFrom: URL): LoaderResult;

declare const defaultConfigFilenames: readonly string[];

Expand Down Expand Up @@ -958,4 +957,4 @@ interface PerfTimer {
type TimeNowFn = () => number;
declare function createPerfTimer(name: string, onEnd?: (elapsed: number, name: string) => void, timeNowFn?: TimeNowFn): PerfTimer;

export { type CheckTextInfo, type ConfigurationDependencies, type CreateTextDocumentParams, type DetermineFinalDocumentSettingsResult, type Document, DocumentValidator, type DocumentValidatorOptions, ENV_CSPELL_GLOB_ROOT, type ExcludeFilesGlobMap, type ExclusionFunction, exclusionHelper_d as ExclusionHelper, type FeatureFlag, FeatureFlags, ImportError, type ImportFileRefWithError$1 as ImportFileRefWithError, IncludeExcludeFlag, type IncludeExcludeOptions, index_link_d as Link, type Logger, type PerfTimer, type SpellCheckFileOptions, type SpellCheckFileResult, SpellingDictionaryLoadError, type SuggestedWord, SuggestionError, type SuggestionOptions, type SuggestionsForWordResult, text_d as Text, type TextDocument, type TextDocumentLine, type TextDocumentRef, type TextInfoItem, type TraceOptions, type TraceResult, UnknownFeatureFlagError, type ValidationIssue, calcOverrideSettings, checkFilenameMatchesGlob, checkText, checkTextDocument, clearCachedFiles, clearCaches, combineTextAndLanguageSettings, combineTextAndLanguageSettings as constructSettingsForText, createPerfTimer, createTextDocument, currentSettingsFileVersion, defaultConfigFilenames, defaultFileName, defaultFileName as defaultSettingsFilename, determineFinalDocumentSettings, extractDependencies, extractImportErrors, fileToDocument, fileToTextDocument, finalizeSettings, getCachedFileSize, getDefaultBundledSettingsAsync, getDefaultSettings, getDictionary, getGlobalSettings, getGlobalSettingsAsync, getLanguagesForBasename as getLanguageIdsForBaseFilename, getLanguagesForExt, getLogger, getSources, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, loadConfig, loadPnP, loadPnPSync, mergeInDocSettings, mergeSettings, readRawSettings, readSettings, readSettingsFiles, refreshDictionaryCache, resolveFile, searchForConfig, sectionCSpell, setLogger, shouldCheckDocument, spellCheckDocument, spellCheckFile, suggestionsForWord, suggestionsForWords, traceWords, traceWordsAsync, updateTextDocument, validateText };
export { type CheckTextInfo, type ConfigurationDependencies, type CreateTextDocumentParams, type DetermineFinalDocumentSettingsResult, type Document, DocumentValidator, type DocumentValidatorOptions, ENV_CSPELL_GLOB_ROOT, type ExcludeFilesGlobMap, type ExclusionFunction, exclusionHelper_d as ExclusionHelper, type FeatureFlag, FeatureFlags, ImportError, type ImportFileRefWithError$1 as ImportFileRefWithError, IncludeExcludeFlag, type IncludeExcludeOptions, index_link_d as Link, type Logger, type PerfTimer, type SpellCheckFileOptions, type SpellCheckFileResult, SpellingDictionaryLoadError, type SuggestedWord, SuggestionError, type SuggestionOptions, type SuggestionsForWordResult, text_d as Text, type TextDocument, type TextDocumentLine, type TextDocumentRef, type TextInfoItem, type TraceOptions, type TraceResult, UnknownFeatureFlagError, type ValidationIssue, calcOverrideSettings, checkFilenameMatchesGlob, checkText, checkTextDocument, clearCachedFiles, clearCaches, combineTextAndLanguageSettings, combineTextAndLanguageSettings as constructSettingsForText, createPerfTimer, createTextDocument, currentSettingsFileVersion, defaultConfigFilenames, defaultFileName, defaultFileName as defaultSettingsFilename, determineFinalDocumentSettings, extractDependencies, extractImportErrors, fileToDocument, fileToTextDocument, finalizeSettings, getCachedFileSize, getDefaultBundledSettingsAsync, getDefaultSettings, getDictionary, getGlobalSettings, getGlobalSettingsAsync, getLanguagesForBasename as getLanguageIdsForBaseFilename, getLanguagesForExt, getLogger, getSources, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, loadConfig, loadPnP, mergeInDocSettings, mergeSettings, readRawSettings, readSettings, readSettingsFiles, refreshDictionaryCache, resolveFile, searchForConfig, sectionCSpell, setLogger, shouldCheckDocument, spellCheckDocument, spellCheckFile, suggestionsForWord, suggestionsForWords, traceWords, traceWordsAsync, updateTextDocument, validateText };
1 change: 0 additions & 1 deletion packages/cspell-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
"cspell-io": "workspace:*",
"cspell-trie-lib": "workspace:*",
"fast-equals": "^5.0.1",
"find-up": "^6.3.0",
"gensequence": "^6.0.0",
"import-fresh": "^3.3.0",
"resolve-from": "^5.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { currentSettingsFileVersion, ENV_CSPELL_GLOB_ROOT } from '../../constant
import type { ImportFileRefWithError } from '../../CSpellSettingsServer.js';
import { extractDependencies, getSources, mergeSettings } from '../../CSpellSettingsServer.js';
import { _defaultSettings, getDefaultBundledSettingsAsync } from '../../DefaultSettings.js';
import { __testing__ as __configLoader_testing__, loadPnP, loadPnPSync } from './configLoader.js';
import { __testing__ as __configLoader_testing__, loadPnP } from './configLoader.js';
import {
clearCachedSettingsFiles,
getCachedFileSize,
Expand Down Expand Up @@ -577,12 +577,6 @@ describe('Validate search/load config files', () => {
await expect(loadPnP({ usePnP: true }, urlSrcDir)).resolves.toBeUndefined();
});

test('loadPnPSync', () => {
expect(loadPnPSync({}, urlSrcDir)).toBeUndefined();
// Look for a pnp file from the current location, but it won't be found.
expect(loadPnPSync({ usePnP: true }, urlSrcDir)).toBeUndefined();
});

test('config needing PnP', async () => {
const uriTestPackages = path.join(root, 'test-packages/yarn');
const uriYarn2TestMedCspell = path.join(uriTestPackages, 'yarn2/test-yarn3-med/cspell.json');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class ConfigLoader {
const { usePnP = pnpSettings.usePnP, pnpFiles = pnpSettings.pnpFiles } = cfgFile.settings;
const pnpSettingsToUse: PnPSettingsOptional = normalizePnPSettings({ usePnP, pnpFiles });
const pathToSettingsDir = new URL('.', cfgFile.url);
loadPnPSync(pnpSettingsToUse, pathToSettingsDir);
await loadPnP(pnpSettingsToUse, pathToSettingsDir);
}

public mergeConfigFileWithImports(
Expand Down Expand Up @@ -426,14 +426,6 @@ export function loadPnP(pnpSettings: PnPSettingsOptional, searchFrom: URL): Prom
return loader.load(searchFrom);
}

export function loadPnPSync(pnpSettings: PnPSettingsOptional, searchFrom: URL): LoaderResult {
if (!pnpSettings.usePnP) {
return undefined;
}
const loader = pnpLoader(pnpSettings.pnpFiles);
return loader.loadSync(searchFrom);
}

function resolveFilename(filename: string | URL, relativeTo: string | URL): ImportFileRef {
if (filename instanceof URL) return { filename: toFilePathOrHref(filename) };
const r = resolveFile(filename, relativeTo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { Dirent } from 'node:fs';
import { readdir, readFile, stat } from 'node:fs/promises';
import path from 'node:path';

import { findUp } from 'find-up';
import { pathToFileURL } from 'url';

import { createAutoResolveCache } from '../../../util/AutoResolve.js';
import { addTrailingSlash, fileURLOrPathToPath, toFileDirUrl, toURL } from '../../../util/url.js';
import { findUp } from '../../../util/findUp.js';

export class ConfigSearch {
private searchCache = new Map<string, Promise<URL | undefined>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export {
createConfigLoader,
defaultFileName,
loadPnP,
loadPnPSync,
sectionCSpell,
} from './configLoader.js';
export { defaultConfigFilenames } from './configLocations.js';
Expand Down
15 changes: 0 additions & 15 deletions packages/cspell-lib/src/lib/Settings/Controller/pnpLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,6 @@ describe('Validate PnPLoader', () => {
expect(yarnPnp?.toString().toLocaleLowerCase()).toBe(uriYarn2TestMedPnp.toString().toLowerCase());
});

test('pnpLoader sync and async', async () => {
const loader = pnpLoader();
const yarnPnp = await loader.load(uriYarn2TestMed);
expect(yarnPnp?.toString().toLocaleLowerCase()).toBe(uriYarn2TestMedPnp.toString().toLowerCase());
expect(loader.peekSync(uriYarn2TestMed)?.toString().toLocaleLowerCase()).toBe(
yarnPnp?.toString().toLocaleLowerCase(),
);
expect(loader.loadSync(uriYarn2TestMed)?.toString().toLocaleLowerCase()).toBe(
yarnPnp?.toString().toLocaleLowerCase(),
);

const sciPnp = loader.loadSync(uriYarn2TestSci);
await expect(loader.peek(uriYarn2TestSci)).resolves.toBe(sciPnp);
});

test('pnpLoader shared cache', async () => {
const loader = pnpLoader();
const yarnPnp = await loader.load(uriYarn2TestMed);
Expand Down
33 changes: 1 addition & 32 deletions packages/cspell-lib/src/lib/Settings/Controller/pnpLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
* Handles loading of `.pnp.js` and `.pnp.js` files.
*/
import clearModule from 'clear-module';
import { findUp, findUpSync } from 'find-up';
import importFresh from 'import-fresh';
import { fileURLToPath } from 'url';

import { toFileUrl } from '../../util/url.js';
import { UnsupportedPnpFile } from './ImportError.js';
import { findUp } from '../../util/findUp.js';

const defaultPnpFiles = ['.pnp.cjs', '.pnp.js'];

Expand Down Expand Up @@ -52,29 +52,6 @@ export class PnpLoader {
return cachedRequests.get(cacheKey) ?? Promise.resolve(undefined);
}

/**
* Request that the nearest .pnp file gets loaded
* @param urlDirectory starting directory
* @returns promise - rejects on error - success if loaded or not found.
*/
public loadSync(urlDirectory: URL): LoaderResult {
if (!isSupported(urlDirectory)) return undefined;
const cacheKey = this.calcKey(urlDirectory);
const cached = cachedRequestsSync.get(cacheKey);
if (cached) return cached;

const r = findPnpAndLoadSync(urlDirectory, this.pnpFiles);
cachedRequestsSync.set(cacheKey, r);
cachedRequests.set(cacheKey, Promise.resolve(r));
return r;
}

public peekSync(urlDirectory: URL): LoaderResult {
if (!isSupported(urlDirectory)) return undefined;
const cacheKey = this.calcKey(urlDirectory);
return cachedRequestsSync.get(cacheKey);
}

/**
* Clears the cached so .pnp files will get reloaded on request.
*/
Expand All @@ -99,14 +76,6 @@ async function findPnpAndLoad(urlDirectory: URL, pnpFiles: string[]): Promise<Lo
return loadPnpIfNeeded(found);
}

/**
* @param urlDirectory - directory to start at.
*/
function findPnpAndLoadSync(urlDirectory: URL, pnpFiles: string[]): LoaderResult {
const found = findUpSync(pnpFiles, { cwd: fileURLToPath(urlDirectory) });
return loadPnpIfNeeded(found);
}

function loadPnpIfNeeded(found: string | undefined): LoaderResult {
if (!found) return undefined;
const c = cachedPnpImportsSync.get(found);
Expand Down
1 change: 0 additions & 1 deletion packages/cspell-lib/src/lib/Settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export {
getGlobalSettingsAsync,
loadConfig,
loadPnP,
loadPnPSync,
readRawSettings,
readSettings,
readSettingsFiles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ exports[`Validate the cspell API > Verify API exports 1`] = `
"isSpellingDictionaryLoadError": [Function],
"loadConfig": [Function],
"loadPnP": [Function],
"loadPnPSync": [Function],
"mergeInDocSettings": [Function],
"mergeSettings": [Function],
"readFile": [Function],
Expand Down
1 change: 0 additions & 1 deletion packages/cspell-lib/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export {
type ImportFileRefWithError,
loadConfig,
loadPnP,
loadPnPSync,
mergeInDocSettings,
mergeSettings,
readRawSettings,
Expand Down
56 changes: 56 additions & 0 deletions packages/cspell-lib/src/lib/util/findUp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { describe, expect, test, vi } from 'vitest';
import path from 'node:path';
import { findUp } from './findUp.js';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageRoot = path.resolve(__dirname, '../../..');
const reposRoot = path.resolve(packageRoot, '../..');
const relPackage = path.relative(packageRoot, __dirname);

const cwd = process.cwd();

describe('findUp', () => {
test('should find the file in the current directory', async () => {
const result = await findUp('README.md');
expect(result).toBe(path.resolve(packageRoot, 'README.md'));
});

test('should find the `fixtures` in the current directory', async () => {
const result = await findUp('fixtures', { type: 'directory' });
expect(result).toBe(path.resolve(packageRoot, './fixtures'));
});

test('should find the directory in the current directory', async () => {
const result = await findUp('fixtures', { type: 'directory' });
expect(result).toBe(path.resolve(packageRoot, './fixtures'));
});

test('should stop searching at the specified directory', async () => {
const result = await findUp('missing.txt', { stopAt: reposRoot });
expect(result).toBeUndefined();
});

test('should return undefined if the file or directory is not found', async () => {
const result = await findUp('nonexistent.txt');
expect(result).toBeUndefined();
});

test('using a predicate', async () => {
const predicate = vi.fn((dir: string) => (dir === cwd ? dir : 'found'));
const result = await findUp(predicate, { cwd: __dirname });
expect(result).toBe('found');
});

test.each`
name | cwd | expected
${'README.md'} | ${undefined} | ${path.resolve(packageRoot, 'README.md')}
${'README.md'} | ${'..'} | ${path.resolve(reposRoot, 'README.md')}
${path.basename(__filename)} | ${path.join(relPackage, 'deeper/and/deeper')} | ${__filename}
${['fixtures', 'package.json']} | ${__dirname} | ${path.resolve(packageRoot, 'package.json')}
`('findUp $name $cwd', async ({ name, cwd, expected }) => {
const result = await findUp(name, { cwd });
expect(result).toBe(expected);
});
});
57 changes: 57 additions & 0 deletions packages/cspell-lib/src/lib/util/findUp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import path from 'path';
import { stat } from 'node:fs/promises';
import { fileURLToPath } from 'url';

interface FindUpOptions {
cwd?: string;
type?: 'file' | 'directory';
stopAt?: string;
}

type FindUpPredicate = (dir: string) => string | undefined | Promise<string | undefined>;

export async function findUp(
name: string | string[] | FindUpPredicate,
options: FindUpOptions = {},
): Promise<string | undefined> {
const { cwd = process.cwd(), type: entryType = 'file', stopAt } = options;
let dir = path.resolve(toDirPath(cwd));
const root = path.parse(dir).root;
const predicate = makePredicate(name, entryType);
const stopAtDir = path.resolve(toDirPath(stopAt || root));

while (dir !== root && dir !== stopAtDir) {
const found = await predicate(dir);
if (found !== undefined) return found;
dir = path.dirname(dir);
}
return undefined;
}

function makePredicate(name: string | string[] | FindUpPredicate, entryType: 'file' | 'directory'): FindUpPredicate {
if (typeof name === 'function') return name;

const checkStat = entryType === 'file' ? 'isFile' : 'isDirectory';

function checkName(dir: string, name: string) {
const f = path.join(dir, name);
return stat(f)
.then((stats) => (stats[checkStat]() && f) || undefined)
.catch(() => undefined);
}

if (!Array.isArray(name)) return (dir) => checkName(dir, name);

return async (dir) => {
const pending = name.map((n) => checkName(dir, n));
for (const p of pending) {
const found = await p;
if (found) return found;
}
return undefined;
};
}

function toDirPath(urlOrPath: string | URL) {
return urlOrPath instanceof URL ? fileURLToPath(new URL('.', urlOrPath)) : urlOrPath;
}
Loading

0 comments on commit c00f2aa

Please sign in to comment.