Skip to content

Commit 9c35799

Browse files
authored
fix: fixes an issue where the symlinks where not correctly preserved for pnpm monorepos (#216)
1 parent 2550490 commit 9c35799

File tree

6 files changed

+26
-8
lines changed

6 files changed

+26
-8
lines changed

.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = {
2727
'require-await': 'off',
2828
'no-inline-comments': 'off',
2929
'line-comment-position': 'off',
30+
complexity: 'off',
3031
'max-lines': 'off',
3132
},
3233
overrides: [

src/build/content/server.ts

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { existsSync } from 'node:fs'
22
import { cp, mkdir, readFile, readdir, readlink, symlink, writeFile } from 'node:fs/promises'
3-
import { dirname, join } from 'node:path'
3+
import { createRequire } from 'node:module'
4+
// eslint-disable-next-line no-restricted-imports
5+
import { dirname, join, resolve } from 'node:path'
46

57
import glob from 'fast-glob'
68

@@ -73,7 +75,7 @@ async function recreateNodeModuleSymlinks(src: string, dest: string, org?: strin
7375
// if it is an organization folder let's create the folder first
7476
await mkdir(join(dest, org), { recursive: true })
7577
}
76-
await symlink(symlinkDest, symlinkSrc)
78+
await symlink(symlinkTarget, symlinkSrc)
7779
}
7880
}
7981
}),
@@ -90,7 +92,7 @@ export const copyNextDependencies = async (ctx: PluginContext): Promise<void> =>
9092
}
9193
const src = join(ctx.standaloneDir, entry)
9294
const dest = join(ctx.serverHandlerDir, entry)
93-
await cp(src, dest, { recursive: true })
95+
await cp(src, dest, { recursive: true, verbatimSymlinks: true })
9496

9597
if (entry === 'node_modules') {
9698
await recreateNodeModuleSymlinks(ctx.resolve('node_modules'), dest)
@@ -101,19 +103,30 @@ export const copyNextDependencies = async (ctx: PluginContext): Promise<void> =>
101103
const rootSrcDir = join(ctx.standaloneRootDir, 'node_modules')
102104
const rootDestDir = join(ctx.serverHandlerRootDir, 'node_modules')
103105

104-
// TODO: @Lukas Holzer test this in monorepos
105106
// use the node_modules tree from the process.cwd() and not the one from the standalone output
106107
// as the standalone node_modules are already wrongly assembled by Next.js.
107108
// see: https://github.com/vercel/next.js/issues/50072
108109
if (existsSync(rootSrcDir) && ctx.standaloneRootDir !== ctx.standaloneDir) {
109110
promises.push(
110-
cp(rootSrcDir, rootDestDir, { recursive: true }).then(() =>
111-
recreateNodeModuleSymlinks(ctx.resolve('node_modules'), rootDestDir),
111+
cp(rootSrcDir, rootDestDir, { recursive: true, verbatimSymlinks: true }).then(() =>
112+
recreateNodeModuleSymlinks(resolve('node_modules'), rootDestDir),
112113
),
113114
)
114115
}
115116

116117
await Promise.all(promises)
118+
119+
// detect if it might lead to a runtime issue and throw an error upfront on build time instead of silently failing during runtime
120+
const require = createRequire(ctx.serverHandlerDir)
121+
try {
122+
require.resolve('styled-jsx')
123+
require.resolve('next')
124+
} catch {
125+
throw new Error(
126+
'node_modules are not installed correctly, if you are using pnpm please set the public hoist pattern to: `public-hoist-pattern[]=*`.\n' +
127+
'Refer to your docs for more details: https://docs.netlify.com/integrations/frameworks/next-js/overview/#pnpm-support',
128+
)
129+
}
117130
}
118131

119132
export const writeTagsManifest = async (ctx: PluginContext): Promise<void> => {

src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export const onPreBuild = async (options: NetlifyPluginOptions) => {
2626
export const onBuild = async (options: NetlifyPluginOptions) => {
2727
const ctx = new PluginContext(options)
2828
if (!existsSync(ctx.publishDir)) {
29-
ctx.failBuild('Publish directory not found, please check your netlify.toml')
29+
ctx.failBuild(
30+
`Publish directory not found under: ${ctx.publishDir}, please check your netlify.toml`,
31+
)
3032
}
3133
await setImageConfig(ctx)
3234

tests/e2e/turborepo.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ test.describe('[PNPM] Package manager', () => {
3232
await ctx?.cleanup?.(!!testInfo.errors.length)
3333
})
3434

35-
test.skip('Static revalidate works correctly', async ({ page }) => {
35+
test('Static revalidate works correctly', async ({ page }) => {
3636
// this disables browser cache - otherwise If-None-Match header
3737
// is added to repeat requests which result in actual 304 response
3838
// with custom headers from function not being returned

tests/fixtures/turborepo-npm/package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/fixtures/turborepo/.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public-hoist-pattern[]=*

0 commit comments

Comments
 (0)