Skip to content

Commit c0d94dc

Browse files
authored
feat: next-rspack-binding (#82811)
Added a new `rspack` directory as an independent Rust and pnpm workspace, dedicated to building Next.js Rspack bindings. It shares code with Turbopack through the `next-taskless` crate.
1 parent a0e5996 commit c0d94dc

30 files changed

+12243
-87
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Release next-rspack bindings
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
dry-run:
7+
description: 'Run in dry-run mode (no actual publishing)'
8+
required: false
9+
default: false
10+
type: boolean
11+
npm-tag:
12+
description: 'NPM tag for publishing'
13+
required: false
14+
default: 'latest'
15+
type: choice
16+
options:
17+
- latest
18+
- alpha
19+
- beta
20+
- canary
21+
22+
env:
23+
DEBUG: napi:*
24+
25+
jobs:
26+
build:
27+
name: Build
28+
uses: rspack-contrib/rspack-toolchain/.github/workflows/build.yml@f69dc04fcae6b38d97b87acef448ed7a285b01cc
29+
with:
30+
package-json-path: rspack/crates/binding/package.json
31+
napi-build-command: pnpm build --release
32+
working-directory: rspack
33+
34+
release:
35+
runs-on: ubuntu-latest
36+
environment: npm
37+
name: Release
38+
permissions:
39+
contents: write
40+
id-token: write
41+
needs: [build]
42+
43+
steps:
44+
- name: Checkout code
45+
uses: actions/checkout@v4
46+
47+
- name: Display release mode
48+
run: |
49+
echo "🚀 Release Configuration:"
50+
echo " - Dry-run mode: ${{ inputs.dry-run }}"
51+
echo " - NPM tag: ${{ inputs.npm-tag || 'latest' }}"
52+
if [ "${{ inputs.dry-run }}" == "true" ]; then
53+
echo " - ⚠️ This is a DRY RUN - no packages will be published"
54+
else
55+
echo " - 📦 This will PUBLISH packages to npm"
56+
fi
57+
58+
- name: Setup Node.js
59+
uses: actions/setup-node@v4
60+
with:
61+
node-version: '22'
62+
63+
- name: Enable corepack
64+
run: corepack enable
65+
66+
- name: Setup pnpm
67+
run: corepack prepare
68+
69+
- name: Cache pnpm dependencies
70+
uses: actions/cache@v3
71+
with:
72+
path: ~/.pnpm-store
73+
key: ${{ runner.os }}-${{ runner.arch }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
74+
restore-keys: |
75+
${{ runner.os }}-${{ runner.arch }}-pnpm-
76+
77+
- name: Install dependencies
78+
run: pnpm install
79+
working-directory: ./rspack
80+
81+
- name: Get NAPI info
82+
id: napi-info
83+
uses: rspack-contrib/rspack-toolchain/get-napi-info@f69dc04fcae6b38d97b87acef448ed7a285b01cc
84+
with:
85+
package-json-path: rspack/crates/binding/package.json
86+
87+
- name: Download rspack binding
88+
uses: rspack-contrib/rspack-toolchain/download-rspack-binding@f69dc04fcae6b38d97b87acef448ed7a285b01cc
89+
with:
90+
path: ${{ steps.napi-info.outputs.binding-directory }}/artifacts
91+
92+
- name: List artifacts
93+
run: ls -R artifacts
94+
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
95+
96+
- name: Create npm dirs
97+
run: pnpm napi create-npm-dirs
98+
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
99+
100+
- name: Move artifacts
101+
run: pnpm napi artifacts
102+
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
103+
104+
- name: List npm dirs
105+
run: ls -R npm
106+
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
107+
108+
- name: Create npm token
109+
run: |
110+
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
111+
env:
112+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
113+
114+
- name: Release npm binding packages
115+
run: |
116+
npm config set access public
117+
npm config set provenance true
118+
pnpm napi pre-publish --no-gh-release -t npm ${{ inputs.dry-run && '--dry-run' || '' }}
119+
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
120+
121+
- name: Release npm packages
122+
run: |
123+
pnpm publish -r --tag ${{ inputs.npm-tag }} --no-git-checks --provenance --access public ${{ inputs.dry-run && '--dry-run' || '' }}
124+
working-directory: ./rspack

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ members = [
1616
"turbopack/xtask",
1717
]
1818

19-
exclude = ["crates/next-error-code-swc-plugin"]
19+
exclude = [
20+
"crates/next-error-code-swc-plugin",
21+
"rspack/crates/binding"
22+
]
2023

2124
[workspace.lints.clippy]
2225
too_many_arguments = "allow"

crates/next-core/src/next_server/resolve.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use std::sync::LazyLock;
2-
31
use anyhow::Result;
4-
use regex::Regex;
2+
use next_taskless::NEVER_EXTERNAL_RE;
53
use serde::{Deserialize, Serialize};
64
use turbo_rcstr::{RcStr, rcstr};
75
use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
@@ -101,11 +99,6 @@ impl AfterResolvePlugin for ExternalCjsModulesResolvePlugin {
10199
return Ok(ResolveResultOption::none());
102100
};
103101

104-
// from https://github.com/vercel/next.js/blob/8d1c619ad650f5d147207f267441caf12acd91d1/packages/next/src/build/handle-externals.ts#L188
105-
static NEVER_EXTERNAL_RE: LazyLock<Regex> = LazyLock::new(|| {
106-
Regex::new("^(?:private-next-pages\\/|next\\/(?:dist\\/pages\\/|(?:app|cache|document|link|form|head|image|legacy\\/image|constants|dynamic|script|navigation|headers|router|compat\\/router|server)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-server-reference|private-next-rsc-cache-wrapper$)").unwrap()
107-
});
108-
109102
let (Pattern::Constant(package), Pattern::Constant(package_subpath)) =
110103
(package, package_subpath)
111104
else {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
pub const NODE_EXTERNALS: [&str; 64] = [
2+
"assert",
3+
"assert/strict",
4+
"async_hooks",
5+
"buffer",
6+
"child_process",
7+
"cluster",
8+
"console",
9+
"constants",
10+
"crypto",
11+
"dgram",
12+
"diagnostics_channel",
13+
"dns",
14+
"dns/promises",
15+
"domain",
16+
"events",
17+
"fs",
18+
"fs/promises",
19+
"http",
20+
"http2",
21+
"https",
22+
"inspector",
23+
"module",
24+
"net",
25+
"os",
26+
"path",
27+
"path/posix",
28+
"path/win32",
29+
"perf_hooks",
30+
"process",
31+
"punycode",
32+
"querystring",
33+
"readline",
34+
"repl",
35+
"stream",
36+
"stream/promises",
37+
"stream/web",
38+
"string_decoder",
39+
"sys",
40+
"timers",
41+
"timers/promises",
42+
"tls",
43+
"trace_events",
44+
"tty",
45+
"url",
46+
"util",
47+
"util/types",
48+
"v8",
49+
"vm",
50+
"wasi",
51+
"worker_threads",
52+
"zlib",
53+
"pnpapi",
54+
"_http_agent",
55+
"_http_client",
56+
"_http_common",
57+
"_http_incoming",
58+
"_http_outgoing",
59+
"_http_server",
60+
"_stream_duplex",
61+
"_stream_passthrough",
62+
"_stream_readable",
63+
"_stream_transform",
64+
"_stream_wrap",
65+
"_stream_writable",
66+
];
67+
68+
pub const EDGE_NODE_EXTERNALS: [&str; 5] = ["buffer", "events", "assert", "util", "async_hooks"];
69+
70+
pub const BUN_EXTERNALS: [&str; 6] = [
71+
"bun:ffi",
72+
"bun:jsc",
73+
"bun:sqlite",
74+
"bun:test",
75+
"bun:wrap",
76+
"bun",
77+
];

crates/next-taskless/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
#![doc = include_str!("../README.md")]
22

3+
mod constants;
4+
mod patterns;
5+
36
use std::sync::LazyLock;
47

58
use anyhow::{Context, Result, bail};
9+
pub use constants::*;
10+
pub use patterns::*;
611
use regex::Regex;
712
use turbo_unix_path::{get_parent_path, get_relative_path_to, join_path, normalize_path};
813

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use std::sync::LazyLock;
2+
3+
use regex::Regex;
4+
5+
// from https://github.com/vercel/next.js/blob/8d1c619ad650f5d147207f267441caf12acd91d1/packages/next/src/build/handle-externals.ts#L188
6+
pub static NEVER_EXTERNAL_RE: LazyLock<Regex> = LazyLock::new(|| {
7+
Regex::new("^(?:private-next-pages\\/|next\\/(?:dist\\/pages\\/|(?:app|cache|document|link|form|head|image|legacy\\/image|constants|dynamic|script|navigation|headers|router|compat\\/router|server)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-server-reference|private-next-rsc-cache-wrapper|private-next-rsc-track-dynamic-import$)").unwrap()
8+
});

rspack/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.node

rspack/.rustfmt.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
max_width = 100
2+
3+
comment_width = 100
4+
wrap_comments = true
5+
6+
tab_spaces = 4
7+
hard_tabs = false
8+
9+
format_strings = true
10+
use_field_init_shorthand = true
11+
12+
imports_granularity = "Crate"
13+
group_imports = "StdExternalCrate"

0 commit comments

Comments
 (0)