mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-29 10:25:05 -04:00
feat(view): support searching package.json upward when package name is omitted (#11696)
* feat(view): support searching package.json upward when package name is omitted * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: apply review * fix: apply review * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: update --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
15
.changeset/view-package-json-upward-search.md
Normal file
15
.changeset/view-package-json-upward-search.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
"@pnpm/deps.inspection.commands": minor
|
||||
"@pnpm/workspace.projects-filter": patch
|
||||
"@pnpm/workspace.root-finder": patch
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
feat(view): support searching project manifest upward when package name is omitted
|
||||
|
||||
When running `pnpm view` without a package name, the command now searches
|
||||
upward for the nearest project manifest (`package.json`, `package.yaml`, or `package.json5`) and uses its `name` field.
|
||||
If the manifest exists but lacks a `name` field, an error is thrown.
|
||||
|
||||
This change also replaces the `find-up` dependency with `empathic` for
|
||||
improved performance and consistency across workspace tools.
|
||||
1
deps/inspection/commands/package.json
vendored
1
deps/inspection/commands/package.json
vendored
@@ -56,6 +56,7 @@
|
||||
"@pnpm/semver-diff": "catalog:",
|
||||
"@pnpm/store.path": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@pnpm/workspace.project-manifest-reader": "workspace:*",
|
||||
"@zkochan/table": "catalog:",
|
||||
"chalk": "catalog:",
|
||||
"hosted-git-info": "catalog:",
|
||||
|
||||
50
deps/inspection/commands/src/view/index.ts
vendored
50
deps/inspection/commands/src/view/index.ts
vendored
@@ -1,6 +1,10 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { formatTimeAgo } from '@pnpm/resolving.npm-resolver'
|
||||
import type { ProjectManifest } from '@pnpm/types'
|
||||
import { tryReadProjectManifest } from '@pnpm/workspace.project-manifest-reader'
|
||||
import chalk from 'chalk'
|
||||
import { pick } from 'ramda'
|
||||
import { renderHelp } from 'render-help'
|
||||
@@ -22,11 +26,11 @@ export const commandNames = ['view', 'info', 'show', 'v']
|
||||
|
||||
export function help (): string {
|
||||
return renderHelp({
|
||||
description: 'View package information from the registry without using npm CLI.',
|
||||
description: 'View package information from the registry. If package name is omitted, searches upward for the nearest package manifest.',
|
||||
usages: [
|
||||
'pnpm view <package-name>',
|
||||
'pnpm view <package-name>@<version>',
|
||||
'pnpm view <package-name> [<field>[.subfield]...]',
|
||||
'pnpm view [<package-name>]',
|
||||
'pnpm view [<package-name>@<version>]',
|
||||
'pnpm view [<package-name>] [<field>[.subfield]...]',
|
||||
],
|
||||
descriptionLists: [
|
||||
{
|
||||
@@ -48,10 +52,20 @@ export async function handler (
|
||||
},
|
||||
params: string[]
|
||||
): Promise<string | void> {
|
||||
const packageSpec = params[0]
|
||||
let packageSpec = params[0]
|
||||
|
||||
if (!packageSpec) {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', 'Package name is required. Usage: pnpm view <package-name>')
|
||||
const nearestManifest = await findNearestProjectManifest(opts.dir ?? process.cwd(), opts)
|
||||
if (!nearestManifest) {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', 'Package name is required. Usage: pnpm view [<package-name>]')
|
||||
}
|
||||
if (typeof nearestManifest.manifest.name !== 'string' || nearestManifest.manifest.name.length === 0) {
|
||||
throw new PnpmError(
|
||||
'INVALID_PACKAGE_JSON',
|
||||
`Invalid ${nearestManifest.fileName} at "${nearestManifest.projectDir}". The "name" field is required and must be a non-empty string.`
|
||||
)
|
||||
}
|
||||
packageSpec = nearestManifest.manifest.name
|
||||
}
|
||||
|
||||
const fields = params.slice(1)
|
||||
@@ -248,6 +262,30 @@ function getPublishedInfo (info: ExtendedPackageInfo): string | null {
|
||||
return `published ${chalk.cyan(timeAgo)}`
|
||||
}
|
||||
|
||||
async function findNearestProjectManifest (
|
||||
startDir: string,
|
||||
_opts: Config & ConfigContext
|
||||
): Promise<{ manifest: ProjectManifest, fileName: string, projectDir: string } | null> {
|
||||
try {
|
||||
const result = await tryReadProjectManifest(startDir)
|
||||
if (result.manifest != null) {
|
||||
return {
|
||||
manifest: result.manifest,
|
||||
fileName: result.fileName,
|
||||
projectDir: startDir,
|
||||
}
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
throw new PnpmError('INVALID_PACKAGE_JSON', `Failed to read or parse project manifest in "${startDir}": ${message}`)
|
||||
}
|
||||
const parentDir = path.dirname(startDir)
|
||||
if (parentDir === startDir) {
|
||||
return null
|
||||
}
|
||||
return findNearestProjectManifest(parentDir, _opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the publisher name from package metadata.
|
||||
* Checks fields in order: _npmUser, maintainers, author.
|
||||
|
||||
162
deps/inspection/commands/test/view.ts
vendored
162
deps/inspection/commands/test/view.ts
vendored
@@ -1,3 +1,8 @@
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { stripVTControlCharacters as stripAnsi } from 'node:util'
|
||||
|
||||
import { expect, test } from '@jest/globals'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { view } from '@pnpm/deps.inspection.commands'
|
||||
@@ -37,9 +42,18 @@ test('view: rcOptionsTypes should return object', () => {
|
||||
})
|
||||
|
||||
test('view: missing package name throws error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_MISSING_PACKAGE_NAME' })
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
|
||||
try {
|
||||
process.chdir(tmpDir)
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_MISSING_PACKAGE_NAME' })
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: non-registry spec throws error', async () => {
|
||||
@@ -142,8 +156,9 @@ test('view: text output includes dist section', async () => {
|
||||
|
||||
test('view: text output includes dist-tags', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative']) as string
|
||||
expect(result).toContain('dist-tags:')
|
||||
expect(result).toContain('latest:')
|
||||
const plainTextResult = stripAnsi(result)
|
||||
expect(plainTextResult).toContain('dist-tags:')
|
||||
expect(plainTextResult).toContain('latest:')
|
||||
})
|
||||
|
||||
test('view: text output for package with dependencies shows deps count', async () => {
|
||||
@@ -233,5 +248,140 @@ test('view: published info includes timestamp', async () => {
|
||||
test('view: published info includes publisher when maintainer data is available', async () => {
|
||||
// Note: is-negative package has maintainer data in the mock registry
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0']) as string
|
||||
expect(result).toMatch(/published .* ago by /)
|
||||
expect(stripAnsi(result)).toMatch(/published .* ago by /)
|
||||
})
|
||||
|
||||
test('view: uses package manifest name when no package name provided', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify({ name: 'is-negative' }))
|
||||
process.chdir(tmpDir)
|
||||
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: searches upward for package manifest in nested directory', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const nestedDir = path.join(tmpDir, 'a', 'b')
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
|
||||
try {
|
||||
fs.mkdirSync(nestedDir, { recursive: true })
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify({ name: 'is-negative' }))
|
||||
process.chdir(nestedDir)
|
||||
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: package.json without name field throws error', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify({ version: '1.0.0' }))
|
||||
process.chdir(tmpDir)
|
||||
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_INVALID_PACKAGE_JSON' })
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: uses package.yaml name when no package name provided', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgYamlPath = path.join(tmpDir, 'package.yaml')
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgYamlPath, 'name: is-negative\n')
|
||||
process.chdir(tmpDir)
|
||||
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: package.json with non-object JSON throws error', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgJsonPath, 'null')
|
||||
process.chdir(tmpDir)
|
||||
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_INVALID_PACKAGE_JSON' })
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: resolves package.json from opts.dir when cwd differs', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
const otherDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-other-'))
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify({ name: 'is-negative' }))
|
||||
process.chdir(otherDir)
|
||||
|
||||
const result = await view.handler({ ...VIEW_OPTIONS, dir: tmpDir } as unknown as Config & ConfigContext, [])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
fs.rmSync(otherDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('view: derives package name even when engines.pnpm is incompatible', async () => {
|
||||
const cwd = process.cwd()
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'view-test-'))
|
||||
const pkgJsonPath = path.join(tmpDir, 'package.json')
|
||||
|
||||
try {
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify({
|
||||
name: 'is-negative',
|
||||
engines: {
|
||||
pnpm: '999.0.0',
|
||||
},
|
||||
}))
|
||||
process.chdir(tmpDir)
|
||||
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
} finally {
|
||||
process.chdir(cwd)
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
3
deps/inspection/commands/tsconfig.json
vendored
3
deps/inspection/commands/tsconfig.json
vendored
@@ -81,6 +81,9 @@
|
||||
{
|
||||
"path": "../../../testing/registry-mock"
|
||||
},
|
||||
{
|
||||
"path": "../../../workspace/project-manifest-reader"
|
||||
},
|
||||
{
|
||||
"path": "../../../workspace/projects-filter"
|
||||
},
|
||||
|
||||
42
pnpm-lock.yaml
generated
42
pnpm-lock.yaml
generated
@@ -620,6 +620,9 @@ catalogs:
|
||||
dir-is-case-sensitive:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
empathic:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.1
|
||||
encode-registry:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@@ -662,9 +665,6 @@ catalogs:
|
||||
fast-glob:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
find-up:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
fs-extra:
|
||||
specifier: ^11.3.5
|
||||
version: 11.3.5
|
||||
@@ -3443,6 +3443,9 @@ importers:
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/types
|
||||
'@pnpm/workspace.project-manifest-reader':
|
||||
specifier: workspace:*
|
||||
version: link:../../../workspace/project-manifest-reader
|
||||
'@zkochan/table':
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.1
|
||||
@@ -9794,12 +9797,12 @@ importers:
|
||||
'@pnpm/workspace.workspace-manifest-reader':
|
||||
specifier: workspace:*
|
||||
version: link:../workspace-manifest-reader
|
||||
empathic:
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.1
|
||||
execa:
|
||||
specifier: 'catalog:'
|
||||
version: safe-execa@0.3.0
|
||||
find-up:
|
||||
specifier: 'catalog:'
|
||||
version: 8.0.0
|
||||
is-subdir:
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.0
|
||||
@@ -9946,9 +9949,9 @@ importers:
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../core/error
|
||||
find-up:
|
||||
empathic:
|
||||
specifier: 'catalog:'
|
||||
version: 8.0.0
|
||||
version: 2.0.1
|
||||
devDependencies:
|
||||
'@jest/globals':
|
||||
specifier: 'catalog:'
|
||||
@@ -13477,6 +13480,10 @@ packages:
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
empathic@2.0.1:
|
||||
resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
encode-registry@3.0.1:
|
||||
resolution: {integrity: sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -13859,10 +13866,6 @@ packages:
|
||||
resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
find-up@8.0.0:
|
||||
resolution: {integrity: sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
find-yarn-workspace-root2@1.2.16:
|
||||
resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==}
|
||||
|
||||
@@ -14882,10 +14885,6 @@ packages:
|
||||
resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
locate-path@8.0.0:
|
||||
resolution: {integrity: sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
lodash._baseclone@4.5.7:
|
||||
resolution: {integrity: sha512-nOtLg6tdIdD+TehqBv0WI7jbkLaohHhKSwLmS/UXSFWMWWUxdJc9EVtAfD4L0mV15vV+lZVfF4LEo363VdrMBw==}
|
||||
|
||||
@@ -21334,6 +21333,8 @@ snapshots:
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
empathic@2.0.1: {}
|
||||
|
||||
encode-registry@3.0.1:
|
||||
dependencies:
|
||||
mem: 8.1.1
|
||||
@@ -21852,11 +21853,6 @@ snapshots:
|
||||
locate-path: 7.2.0
|
||||
path-exists: 5.0.0
|
||||
|
||||
find-up@8.0.0:
|
||||
dependencies:
|
||||
locate-path: 8.0.0
|
||||
unicorn-magic: 0.3.0
|
||||
|
||||
find-yarn-workspace-root2@1.2.16:
|
||||
dependencies:
|
||||
micromatch: 4.0.8
|
||||
@@ -23145,10 +23141,6 @@ snapshots:
|
||||
dependencies:
|
||||
p-locate: 6.0.0
|
||||
|
||||
locate-path@8.0.0:
|
||||
dependencies:
|
||||
p-locate: 6.0.0
|
||||
|
||||
lodash._baseclone@4.5.7: {}
|
||||
|
||||
lodash.assign@4.2.0: {}
|
||||
|
||||
@@ -193,6 +193,7 @@ catalog:
|
||||
didyoumean2: ^7.0.4
|
||||
dint: ^5.1.0
|
||||
dir-is-case-sensitive: ^3.0.0
|
||||
empathic: ^2.0.0
|
||||
encode-registry: ^3.0.1
|
||||
esbuild: ^0.28.1
|
||||
escape-string-regexp: ^5.0.0
|
||||
@@ -207,7 +208,6 @@ catalog:
|
||||
exists-link: 2.0.0
|
||||
fast-deep-equal: ^3.1.3
|
||||
fast-glob: ^3.3.3
|
||||
find-up: ^8.0.0
|
||||
fs-extra: ^11.3.5
|
||||
fuse-native: ^2.2.6
|
||||
get-npm-tarball-url: ^2.1.0
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
"@pnpm/workspace.projects-graph": "workspace:*",
|
||||
"@pnpm/workspace.projects-reader": "workspace:*",
|
||||
"@pnpm/workspace.workspace-manifest-reader": "workspace:*",
|
||||
"empathic": "catalog:",
|
||||
"execa": "catalog:",
|
||||
"find-up": "catalog:",
|
||||
"is-subdir": "catalog:",
|
||||
"micromatch": "catalog:",
|
||||
"ramda": "catalog:"
|
||||
|
||||
@@ -4,8 +4,8 @@ import util from 'node:util'
|
||||
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import type { ProjectRootDir } from '@pnpm/types'
|
||||
import * as find from 'empathic/find'
|
||||
import { safeExeca as execa } from 'execa'
|
||||
import { findUp } from 'find-up'
|
||||
import * as micromatch from 'micromatch'
|
||||
|
||||
type ChangeType = 'source' | 'test'
|
||||
@@ -21,8 +21,8 @@ export async function getChangedProjects (
|
||||
): Promise<[ProjectRootDir[], ProjectRootDir[]]> {
|
||||
|
||||
// .git is a directory in regular repos, but a file in worktrees
|
||||
const gitPath = await findUp('.git', { cwd: opts.workspaceDir, type: 'directory' }) ??
|
||||
await findUp('.git', { cwd: opts.workspaceDir, type: 'file' })
|
||||
const gitPath = find.dir('.git', { cwd: opts.workspaceDir }) ??
|
||||
find.file('.git', { cwd: opts.workspaceDir })
|
||||
|
||||
const repoRoot = path.resolve(gitPath ?? opts.workspaceDir, '..')
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/error": "workspace:*",
|
||||
"find-up": "catalog:"
|
||||
"empathic": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "catalog:",
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { findUp } from 'find-up'
|
||||
import * as find from 'empathic/find'
|
||||
|
||||
const WORKSPACE_DIR_ENV_VAR = 'NPM_CONFIG_WORKSPACE_DIR'
|
||||
const WORKSPACE_MANIFEST_FILENAME = 'pnpm-workspace.yaml'
|
||||
@@ -20,7 +20,7 @@ export async function findWorkspaceDir (cwd: string): Promise<string | undefined
|
||||
const workspaceManifestDirEnvVar = process.env[WORKSPACE_DIR_ENV_VAR] ?? process.env[WORKSPACE_DIR_ENV_VAR.toLowerCase()]
|
||||
const workspaceManifestLocation = workspaceManifestDirEnvVar
|
||||
? path.join(workspaceManifestDirEnvVar, WORKSPACE_MANIFEST_FILENAME)
|
||||
: await findUp([WORKSPACE_MANIFEST_FILENAME, ...INVALID_WORKSPACE_MANIFEST_FILENAME], { cwd: await getRealPath(cwd) })
|
||||
: find.any([WORKSPACE_MANIFEST_FILENAME, ...INVALID_WORKSPACE_MANIFEST_FILENAME], { cwd: await getRealPath(cwd) })
|
||||
if (workspaceManifestLocation && path.basename(workspaceManifestLocation) !== WORKSPACE_MANIFEST_FILENAME) {
|
||||
throw new PnpmError('BAD_WORKSPACE_MANIFEST_NAME', `The workspace manifest file should be named "pnpm-workspace.yaml". File found: ${workspaceManifestLocation}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user