Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions crates/pack-core/src/client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use turbo_tasks::{
FxIndexMap, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString, Vc, trace::TraceRawVcs,
};
use turbo_tasks_env::EnvMap;
use turbo_tasks_fs::File;
use turbo_tasks_fs::FileSystemPath;
use turbopack::{
css::chunk::CssChunkType,
Expand All @@ -18,6 +19,7 @@ use turbopack::{
};
use turbopack_browser::{BrowserChunkingContext, CurrentChunkMethod};
use turbopack_core::{
asset::AssetContent,
chunk::{
ChunkingConfig, ChunkingContext, MangleType, MinifyType, SourceMapSourceType,
SourceMapsType, module_id_strategies::ModuleIdStrategy,
Expand All @@ -30,6 +32,7 @@ use turbopack_core::{
file_source::FileSource,
free_var_references,
module_graph::export_usage::OptionExportUsageInfo,
virtual_source::VirtualSource,
};
use turbopack_ecmascript::{TypeofWindow, chunk::EcmascriptChunkType};
use turbopack_node::{
Expand Down Expand Up @@ -212,6 +215,34 @@ pub async fn get_client_runtime_entries(
let watch = *watch.await?;
let hot = *hot.await?;

// Add runtime bootstrap code if configured
// This runs synchronously in the runtime chunk before any entry modules execute
let bootstrap_config_opt = config.runtime_bootstrap().await?;
if let Some(bootstrap_config) = bootstrap_config_opt.as_ref() {
match bootstrap_config {
crate::config::RuntimeBootstrapConfig::Code(code) => {
// Inline code: create a virtual source
let bootstrap_source = VirtualSource::new(
project_root.join("__runtime_bootstrap__.js")?,
AssetContent::file(File::from(code.clone()).into()),
)
.to_resolved()
.await?;
runtime_entries.push(
RuntimeEntry::Source(ResolvedVc::upcast(bootstrap_source)).resolved_cell(),
);
}
crate::config::RuntimeBootstrapConfig::Path(path) => {
// File path: use FileSource to load the file
let bootstrap_path = project_root.join(path.as_str())?;
let bootstrap_source = FileSource::new(bootstrap_path).to_resolved().await?;
runtime_entries.push(
RuntimeEntry::Source(ResolvedVc::upcast(bootstrap_source)).resolved_cell(),
);
}
}
Comment on lines +221 to +243
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

match 表达式的 CodePath 分支中存在代码重复。创建 bootstrap_source 后的逻辑(即 runtime_entries.push(...))是完全相同的。建议将这部分通用逻辑提取到 match 表达式之外,以减少重复并提高代码的可读性和可维护性。

        let bootstrap_source = match bootstrap_config {
            crate::config::RuntimeBootstrapConfig::Code(code) => {
                // Inline code: create a virtual source
                VirtualSource::new(
                    project_root.join("__runtime_bootstrap__.js")?,
                    AssetContent::file(File::from(code.clone()).into()),
                )
                .to_resolved()
                .await?
            }
            crate::config::RuntimeBootstrapConfig::Path(path) => {
                // File path: use FileSource to load the file
                let bootstrap_path = project_root.join(path.as_str())?;
                FileSource::new(bootstrap_path).to_resolved().await?
            }
        };
        runtime_entries.push(
            RuntimeEntry::Source(ResolvedVc::upcast(bootstrap_source)).resolved_cell(),
        );

}

if is_development && watch {
let enable_react_refresh =
assert_can_resolve_react_refresh(project_root.clone(), resolve_options_context)
Expand Down
21 changes: 21 additions & 0 deletions crates/pack-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ pub enum ProviderConfigValue {
#[turbo_tasks::value(transparent)]
pub struct ProviderConfig(FxIndexMap<RcStr, ProviderConfigValue>);

/// Runtime bootstrap configuration - can be either inline code or a file path
#[derive(
Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
)]
#[serde(untagged)]
pub enum RuntimeBootstrapConfig {
/// Inline JavaScript code
Code(RcStr),
/// Path to a JavaScript/TypeScript file
Path(RcStr),
}
Comment on lines +124 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

RuntimeBootstrapConfig 枚举的定义存在一个严重问题。当 #[serde(untagged)] 与两个都包装了 RcStr(类似于 String)的变体一起使用时,serde 无法区分它们。在反序列化时,任何字符串值(无论是内联代码还是文件路径)都将被错误地解析为第一个变体,即 Code(RcStr)。这会导致文件路径被当作内联代码处理,从而引发运行时错误。
为了解决这个问题,建议将其中一个变体(例如 Path)修改为结构体,以便 serde 能够根据 JSON 结构进行区分。例如,用户可以通过 { "path": "./bootstrap.js" } 来指定文件路径,通过 "console.log('hello')" 来指定内联代码。
同时,别忘了更新 crates/pack-core/src/client/context.rs 中对应的 match 分支来匹配新的结构 (e.g. RuntimeBootstrapConfig::Path { path } => ...)。

pub enum RuntimeBootstrapConfig {
    /// Path to a JavaScript/TypeScript file, as an object: `{ "path": "./bootstrap.js" }`
    Path { path: RcStr },
    /// Inline JavaScript code, as a string
    Code(RcStr),
}


#[turbo_tasks::value(transparent)]
pub struct OptionRuntimeBootstrapConfig(Option<RuntimeBootstrapConfig>);

#[turbo_tasks::value(serialization = "custom", eq = "manual")]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, OperationValue)]
#[serde(rename_all = "camelCase")]
Expand All @@ -142,6 +157,7 @@ pub struct Config {
cache_handler: Option<RcStr>,
node_polyfill: Option<bool>,
dev_server: Option<DevServer>,
runtime_bootstrap: Option<RuntimeBootstrapConfig>,
#[serde(default)]
experimental: ExperimentalConfig,
#[cfg(feature = "test")]
Expand Down Expand Up @@ -1191,6 +1207,11 @@ impl Config {
Vc::cell(self.node_polyfill.unwrap_or(false))
}

#[turbo_tasks::function]
pub fn runtime_bootstrap(&self) -> Vc<OptionRuntimeBootstrapConfig> {
OptionRuntimeBootstrapConfig(self.runtime_bootstrap.clone()).cell()
}

#[turbo_tasks::function]
pub async fn import_externals(&self) -> Result<Vc<bool>> {
Ok(Vc::cell(match self.experimental.esm_externals {
Expand Down
Loading