diff --git a/src/self-dep.ts b/src/self-dep.ts index f7e29c5..93d9d7a 100644 --- a/src/self-dep.ts +++ b/src/self-dep.ts @@ -40,11 +40,8 @@ const linkedAlready = (pkg: Package) => { } export const link = (pkg: Package, where: string) => { - if ( - !pkg.name || - pkg?.tshy?.selfLink === false || - linkedAlready(pkg) - ) { + const selfLink = pkg?.tshy?.selfLink + if (!pkg.name || selfLink === false || linkedAlready(pkg)) { return } const dest = resolve(where, 'node_modules', pkg.name) @@ -56,7 +53,14 @@ export const link = (pkg: Package, where: string) => { symlinkSync(src, dest) } catch { rimrafSync(dest) - symlinkSync(src, dest) + let threw = true + try { + symlinkSync(src, dest) + threw = false + } finally { + // best effort if not set explicitly. suppress error with return. + if (threw && selfLink === undefined) return + } } } diff --git a/tap-snapshots/test/self-dep.ts.test.cjs b/tap-snapshots/test/self-dep.ts.test.cjs index 662fae8..1bc6025 100644 --- a/tap-snapshots/test/self-dep.ts.test.cjs +++ b/tap-snapshots/test/self-dep.ts.test.cjs @@ -58,6 +58,64 @@ Array [ ] ` +exports[`test/self-dep.ts > TAP > throw both times, but accept if best-effort > mkdirps 1`] = ` +Array [ + Array [ + "{CWD}/some/path/node_modules", + ], +] +` + +exports[`test/self-dep.ts > TAP > throw both times, but accept if best-effort > rimrafs 1`] = ` +Array [ + Array [ + "{CWD}/some/path/node_modules/name", + ], +] +` + +exports[`test/self-dep.ts > TAP > throw both times, but accept if best-effort > symlinks 1`] = ` +Array [ + Array [ + "../../..", + "{CWD}/some/path/node_modules/name", + ], + Array [ + "../../..", + "{CWD}/some/path/node_modules/name", + ], +] +` + +exports[`test/self-dep.ts > TAP > throw both times, but self-link is required > mkdirps 1`] = ` +Array [ + Array [ + "{CWD}/some/path/node_modules", + ], +] +` + +exports[`test/self-dep.ts > TAP > throw both times, but self-link is required > rimrafs 1`] = ` +Array [ + Array [ + "{CWD}/some/path/node_modules/name", + ], +] +` + +exports[`test/self-dep.ts > TAP > throw both times, but self-link is required > symlinks 1`] = ` +Array [ + Array [ + "../../..", + "{CWD}/some/path/node_modules/name", + ], + Array [ + "../../..", + "{CWD}/some/path/node_modules/name", + ], +] +` + exports[`test/self-dep.ts > TAP > try one more time if it fails > mkdirps 1`] = ` Array [ Array [ diff --git a/test/self-dep.ts b/test/self-dep.ts index 4c709f6..a38aa98 100644 --- a/test/self-dep.ts +++ b/test/self-dep.ts @@ -14,13 +14,15 @@ const mkdirpCalls = t.capture( import * as FS from 'node:fs' let symlinkThrow: Error | undefined = undefined +let symlinkThrowAgain: Error | undefined = undefined const fs = { symlinkSync: () => { if (symlinkThrow) { try { throw symlinkThrow } finally { - symlinkThrow = undefined + symlinkThrow = symlinkThrowAgain + symlinkThrowAgain = undefined } } }, @@ -75,6 +77,31 @@ t.test('try one more time if it fails', t => { t.end() }) +t.test('throw both times, but accept if best-effort', t => { + symlinkThrow = new Error('EPERM') + symlinkThrowAgain = new Error('EPERM') + link({ name: 'name', version: '1.2.3' }, 'some/path') + t.matchSnapshot(symlinkCalls(), 'symlinks') + t.matchSnapshot(rimrafCalls(), 'rimrafs') + t.matchSnapshot(mkdirpCalls(), 'mkdirps') + t.end() +}) + +t.test('throw both times, but self-link is required', t => { + symlinkThrow = new Error('EPERM') + symlinkThrowAgain = new Error('EPERM') + t.throws(() => + link( + { name: 'name', version: '1.2.3', tshy: { selfLink: true } }, + 'some/path' + ) + ) + t.matchSnapshot(symlinkCalls(), 'symlinks') + t.matchSnapshot(rimrafCalls(), 'rimrafs') + t.matchSnapshot(mkdirpCalls(), 'mkdirps') + t.end() +}) + t.test('link, but no dirs made', t => { link({ name: 'name', version: '1.2.3' }, 'some/path') unlink({ name: 'name', version: '1.2.3' }, 'some/path')