Skip to content

Commit

Permalink
Next.js plugin for alternate bundler (#76186)
Browse files Browse the repository at this point in the history
This adds a new package allowing users to opt-into using Rspack in place of webpack in Next.js. While there is some logic to handle rspack in core, the actual third-party dependencies are not included there, only conditionally loaded. This package:
    
- Includes the actual npm dependencies on `@rspack/core` and `@rspack/plugin-react-refresh`
- Sets the `NEXT_RSPACK` environment variable, which Next.js itself uses to load the dependencies
- It exists as a kind of no-op `withRspack` decorator. @timneutkens is this the best approach to this?
  • Loading branch information
wbinnssmith authored Feb 20, 2025
1 parent 2074881 commit e9dcee6
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"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-rspack": "cross-env NEXT_TEST_MODE=dev NEXT_TEST_USE_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",
Expand Down
44 changes: 44 additions & 0 deletions packages/next-plugin-rspack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# @next/plugin-rspack

This plugin allows you to use [Rspack](https://rspack.dev) in place of webpack with Next.js.

## Installation

```
npm install @next/plugin-rspack
```

or

```
yarn add @next/plugin-rspack
```

## Usage

Create or update a `next.config.js`/`next.config.ts` and wrap your existing configuration:

```js
const withRspack = require('@next/plugin-rspack')

/** @type {import('next').NextConfig} */
const nextConfig = {
/* config options here */
}

module.exports = withRspack(nextConfig)
```

## Usage with next-compose-plugins

Alternatively, you can use `next-compose-plugins` to quickly integrate `@next/plugin-rspack` with other Next.js plugins:

```js
const withPlugins = require('next-compose-plugins')
const withRspack = require('@next/plugin-rspack')

module.exports = withPlugins([
[withRspack],
// your other plugins here
])
```
7 changes: 7 additions & 0 deletions packages/next-plugin-rspack/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function withRspack(config) {
process.env.NEXT_RSPACK = 'true'
process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN = 'true'
process.env.BUILTIN_APP_LOADER = 'true'
process.env.BUILTIN_SWC_LOADER = 'true'
return config
}
12 changes: 12 additions & 0 deletions packages/next-plugin-rspack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "@next/plugin-rspack",
"version": "15.2.0-canary.65",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-rspack"
},
"dependencies": {
"@rspack/core": "npm:@rspack-canary/core@1.2.0-canary-37cc738d-20250207113050",
"@rspack/plugin-react-refresh": "1.0.1"
}
}
4 changes: 3 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -644,5 +644,7 @@
"643": "Invalid \"devIndicator.position\" provided, expected one of %s, received %s",
"644": "@rspack/core is not available. Please make sure the appropriate Next.js plugin is installed.",
"645": "@rspack/plugin-react-refresh is not available. Please make sure the appropriate Next.js plugin is installed.",
"646": "No span found for compilation"
"646": "No span found for compilation",
"647": "@rspack/core is not available. Please make sure `@next/plugin-rspack` is correctly installed.",
"648": "@rspack/plugin-react-refresh is not available. Please make sure `@next/plugin-rspack` is correctly installed."
}
4 changes: 2 additions & 2 deletions packages/next/src/shared/lib/get-rspack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function getRspackCore() {
} 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.'
'@rspack/core is not available. Please make sure `@next/plugin-rspack` is correctly installed.'
)
}

Expand All @@ -20,7 +20,7 @@ export function getRspackReactRefresh() {
} catch (e) {
if (e instanceof Error && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
throw new Error(
'@rspack/plugin-react-refresh is not available. Please make sure the appropriate Next.js plugin is installed.'
'@rspack/plugin-react-refresh is not available. Please make sure `@next/plugin-rspack` is correctly installed.'
)
}

Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions test/lib/create-next-install.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { linkPackages } =
require('../../.github/actions/next-stats-action/src/prepare/repo-setup')()

const PREFER_OFFLINE = process.env.NEXT_TEST_PREFER_OFFLINE === '1'
const useRspack = process.env.NEXT_TEST_USE_RSPACK === '1'

async function installDependencies(cwd, tmpDir) {
const args = [
Expand Down Expand Up @@ -122,6 +123,7 @@ async function createNextInstall({
})
)
}

const combinedDependencies = {
next: pkgPaths.get('next'),
...Object.keys(dependencies).reduce((prev, pkg) => {
Expand All @@ -131,6 +133,12 @@ async function createNextInstall({
}, {}),
}

if (useRspack) {
combinedDependencies['@next/plugin-rspack'] = pkgPaths = pkgPaths.get(
'@next/plugin-rspack'
)
}

const scripts = {
debug: `NEXT_PRIVATE_SKIP_CANARY_CHECK=1 NEXT_TELEMETRY_DISABLED=1 NEXT_TEST_NATIVE_DIR=${process.env.NEXT_TEST_NATIVE_DIR} node --inspect --trace-deprecation --enable-source-maps node_modules/next/dist/bin/next`,
...packageJson.scripts,
Expand Down Expand Up @@ -183,6 +191,15 @@ async function createNextInstall({
.traceAsyncFn(() => installDependencies(installDir, tmpDir))
}

if (useRspack) {
// This is what the @next/plugin-rspack plugin does.
// TODO: Load the plugin properly during test
process.env.NEXT_RSPACK = 'true'
process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN = 'true'
process.env.BUILTIN_APP_LOADER = 'true'
process.env.BUILTIN_SWC_LOADER = 'true'
}

return {
installDir,
pkgPaths,
Expand Down

0 comments on commit e9dcee6

Please sign in to comment.