mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
fix: don't break when injecting a dependency with broken symlinks (#5599)
close #5598
This commit is contained in:
5
.changeset/great-jeans-teach.md
Normal file
5
.changeset/great-jeans-teach.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/directory-fetcher": major
|
||||
---
|
||||
|
||||
@pnpm/logger added to peer dependencies.
|
||||
6
.changeset/young-toes-talk.md
Normal file
6
.changeset/young-toes-talk.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/directory-fetcher": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Installation shouldn't fail when the injected dependency has broken symlinks. The broken symlinks should be just skipped [#5598](https://github.com/pnpm/pnpm/issues/5598).
|
||||
@@ -30,6 +30,9 @@
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/packages/directory-fetcher#readme",
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/fetcher-base": "workspace:*",
|
||||
"@pnpm/read-project-manifest": "workspace:*",
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import { promises as fs, Stats } from 'fs'
|
||||
import path from 'path'
|
||||
import type { DirectoryFetcher, DirectoryFetcherOptions } from '@pnpm/fetcher-base'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
|
||||
import fromPairs from 'ramda/src/fromPairs'
|
||||
import packlist from 'npm-packlist'
|
||||
|
||||
const directoryFetcherLogger = logger('directory-fetcher')
|
||||
|
||||
export interface CreateDirectoryFetcherOptions {
|
||||
includeOnlyPackageFiles?: boolean
|
||||
}
|
||||
@@ -65,7 +68,17 @@ async function _fetchAllFilesFromDir (
|
||||
.filter((file) => file !== 'node_modules')
|
||||
.map(async (file) => {
|
||||
const filePath = path.join(dir, file)
|
||||
const stat = await fs.stat(filePath)
|
||||
let stat: Stats
|
||||
try {
|
||||
stat = await fs.stat(filePath)
|
||||
} catch (err: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// Broken symlinks are skipped
|
||||
if (err.code === 'ENOENT') {
|
||||
directoryFetcherLogger.debug({ brokenSymlink: filePath })
|
||||
return
|
||||
}
|
||||
throw err
|
||||
}
|
||||
const relativeSubdir = `${relativeDir}${relativeDir ? '/' : ''}${file}`
|
||||
if (stat.isDirectory()) {
|
||||
const subFilesIndex = await _fetchAllFilesFromDir(filePath, relativeSubdir)
|
||||
|
||||
0
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/index.js
vendored
Normal file
0
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/index.js
vendored
Normal file
1
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists
vendored
Symbolic link
1
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
broken-symlink
|
||||
4
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json
vendored
Normal file
4
packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "simple-pkg",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
/// <reference path="../../../typings/index.d.ts"/>
|
||||
import path from 'path'
|
||||
import { createDirectoryFetcher } from '@pnpm/directory-fetcher'
|
||||
// @ts-expect-error
|
||||
import { debug } from '@pnpm/logger'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
|
||||
const f = fixtures(__dirname)
|
||||
jest.mock('@pnpm/logger', () => {
|
||||
const debug = jest.fn()
|
||||
return ({ debug, logger: () => ({ debug }) })
|
||||
})
|
||||
|
||||
test('fetch including only package files', async () => {
|
||||
process.chdir(f.find('simple-pkg'))
|
||||
@@ -79,3 +85,28 @@ test('fetch a directory that has no package.json', async () => {
|
||||
'index.js',
|
||||
])
|
||||
})
|
||||
|
||||
test('fetch does not fail on package with broken symlink', async () => {
|
||||
debug.mockClear()
|
||||
process.chdir(f.find('pkg-with-broken-symlink'))
|
||||
const fetcher = createDirectoryFetcher()
|
||||
|
||||
// eslint-disable-next-line
|
||||
const fetchResult = await fetcher.directory({} as any, {
|
||||
directory: '.',
|
||||
type: 'directory',
|
||||
}, {
|
||||
lockfileDir: process.cwd(),
|
||||
})
|
||||
|
||||
expect(fetchResult.local).toBe(true)
|
||||
expect(fetchResult.packageImportMethod).toBe('hardlink')
|
||||
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
|
||||
|
||||
// Only those files are included which would get published
|
||||
expect(Object.keys(fetchResult.filesIndex).sort()).toStrictEqual([
|
||||
'index.js',
|
||||
'package.json',
|
||||
])
|
||||
expect(debug).toHaveBeenCalledWith({ brokenSymlink: path.resolve('not-exists') })
|
||||
})
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -1047,6 +1047,9 @@ importers:
|
||||
'@pnpm/fetcher-base':
|
||||
specifier: workspace:*
|
||||
version: link:../fetcher-base
|
||||
'@pnpm/logger':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
'@pnpm/read-project-manifest':
|
||||
specifier: workspace:*
|
||||
version: link:../read-project-manifest
|
||||
|
||||
Reference in New Issue
Block a user