Skip to content

Commit c5b2cf1

Browse files
authored
Turbopack: Allow turbopack-node backend to be swapped at runtime using an experimental config option (#90671)
<!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # --> Follow-up to #86266 This was mostly LLM-generated, but it's just moving things around and threading through a config option. We'd like to be able to turn this on/off with an experimental config option: \- Fixes issues where RA thinks a bunch of code is dead. \- Lets us isolate potential issues to the worker thread implementation. \- Provides an escape hatch for users in case there are issues. \- Better fits the cargo "additive feature" model.
1 parent 28df39b commit c5b2cf1

26 files changed

Lines changed: 387 additions & 88 deletions

File tree

crates/next-api/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ autobenches = false
99
[lib]
1010
bench = false
1111

12+
[features]
13+
default = ["process_pool"]
14+
process_pool = ["next-core/process_pool", "turbopack-node/process_pool"]
15+
worker_pool = ["next-core/worker_pool", "turbopack-node/worker_pool"]
16+
1217
[lints]
1318
workspace = true
1419

crates/next-api/src/project.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use next_core::{
1414
next_client::{
1515
ClientChunkingContextOptions, get_client_chunking_context, get_client_compile_time_info,
1616
},
17-
next_config::{ModuleIds as ModuleIdStrategyConfig, NextConfig},
17+
next_config::{
18+
ModuleIds as ModuleIdStrategyConfig, NextConfig, TurbopackPluginRuntimeStrategy,
19+
},
1820
next_edge::context::EdgeChunkingContextOptions,
1921
next_server::{
2022
ServerChunkingContextOptions, ServerContextType, get_server_chunking_context,
@@ -79,7 +81,11 @@ use turbopack_core::{
7981
NotFoundVersion, OptionVersionedContent, Update, Version, VersionState, VersionedContent,
8082
},
8183
};
84+
#[cfg(feature = "process_pool")]
85+
use turbopack_node::child_process_backend;
8286
use turbopack_node::execution_context::ExecutionContext;
87+
#[cfg(feature = "worker_pool")]
88+
use turbopack_node::worker_threads_backend;
8389
use turbopack_nodejs::NodeJsChunkingContext;
8490

8591
use crate::{
@@ -1217,6 +1223,16 @@ impl Project {
12171223
pub(super) async fn execution_context(self: Vc<Self>) -> Result<Vc<ExecutionContext>> {
12181224
let node_root = self.node_root().owned().await?;
12191225
let next_mode = self.next_mode().await?;
1226+
let strategy = *self
1227+
.next_config()
1228+
.turbopack_plugin_runtime_strategy()
1229+
.await?;
1230+
let node_backend = match strategy {
1231+
#[cfg(feature = "worker_pool")]
1232+
TurbopackPluginRuntimeStrategy::WorkerThreads => worker_threads_backend(),
1233+
#[cfg(feature = "process_pool")]
1234+
TurbopackPluginRuntimeStrategy::ChildProcesses => child_process_backend(),
1235+
};
12201236

12211237
let node_execution_chunking_context = Vc::upcast(
12221238
NodeJsChunkingContext::builder(
@@ -1237,6 +1253,7 @@ impl Project {
12371253
self.project_path().owned().await?,
12381254
node_execution_chunking_context,
12391255
self.env(),
1256+
node_backend,
12401257
))
12411258
}
12421259

@@ -1475,10 +1492,12 @@ impl Project {
14751492

14761493
// At this point all modules have been computed and we can get rid of the node.js
14771494
// process pools
1495+
let execution_context = self.execution_context().await?;
1496+
let node_backend = execution_context.node_backend.into_trait_ref().await?;
14781497
if *self.is_watch_enabled().await? {
1479-
turbopack_node::evaluate::scale_down();
1498+
node_backend.scale_down()?;
14801499
} else {
1481-
turbopack_node::evaluate::scale_zero();
1500+
node_backend.scale_zero()?;
14821501
}
14831502

14841503
Ok(module_graphs_vc)

crates/next-core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ turbopack-static = { workspace = true }
8787
turbopack-trace-utils = { workspace = true }
8888

8989
[features]
90+
default = ["process_pool"]
91+
process_pool = ["turbopack-node/process_pool"]
92+
worker_pool = ["turbopack-node/worker_pool"]
9093
next-font-local = []
9194
plugin = [
9295
"swc_core/plugin_transform_host_native",

crates/next-core/src/next_config.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,16 @@ pub enum ModuleIds {
920920
#[turbo_tasks::value(transparent)]
921921
pub struct OptionModuleIds(pub Option<ModuleIds>);
922922

923+
#[turbo_tasks::value(operation)]
924+
#[derive(Copy, Clone, Debug, Deserialize)]
925+
#[serde(rename_all = "camelCase")]
926+
pub enum TurbopackPluginRuntimeStrategy {
927+
#[cfg(feature = "worker_pool")]
928+
WorkerThreads,
929+
#[cfg(feature = "process_pool")]
930+
ChildProcesses,
931+
}
932+
923933
#[derive(
924934
Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
925935
)]
@@ -1150,6 +1160,7 @@ pub struct ExperimentalConfig {
11501160

11511161
turbopack_minify: Option<bool>,
11521162
turbopack_module_ids: Option<ModuleIds>,
1163+
turbopack_plugin_runtime_strategy: Option<TurbopackPluginRuntimeStrategy>,
11531164
turbopack_source_maps: Option<bool>,
11541165
turbopack_input_source_maps: Option<bool>,
11551166
turbopack_tree_shaking: Option<bool>,
@@ -2046,6 +2057,19 @@ impl NextConfig {
20462057
)
20472058
}
20482059

2060+
#[turbo_tasks::function]
2061+
pub fn turbopack_plugin_runtime_strategy(&self) -> Vc<TurbopackPluginRuntimeStrategy> {
2062+
#[cfg(feature = "worker_pool")]
2063+
let default = TurbopackPluginRuntimeStrategy::WorkerThreads;
2064+
#[cfg(all(not(feature = "worker_pool"), feature = "process_pool"))]
2065+
let default = TurbopackPluginRuntimeStrategy::ChildProcesses;
2066+
2067+
self.experimental
2068+
.turbopack_plugin_runtime_strategy
2069+
.unwrap_or(default)
2070+
.cell()
2071+
}
2072+
20492073
#[turbo_tasks::function]
20502074
pub async fn module_ids(&self, mode: Vc<NextMode>) -> Result<Vc<ModuleIds>> {
20512075
Ok(match *mode.await? {

crates/next-core/src/next_font/google/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ async fn get_mock_stylesheet(
741741
env,
742742
project_path: _,
743743
chunking_context,
744+
node_backend,
744745
} = *execution_context.await?;
745746
let asset_context = node_evaluate_asset_context(
746747
execution_context,
@@ -770,7 +771,7 @@ async fn get_mock_stylesheet(
770771
)
771772
.module();
772773

773-
let entries = get_evaluate_entries(mocked_response_asset, asset_context, None);
774+
let entries = get_evaluate_entries(mocked_response_asset, asset_context, *node_backend, None);
774775
let module_graph = ModuleGraph::from_single_graph(SingleModuleGraph::new_with_entries(
775776
entries.graph_entries().to_resolved().await?,
776777
false,
@@ -783,6 +784,7 @@ async fn get_mock_stylesheet(
783784
entries,
784785
root,
785786
*env,
787+
*node_backend,
786788
loader_source,
787789
*chunking_context,
788790
module_graph,

crates/next-napi-bindings/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ turbo-tasks = { workspace = true }
110110
turbo-tasks-backend = { workspace = true }
111111
turbo-tasks-fs = { workspace = true }
112112
turbo-unix-path = { workspace = true }
113-
next-api = { workspace = true }
113+
next-api = { workspace = true, features = ["worker_pool"] }
114114
next-build = { workspace = true }
115115
next-core = { workspace = true }
116116

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ export default defineConfig([
308308
'itCI',
309309
'itHeaded',
310310
'itTurbopackDev',
311+
'itOnlyTurbopack',
311312
],
312313
},
313314
],

packages/next/src/server/config-schema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ export const experimentalSchema = {
324324
webpackBuildWorker: z.boolean().optional(),
325325
webpackMemoryOptimizations: z.boolean().optional(),
326326
turbopackMemoryLimit: z.number().optional(),
327+
turbopackPluginRuntimeStrategy: z
328+
.enum(['workerThreads', 'childProcesses'])
329+
.optional(),
327330
turbopackMinify: z.boolean().optional(),
328331
turbopackFileSystemCacheForDev: z.boolean().optional(),
329332
turbopackFileSystemCacheForBuild: z.boolean().optional(),

packages/next/src/server/config-shared.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,11 @@ export interface ExperimentalConfig {
497497
*/
498498
turbopackMemoryLimit?: number
499499

500+
/**
501+
* Selects the runtime backend used by Turbopack for Node.js evaluation.
502+
*/
503+
turbopackPluginRuntimeStrategy?: 'workerThreads' | 'childProcesses'
504+
500505
/**
501506
* Enable minification. Defaults to true in build mode and false in dev mode.
502507
*/
@@ -1717,6 +1722,7 @@ export const defaultConfig = Object.freeze({
17171722
turbopackFileSystemCacheForDev: true,
17181723
turbopackFileSystemCacheForBuild: false,
17191724
turbopackInferModuleSideEffects: true,
1725+
turbopackPluginRuntimeStrategy: 'workerThreads',
17201726
devCacheControlNoCache: false,
17211727
},
17221728
htmlLimitedBots: undefined,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pid

0 commit comments

Comments
 (0)