Skip to content

Commit

Permalink
Implement foundations for additional bundler option
Browse files Browse the repository at this point in the history
This implements core code from https://github.com/vercel/next.js/tree/wbinnssmith/try-ci-test for rspack. It does not include code for the built-in flight or app loader.

These code paths are currently taken when `NEXT_RSPACK` is set when running Next.js. Ultimately, this will be set with a new Next.js plugin, which will also install the necessary dependencies.

Co-authored-by: hardfist <yangjianzju@gmail.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
  • Loading branch information
4 people committed Feb 19, 2025
1 parent b36d763 commit f47776d
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 14 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@
"test-types": "tsc",
"test-unit": "jest test/unit/ packages/next/ packages/font",
"test-dev": "cross-env NEXT_TEST_MODE=dev pnpm testheadless",
"test-dev-rspack": "cross-env NEXT_TEST_MODE=dev NEXT_RSPACK=1 BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN=1 BUILTIN_APP_LOADER=1 BUILTIN_SWC_LOADER=1 pnpm testheadless",
"test-dev-turbo": "cross-env NEXT_TEST_MODE=dev TURBOPACK=1 TURBOPACK_DEV=1 pnpm testheadless",
"test-start": "cross-env NEXT_TEST_MODE=start pnpm testheadless",
"test-start-rspack": "cross-env NEXT_TEST_MODE=start NEXT_RSPACK=1 BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN=1 BUILTIN_APP_LOADER=1 BUILTIN_SWC_LOADER=1 pnpm testheadless",
"test-start-turbo": "cross-env NEXT_TEST_MODE=start TURBOPACK=1 TURBOPACK_BUILD=1 pnpm testheadless",
"test-deploy": "cross-env NEXT_TEST_MODE=deploy pnpm testheadless",
"testonly-dev": "cross-env NEXT_TEST_MODE=dev pnpm testonly",
"testonly-dev-rspack": "cross-env NEXT_TEST_MODE=dev NEXT_RSPACK=1 pnpm testonly",
"testonly-dev-turbo": "cross-env NEXT_TEST_MODE=dev TURBOPACK=1 TURBOPACK_DEV=1 pnpm testonly",
"testonly-start": "cross-env NEXT_TEST_MODE=start pnpm testonly",
"testonly-start-rspack": "cross-env NEXT_TEST_MODE=start NEXT_RSPACK=1 pnpm testonly",
"testonly-start-turbo": "cross-env NEXT_TEST_MODE=start TURBOPACK=1 TURBOPACK_BUILD=1 pnpm testonly",
"testonly-deploy": "cross-env NEXT_TEST_MODE=deploy pnpm testonly",
"test": "pnpm testheadless",
"test-rspack": "cross-env NEXT_RSPACK=1 pnpm testheadless",
"test-turbo": "cross-env TURBOPACK=1 TURBOPACK_DEV=1 TURBOPACK_BUILD=1 pnpm testheadless",
"testonly": "jest --runInBand",
"testheadless": "cross-env HEADLESS=true pnpm testonly",
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import type { NextInfoOptions } from '../cli/next-info.js'
import type { NextDevOptions } from '../cli/next-dev.js'
import type { NextBuildOptions } from '../cli/next-build.js'

if (process.env.NEXT_RSPACK) {
// silent rspack's schema check
process.env.RSPACK_CONFIG_VALIDATE = 'loose-silent'
}

