Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error message for importing native Node APIs in the edge runtime #30696

Merged
merged 6 commits into from
Nov 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,18 @@ export default async function build(
console.error(error)
console.error()

// When using the web runtime, common Node.js native APIs are not available.
if (
hasConcurrentFeatures &&
error.indexOf("Module not found: Can't resolve 'fs'") > -1
shuding marked this conversation as resolved.
Show resolved Hide resolved
) {
const err = new Error(
`Native Node.js APIs are not supported in the Edge Runtime with \`concurrentFeatures\` enabled. Found \`fs\` imported.\n\n`
) as NextError
err.code = 'EDGE_RUNTIME_UNSUPPORTED_API'
throw err
}

if (
error.indexOf('private-next-pages') > -1 ||
error.indexOf('__next_polyfill__') > -1
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/output/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ buildStore.subscribe((state) => {
(clientWasLoading ? client.modules : 0) +
(serverWasLoading ? server.modules : 0) +
(serverWebWasLoading ? serverWeb?.modules || 0 : 0),
hasServerWeb: !!serverWeb,
}
if (client.errors) {
// Show only client errors
Expand Down
11 changes: 11 additions & 0 deletions packages/next/build/output/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type OutputState =
modules: number
errors: string[] | null
warnings: string[] | null
hasServerWeb: boolean
}
))

Expand Down Expand Up @@ -85,6 +86,16 @@ store.subscribe((state) => {
}
}

if (
state.hasServerWeb &&
cleanError.indexOf("Module not found: Can't resolve 'fs'") > -1
) {
console.error(
`Native Node.js APIs are not supported in the Edge Runtime with \`concurrentFeatures\` enabled. Found \`fs\` imported.\n`
)
return
}

// Ensure traces are flushed after each compile in development mode
flushAllTraces()
return
Expand Down
8 changes: 6 additions & 2 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,13 @@ export default async function getBaseWebpackConfig(
}

if (webServerRuntime) {
Log.info('Using the experimental web runtime.')
Log.warn(
'You are using the experimental Edge Runtime with `concurrentFeatures`.'
)
if (hasServerComponents) {
Log.info('You have experimental React Server Components enabled.')
Log.warn(
'You have experimental React Server Components enabled. Continue at your own risk.'
)
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/next/cli/next-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ const nextBuild: cliCommand = (argv) => {
isError(err) &&
(err.code === 'INVALID_RESOLVE_ALIAS' ||
err.code === 'WEBPACK_ERRORS' ||
err.code === 'BUILD_OPTIMIZATION_FAILED')
err.code === 'BUILD_OPTIMIZATION_FAILED' ||
err.code === 'EDGE_RUNTIME_UNSUPPORTED_API')
) {
printAndExit(`> ${err.message}`)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
experimental: {
reactRoot: true,
concurrentFeatures: true,
serverComponents: true,
},
webpack(config) {
const { alias } = config.resolve
alias['react/jsx-dev-runtime'] = 'react-18/jsx-dev-runtime.js'
alias['react/jsx-runtime'] = 'react-18/jsx-runtime.js'

// Use react 18
alias['react'] = 'react-18'
alias['react-dom'] = 'react-dom-18'
alias['react-dom/server'] = 'react-dom-18/server'

return config
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let F_OK

if (typeof window === 'undefined') {
F_OK = require('fs').constants.F_OK
}

export default function Index() {
console.log(F_OK)
return 'Access Node.js fs'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"private": true,
"scripts": {
"lnext": "node -r ../../react-18/test/require-hook.js ../../../../packages/next/dist/bin/next",
"dev": "yarn lnext dev",
"build": "yarn lnext build",
"start": "yarn lnext start"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import css from './css'

const nodeArgs = ['-r', join(__dirname, '../../react-18/test/require-hook.js')]
const appDir = join(__dirname, '../app')
const fsTestAppDir = join(__dirname, '../app-fs')
const distDir = join(__dirname, '../app/.next')
const documentPage = new File(join(appDir, 'pages/_document.js'))
const appPage = new File(join(appDir, 'pages/_app.js'))
Expand Down Expand Up @@ -77,18 +78,24 @@ async function nextDev(dir, port) {
})
}

describe('RSC basic', () => {
const context = { appDir }
describe('concurrentFeatures - basic', () => {
it('should warn user for experimental risk with server components', async () => {
const middlewareWarning = `Using the experimental web runtime.`
const rscWarning = `You have experimental React Server Components enabled.`
const { stdout } = await nextBuild(context.appDir)
expect(stdout).toContain(rscWarning)
expect(stdout).toContain(middlewareWarning)
const edgeRuntimeWarning =
'You are using the experimental Edge Runtime with `concurrentFeatures`.'
const rscWarning = `You have experimental React Server Components enabled. Continue at your own risk.`
const { stderr } = await nextBuild(appDir)
expect(stderr).toContain(edgeRuntimeWarning)
expect(stderr).toContain(rscWarning)
})
it('should warn user that native node APIs are not supported', async () => {
const fsImportedErrorMessage =
'Native Node.js APIs are not supported in the Edge Runtime with `concurrentFeatures` enabled. Found `fs` imported.'
const { stderr } = await nextBuild(fsTestAppDir)
expect(stderr).toContain(fsImportedErrorMessage)
})
})

describe('RSC prod', () => {
describe('concurrentFeatures - prod', () => {
const context = { appDir }

beforeAll(async () => {
Expand Down Expand Up @@ -136,7 +143,7 @@ describe('RSC prod', () => {
runBasicTests(context)
})

describe('RSC dev', () => {
describe('concurrentFeatures - dev', () => {
const context = { appDir }

beforeAll(async () => {
Expand Down