Skip to content

Commit eb6d903

Browse files
authored
feat(pack): support output.crossOriginLoading config (#2740)
1 parent 56f6868 commit eb6d903

25 files changed

Lines changed: 1365 additions & 24 deletions

File tree

crates/pack-core/src/client/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,10 @@ pub async fn get_client_chunking_context(
540540
builder = builder.chunk_loading_global(chunk_loading_global.clone());
541541
}
542542

543+
if let Some(cross_origin_loading) = &*config.client_cross_origin_loading().await? {
544+
builder = builder.cross_origin_loading(cross_origin_loading.clone());
545+
}
546+
543547
// Read entry_root_export from config
544548
if let Some(entry_root_export) = &*config.entry_root_export().await? {
545549
builder = builder.entry_root_export(Some(entry_root_export.clone()));

crates/pack-core/src/config.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ pub struct OutputConfig {
373373
/// Note: This path will not appear in chunk paths or chunk data on disk,
374374
/// it only affects the URLs used by the browser to fetch resources.
375375
pub public_path: Option<RcStr>,
376+
/// Controls the `crossorigin` attribute for dynamically loaded JS chunks.
377+
/// Webpack-compatible values: false, "anonymous", "use-credentials".
378+
pub cross_origin_loading: Option<CrossOriginLoading>,
376379
/// The global variable name used by the runtime for loading chunks.
377380
/// This is similar to webpack's `output.chunkLoadingGlobal`.
378381
/// Default: "TURBOPACK"
@@ -392,6 +395,22 @@ pub enum OutputType {
392395
Export,
393396
}
394397

398+
#[turbo_tasks::value]
399+
#[derive(Clone, Debug, Deserialize, OperationValue)]
400+
#[serde(rename_all = "kebab-case")]
401+
pub enum CrossOriginLoadingMode {
402+
Anonymous,
403+
UseCredentials,
404+
}
405+
406+
#[turbo_tasks::value]
407+
#[derive(Clone, Debug, Deserialize, OperationValue)]
408+
#[serde(untagged)]
409+
pub enum CrossOriginLoading {
410+
Boolean(bool),
411+
Mode(CrossOriginLoadingMode),
412+
}
413+
395414
#[derive(
396415
Clone,
397416
PartialEq,
@@ -1021,6 +1040,29 @@ impl Config {
10211040
Ok(Vc::cell(None))
10221041
}
10231042

1043+
#[turbo_tasks::function]
1044+
pub async fn client_cross_origin_loading(&self) -> Result<Vc<Option<RcStr>>> {
1045+
let cross_origin_loading = self
1046+
.output
1047+
.as_ref()
1048+
.and_then(|o| o.cross_origin_loading.as_ref());
1049+
1050+
let value = match cross_origin_loading {
1051+
Some(CrossOriginLoading::Mode(CrossOriginLoadingMode::Anonymous)) => {
1052+
Some(rcstr!("anonymous"))
1053+
}
1054+
Some(CrossOriginLoading::Mode(CrossOriginLoadingMode::UseCredentials)) => {
1055+
Some(rcstr!("use-credentials"))
1056+
}
1057+
// Webpack-compatible: false disables crossorigin attribute.
1058+
Some(CrossOriginLoading::Boolean(false)) | None => None,
1059+
// Treat true as anonymous for compatibility with legacy configs.
1060+
Some(CrossOriginLoading::Boolean(true)) => Some(rcstr!("anonymous")),
1061+
};
1062+
1063+
Ok(Vc::cell(value))
1064+
}
1065+
10241066
#[turbo_tasks::function]
10251067
pub async fn entry_root_export(&self) -> Result<Vc<Option<RcStr>>> {
10261068
// Check if entry_root_export is configured

crates/pack-schema/src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,14 @@ pub struct SchemaOutputConfig {
315315
)]
316316
pub public_path: Option<String>,
317317

318+
/// Controls the `crossorigin` attribute for dynamically loaded chunks.
319+
/// Webpack-compatible values: false, "anonymous", "use-credentials".
320+
#[serde(skip_serializing_if = "Option::is_none")]
321+
#[schemars(
322+
description = "Controls crossorigin for dynamically loaded chunks. Supports false, 'anonymous', or 'use-credentials'."
323+
)]
324+
pub cross_origin_loading: Option<SchemaCrossOriginLoading>,
325+
318326
/// The global variable name used by the runtime for loading chunks.
319327
/// This is similar to webpack's `output.chunkLoadingGlobal`.
320328
/// Default: "TURBOPACK"
@@ -332,6 +340,23 @@ pub struct SchemaOutputConfig {
332340
pub entry_root_export: Option<String>,
333341
}
334342

343+
/// Cross-origin loading mode for dynamic chunks.
344+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
345+
#[serde(rename_all = "kebab-case")]
346+
pub enum SchemaCrossOriginLoadingMode {
347+
Anonymous,
348+
UseCredentials,
349+
}
350+
351+
/// Webpack-compatible `output.crossOriginLoading`.
352+
/// Supports `false`, `"anonymous"`, and `"use-credentials"`.
353+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
354+
#[serde(untagged)]
355+
pub enum SchemaCrossOriginLoading {
356+
Boolean(bool),
357+
Mode(SchemaCrossOriginLoadingMode),
358+
}
359+
335360
/// Output type
336361
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
337362
#[serde(rename_all = "kebab-case")]
@@ -954,6 +979,7 @@ mod tests {
954979
assert!(schema_str.contains("provider"));
955980
assert!(schema_str.contains("postcss"));
956981
assert!(schema_str.contains("publicPath"));
982+
assert!(schema_str.contains("crossOriginLoading"));
957983
assert!(schema_str.contains("cssFilename"));
958984
assert!(schema_str.contains("assetModuleFilename"));
959985
}
@@ -1022,6 +1048,7 @@ mod tests {
10221048
"chunkFilename": "[name].[contenthash:8].js",
10231049
"cssFilename": "[name].[contenthash:6].css",
10241050
"publicPath": "/assets/",
1051+
"crossOriginLoading": "anonymous",
10251052
"clean": true
10261053
},
10271054
"optimization": {
@@ -1057,6 +1084,12 @@ mod tests {
10571084
Some("[name].[contenthash:6].css".to_string())
10581085
);
10591086
assert_eq!(output.public_path, Some("/assets/".to_string()));
1087+
assert!(matches!(
1088+
output.cross_origin_loading,
1089+
Some(SchemaCrossOriginLoading::Mode(
1090+
SchemaCrossOriginLoadingMode::Anonymous
1091+
))
1092+
));
10601093

10611094
// Test concatenateModules configuration
10621095
let optimization = config.optimization.as_ref().unwrap();

crates/pack-tests/tests/snapshot/public_path/basic/output/main.js

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

crates/pack-tests/tests/snapshot/public_path/basic/output/main.js.map

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

crates/pack-tests/tests/snapshot/public_path/runtime/output/main.js

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

crates/pack-tests/tests/snapshot/public_path/runtime/output/main.js.map

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

crates/pack-tests/tests/snapshot/runtime/app_build_runtime/output/main.js

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

crates/pack-tests/tests/snapshot/runtime/app_build_runtime/output/main.js.map

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

crates/pack-tests/tests/snapshot/runtime/app_dev_runtime/output/main.js

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

0 commit comments

Comments
 (0)