if (
!semver.satisfies(
process.versions.node,
Expand Down
4 changes: 3 additions & 1 deletion packages/next/src/build/webpack/config/blocks/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const base = curry(function base(
if (ctx.isDevelopment) {
if (process.env.__NEXT_TEST_MODE && !process.env.__NEXT_TEST_WITH_DEVTOOL) {
config.devtool = false
} else if (process.env.NEXT_RSPACK) {
config.devtool = 'source-map'
} else {
// `eval-source-map` provides full-fidelity source maps for the
// original source, including columns and original variable names.
Expand Down Expand Up @@ -73,7 +75,7 @@ export const base = curry(function base(
shouldIgnorePath,
})
)
} else if (config.devtool === 'eval-source-map') {
} else if (config.devtool === 'eval-source-map' && !process.env.NEXT_RSPACK) {
// We're using a fork of `eval-source-map`
config.devtool = false
config.plugins.push(
Expand Down
8 changes: 6 additions & 2 deletions packages/next/src/build/webpack/config/blocks/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { getPostCssPlugins } from './plugins'
import { nonNullable } from '../../../../../lib/non-nullable'
import { WEBPACK_LAYERS } from '../../../../../lib/constants'
import { getRspackCore } from '../../../../../shared/lib/get-rspack'

// RegExps for all Style Sheet variants
export const regexLikeCss = /\.(css|scss|sass)$/
Expand Down Expand Up @@ -147,6 +148,7 @@ export const css = curry(async function css(
ctx: ConfigurationContext,
config: webpack.Configuration
) {
const isRspack = Boolean(process.env.NEXT_RSPACK)
const {
prependData: sassPrependData,
additionalData: sassAdditionalData,
Expand Down Expand Up @@ -592,8 +594,10 @@ export const css = curry(async function css(
// Enable full mini-css-extract-plugin hmr for prod mode pages or app dir
if (ctx.isClient && (ctx.isProduction || ctx.hasAppDir)) {
// Extract CSS as CSS file(s) in the client-side production bundle.
const MiniCssExtractPlugin =
require('../../../plugins/mini-css-extract-plugin').default
const MiniCssExtractPlugin = isRspack
? getRspackCore().CssExtractRspackPlugin
: require('../../../plugins/mini-css-extract-plugin').default

fns.push(
plugin(
// @ts-ignore webpack 5 compat
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { webpack } from 'next/dist/compiled/webpack/webpack'
import { getRspackCore } from '../../../../../../shared/lib/get-rspack'

export function getClientStyleLoader({
hasAppDir,
Expand All @@ -11,6 +12,7 @@ export function getClientStyleLoader({
isDevelopment: boolean
assetPrefix: string
}): webpack.RuleSetUseItem {
const isRspack = Boolean(process.env.NEXT_RSPACK)
const shouldEnableApp = typeof isAppDir === 'boolean' ? isAppDir : hasAppDir

// Keep next-style-loader for development mode in `pages/`
Expand Down Expand Up @@ -41,8 +43,10 @@ export function getClientStyleLoader({
}
}

const MiniCssExtractPlugin =
require('../../../../plugins/mini-css-extract-plugin').default
const MiniCssExtractPlugin = isRspack
? getRspackCore().rspack.CssExtractRspackPlugin
: require('../../../../plugins/mini-css-extract-plugin').default

return {
loader: MiniCssExtractPlugin.loader,
options: {
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/build/webpack/loaders/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export function isCSSMod(mod: {
mod.loaders?.some(
({ loader }) =>
loader.includes('next-style-loader/index.js') ||
(process.env.NEXT_RSPACK &&
loader.includes('rspack.CssExtractRspackPlugin.loader')) ||
loader.includes('mini-css-extract-plugin/loader.js') ||
loader.includes('@vanilla-extract/webpack-plugin/loader/')
)
Expand Down
10 changes: 7 additions & 3 deletions packages/next/src/build/webpack/plugins/middleware-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,13 @@ export default class MiddlewarePlugin {
compiler,
compilation,
})
hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/esm').tap(NAME, codeAnalyzer)

// parser hooks aren't available in rspack
if (!process.env.NEXT_RSPACK) {
hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/esm').tap(NAME, codeAnalyzer)
}

/**
* Extract all metadata for the entry points in a Map object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
for (const entrypoint of compilation.entrypoints.values()) {
const entryFiles = new Set<string>()

for (const chunk of entrypoint
.getEntrypointChunk()
.getAllReferencedChunks()) {
for (const chunk of process.env.NEXT_RSPACK
? entrypoint.chunks
: entrypoint.getEntrypointChunk().getAllReferencedChunks()) {
for (const file of chunk.files) {
if (isTraceable(file)) {
const filePath = nodePath.join(outputPath, file)
Expand Down Expand Up @@ -580,6 +580,23 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {

apply(compiler: webpack.Compiler) {
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: PLUGIN_NAME,
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
},
(_assets: any, callback: any) => {
this.createTraceAssets(compilation, traceEntrypointsPluginSpan)
.then(() => callback())
.catch((err) => callback(err))
}
)

// rspack doesn't support all API below so only create trace assets
if (process.env.NEXT_RSPACK) {
return
}

const readlink = async (path: string): Promise<string | null> => {
try {
return await new Promise((resolve, reject) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class NextJsRequireCacheHotReloader implements WebpackPluginInstance {
// we need to make sure to clear all server entries from cache
// since they can have a stale webpack-runtime cache
// which needs to always be in-sync
const entries = [...compilation.entries.keys()].filter((entry) => {
const entries = [...compilation.entrypoints.keys()].filter((entry) => {
const isAppPath = entry.toString().startsWith('app/')
return entry.toString().startsWith('pages/') || isAppPath
})
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/build/webpack/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ export function getModuleReferencesInOrder(
module: Module,
moduleGraph: ModuleGraph
): ModuleGraphConnection[] {
if (
'getOutgoingConnectionsInOrder' in moduleGraph &&
typeof moduleGraph.getOutgoingConnectionsInOrder === 'function'
) {
return moduleGraph.getOutgoingConnectionsInOrder(module)
}
const connections = []
for (const connection of moduleGraph.getOutgoingConnections(module)) {
if (connection.dependency && connection.module) {
Expand Down
24 changes: 23 additions & 1 deletion packages/next/src/bundles/webpack/packages/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ exports.__esModule = true
exports.default = undefined

exports.init = function () {
if (process.env.NEXT_PRIVATE_LOCAL_WEBPACK) {
if (process.env.NEXT_RSPACK) {
// eslint-disable-next-line
Object.assign(exports, getRspackCore())
Object.assign(exports, {
// eslint-disable-next-line import/no-extraneous-dependencies
StringXor: require('./bundle5')().StringXor,
})
} else if (process.env.NEXT_PRIVATE_LOCAL_WEBPACK) {
Object.assign(exports, {
// eslint-disable-next-line import/no-extraneous-dependencies
BasicEvaluatedExpression: require('webpack/lib/javascript/BasicEvaluatedExpression'),
Expand Down Expand Up @@ -33,3 +40,18 @@ exports.init = function () {
Object.assign(exports, require('./bundle5')())
}
}

function getRspackCore() {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
return require('@rspack/core')
} catch (e) {
if (e instanceof Error && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
throw new Error(
'@rspack/core is not available. Please make sure the appropriate Next.js plugin is installed.'
)
}

throw e
}
}
3 changes: 3 additions & 0 deletions packages/next/src/client/app-dir/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ function mountLinkInstance(
router: AppRouterInstance,
kind: PrefetchKind.AUTO | PrefetchKind.FULL
) {
// element can be falsy which can break WeakMap and observing
if (!element) return

let prefetchUrl: URL | null = null
try {
prefetchUrl = createPrefetchURL(href)
Expand Down
24 changes: 23 additions & 1 deletion packages/next/src/compiled/webpack/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ exports.__esModule = true
exports.default = undefined

exports.init = function () {
if (process.env.NEXT_PRIVATE_LOCAL_WEBPACK) {
if (process.env.NEXT_RSPACK) {
// eslint-disable-next-line
Object.assign(exports, getRspackCore())
Object.assign(exports, {
// eslint-disable-next-line import/no-extraneous-dependencies
StringXor: require('./bundle5')().StringXor,
})
} else if (process.env.NEXT_PRIVATE_LOCAL_WEBPACK) {
Object.assign(exports, {
// eslint-disable-next-line import/no-extraneous-dependencies
BasicEvaluatedExpression: require('webpack/lib/javascript/BasicEvaluatedExpression'),
Expand Down Expand Up @@ -33,3 +40,18 @@ exports.init = function () {
Object.assign(exports, require('./bundle5')())
}
}

function getRspackCore() {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
return require('@rspack/core')
} catch (e) {
if (e instanceof Error && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
throw new Error(
'@rspack/core is not available. Please make sure the appropriate Next.js plugin is installed.'
)
}

throw e
}
}

0 comments on commit f47776d

Please sign in to comment.