fix: don't break when injecting a dependency with broken symlinks (#5599)

close #5598
This commit is contained in:
Zoltan Kochan
2022-11-07 12:37:57 +02:00
committed by GitHub
parent 46852d4b89
commit 6710d9dd90
9 changed files with 68 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/directory-fetcher": major
---
@pnpm/logger added to peer dependencies.

View 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).

View File

@@ -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:*",

View File

@@ -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)

View File

View File

@@ -0,0 +1 @@
broken-symlink

View File

@@ -0,0 +1,4 @@
{
"name": "simple-pkg",
"version": "0.0.0"
}

View File

@@ -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
View File

@@ -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