diff --git a/package.json b/package.json index dad4678197..50961f293a 100644 --- a/package.json +++ b/package.json @@ -64,12 +64,12 @@ "clean-regexp": "^1.0.0", "core-js-compat": "^3.41.0", "esquery": "^1.6.0", + "find-up-simple": "^1.0.1", "globals": "^16.0.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", - "read-package-up": "^11.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", "semver": "^7.7.1", diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 5e227b93e3..7e8a8485d4 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -1,9 +1,9 @@ import path from 'node:path'; import {isRegExp} from 'node:util/types'; -import {readPackageUpSync} from 'read-package-up'; import semver from 'semver'; import * as ci from 'ci-info'; import getBuiltinRule from './utils/get-builtin-rule.js'; +import {readPackageJson} from './shared/package-json.js'; const baseRule = getBuiltinRule('no-warning-comments'); @@ -50,20 +50,9 @@ const messages = { /** @param {string} dirname */ function getPackageHelpers(dirname) { - // We don't need to normalize the package.json data, because we are only using 2 properties and those 2 properties - // aren't validated by the normalization. But when this plugin is used in a monorepo, the name field in the - // package.json can be invalid and would make this plugin throw an error. See also #1871 - /** @type {readPkgUp.ReadResult | undefined} */ - let packageResult; - try { - packageResult = readPackageUpSync({normalize: false, cwd: dirname}); - } catch { - // This can happen if package.json files have comments in them etc. - packageResult = undefined; - } - - const hasPackage = Boolean(packageResult); - const packageJson = packageResult ? packageResult.packageJson : {}; + const packageJsonResult = readPackageJson(dirname); + const packageJson = packageJsonResult?.packageJson ?? {}; + const hasPackage = Boolean(packageJsonResult); const packageDependencies = { ...packageJson.dependencies, @@ -187,7 +176,7 @@ function getPackageHelpers(dirname) { } return { - packageResult, + packageResult: packageJsonResult, hasPackage, packageJson, packageDependencies, diff --git a/rules/no-unnecessary-polyfills.js b/rules/no-unnecessary-polyfills.js index 835aeb9116..73f36612a5 100644 --- a/rules/no-unnecessary-polyfills.js +++ b/rules/no-unnecessary-polyfills.js @@ -1,8 +1,8 @@ import path from 'node:path'; -import {readPackageUpSync} from 'read-package-up'; import coreJsCompat from 'core-js-compat'; import {camelCase} from './utils/lodash.js'; import isStaticRequire from './ast/is-static-require.js'; +import {readPackageJson} from './shared/package-json.js'; const {data: compatData, entries: coreJsEntries} = coreJsCompat; @@ -57,18 +57,13 @@ function getTargets(options, dirname) { return options.targets; } - /** @type {readPkgUp.ReadResult | undefined} */ - let packageResult; - try { - // It can fail if, for example, the package.json file has comments. - packageResult = readPackageUpSync({normalize: false, cwd: dirname}); - } catch {} + const packageJsonResult = readPackageJson(dirname); - if (!packageResult) { + if (!packageJsonResult) { return; } - const {browserslist, engines} = packageResult.packageJson; + const {browserslist, engines} = packageJsonResult.packageJson; return browserslist ?? engines; } diff --git a/rules/shared/package-json.js b/rules/shared/package-json.js new file mode 100644 index 0000000000..b635a6edcc --- /dev/null +++ b/rules/shared/package-json.js @@ -0,0 +1,42 @@ +import fs from 'node:fs'; +import {findUpSync} from 'find-up-simple'; + +const directoryCache = new Map(); +const dataCache = new Map(); + +/** +Finds the closest package.json file to the given directory and returns its path and contents. + +Caches the result for future lookups. + +@param dirname {string} +@return {{ path: string, packageJson: Record } | undefined} +*/ +export function readPackageJson(dirname) { + let packageJsonPath; + if (directoryCache.has(dirname)) { + packageJsonPath = directoryCache.get(dirname); + } else { + packageJsonPath = findUpSync('package.json', {cwd: dirname, type: 'file'}); + directoryCache.set(dirname, packageJsonPath); + } + + if (!packageJsonPath) { + return; + } + + let packageJson; + if (dataCache.has(packageJsonPath)) { + packageJson = dataCache.get(packageJsonPath); + } else { + try { + packageJson = JSON.parse(fs.readFileSync(packageJsonPath)); + dataCache.set(packageJsonPath, packageJson); + } catch { + // This can happen if package.json files have comments in them etc. + return; + } + } + + return {path: packageJsonPath, packageJson}; +} diff --git a/test/expiring-todo-comments.js b/test/expiring-todo-comments.js index 726716317e..57728e3cb6 100644 --- a/test/expiring-todo-comments.js +++ b/test/expiring-todo-comments.js @@ -60,16 +60,16 @@ test({ }, '// Expire Condition [2000-01-01]: new term name', '// TODO [>2000]: We sure didnt past this version', - '// TODO [-read-package-up]: We actually use this.', + '// TODO [-find-up-simple]: We actually use this.', '// TODO [+popura]: I think we wont need a broken package.', '// TODO [semver@>1000]: Welp hopefully we wont get at that.', '// TODO [semver@>=1000]: Welp hopefully we wont get at that.', '// TODO [@lubien/fixture-beta-package@>=1.0.0]: we are using a pre-release', '// TODO [@lubien/fixture-beta-package@>=1.0.0-gamma.1]: beta comes first from gamma', '// TODO [@lubien/fixture-beta-package@>=1.0.0-beta.2]: we are in beta.1', - '// TODO [2200-12-12, -read-package-up]: Combo', - '// TODO [2200-12-12, -read-package-up, +popura]: Combo', - '// TODO [2200-12-12, -read-package-up, +popura, semver@>=1000]: Combo', + '// TODO [2200-12-12, -find-up-simple]: Combo', + '// TODO [2200-12-12, -find-up-simple, +popura]: Combo', + '// TODO [2200-12-12, -find-up-simple, +popura, semver@>=1000]: Combo', '// TODO [engine:node@>=100]: When we start supporting only >= 10', `// TODO [2200-12-12]: Multiple // TODO [2200-12-12]: Lines`, @@ -209,24 +209,24 @@ test({ errors: [reachedPackageVersionError('>=1', 'if your package.json version is >=1')], }, { - code: '// TODO [+read-package-up]: when you install `read-package-up`', - errors: [havePackageError('read-package-up', 'when you install `read-package-up`')], + code: '// TODO [+find-up-simple]: when you install `find-up-simple`', + errors: [havePackageError('find-up-simple', 'when you install `find-up-simple`')], }, { code: '// TODO [-popura]: when you uninstall `popura`', errors: [dontHavePackageError('popura', 'when you uninstall `popura`')], }, { - code: '// TODO [read-package-up@>1]: when `read-package-up` version is > 1', - errors: [versionMatchesError('read-package-up > 1', 'when `read-package-up` version is > 1')], + code: '// TODO [find-up-simple@>=1]: when `find-up-simple` version is >= 1', + errors: [versionMatchesError('find-up-simple >= 1', 'when `find-up-simple` version is >= 1')], }, { code: '// TODO [engine:node@>=8]: when support is for node >= 8', errors: [engineMatchesError('node>=8', 'when support is for node >= 8')], }, { - code: '// TODO [read-package-up@>=5.1.1]: when `read-package-up` version is >= 5.1.1', - errors: [versionMatchesError('read-package-up >= 5.1.1', 'when `read-package-up` version is >= 5.1.1')], + code: '// TODO [find-up-simple@>0.2.0]: when `find-up-simple` version is > 0.2.0', + errors: [versionMatchesError('find-up-simple > 0.2.0', 'when `find-up-simple` version is > 0.2.0')], }, { code: '// TODO [@lubien/fixture-beta-package@>=1.0.0-alfa.1]: when `@lubien/fixture-beta-package` version is >= 1.0.0-alfa.1', @@ -323,16 +323,16 @@ test({ ], }, { - code: '// TODO [-popura, read-package-up@>1]: Combine not having a package with version match', + code: '// TODO [-popura, find-up-simple@>=1]: Combine not having a package with version match', errors: [ dontHavePackageError('popura', 'Combine not having a package with version match'), - versionMatchesError('read-package-up > 1', 'Combine not having a package with version match'), + versionMatchesError('find-up-simple >= 1', 'Combine not having a package with version match'), ], }, { - code: '// TODO [+read-package-up, -popura]: Combine presence/absence of packages', + code: '// TODO [+find-up-simple, -popura]: Combine presence/absence of packages', errors: [ - havePackageError('read-package-up', 'Combine presence/absence of packages'), + havePackageError('find-up-simple', 'Combine presence/absence of packages'), dontHavePackageError('popura', 'Combine presence/absence of packages'), ], }, @@ -352,13 +352,13 @@ test({ ], }, { - code: '// HUGETODO [semver @>=1, engine:node@>=8, 2000-01-01, -popura, >1, +read-package-up, read-package-up@>1]: Big mix', + code: '// HUGETODO [semver @>=1, engine:node@>=8, 2000-01-01, -popura, >1, +find-up-simple, find-up-simple@>=1]: Big mix', errors: [ expiredTodoError('2000-01-01', 'Big mix'), reachedPackageVersionError('>1', 'Big mix'), dontHavePackageError('popura', 'Big mix'), - havePackageError('read-package-up', 'Big mix'), - versionMatchesError('read-package-up > 1', 'Big mix'), + havePackageError('find-up-simple', 'Big mix'), + versionMatchesError('find-up-simple >= 1', 'Big mix'), engineMatchesError('node>=8', 'Big mix'), removeWhitespaceError('semver @>=1', 'Big mix'), ],