diff --git a/package-lock.json b/package-lock.json index 97c8ac0a59..3719b5ec18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10248,6 +10248,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -18520,21 +18529,6 @@ "node": ">=8" } }, - "node_modules/package-directory": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/package-directory/-/package-directory-8.1.0.tgz", - "integrity": "sha512-qHKRW0pw3lYdZMQVkjDBqh8HlamH/LCww2PH7OWEp4Qrt3SFeYMNpnJrQzlSnGrDD5zGR51XqBh7FnNCdVNEHA==", - "license": "MIT", - "dependencies": { - "find-up-simple": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -23736,6 +23730,7 @@ "ansi-escapes": "^7.0.0", "ansis": "^4.1.0", "clean-stack": "^5.0.0", + "empathic": "^2.0.0", "execa": "^8.0.0", "fdir": "^6.0.1", "figures": "^6.0.0", @@ -23753,7 +23748,6 @@ "p-locate": "^6.0.0", "p-map": "^7.0.0", "p-reduce": "^3.0.0", - "package-directory": "^8.0.0", "path-exists": "^5.0.0", "pretty-ms": "^9.0.0", "ps-list": "^8.0.0", @@ -23818,7 +23812,7 @@ "@bugsnag/js": "^8.0.0", "@iarna/toml": "^2.2.5", "dot-prop": "^9.0.0", - "find-up": "^7.0.0", + "empathic": "^2.0.0", "minimatch": "^9.0.0", "read-pkg": "^9.0.0", "semver": "^7.3.8", @@ -24124,11 +24118,11 @@ "cron-parser": "^4.1.0", "deepmerge": "^4.2.2", "dot-prop": "^9.0.0", + "empathic": "^2.0.0", "execa": "^8.0.0", "fast-safe-stringify": "^2.0.7", "figures": "^6.0.0", "filter-obj": "^6.0.0", - "find-up": "^7.0.0", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "map-obj": "^5.0.0", @@ -24187,10 +24181,10 @@ "ajv-errors": "^3.0.0", "better-ajv-errors": "^1.2.0", "common-path-prefix": "^3.0.0", + "empathic": "^2.0.0", "env-paths": "^3.0.0", "esbuild": "0.25.6", "execa": "^8.0.0", - "find-up": "^7.0.0", "get-port": "^7.0.0", "node-stream-zip": "^1.15.0", "p-retry": "^6.0.0", @@ -25414,12 +25408,12 @@ "archiver": "^7.0.0", "common-path-prefix": "^3.0.0", "copy-file": "^11.0.0", + "empathic": "^2.0.0", "es-module-lexer": "^1.0.0", "esbuild": "0.25.6", "execa": "^8.0.0", "fast-glob": "^3.3.3", "filter-obj": "^6.0.0", - "find-up": "^7.0.0", "is-path-inside": "^4.0.0", "junk": "^4.0.0", "locate-path": "^7.0.0", diff --git a/packages/build-info/package.json b/packages/build-info/package.json index 4dcbcb7036..0420150a9a 100644 --- a/packages/build-info/package.json +++ b/packages/build-info/package.json @@ -47,7 +47,7 @@ "@bugsnag/js": "^8.0.0", "@iarna/toml": "^2.2.5", "dot-prop": "^9.0.0", - "find-up": "^7.0.0", + "empathic": "^2.0.0", "minimatch": "^9.0.0", "read-pkg": "^9.0.0", "semver": "^7.3.8", diff --git a/packages/build-info/src/node/file-system.ts b/packages/build-info/src/node/file-system.ts index 75919cfa71..a4dd512ba5 100644 --- a/packages/build-info/src/node/file-system.ts +++ b/packages/build-info/src/node/file-system.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'fs' import { basename, dirname, isAbsolute, join, relative, resolve } from 'path' -import { findUp, findUpMultiple } from 'find-up' +import { up as walkUp } from 'empathic/walk' import { DirType, Environment, FileSystem, findUpOptions } from '../file-system.js' @@ -68,12 +68,50 @@ export class NodeFS extends FileSystem { } /** Node implementation of finding a file or directory by walking up parent directories. */ - findUp(name: string | readonly string[], options: findUpOptions = {}): Promise { - return findUp(name, options) + async findUp(name: string | string[], options: findUpOptions = {}): Promise { + const walkOptions = { + cwd: options.cwd, + last: options.stopAt, + } + const names = typeof name === 'string' ? [name] : name + for (const dir of walkUp('.', walkOptions)) { + for (const potentialName of names) { + const filePath = join(dir, potentialName) + try { + const stats = await fs.stat(filePath) + const type = stats.isFile() ? 'file' : 'directory' + if (options.type === type || !options.type) { + return filePath + } + } catch { + // ignore + } + } + } } /** Node implementation of finding files or directories by walking up parent directories. */ - findUpMultiple(name: string | readonly string[], options: findUpOptions = {}): Promise { - return findUpMultiple(name, options) + async findUpMultiple(name: string | readonly string[], options: findUpOptions = {}): Promise { + const results: string[] = [] + const normalisedNames = typeof name === 'string' ? [name] : name + const walkOptions = { + cwd: options.cwd, + last: options.stopAt, + } + for (const dir of walkUp(options.cwd ?? '.', walkOptions)) { + for (const potentialName of normalisedNames) { + const filePath = join(dir, potentialName) + try { + const stats = await fs.stat(filePath) + const type = stats.isFile() ? 'file' : 'directory' + if (options.type === type || !options.type) { + results.push(filePath) + } + } catch { + // ignore + } + } + } + return results } } diff --git a/packages/build-info/vite.config.ts b/packages/build-info/vite.config.ts index aa694bdfba..725ff65b7e 100644 --- a/packages/build-info/vite.config.ts +++ b/packages/build-info/vite.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ setupFiles: ['tests/test-setup.ts'], deps: { // this is to work inside memfs as well - inline: ['find-up', 'locate-path'], + inline: ['empathic', 'locate-path'], }, }, }) diff --git a/packages/build/package.json b/packages/build/package.json index 0a52e5a506..efeec0d330 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -81,6 +81,7 @@ "ansi-escapes": "^7.0.0", "ansis": "^4.1.0", "clean-stack": "^5.0.0", + "empathic": "^2.0.0", "execa": "^8.0.0", "fdir": "^6.0.1", "figures": "^6.0.0", @@ -98,7 +99,6 @@ "p-locate": "^6.0.0", "p-map": "^7.0.0", "p-reduce": "^3.0.0", - "package-directory": "^8.0.0", "path-exists": "^5.0.0", "pretty-ms": "^9.0.0", "ps-list": "^8.0.0", diff --git a/packages/build/src/install/local.js b/packages/build/src/install/local.js index ff6a33d108..56236948eb 100644 --- a/packages/build/src/install/local.js +++ b/packages/build/src/install/local.js @@ -1,4 +1,6 @@ -import { packageDirectory } from 'package-directory' +import { dirname } from 'path' + +import { up as packagePath } from 'empathic/package' import { logInstallLocalPluginsDeps } from '../log/messages/install.js' @@ -57,6 +59,7 @@ const hasPackageDir = function ({ packageDir }) { // We only install dependencies of local plugins that have their own `package.json` const removeMainRoot = async function (localPluginsOptions, buildDir) { - const mainPackageDir = await packageDirectory({ cwd: buildDir }) + const mainPackagePath = packagePath({ cwd: buildDir }) + const mainPackageDir = mainPackagePath ? dirname(mainPackagePath) : undefined return localPluginsOptions.filter(({ packageDir }) => packageDir !== mainPackageDir) } diff --git a/packages/config/package.json b/packages/config/package.json index e9f8a7db18..0c8970de6e 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -66,11 +66,11 @@ "cron-parser": "^4.1.0", "deepmerge": "^4.2.2", "dot-prop": "^9.0.0", + "empathic": "^2.0.0", "execa": "^8.0.0", "fast-safe-stringify": "^2.0.7", "figures": "^6.0.0", "filter-obj": "^6.0.0", - "find-up": "^7.0.0", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "map-obj": "^5.0.0", diff --git a/packages/config/src/options/repository_root.js b/packages/config/src/options/repository_root.js index 810f1a7594..9d18937ac3 100644 --- a/packages/config/src/options/repository_root.js +++ b/packages/config/src/options/repository_root.js @@ -1,6 +1,6 @@ import { dirname } from 'path' -import { findUp } from 'find-up' +import { dir as findUp } from 'empathic/find' // Find out repository root among (in priority order): // - `repositoryRoot` option @@ -11,7 +11,7 @@ export const getRepositoryRoot = async function ({ repositoryRoot, cwd }) { return repositoryRoot } - const repositoryRootA = await findUp('.git', { cwd, type: 'directory' }) + const repositoryRootA = findUp('.git', { cwd }) if (repositoryRootA === undefined) { return cwd diff --git a/packages/config/src/path.ts b/packages/config/src/path.ts index bbcd2d6a8c..1ab4f60eb8 100644 --- a/packages/config/src/path.ts +++ b/packages/config/src/path.ts @@ -1,7 +1,7 @@ import { existsSync } from 'fs' import { join, resolve } from 'path' -import { findUp } from 'find-up' +import { file as findUp } from 'empathic/find' import pLocate from 'p-locate' const FILENAME = 'netlify.toml' diff --git a/packages/edge-bundler/node/npm_dependencies.ts b/packages/edge-bundler/node/npm_dependencies.ts index 4c72a927f0..f8a9831327 100644 --- a/packages/edge-bundler/node/npm_dependencies.ts +++ b/packages/edge-bundler/node/npm_dependencies.ts @@ -5,7 +5,7 @@ import { fileURLToPath, pathToFileURL } from 'url' import { resolve, ParsedImportMap } from '@import-maps/resolve' import { build } from 'esbuild' -import { findUp } from 'find-up' +import { up as findUp } from 'empathic/find' import { parseImports } from 'parse-imports' import tmp from 'tmp-promise' @@ -39,7 +39,7 @@ const getTypePathFromTypesPackage = async ( packageName: string, packageJsonPath: string, ): Promise => { - const typesPackagePath = await findUp(`node_modules/${getTypesPackageName(packageName)}/package.json`, { + const typesPackagePath = findUp(`node_modules/${getTypesPackageName(packageName)}/package.json`, { cwd: packageJsonPath, }) if (!typesPackagePath) { @@ -80,7 +80,7 @@ function packageName(specifier: string) { const safelyDetectTypes = async (pkg: string, basePath: string): Promise => { try { - const json = await findUp(`node_modules/${packageName(pkg)}/package.json`, { + const json = findUp(`node_modules/${packageName(pkg)}/package.json`, { cwd: basePath, }) if (json) { diff --git a/packages/edge-bundler/node/package_json.ts b/packages/edge-bundler/node/package_json.ts index 75ac6ae37e..d35cb724b4 100644 --- a/packages/edge-bundler/node/package_json.ts +++ b/packages/edge-bundler/node/package_json.ts @@ -1,18 +1,12 @@ import { readFileSync } from 'fs' -import { join } from 'path' +import { dirname, join } from 'path' import { fileURLToPath } from 'url' -import { findUpSync, pathExistsSync } from 'find-up' +import { file as findUp } from 'empathic/find' const getPackagePath = () => { - const packagePath = findUpSync( - (directory: string) => { - if (pathExistsSync(join(directory, 'package.json'))) { - return directory - } - }, - { cwd: fileURLToPath(import.meta.url), type: 'directory' }, - ) + const packageJsonPath = findUp('package.json', { cwd: fileURLToPath(import.meta.url) }) + const packagePath = packageJsonPath ? dirname(packageJsonPath) : undefined // We should never get here, but let's show a somewhat useful error message. if (packagePath === undefined) { diff --git a/packages/edge-bundler/package.json b/packages/edge-bundler/package.json index 6831194b02..eb831e30d5 100644 --- a/packages/edge-bundler/package.json +++ b/packages/edge-bundler/package.json @@ -64,10 +64,10 @@ "ajv-errors": "^3.0.0", "better-ajv-errors": "^1.2.0", "common-path-prefix": "^3.0.0", + "empathic": "^2.0.0", "env-paths": "^3.0.0", "esbuild": "0.25.6", "execa": "^8.0.0", - "find-up": "^7.0.0", "get-port": "^7.0.0", "node-stream-zip": "^1.15.0", "p-retry": "^6.0.0", diff --git a/packages/zip-it-and-ship-it/package.json b/packages/zip-it-and-ship-it/package.json index b8faf84018..3b4d24192d 100644 --- a/packages/zip-it-and-ship-it/package.json +++ b/packages/zip-it-and-ship-it/package.json @@ -52,9 +52,9 @@ "es-module-lexer": "^1.0.0", "esbuild": "0.25.6", "execa": "^8.0.0", + "empathic": "^2.0.0", "fast-glob": "^3.3.3", "filter-obj": "^6.0.0", - "find-up": "^7.0.0", "is-path-inside": "^4.0.0", "junk": "^4.0.0", "locate-path": "^7.0.0", diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/bundlers/zisi/resolve.ts b/packages/zip-it-and-ship-it/src/runtimes/node/bundlers/zisi/resolve.ts index 1d7664c806..4c8a195fe3 100644 --- a/packages/zip-it-and-ship-it/src/runtimes/node/bundlers/zisi/resolve.ts +++ b/packages/zip-it-and-ship-it/src/runtimes/node/bundlers/zisi/resolve.ts @@ -1,7 +1,7 @@ import { createRequire } from 'module' import { version as nodeVersion } from 'process' -import { findUp } from 'find-up' +import { up as walkUp } from 'empathic/walk' import { pathExists } from 'path-exists' // @ts-expect-error doesnt export async import { async as asyncResolve } from 'resolve' @@ -94,7 +94,12 @@ const resolvePathFollowSymlinks = function (path: string, baseDirs: string[]) { // unlikely, and we don't have any better alternative. const resolvePackageFallback = async function (moduleName: string, baseDirs: string[], error: Error) { const mainFilePath = resolvePathFollowSymlinks(moduleName, baseDirs) - const packagePath = await findUp(isPackageDir.bind(null, moduleName), { cwd: mainFilePath, type: 'directory' }) + let packagePath: string | undefined + for (const dir of walkUp(mainFilePath)) { + if (await isPackageDir(moduleName, dir)) { + packagePath = dir + } + } if (packagePath === undefined) { throw error diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/package_json.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/package_json.ts index bf64d10d76..336a00008a 100644 --- a/packages/zip-it-and-ship-it/src/runtimes/node/utils/package_json.ts +++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/package_json.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'fs' import { basename, join } from 'path' -import { findUp, findUpStop, pathExists } from 'find-up' +import { up as walkUp } from 'empathic/walk' export interface PackageJson { name?: string @@ -23,21 +23,26 @@ export interface PackageJsonFile { } export const getClosestPackageJson = async (resolveDir: string, boundary?: string): Promise => { - const packageJsonPath = await findUp( - async (directory) => { - // We stop traversing if we're about to leave the boundaries of any - // node_modules directory. - if (basename(directory) === 'node_modules') { - return findUpStop + let packageJsonPath + + for (const directory of walkUp(resolveDir, { last: boundary })) { + // We stop traversing if we're about to leave the boundaries of any + // node_modules directory. + if (basename(directory) === 'node_modules') { + break + } + + const path = join(directory, 'package.json') + try { + const stats = await fs.stat(path) + if (stats.isFile()) { + packageJsonPath = path + break } - - const path = join(directory, 'package.json') - const hasPackageJson = await pathExists(path) - - return hasPackageJson ? path : undefined - }, - { cwd: resolveDir, stopAt: boundary }, - ) + } catch { + // do nothing, continue searching + } + } if (packageJsonPath === undefined) { return null diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/plugin_modules_path.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/plugin_modules_path.ts index 447510a14e..972e7b01d8 100644 --- a/packages/zip-it-and-ship-it/src/runtimes/node/utils/plugin_modules_path.ts +++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/plugin_modules_path.ts @@ -1,6 +1,6 @@ import { join, relative } from 'path' -import { findUp } from 'find-up' +import { dir as findUp } from 'empathic/find' const AUTO_PLUGINS_DIR = '.netlify/plugins/' @@ -21,5 +21,7 @@ export const createAliases = ( }) } -export const getPluginsModulesPath = (srcDir: string): Promise => - findUp(`${AUTO_PLUGINS_DIR}node_modules`, { cwd: srcDir, type: 'directory' }) +export const getPluginsModulesPath = (srcDir: string): Promise => { + const result = findUp(`${AUTO_PLUGINS_DIR}node_modules`, { cwd: srcDir }) + return Promise.resolve(result) +}