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

Transpile all code on app browser layer #59569

Merged
merged 12 commits into from
Dec 20, 2023
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
41 changes: 30 additions & 11 deletions packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { WEBPACK_LAYERS, type WebpackLayerName } from '../../lib/constants'
import type {
NextConfig,
ExperimentalConfig,
Expand All @@ -9,6 +10,8 @@ import type { ResolvedBaseUrl } from '../load-jsconfig'
const nextDistPath =
/(next[\\/]dist[\\/]shared[\\/]lib)|(next[\\/]dist[\\/]client)|(next[\\/]dist[\\/]pages)/

const nodeModulesPath = /[\\/]node_modules[\\/]/

const regeneratorRuntimePath = require.resolve(
'next/dist/compiled/regenerator-runtime'
)
Expand Down Expand Up @@ -44,7 +47,7 @@ function getBaseSWCOptions({
jsConfig,
swcCacheDir,
serverComponents,
isReactServerLayer,
bundleLayer,
}: {
filename: string
jest?: boolean
Expand All @@ -59,8 +62,10 @@ function getBaseSWCOptions({
jsConfig: any
swcCacheDir?: string
serverComponents?: boolean
isReactServerLayer?: boolean
bundleLayer?: WebpackLayerName
}) {
const isReactServerLayer =
bundleLayer === WEBPACK_LAYERS.reactServerComponents
const parserConfig = getParserOptions({ filename, jsConfig })
const paths = jsConfig?.compilerOptions?.paths
const enableDecorators = Boolean(
Expand Down Expand Up @@ -177,7 +182,7 @@ function getBaseSWCOptions({
serverComponents:
serverComponents && !jest
? {
isReactServerLayer: !!isReactServerLayer,
isReactServerLayer,
}
: undefined,
serverActions:
Expand All @@ -186,7 +191,7 @@ function getBaseSWCOptions({
// always enable server actions
// TODO: remove this option
enabled: true,
isReactServerLayer: !!isReactServerLayer,
isReactServerLayer,
}
: undefined,
// For app router we prefer to bundle ESM,
Expand Down Expand Up @@ -295,8 +300,8 @@ export function getJestSWCOptions({
resolvedBaseUrl,
esm,
// Don't apply server layer transformations for Jest
isReactServerLayer: false,
// Disable server / client graph assertions for Jest
bundleLayer: undefined,
serverComponents: false,
})

Expand Down Expand Up @@ -339,7 +344,7 @@ export function getLoaderSWCOptions({
swcCacheDir,
relativeFilePathFromRoot,
serverComponents,
isReactServerLayer,
bundleLayer,
esm,
}: {
filename: string
Expand All @@ -362,7 +367,7 @@ export function getLoaderSWCOptions({
relativeFilePathFromRoot: string
esm?: boolean
serverComponents?: boolean
isReactServerLayer?: boolean
bundleLayer?: WebpackLayerName
}) {
let baseOptions: any = getBaseSWCOptions({
filename,
Expand All @@ -375,7 +380,7 @@ export function getLoaderSWCOptions({
jsConfig,
// resolvedBaseUrl,
swcCacheDir,
isReactServerLayer,
bundleLayer,
serverComponents,
esm: !!esm,
})
Expand Down Expand Up @@ -418,9 +423,12 @@ export function getLoaderSWCOptions({
}

const isNextDist = nextDistPath.test(filename)
const isNodeModules = nodeModulesPath.test(filename)
const isAppBrowserLayer = bundleLayer === WEBPACK_LAYERS.appPagesBrowser

let options: any
if (isServer) {
return {
options = {
...baseOptions,
// Disables getStaticProps/getServerSideProps tree shaking on the server compilation for pages
disableNextSsg: true,
Expand All @@ -440,7 +448,7 @@ export function getLoaderSWCOptions({
...getModuleOptions(esm),
}
} else {
const options = {
options = {
...baseOptions,
// Ensure Next.js internals are output as commonjs modules
...(isNextDist
Expand Down Expand Up @@ -468,6 +476,17 @@ export function getLoaderSWCOptions({
// Matches default @babel/preset-env behavior
options.jsc.target = 'es5'
}
return options
}

// For node_modules in app browser layer, we don't need to do any server side transformation.
// Only keep server actions transform to discover server actions from client components.
if (isAppBrowserLayer && isNodeModules) {
options.disableNextSsg = true
options.disablePageConfig = true
options.isPageFile = false
options.optimizeServerReact = undefined
options.cjsRequireOptimizer = undefined
}

return options
}
111 changes: 65 additions & 46 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ const devtoolRevertWarning = execOnce(

let loggedSwcDisabled = false
let loggedIgnoredCompilerOptions = false
const reactRefreshLoaderName =
'next/dist/compiled/@next/react-refresh-utils/dist/loader'

export function attachReactRefresh(
webpackConfig: webpack.Configuration,
targetLoader: webpack.RuleSetUseItem
) {
let injections = 0
const reactRefreshLoaderName =
'next/dist/compiled/@next/react-refresh-utils/dist/loader'
const reactRefreshLoader = require.resolve(reactRefreshLoaderName)
webpackConfig.module?.rules?.forEach((rule) => {
if (rule && typeof rule === 'object' && 'use' in rule) {
Expand Down Expand Up @@ -445,18 +445,22 @@ export default async function getBaseWebpackConfig(
// RSC loaders, prefer ESM, set `esm` to true
const swcServerLayerLoader = getSwcLoader({
serverComponents: true,
isReactServerLayer: true,
bundleLayer: WEBPACK_LAYERS.reactServerComponents,
esm: true,
})
const swcClientLayerLoader = getSwcLoader({
const swcSSRLayerLoader = getSwcLoader({
serverComponents: true,
isReactServerLayer: false,
bundleLayer: WEBPACK_LAYERS.serverSideRendering,
esm: true,
})
const swcBrowserLayerLoader = getSwcLoader({
serverComponents: true,
bundleLayer: WEBPACK_LAYERS.appPagesBrowser,
esm: true,
})
// Default swc loaders for pages doesn't prefer ESM.
const swcDefaultLoader = getSwcLoader({
serverComponents: true,
isReactServerLayer: false,
esm: false,
})

Expand All @@ -475,31 +479,30 @@ export default async function getBaseWebpackConfig(
].filter(Boolean)
: []

const swcLoaderForMiddlewareLayer = useSWCLoader
? getSwcLoader({
serverComponents: false,
isReactServerLayer: false,
})
: // When using Babel, we will have to use SWC to do the optimization
// for middleware to tree shake the unused default optimized imports like "next/server".
// This will cause some performance overhead but
// acceptable as Babel will not be recommended.
[
getSwcLoader({
serverComponents: false,
isReactServerLayer: false,
}),
]
const swcLoaderForMiddlewareLayer = [
// When using Babel, we will have to use SWC to do the optimization
// for middleware to tree shake the unused default optimized imports like "next/server".
// This will cause some performance overhead but
// acceptable as Babel will not be recommended.
getSwcLoader({
serverComponents: false,
bundleLayer: WEBPACK_LAYERS.middleware,
}),
babelLoader,
].filter(Boolean)

// client components layers: SSR + browser
const swcLoaderForClientLayer = [
...(dev && isClient
? [
require.resolve(
'next/dist/compiled/@next/react-refresh-utils/dist/loader'
),
]
: []),
const reactRefreshLoaders =
dev && isClient ? [require.resolve(reactRefreshLoaderName)] : []

// client components layers: SSR or browser
const createSwcLoaderForClientLayer = ({
isBrowserLayer,
reactRefresh,
}: {
isBrowserLayer: boolean
reactRefresh: boolean
}) => [
...(reactRefresh ? reactRefreshLoaders : []),
{
// This loader handles actions and client entries
// in the client layer.
Expand All @@ -511,20 +514,30 @@ export default async function getBaseWebpackConfig(
// as an additional pass to handle RSC correctly.
// This will cause some performance overhead but
// acceptable as Babel will not be recommended.
swcClientLayerLoader,
isBrowserLayer ? swcBrowserLayerLoader : swcSSRLayerLoader,
babelLoader,
].filter(Boolean)
: []),
]

const swcLoaderForBrowserLayer = createSwcLoaderForClientLayer({
isBrowserLayer: true,
// reactRefresh for browser layer is applied conditionally to user-land source
reactRefresh: false,
})
const swcLoaderForSSRLayer = createSwcLoaderForClientLayer({
isBrowserLayer: false,
reactRefresh: true,
})

// Loader for API routes needs to be differently configured as it shouldn't
// have RSC transpiler enabled, so syntax checks such as invalid imports won't
// be performed.
const loaderForAPIRoutes =
hasAppDir && useSWCLoader
? getSwcLoader({
serverComponents: false,
isReactServerLayer: false,
bundleLayer: WEBPACK_LAYERS.api,
})
: defaultLoaders.babel

Expand Down Expand Up @@ -1361,6 +1374,20 @@ export default async function getBaseWebpackConfig(
},
]
: []),
// Do not apply react-refresh-loader to node_modules for app router browser layer
...(hasAppDir && dev && isClient
? [
{
test: codeCondition.test,
exclude: codeCondition.exclude,
issuerLayer: WEBPACK_LAYERS.appPagesBrowser,
use: reactRefreshLoaders,
resolve: {
mainFields: getMainField(compilerType, true),
},
},
]
: []),
{
oneOf: [
{
Expand Down Expand Up @@ -1394,17 +1421,16 @@ export default async function getBaseWebpackConfig(
},
{
test: codeCondition.test,
exclude: codeCondition.exclude,
issuerLayer: [WEBPACK_LAYERS.appPagesBrowser],
use: swcLoaderForClientLayer,
issuerLayer: WEBPACK_LAYERS.appPagesBrowser,
use: swcLoaderForBrowserLayer,
resolve: {
mainFields: getMainField(compilerType, true),
},
},
{
test: codeCondition.test,
issuerLayer: [WEBPACK_LAYERS.serverSideRendering],
use: swcLoaderForClientLayer,
issuerLayer: WEBPACK_LAYERS.serverSideRendering,
use: swcLoaderForSSRLayer,
resolve: {
mainFields: getMainField(compilerType, true),
},
Expand All @@ -1413,18 +1439,11 @@ export default async function getBaseWebpackConfig(
: []),
{
...codeCondition,
use:
dev && isClient
? [
require.resolve(
'next/dist/compiled/@next/react-refresh-utils/dist/loader'
),
defaultLoaders.babel,
]
: defaultLoaders.babel,
use: [...reactRefreshLoaders, defaultLoaders.babel],
},
],
},

...(!config.images.disableStaticImages
? [
{
Expand Down
7 changes: 4 additions & 3 deletions packages/next/src/build/webpack/loaders/next-swc-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ DEALINGS IN THE SOFTWARE.
*/

import type { NextConfig } from '../../../../types'
import type { WebpackLayerName } from '../../../lib/constants'
import { isWasm, transform } from '../../swc'
import { getLoaderSWCOptions } from '../../swc/options'
import path, { isAbsolute } from 'path'
Expand All @@ -43,7 +44,7 @@ export interface SWCLoaderOptions {
supportedBrowsers: string[] | undefined
swcCacheDir: string
serverComponents?: boolean
isReactServerLayer?: boolean
bundleLayer?: WebpackLayerName
esm?: boolean
}

Expand All @@ -69,7 +70,7 @@ async function loaderTransform(
supportedBrowsers,
swcCacheDir,
serverComponents,
isReactServerLayer,
bundleLayer,
esm,
} = loaderOptions
const isPageFile = filename.startsWith(pagesDir)
Expand All @@ -93,7 +94,7 @@ async function loaderTransform(
swcCacheDir,
relativeFilePathFromRoot,
serverComponents,
isReactServerLayer,
bundleLayer,
esm,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
)
expect(await session.hasRedbox(true)).toBe(true)
expect(await session.getRedboxSource()).toMatchInlineSnapshot(`
"./node_modules/my-package/index.js:1:0
"./node_modules/my-package/index.js:1:12
Module not found: Can't resolve 'dns'

https://nextjs.org/docs/messages/module-not-found
Expand Down
Loading