From 631877ebf56fef4c847a6b0b8ce65708f8b73bff Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sun, 14 Nov 2021 09:28:00 +0200 Subject: [PATCH] fix: don't fail if a linked directory is not found (#3983) close #3746 --- .changeset/dirty-trains-perform.md | 5 +++ .changeset/tiny-rules-wait.md | 6 +++ packages/core/test/install/only.ts | 13 +++++++ packages/local-resolver/src/index.ts | 37 +++++++++++-------- packages/local-resolver/test/index.ts | 13 ++++++- .../src/symlinkDirectRootDependency.ts | 14 +++++-- 6 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 .changeset/dirty-trains-perform.md create mode 100644 .changeset/tiny-rules-wait.md diff --git a/.changeset/dirty-trains-perform.md b/.changeset/dirty-trains-perform.md new file mode 100644 index 0000000000..3a090ceb3d --- /dev/null +++ b/.changeset/dirty-trains-perform.md @@ -0,0 +1,5 @@ +--- +"@pnpm/local-resolver": patch +--- + +Don't fail if a local linked directory is not found (unless it should be injected). This is the intended behavior of the "link:" protocol as per Yarn's docs. diff --git a/.changeset/tiny-rules-wait.md b/.changeset/tiny-rules-wait.md new file mode 100644 index 0000000000..49ca6e59ea --- /dev/null +++ b/.changeset/tiny-rules-wait.md @@ -0,0 +1,6 @@ +--- +"@pnpm/symlink-dependency": patch +"pnpm": patch +--- + +Don't fail if a linked directory is not found. Just print a warning about it [#3746](https://github.com/pnpm/pnpm/issues/3746). diff --git a/packages/core/test/install/only.ts b/packages/core/test/install/only.ts index 7d61cb52b7..e99851db06 100644 --- a/packages/core/test/install/only.ts +++ b/packages/core/test/install/only.ts @@ -151,3 +151,16 @@ test('fail if installing different types of dependencies in a project that uses await install(manifest, newOpts) }) + +test('installation should not fail if a linked dependency points to a directory that does not exist', async () => { + const project = prepareEmpty() + + await install({ + dependencies: { + 'is-positive': '1.0.0', + 'not-exists': 'link:../not-exists', + }, + }, await testDefaults()) + + await project.has('is-positive') +}) diff --git a/packages/local-resolver/src/index.ts b/packages/local-resolver/src/index.ts index fe3496ecd2..a76bc0d591 100644 --- a/packages/local-resolver/src/index.ts +++ b/packages/local-resolver/src/index.ts @@ -55,25 +55,32 @@ export default async function resolveLocal ( localDependencyManifest = await readProjectManifestOnly(spec.fetchSpec) as DependencyManifest } catch (internalErr: any) { // eslint-disable-line if (!existsSync(spec.fetchSpec)) { - throw new PnpmError('LINKED_PKG_DIR_NOT_FOUND', - `Could not install from "${spec.fetchSpec}" as it does not exist.`) - } - switch (internalErr.code) { - case 'ENOTDIR': { - throw new PnpmError('NOT_PACKAGE_DIRECTORY', - `Could not install from "${spec.fetchSpec}" as it is not a directory.`) - } - case 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND': - case 'ENOENT': { + if (wantedDependency.injected) { + throw new PnpmError('LINKED_PKG_DIR_NOT_FOUND', + `Could not install from "${spec.fetchSpec}" as it does not exist.`) + } localDependencyManifest = { name: path.basename(spec.fetchSpec), version: '0.0.0', } - break - } - default: { - throw internalErr - } + } else { + switch (internalErr.code) { + case 'ENOTDIR': { + throw new PnpmError('NOT_PACKAGE_DIRECTORY', + `Could not install from "${spec.fetchSpec}" as it is not a directory.`) + } + case 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND': + case 'ENOENT': { + localDependencyManifest = { + name: path.basename(spec.fetchSpec), + version: '0.0.0', + } + break + } + default: { + throw internalErr + } + } } } return { diff --git a/packages/local-resolver/test/index.ts b/packages/local-resolver/test/index.ts index f4312639c8..1dabd1d5c4 100644 --- a/packages/local-resolver/test/index.ts +++ b/packages/local-resolver/test/index.ts @@ -107,14 +107,23 @@ test('fail when resolving tarball specified with the link: protocol', async () = } }) -test('fail when resolving from not existing directory', async () => { - const wantedDependency = { pref: 'link:./dir-does-not-exist' } +test('fail when resolving from not existing directory an injected dependency', async () => { + const wantedDependency = { injected: true, pref: 'link:./dir-does-not-exist' } const projectDir = __dirname await expect( resolveFromLocal(wantedDependency, { projectDir }) ).rejects.toThrow(`Could not install from "${path.join(projectDir, 'dir-does-not-exist')}" as it does not exist.`) }) +test('do not fail when resolving from not existing directory', async () => { + const wantedDependency = { pref: 'link:./dir-does-not-exist' } + const resolveResult = await resolveFromLocal(wantedDependency, { projectDir: __dirname }) + expect(resolveResult?.manifest).toStrictEqual({ + name: 'dir-does-not-exist', + version: '0.0.0', + }) +}) + test('throw error when the path: protocol is used', async () => { try { await resolveFromLocal({ pref: 'path:..' }, { projectDir: __dirname }) diff --git a/packages/symlink-dependency/src/symlinkDirectRootDependency.ts b/packages/symlink-dependency/src/symlinkDirectRootDependency.ts index 1a2ce4b839..8d060d81bd 100644 --- a/packages/symlink-dependency/src/symlinkDirectRootDependency.ts +++ b/packages/symlink-dependency/src/symlinkDirectRootDependency.ts @@ -4,6 +4,7 @@ import { DependencyType, rootLogger, } from '@pnpm/core-loggers' +import { globalWarn } from '@pnpm/logger' import { DependenciesField } from '@pnpm/types' import symlinkDir from 'symlink-dir' @@ -43,15 +44,22 @@ export default async function symlinkDirectRootDependency ( } } - const dependencyRealocation = await fs.realpath(dependencyLocation) + let dependencyRealLocation!: string + try { + dependencyRealLocation = await fs.realpath(dependencyLocation) + } catch (err: any) { // eslint-disable-line + if (err.code !== 'ENOENT') throw err + globalWarn(`Local dependency not found at ${dependencyLocation}`) + return + } const dest = path.join(destModulesDirReal, importAs) - const { reused } = await symlinkDir(dependencyRealocation, dest) + const { reused } = await symlinkDir(dependencyRealLocation, dest) if (reused) return // if the link was already present, don't log rootLogger.debug({ added: { dependencyType: opts.fromDependenciesField && DEP_TYPE_BY_DEPS_FIELD_NAME[opts.fromDependenciesField] as DependencyType, - linkedFrom: dependencyRealocation, + linkedFrom: dependencyRealLocation, name: importAs, realName: opts.linkedPackage.name, version: opts.linkedPackage.version,