Skip to content
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
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"files": ["packages/**"],
"excludedFiles": [
"packages/next/taskfile*.js",
"packages/next/next-devtools.webpack-config.js",
"packages/next/next-runtime.webpack-config.js"
],
"rules": {
Expand Down
4 changes: 3 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -693,5 +693,7 @@
"692": "Expected clientReferenceManifest to be defined.",
"693": "%s must not be used within a client component. Next.js should be preventing %s from being included in client components statically, but did not in this case.",
"694": "createPrerenderPathname was called inside a client component scope.",
"695": "Expected workUnitAsyncStorage to have a store."
"695": "Expected workUnitAsyncStorage to have a store.",
"696": "Next DevTools: Can't dispatch %s in this environment. This is a bug in Next.js",
"697": "Next DevTools: Can't render in this environment. This is a bug in Next.js"
}
122 changes: 122 additions & 0 deletions packages/next/next-devtools.webpack-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
const path = require('path')
const webpack = require('@rspack/core')
const MODERN_BROWSERSLIST_TARGET = require('./src/shared/lib/modern-browserslist-target')
const DevToolsIgnoreListPlugin = require('./webpack-plugins/devtools-ignore-list-plugin')

function shouldIgnorePath(modulePath) {
// For consumers, everything will be considered 3rd party dependency if they use
// the bundles we produce here.
// In other words, this is all library code and should therefore be ignored.
return true
}

