diff --git a/turbopack/crates/turbopack-ecmascript-runtime/js/src/browser/runtime/base/runtime-base.ts b/turbopack/crates/turbopack-ecmascript-runtime/js/src/browser/runtime/base/runtime-base.ts index 60cc94c4c8b50..77ce91e2f0d38 100644 --- a/turbopack/crates/turbopack-ecmascript-runtime/js/src/browser/runtime/base/runtime-base.ts +++ b/turbopack/crates/turbopack-ecmascript-runtime/js/src/browser/runtime/base/runtime-base.ts @@ -24,13 +24,7 @@ declare var CHUNK_SUFFIX_PATH: string // Support runtime public path from window.publicPath function getRuntimeChunkBasePath(): string { if (CHUNK_BASE_PATH === "__RUNTIME_PUBLIC_PATH__") { - if (typeof globalThis !== 'undefined' && typeof (globalThis as any).publicPath === "string") { - return (globalThis as any).publicPath; - } - console.warn( - "publicPath is set to 'runtime' but window.publicPath is not defined or not a string, falling back to '/'" - ); - return "/"; + return contextPrototype.p(); } return CHUNK_BASE_PATH; } diff --git a/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-types.d.ts b/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-types.d.ts index 02a2d7c12bf7c..5bd08e1667f84 100644 --- a/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-types.d.ts +++ b/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-types.d.ts @@ -87,6 +87,7 @@ type AsyncModule = ( type ResolveAbsolutePath = (modulePath?: string) => string type GetWorkerBlobURL = (chunks: ChunkPath[]) => string +type GetPublicPath = () => string type ExternalRequire = ( id: DependencySpecifier, @@ -138,4 +139,5 @@ interface TurbopackBaseContext { y: ExternalImport z: CommonJsRequire g: typeof globalThis + p: GetPublicPath } diff --git a/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-utils.ts b/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-utils.ts index c1adc83bc9d1e..721fd58c42a85 100644 --- a/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-utils.ts +++ b/turbopack/crates/turbopack-ecmascript-runtime/js/src/shared/runtime-utils.ts @@ -712,6 +712,19 @@ contextPrototype.z = requireStub // Make `globalThis` available to the module in a way that cannot be shadowed by a local variable. contextPrototype.g = globalThis +/** + * Gets the public path for runtime assets. + * Checks globalThis.publicPath and falls back to empty string. + */ +function getPublicPath(): string { + if (typeof globalThis !== 'undefined' && typeof (globalThis as any).publicPath === "string") { + const publicPath = (globalThis as any).publicPath as string; + return publicPath.endsWith('/') ? publicPath : `${publicPath}/`; + } + return ''; +} +contextPrototype.p = getPublicPath + type ContextConstructor = { new (module: Module, exports: Exports): TurbopackBaseContext } diff --git a/turbopack/crates/turbopack-ecmascript/src/runtime_functions.rs b/turbopack/crates/turbopack-ecmascript/src/runtime_functions.rs index ecb1fdf1831e3..60c830c31c520 100644 --- a/turbopack/crates/turbopack-ecmascript/src/runtime_functions.rs +++ b/turbopack/crates/turbopack-ecmascript/src/runtime_functions.rs @@ -92,10 +92,11 @@ pub const TURBOPACK_REQUIRE_REAL: &TurbopackRuntimeFunctionShortcut = make_short pub const TURBOPACK_WASM: &TurbopackRuntimeFunctionShortcut = make_shortcut!("w"); pub const TURBOPACK_WASM_MODULE: &TurbopackRuntimeFunctionShortcut = make_shortcut!("u"); pub const TURBOPACK_GLOBAL: &TurbopackRuntimeFunctionShortcut = make_shortcut!("g"); +pub const TURBOPACK_PUBLIC_PATH: &TurbopackRuntimeFunctionShortcut = make_shortcut!("p"); /// Adding an entry to this list will automatically ensure that `__turbopack_XXX__` can be called /// from user code (by inserting a replacement into free_var_references) -pub const TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS: [(&str, &TurbopackRuntimeFunctionShortcut); 22] = [ +pub const TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS: [(&str, &TurbopackRuntimeFunctionShortcut); 23] = [ ("__turbopack_require__", TURBOPACK_REQUIRE), ("__turbopack_module_context__", TURBOPACK_MODULE_CONTEXT), ("__turbopack_import__", TURBOPACK_IMPORT), @@ -127,4 +128,5 @@ pub const TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS: [(&str, &TurbopackRuntimeFunctio ), ("__turbopack_wasm__", TURBOPACK_WASM), ("__turbopack_wasm_module__", TURBOPACK_WASM_MODULE), + ("__turbopack_public_path__", TURBOPACK_PUBLIC_PATH), ]; diff --git a/turbopack/crates/turbopack-static/src/ecma.rs b/turbopack/crates/turbopack-static/src/ecma.rs index e901981595e67..09ec3b5fd3021 100644 --- a/turbopack/crates/turbopack-static/src/ecma.rs +++ b/turbopack/crates/turbopack-static/src/ecma.rs @@ -15,7 +15,7 @@ use turbopack_ecmascript::{ EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptChunkType, EcmascriptExports, }, - runtime_functions::TURBOPACK_EXPORT_VALUE, + runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_PUBLIC_PATH}, utils::StringifyJs, }; @@ -131,17 +131,28 @@ impl ChunkItem for StaticUrlJsChunkItem { impl EcmascriptChunkItem for StaticUrlJsChunkItem { #[turbo_tasks::function] async fn content(&self) -> Result> { - Ok(EcmascriptChunkItemContent { - inner_code: format!( + let asset_url = self + .chunking_context + .asset_url(self.static_asset.path().owned().await?) + .await?; + + let code = if asset_url.starts_with("__RUNTIME_PUBLIC_PATH__") { + // For runtime publicPath, use the getPublicPath() runtime function + let asset_path = &asset_url["__RUNTIME_PUBLIC_PATH__".len()..]; + format!( + "{TURBOPACK_EXPORT_VALUE}({TURBOPACK_PUBLIC_PATH}() + {path});", + path = StringifyJs(asset_path) + ) + } else { + // For static publicPath, use the full URL as a string literal + format!( "{TURBOPACK_EXPORT_VALUE}({path});", - path = StringifyJs( - &self - .chunking_context - .asset_url(self.static_asset.path().owned().await?) - .await? - ) + path = StringifyJs(&*asset_url) ) - .into(), + }; + + Ok(EcmascriptChunkItemContent { + inner_code: code.into(), ..Default::default() } .into())