[remix] Gracefully handle symlink creation when updating Remix packages (#9569)

Fixes an edge-case error when changing the version of Remix and then making a deployment to Vercel when there is already an existing build cache:

> Error: EEXIST: file already exists, symlink '../.pnpm/@remix-run+server-runtime@1.5.0_biqbaboplfbrettd7655fr4n2y/node_modules/@remix-run/server-runtime' -> '/vercel/path0/node_modules/@remix-run/server-runtime'

This would happen because the symlink was created in a previous run, but no longer points to a valid path because it's a different version. To fix we'll delete the previous symlink when the target value does not match.
This commit is contained in:
Nathan Rajlich
2023-02-28 14:51:58 -08:00
committed by GitHub
parent b78cfc9ba5
commit 1390f6d2ee
2 changed files with 34 additions and 17 deletions

View File

@@ -501,14 +501,7 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
const match = packages.find(p => p.startsWith(prefix));
if (match) {
const pkgDir = join(pnpmDir, match, 'node_modules', pkgName);
const symlinkPath = join(pnpmDir, '..', pkgName);
const symlinkDir = dirname(symlinkPath);
const symlinkTarget = relative(symlinkDir, pkgDir);
await fs.mkdir(symlinkDir, { recursive: true });
await fs.symlink(symlinkTarget, symlinkPath);
console.warn(
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
);
await ensureSymlink(pkgDir, join(pnpmDir, '..'), pkgName);
return;
}
}
@@ -526,14 +519,7 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
const match = packages.find(p => p.startsWith(prefix));
if (match) {
const pkgDir = join(prefixDir, match, 'node_modules', pkgName);
const symlinkPath = join(npmDir, '..', pkgName);
const symlinkDir = dirname(symlinkPath);
const symlinkTarget = relative(symlinkDir, pkgDir);
await fs.mkdir(symlinkDir, { recursive: true });
await fs.symlink(symlinkTarget, symlinkPath);
console.warn(
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
);
await ensureSymlink(pkgDir, join(npmDir, '..'), pkgName);
return;
}
}
@@ -543,6 +529,37 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
);
}
async function ensureSymlink(
target: string,
nodeModulesDir: string,
pkgName: string
) {
const symlinkPath = join(nodeModulesDir, pkgName);
const symlinkDir = dirname(symlinkPath);
const relativeTarget = relative(symlinkDir, target);
try {
const existingTarget = await fs.readlink(symlinkPath);
if (existingTarget === relativeTarget) {
// Symlink is already the expected value, so do nothing
return;
} else {
// If a symlink already exists then delete it if the target doesn't match
await fs.unlink(symlinkPath);
}
} catch (err: any) {
// Ignore when path does not exist or is not a symlink
if (err.code !== 'ENOENT' && err.code !== 'EINVAL') {
throw err;
}
}
await fs.symlink(relativeTarget, symlinkPath);
console.warn(
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
);
}
function isEdgeRuntime(runtime: string): boolean {
return runtime === 'edge' || runtime === 'experimental-edge';
}

View File

@@ -5,7 +5,7 @@
"src": "package.json",
"use": "@vercel/remix",
"config": {
"installCommand": "npm install --install-strategy=linked",
"installCommand": "npm install --install-strategy=linked && ln -sf broken 'node_modules/@remix-run/server-runtime'",
"zeroConfig": true
}
}