Skip to content

Commit 6477a94

Browse files
committed
feat(pack): add target: node support for library builds
- Make library endpoint platform-aware: dispatch compile-time info, module options, and resolve options based on Platform (Web/Node) - Create Node.js-compatible runtime backend (runtime-backend-node.ts) without DOM APIs (no document.createElement, importScripts, etc.) - Conditionally select runtime backend in runtime_code.rs based on is_node_platform flag on LibraryChunkingContext - Always use EdgeWorker environment for library chunking context (ChunkLoading::Edge prevents async chunk splitting for single-file output) - Move and organize target_node snapshot tests under target_node/ - Add library_build_node_target test with dynamic import() to verify single-chunk enforcement and correct Node.js built-in externals
1 parent b0ecb21 commit 6477a94

27 files changed

Lines changed: 380 additions & 150 deletions

File tree

crates/pack-api/src/app.rs

Lines changed: 69 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pack_core::client::context::{
33
get_client_module_options_context, get_client_resolve_options_context,
44
get_client_runtime_entries,
55
};
6-
use pack_core::config::Platform;
6+
77
use pack_core::server::contexts::{
88
get_server_module_options_context, get_server_resolve_options_context,
99
};
@@ -265,20 +265,16 @@ impl AppEntrypoint {
265265
asset_context: Vc<Box<dyn AssetContext>>,
266266
runtime_entries: Vc<EvaluatableAssets>,
267267
) -> Result<Vc<OutputAssets>> {
268-
let platform = &*self.project().platform().await?;
269-
let chunk_group_assets = match platform {
270-
Platform::Web => {
271-
*self
272-
.client_chunk_group(asset_context, runtime_entries)
273-
.await?
274-
.assets
275-
}
276-
Platform::Node => {
277-
*self
278-
.server_chunk_group(asset_context, runtime_entries)
279-
.await?
280-
.assets
281-
}
268+
let chunk_group_assets = if self.project().platform().await?.is_node() {
269+
*self
270+
.server_chunk_group(asset_context, runtime_entries)
271+
.await?
272+
.assets
273+
} else {
274+
*self
275+
.client_chunk_group(asset_context, runtime_entries)
276+
.await?
277+
.assets
282278
};
283279
Ok(chunk_group_assets)
284280
}
@@ -299,49 +295,32 @@ impl AppEndpoint {
299295

300296
#[turbo_tasks::function]
301297
pub async fn app_runtime_entries(self: Vc<Self>) -> Result<Vc<EvaluatableAssets>> {
302-
match &*self.project().platform().await? {
303-
Platform::Web => {
304-
let watch = self.project().await?.watch.enable;
305-
Ok(get_client_runtime_entries(
306-
self.project().project_path().owned().await?,
307-
self.project().mode(),
308-
self.project().config(),
309-
self.project().execution_context(),
310-
self.project().pack_path().owned().await?,
311-
Vc::cell(watch),
312-
Vc::cell(
313-
watch
314-
&& self
315-
.project()
316-
.config()
317-
.dev_server()
318-
.await?
319-
.hot
320-
.unwrap_or_default(),
321-
),
322-
)
323-
.resolve_entries(Vc::upcast(self.app_module_context())))
324-
}
325-
Platform::Node => Ok(EvaluatableAssets::empty()),
298+
let project = self.project();
299+
if project.platform().await?.is_node() {
300+
return Ok(EvaluatableAssets::empty());
326301
}
302+
let watch = project.await?.watch.enable;
303+
Ok(get_client_runtime_entries(
304+
project.project_path().owned().await?,
305+
project.mode(),
306+
project.config(),
307+
project.execution_context(),
308+
project.pack_path().owned().await?,
309+
Vc::cell(watch),
310+
Vc::cell(watch && project.config().dev_server().await?.hot.unwrap_or_default()),
311+
)
312+
.resolve_entries(Vc::upcast(self.app_module_context())))
327313
}
328314

329315
#[turbo_tasks::function]
330316
pub async fn app_module_context(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
331-
let platform = &*self.project().platform().await?;
332-
333-
let compile_time_info = match platform {
334-
Platform::Web => self.project().client_compile_time_info(),
335-
Platform::Node => self.project().server_compile_time_info(),
336-
};
317+
let project = self.project();
318+
let platform = &*project.platform().await?;
337319

338-
let layer = match platform {
339-
Platform::Web => {
340-
Layer::new_with_user_friendly_name(rcstr!("client"), rcstr!("Browser"))
341-
}
342-
Platform::Node => {
343-
Layer::new_with_user_friendly_name(rcstr!("server"), rcstr!("Nodejs"))
344-
}
320+
let layer = if platform.is_node() {
321+
Layer::new_with_user_friendly_name(rcstr!("server"), rcstr!("Nodejs"))
322+
} else {
323+
Layer::new_with_user_friendly_name(rcstr!("client"), rcstr!("Browser"))
345324
};
346325

347326
Ok(ModuleAssetContext::new(
@@ -350,7 +329,7 @@ impl AppEndpoint {
350329
..Default::default()
351330
}
352331
.cell(),
353-
compile_time_info,
332+
project.compile_time_info_for_platform(),
354333
self.app_module_options_context(),
355334
self.app_resolve_options_context(),
356335
layer,
@@ -359,43 +338,48 @@ impl AppEndpoint {
359338

360339
#[turbo_tasks::function]
361340
async fn app_module_options_context(self: Vc<Self>) -> Result<Vc<ModuleOptionsContext>> {
362-
match &*self.project().platform().await? {
363-
Platform::Web => Ok(get_client_module_options_context(
364-
self.project().project_path().owned().await?,
365-
self.project().execution_context(),
366-
self.project().client_compile_time_info().environment(),
367-
self.project().mode(),
368-
self.project().config(),
369-
Vc::cell(self.project().await?.watch.enable),
370-
self.project().pack_path().owned().await?,
371-
)),
372-
Platform::Node => Ok(get_server_module_options_context(
373-
self.project().project_path().owned().await?,
374-
self.project().execution_context(),
375-
self.project().server_compile_time_info().environment(),
376-
self.project().mode(),
377-
self.project().config(),
378-
)),
341+
let project = self.project();
342+
let platform = &*project.platform().await?;
343+
if platform.is_node() {
344+
Ok(get_server_module_options_context(
345+
project.project_path().owned().await?,
346+
project.execution_context(),
347+
project.server_compile_time_info().environment(),
348+
project.mode(),
349+
project.config(),
350+
))
351+
} else {
352+
Ok(get_client_module_options_context(
353+
project.project_path().owned().await?,
354+
project.execution_context(),
355+
project.client_compile_time_info().environment(),
356+
project.mode(),
357+
project.config(),
358+
Vc::cell(project.await?.watch.enable),
359+
project.pack_path().owned().await?,
360+
))
379361
}
380362
}
381363

382364
#[turbo_tasks::function]
383365
async fn app_resolve_options_context(self: Vc<Self>) -> Result<Vc<ResolveOptionsContext>> {
384-
match &*self.project().platform().await? {
385-
Platform::Web => Ok(get_client_resolve_options_context(
386-
self.project().project_path().owned().await?,
387-
self.project().mode(),
388-
self.project().config(),
389-
self.project().execution_context(),
390-
self.project().pack_path().owned().await?,
391-
)),
392-
Platform::Node => Ok(get_server_resolve_options_context(
393-
self.project().project_path().owned().await?,
394-
self.project().mode(),
395-
self.project().config(),
396-
self.project().execution_context(),
397-
self.project().pack_path().owned().await?,
398-
)),
366+
let project = self.project();
367+
if project.platform().await?.is_node() {
368+
Ok(get_server_resolve_options_context(
369+
project.project_path().owned().await?,
370+
project.mode(),
371+
project.config(),
372+
project.execution_context(),
373+
project.pack_path().owned().await?,
374+
))
375+
} else {
376+
Ok(get_client_resolve_options_context(
377+
project.project_path().owned().await?,
378+
project.mode(),
379+
project.config(),
380+
project.execution_context(),
381+
project.pack_path().owned().await?,
382+
))
399383
}
400384
}
401385
}

crates/pack-api/src/library.rs

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use anyhow::{Result, bail};
2-
use pack_core::{
3-
client::context::{
4-
get_client_module_options_context, get_client_resolve_options_context,
5-
get_client_runtime_entries,
6-
},
7-
library::contexts::{LibraryChunkingContextOptions, get_library_chunking_context},
8-
util::convert_to_project_relative,
2+
use pack_core::client::context::{
3+
get_client_module_options_context, get_client_resolve_options_context,
4+
get_client_runtime_entries,
5+
};
6+
7+
use pack_core::library::contexts::{LibraryChunkingContextOptions, get_library_chunking_context};
8+
use pack_core::server::contexts::{
9+
get_server_module_options_context, get_server_resolve_options_context,
910
};
11+
use pack_core::util::convert_to_project_relative;
1012
use tracing::{Instrument, trace_span};
1113
use turbo_rcstr::{RcStr, rcstr};
1214
use turbo_tasks::{Completion, JoinIterExt, ResolvedVc, ValueToString, Vc};
@@ -125,51 +127,87 @@ impl LibraryEndpoint {
125127

126128
#[turbo_tasks::function]
127129
async fn library_module_context(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
130+
let project = self.project();
131+
let platform = &*project.platform().await?;
132+
133+
let layer = if platform.is_node() {
134+
Layer::new_with_user_friendly_name(rcstr!("library-server"), rcstr!("Library (Node)"))
135+
} else {
136+
Layer::new_with_user_friendly_name(rcstr!("library-client"), rcstr!("Library (Web)"))
137+
};
138+
128139
Ok(ModuleAssetContext::new(
129140
// FIXME:
130141
TransitionOptions {
131142
..Default::default()
132143
}
133144
.cell(),
134-
self.project().client_compile_time_info(),
145+
project.compile_time_info_for_platform(),
135146
self.library_module_options_context(),
136147
self.library_resolve_options_context(),
137-
Layer::new_with_user_friendly_name(rcstr!("library"), rcstr!("Umd")),
148+
layer,
138149
))
139150
}
140151

141152
#[turbo_tasks::function]
142153
async fn library_module_options_context(self: Vc<Self>) -> Result<Vc<ModuleOptionsContext>> {
143-
Ok(get_client_module_options_context(
144-
self.project().project_path().owned().await?,
145-
self.project().execution_context(),
146-
self.project().client_compile_time_info().environment(),
147-
self.project().mode(),
148-
self.project().config(),
149-
Vc::cell(false),
150-
self.project().pack_path().owned().await?,
151-
))
154+
let project = self.project();
155+
let platform = &*project.platform().await?;
156+
if platform.is_node() {
157+
Ok(get_server_module_options_context(
158+
project.project_path().owned().await?,
159+
project.execution_context(),
160+
project.server_compile_time_info().environment(),
161+
project.mode(),
162+
project.config(),
163+
))
164+
} else {
165+
Ok(get_client_module_options_context(
166+
project.project_path().owned().await?,
167+
project.execution_context(),
168+
project.client_compile_time_info().environment(),
169+
project.mode(),
170+
project.config(),
171+
Vc::cell(false),
172+
project.pack_path().owned().await?,
173+
))
174+
}
152175
}
153176

154177
#[turbo_tasks::function]
155178
async fn library_resolve_options_context(self: Vc<Self>) -> Result<Vc<ResolveOptionsContext>> {
156-
Ok(get_client_resolve_options_context(
157-
self.project().project_path().owned().await?,
158-
self.project().mode(),
159-
self.project().config(),
160-
self.project().execution_context(),
161-
self.project().pack_path().owned().await?,
162-
))
179+
let project = self.project();
180+
if project.platform().await?.is_node() {
181+
Ok(get_server_resolve_options_context(
182+
project.project_path().owned().await?,
183+
project.mode(),
184+
project.config(),
185+
project.execution_context(),
186+
project.pack_path().owned().await?,
187+
))
188+
} else {
189+
Ok(get_client_resolve_options_context(
190+
project.project_path().owned().await?,
191+
project.mode(),
192+
project.config(),
193+
project.execution_context(),
194+
project.pack_path().owned().await?,
195+
))
196+
}
163197
}
164198

165199
#[turbo_tasks::function]
166200
async fn library_runtime_entries(self: Vc<Self>) -> Result<Vc<EvaluatableAssets>> {
201+
let project = self.project();
202+
if project.platform().await?.is_node() {
203+
return Ok(EvaluatableAssets::empty());
204+
}
167205
Ok(get_client_runtime_entries(
168-
self.project().project_path().owned().await?,
169-
self.project().mode(),
170-
self.project().config(),
171-
self.project().execution_context(),
172-
self.project().pack_path().owned().await?,
206+
project.project_path().owned().await?,
207+
project.mode(),
208+
project.config(),
209+
project.execution_context(),
210+
project.pack_path().owned().await?,
173211
// Library project not support watch mode
174212
Vc::cell(false),
175213
Vc::cell(false),
@@ -257,6 +295,7 @@ impl LibraryEndpoint {
257295
config: project.config(),
258296
export_usage: project.export_usage(),
259297
unused_references: project.unused_references(),
298+
platform: project.platform(),
260299
},
261300
))
262301
}

crates/pack-api/src/project.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,27 @@ impl Project {
10361036
))
10371037
}
10381038

1039+
/// Returns the appropriate compile-time info for the given platform.
1040+
#[turbo_tasks::function]
1041+
pub(super) async fn compile_time_info_for_platform(&self) -> Result<Vc<CompileTimeInfo>> {
1042+
let define_env = (*self.config.define_env().await?).clone();
1043+
let target = (*self.config.target().await?).clone();
1044+
let define_env_vc = Vc::cell(define_env);
1045+
match &*self.config.platform().await? {
1046+
Platform::Web => Ok(get_client_compile_time_info(
1047+
target,
1048+
define_env_vc,
1049+
self.config.mode(),
1050+
self.config.provider_config(),
1051+
)),
1052+
Platform::Node => Ok(get_server_compile_time_info(
1053+
target,
1054+
define_env_vc,
1055+
self.config.provider_config(),
1056+
)),
1057+
}
1058+
}
1059+
10391060
#[turbo_tasks::function]
10401061
pub async fn client_chunking_context(self: Vc<Self>) -> Result<Vc<Box<dyn ChunkingContext>>> {
10411062
let mode = self.mode();

0 commit comments

Comments
 (0)