/**
* @param {Object} options
* @param {boolean} options.dev
* @param {Partial<webpack.Configuration>} options.rest
* @returns {webpack.Configuration}
*/
module.exports = ({ dev, ...rest }) => {
const experimental = false
const turbo = false

const bundledReactChannel = experimental ? '-experimental' : ''

const target = `browserslist:${MODERN_BROWSERSLIST_TARGET.join(', ')}`

return {
entry: path.join(
__dirname,
'src/client/components/react-dev-overlay/app/entrypoint.js'
),
target,
mode: dev ? 'development' : 'production',
output: {
path: path.join(__dirname, 'dist/compiled/next-devtools'),
filename: `index.js`,
iife: false,
library: {
type: 'commonjs-static',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we try modern-module to get better bundle size? webpack supports it in 5.93.0+

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can experiment with that if you want. I don't want to tinker with this for Turbopack.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need all the entrypoints anyway. There's nothing that the bundler would tree-shake. Ideally, it would ignore the source code entirely for further analysis since it's already sufficiently optimized.

},
},
devtool: 'source-map',
optimization: {
moduleIds: 'named',
minimize: true,
concatenateModules: true,
minimizer: [
new webpack.SwcJsMinimizerRspackPlugin({
minimizerOptions: {
mangle: dev || process.env.NEXT_SERVER_NO_MANGLE ? false : true,
},
}),
],
},
plugins: [
// TODO: React Compiler
new DevToolsIgnoreListPlugin({ shouldIgnorePath }),
new webpack.DefinePlugin({
// TODO: Hardcode it and ensure module resolution resolves to .node entrypoint in Node.js
// 'typeof window': JSON.stringify('object'),
'process.env.NEXT_MINIMAL': JSON.stringify('true'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.env.NEXT_MINIMAL and this.minimalMode are not really needed for dev overlay, they shouldn't matter

'this.serverOptions.experimentalTestProxy': JSON.stringify(false),
'this.minimalMode': JSON.stringify(true),
'this.renderOpts.dev': JSON.stringify(dev),
'renderOpts.dev': JSON.stringify(dev),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think renderOpts should be removed, it should always be true in dev, if it's not working without replacing them then it's a bug

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to keep these in sync with the runtime for now until we isolated the bundler better codewise

'process.env.NODE_ENV': JSON.stringify(
dev ? 'development' : 'production'
),
'process.env.__NEXT_EXPERIMENTAL_REACT': JSON.stringify(
experimental ? true : false
),
'process.env.NEXT_RUNTIME': JSON.stringify('nodejs'),
'process.turbopack': JSON.stringify(turbo),
'process.env.TURBOPACK': JSON.stringify(turbo),
}),
].filter(Boolean),
stats: {
optimizationBailout: true,
},
resolve: {
alias: {
// TODO: Get dedicated React version for NDT to uncouple development.
react: `next/dist/compiled/react${bundledReactChannel}`,
'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}`,
'react-dom/client$': `next/dist/compiled/react-dom${bundledReactChannel}/client`,
'react-is$': `next/dist/compiled/react-is${bundledReactChannel}`,
scheduler$: `next/dist/compiled/scheduler${bundledReactChannel}`,
},
extensions: ['.ts', '.tsx', '.js', '.json'],
},
module: {
rules: [
{ test: /\.m?js$/, loader: `source-map-loader`, enforce: `pre` },
{
test: /\.(ts|tsx)$/,
exclude: [/node_modules/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
development: dev,
runtime: 'automatic',
// TODO: Fast Refresh
// refresh: dev,
},
},
},
},
type: 'javascript/auto',
},
],
},
externals: [],
experiments: {},
...rest,
}
}
2 changes: 2 additions & 0 deletions packages/next/next-runtime.webpack-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ const sharedExternals = [

const externalsMap = {
'./web/sandbox': 'next/dist/server/web/sandbox',
'next/dist/compiled/next-devtools':
'commonjs next/dist/client/components/react-dev-overlay/app/app-dev-overlay.shim.js',
}

const externalsRegexMap = {
Expand Down
12 changes: 11 additions & 1 deletion packages/next/src/client/app-next-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

import './app-webpack'

import { renderAppDevOverlay } from 'next/dist/compiled/next-devtools'
import { appBootstrap } from './app-bootstrap'
import {
getComponentStack,
getOwnerStack,
} from './components/errors/stitched-error'
import { isRecoverableError } from './react-client-callbacks/on-recoverable-error'

// eslint-disable-next-line @next/internal/typechecked-require
const instrumentationHooks = require('../lib/require-instrumentation-client')

appBootstrap(() => {
const { hydrate } = require('./app-index') as typeof import('./app-index')
hydrate(instrumentationHooks)
try {
hydrate(instrumentationHooks)
} finally {
renderAppDevOverlay(getComponentStack, getOwnerStack, isRecoverableError)
}
})
17 changes: 14 additions & 3 deletions packages/next/src/client/app-next-turbopack.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// TODO-APP: hydration warning

import { appBootstrap } from './app-bootstrap'
import {
getComponentStack,
getOwnerStack,
} from './components/errors/stitched-error'
import { isRecoverableError } from './react-client-callbacks/on-recoverable-error'

window.next.version += '-turbo'
;(self as any).__webpack_hash__ = ''
Expand All @@ -10,5 +13,13 @@ const instrumentationHooks = require('../lib/require-instrumentation-client')

appBootstrap(() => {
const { hydrate } = require('./app-index') as typeof import('./app-index')
hydrate(instrumentationHooks)
try {
hydrate(instrumentationHooks)
} finally {
if (process.env.NODE_ENV !== 'production') {
const { renderAppDevOverlay } =
require('next/dist/compiled/next-devtools') as typeof import('next/dist/compiled/next-devtools')
renderAppDevOverlay(getComponentStack, getOwnerStack, isRecoverableError)
}
}
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PureComponent } from 'react'
import { dispatcher } from 'next/dist/compiled/next-devtools'
import { RuntimeErrorHandler } from '../../errors/runtime-error-handler'
import { ErrorBoundary } from '../../error-boundary'
import DefaultGlobalError, {
Expand All @@ -8,7 +9,6 @@ import DefaultGlobalError, {
type AppDevOverlayErrorBoundaryProps = {
children: React.ReactNode
globalError: [GlobalErrorComponent, React.ReactNode]
onError: () => void
}

type AppDevOverlayErrorBoundaryState = {
Expand Down Expand Up @@ -53,7 +53,7 @@ export class AppDevOverlayErrorBoundary extends PureComponent<
}

componentDidCatch() {
this.props.onError()
dispatcher.openErrorOverlay()
}

render() {
Expand Down
Loading
Loading