From 015da637d7f020ab3ba692b5ddaa8ab03d2a5a3c Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Mon, 7 Nov 2022 02:38:42 +0200 Subject: [PATCH] fix: don't break when injecting a dependency with broken symlinks close #5598 --- packages/directory-fetcher/src/index.ts | 11 +++++++-- .../fixtures/pkg-with-broken-symlink/index.js | 0 .../pkg-with-broken-symlink/not-exists | 1 + .../pkg-with-broken-symlink/package.json | 4 ++++ packages/directory-fetcher/test/index.ts | 23 +++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/index.js create mode 120000 packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists create mode 100644 packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json diff --git a/packages/directory-fetcher/src/index.ts b/packages/directory-fetcher/src/index.ts index 5a702a35e8c..e483420bfdd 100644 --- a/packages/directory-fetcher/src/index.ts +++ b/packages/directory-fetcher/src/index.ts @@ -1,4 +1,4 @@ -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 { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest' @@ -65,7 +65,14 @@ 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') return + throw err + } const relativeSubdir = `${relativeDir}${relativeDir ? '/' : ''}${file}` if (stat.isDirectory()) { const subFilesIndex = await _fetchAllFilesFromDir(filePath, relativeSubdir) diff --git a/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/index.js b/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/index.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists b/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists new file mode 120000 index 00000000000..a11a2d2d855 --- /dev/null +++ b/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/not-exists @@ -0,0 +1 @@ +broken-symlink \ No newline at end of file diff --git a/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json b/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json new file mode 100644 index 00000000000..6fea5f21a37 --- /dev/null +++ b/packages/directory-fetcher/test/fixtures/pkg-with-broken-symlink/package.json @@ -0,0 +1,4 @@ +{ + "name": "simple-pkg", + "version": "0.0.0" +} diff --git a/packages/directory-fetcher/test/index.ts b/packages/directory-fetcher/test/index.ts index 998c50c2843..7c0e9685b59 100644 --- a/packages/directory-fetcher/test/index.ts +++ b/packages/directory-fetcher/test/index.ts @@ -79,3 +79,26 @@ test('fetch a directory that has no package.json', async () => { 'index.js', ]) }) + +test('fetch does not fail on package with broken symlink', async () => { + 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', + ]) +})