From 6bfa9fd5bca74a9234b2786837f0a9e8d17219dc Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 13 Nov 2024 17:22:48 +0100 Subject: [PATCH 1/3] avoid writing manifests in data/rsc variant of page --- crates/next-api/src/app.rs | 399 ++++++++++++++++++----------------- crates/next-api/src/pages.rs | 166 ++++++++------- 2 files changed, 298 insertions(+), 267 deletions(-) diff --git a/crates/next-api/src/app.rs b/crates/next-api/src/app.rs index 6ca29b1acd412..71897ad15660f 100644 --- a/crates/next-api/src/app.rs +++ b/crates/next-api/src/app.rs @@ -864,13 +864,17 @@ impl AppEndpoint { let app_entry = self.app_endpoint_entry().await?; - let (process_client_components, process_client_assets, process_ssr) = match this.ty { - AppEndpointType::Page { ty, .. } => { - (true, true, matches!(ty, AppPageEndpointType::Html)) - } - AppEndpointType::Route { .. } => (true, false, false), - AppEndpointType::Metadata { .. } => (false, false, false), - }; + let (process_client_components, process_client_assets, process_ssr, emit_manifests) = + match this.ty { + AppEndpointType::Page { ty, .. } => ( + true, + true, + matches!(ty, AppPageEndpointType::Html), + matches!(ty, AppPageEndpointType::Html), + ), + AppEndpointType::Route { .. } => (true, false, false, true), + AppEndpointType::Metadata { .. } => (false, false, false, true), + }; let node_root = this.app_project.project().node_root(); @@ -1034,23 +1038,26 @@ impl AppEndpoint { .collect::>>()?; entry_client_chunks_paths.extend(client_shared_chunks_paths.iter().cloned()); - let app_build_manifest = AppBuildManifest { - pages: [(app_entry.original_name.clone(), entry_client_chunks_paths)] - .into_iter() - .collect(), - }; let manifest_path_prefix = &app_entry.original_name; - let app_build_manifest_output = VirtualOutputAsset::new( - node_root.join( - format!("server/app{manifest_path_prefix}/app-build-manifest.json",).into(), - ), - AssetContent::file( - File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(), - ), - ) - .to_resolved() - .await?; - server_assets.insert(ResolvedVc::upcast(app_build_manifest_output)); + + if emit_manifests { + let app_build_manifest = AppBuildManifest { + pages: [(app_entry.original_name.clone(), entry_client_chunks_paths)] + .into_iter() + .collect(), + }; + let app_build_manifest_output = VirtualOutputAsset::new( + node_root.join( + format!("server/app{manifest_path_prefix}/app-build-manifest.json",).into(), + ), + AssetContent::file( + File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(), + ), + ) + .to_resolved() + .await?; + server_assets.insert(ResolvedVc::upcast(app_build_manifest_output)); + } // polyfill-nomodule.js is a pre-compiled asset distributed as part of next, // load it as a RawModule. @@ -1071,43 +1078,47 @@ impl AppEndpoint { let polyfill_client_paths = vec![polyfill_client_path]; client_assets.insert(ResolvedVc::upcast(polyfill_output_asset)); - if *this - .app_project - .project() - .should_create_webpack_stats() - .await? - { - let webpack_stats = - generate_webpack_stats(app_entry.original_name.clone(), &client_assets).await?; - let stats_output = VirtualOutputAsset::new( + if emit_manifests { + if *this + .app_project + .project() + .should_create_webpack_stats() + .await? + { + let webpack_stats = + generate_webpack_stats(app_entry.original_name.clone(), &client_assets) + .await?; + let stats_output = VirtualOutputAsset::new( + node_root.join( + format!("server/app{manifest_path_prefix}/webpack-stats.json",).into(), + ), + AssetContent::file( + File::from(serde_json::to_string_pretty(&webpack_stats)?).into(), + ), + ) + .to_resolved() + .await?; + server_assets.insert(ResolvedVc::upcast(stats_output)); + } + + let build_manifest = BuildManifest { + root_main_files: client_shared_chunks_paths, + polyfill_files: polyfill_client_paths, + ..Default::default() + }; + let build_manifest_output = VirtualOutputAsset::new( node_root.join( - format!("server/app{manifest_path_prefix}/webpack-stats.json",).into(), + format!("server/app{manifest_path_prefix}/build-manifest.json",).into(), ), AssetContent::file( - File::from(serde_json::to_string_pretty(&webpack_stats)?).into(), + File::from(serde_json::to_string_pretty(&build_manifest)?).into(), ), ) .to_resolved() .await?; - server_assets.insert(ResolvedVc::upcast(stats_output)); + server_assets.insert(ResolvedVc::upcast(build_manifest_output)); } - let build_manifest = BuildManifest { - root_main_files: client_shared_chunks_paths, - polyfill_files: polyfill_client_paths, - ..Default::default() - }; - let build_manifest_output = VirtualOutputAsset::new( - node_root - .join(format!("server/app{manifest_path_prefix}/build-manifest.json",).into()), - AssetContent::file( - File::from(serde_json::to_string_pretty(&build_manifest)?).into(), - ), - ) - .to_resolved() - .await?; - server_assets.insert(ResolvedVc::upcast(build_manifest_output)); - if runtime == NextRuntime::Edge { // as the edge runtime doesn't support chunk loading we need to add all client // references to the middleware manifest so they get loaded during runtime @@ -1171,8 +1182,12 @@ impl AppEndpoint { let app_entry_chunks_ref = app_entry_chunks.await?; server_assets.extend(app_entry_chunks_ref.iter().copied()); + let client_assets = OutputAssets::new(client_assets.iter().map(|asset| **asset).collect()); + // these references are important for turbotrace - let client_reference_manifest = + let mut client_reference_manifest = None; + + if emit_manifests { if let (Some(client_references), Some(client_references_chunks)) = (client_references, client_references_chunks) { @@ -1195,25 +1210,22 @@ impl AppEndpoint { if runtime == NextRuntime::Edge { middleware_assets.push(entry_manifest); } - Some(entry_manifest) - } else { - None - }; - - let client_assets = OutputAssets::new(client_assets.iter().map(|asset| **asset).collect()); + client_reference_manifest = Some(entry_manifest) + } - let next_font_manifest_output = create_font_manifest( - this.app_project.project().client_root(), - node_root, - this.app_project.app_dir(), - &app_entry.original_name, - &app_entry.original_name, - &app_entry.original_name, - client_assets, - true, - ) - .await?; - server_assets.insert(next_font_manifest_output); + let next_font_manifest_output = create_font_manifest( + this.app_project.project().client_root(), + node_root, + this.app_project.app_dir(), + &app_entry.original_name, + &app_entry.original_name, + &app_entry.original_name, + client_assets, + true, + ) + .await?; + server_assets.insert(next_font_manifest_output); + } let endpoint_output = match runtime { NextRuntime::Edge => { @@ -1250,87 +1262,91 @@ impl AppEndpoint { let entry_file = "app-edge-has-no-entrypoint".into(); - // create middleware manifest - let named_regex = get_named_middleware_regex(&app_entry.pathname); - let matchers = MiddlewareMatcher { - regexp: Some(named_regex.into()), - original_source: app_entry.pathname.clone(), - ..Default::default() - }; - let edge_function_definition = EdgeFunctionDefinition { - files: file_paths_from_root, - wasm: wasm_paths_to_bindings(wasm_paths_from_root), - assets: paths_to_bindings(all_assets), - name: app_entry.pathname.clone(), - page: app_entry.original_name.clone(), - regions: app_entry - .config - .await? - .preferred_region - .clone() - .map(Regions::Multiple), - matchers: vec![matchers], - env: this.app_project.project().edge_env().await?.clone_value(), - }; - let middleware_manifest_v2 = MiddlewaresManifestV2 { - sorted_middleware: vec![app_entry.original_name.clone()], - functions: [(app_entry.original_name.clone(), edge_function_definition)] - .into_iter() - .collect(), - ..Default::default() - }; - let manifest_path_prefix = &app_entry.original_name; - let middleware_manifest_v2 = ResolvedVc::upcast( - VirtualOutputAsset::new( - node_root.join( - format!("server/app{manifest_path_prefix}/middleware-manifest.json",) + if emit_manifests { + // create middleware manifest + let named_regex = get_named_middleware_regex(&app_entry.pathname); + let matchers = MiddlewareMatcher { + regexp: Some(named_regex.into()), + original_source: app_entry.pathname.clone(), + ..Default::default() + }; + let edge_function_definition = EdgeFunctionDefinition { + files: file_paths_from_root, + wasm: wasm_paths_to_bindings(wasm_paths_from_root), + assets: paths_to_bindings(all_assets), + name: app_entry.pathname.clone(), + page: app_entry.original_name.clone(), + regions: app_entry + .config + .await? + .preferred_region + .clone() + .map(Regions::Multiple), + matchers: vec![matchers], + env: this.app_project.project().edge_env().await?.clone_value(), + }; + let middleware_manifest_v2 = MiddlewaresManifestV2 { + sorted_middleware: vec![app_entry.original_name.clone()], + functions: [(app_entry.original_name.clone(), edge_function_definition)] + .into_iter() + .collect(), + ..Default::default() + }; + let manifest_path_prefix = &app_entry.original_name; + let middleware_manifest_v2 = ResolvedVc::upcast( + VirtualOutputAsset::new( + node_root.join( + format!( + "server/app{manifest_path_prefix}/middleware-manifest.json", + ) .into(), - ), - AssetContent::file( - FileContent::Content(File::from(serde_json::to_string_pretty( - &middleware_manifest_v2, - )?)) - .cell(), - ), - ) - .to_resolved() - .await?, - ); - server_assets.insert(middleware_manifest_v2); + ), + AssetContent::file( + FileContent::Content(File::from(serde_json::to_string_pretty( + &middleware_manifest_v2, + )?)) + .cell(), + ), + ) + .to_resolved() + .await?, + ); + server_assets.insert(middleware_manifest_v2); - // create app paths manifest - let app_paths_manifest_output = - create_app_paths_manifest(node_root, &app_entry.original_name, entry_file) - .await?; - server_assets.insert(app_paths_manifest_output); + // create app paths manifest + let app_paths_manifest_output = + create_app_paths_manifest(node_root, &app_entry.original_name, entry_file) + .await?; + server_assets.insert(app_paths_manifest_output); - // create react-loadable-manifest for next/dynamic - let mut dynamic_import_modules = collect_next_dynamic_imports( - vec![*ResolvedVc::upcast(app_entry.rsc_entry)], - Vc::upcast(this.app_project.client_module_context()), - VisitedDynamicImportModules::empty(), - ) - .await? - .client_dynamic_imports - .clone(); - dynamic_import_modules.extend(client_dynamic_imports.into_iter().flatten()); - let dynamic_import_entries = collect_evaluated_chunk_group( - Vc::upcast(client_chunking_context), - dynamic_import_modules, - ) - .await?; - let loadable_manifest_output = create_react_loadable_manifest( - dynamic_import_entries, - client_relative_path, - node_root.join( - format!( - "server/app{}/react-loadable-manifest.json", - &app_entry.original_name - ) - .into(), - ), - ); - server_assets.extend(loadable_manifest_output.await?.iter().copied()); + // create react-loadable-manifest for next/dynamic + let mut dynamic_import_modules = collect_next_dynamic_imports( + vec![*ResolvedVc::upcast(app_entry.rsc_entry)], + Vc::upcast(this.app_project.client_module_context()), + VisitedDynamicImportModules::empty(), + ) + .await? + .client_dynamic_imports + .clone(); + dynamic_import_modules.extend(client_dynamic_imports.into_iter().flatten()); + let dynamic_import_entries = collect_evaluated_chunk_group( + Vc::upcast(client_chunking_context), + dynamic_import_modules, + ) + .await?; + let loadable_manifest_output = create_react_loadable_manifest( + dynamic_import_entries, + client_relative_path, + node_root.join( + format!( + "server/app{}/react-loadable-manifest.json", + &app_entry.original_name + ) + .into(), + ), + ); + server_assets.extend(loadable_manifest_output.await?.iter().copied()); + } AppEndpointOutput::Edge { files: *app_entry_chunks, @@ -1342,47 +1358,52 @@ impl AppEndpoint { // For node, there will be exactly one asset in this let rsc_chunk = *app_entry_chunks_ref.first().unwrap(); - let app_paths_manifest_output = create_app_paths_manifest( - node_root, - &app_entry.original_name, - server_path - .await? - .get_path_to(&*rsc_chunk.ident().path().await?) - .context("RSC chunk path should be within app paths manifest directory")? - .into(), - ) - .await?; - server_assets.insert(app_paths_manifest_output); - - // create react-loadable-manifest for next/dynamic - let availability_info = Value::new(AvailabilityInfo::Root); - let mut dynamic_import_modules = collect_next_dynamic_imports( - vec![*ResolvedVc::upcast(app_entry.rsc_entry)], - Vc::upcast(this.app_project.client_module_context()), - VisitedDynamicImportModules::empty(), - ) - .await? - .client_dynamic_imports - .clone(); - dynamic_import_modules.extend(client_dynamic_imports.into_iter().flatten()); - let dynamic_import_entries = collect_chunk_group( - Vc::upcast(client_chunking_context), - dynamic_import_modules, - availability_info, - ) - .await?; - let loadable_manifest_output = create_react_loadable_manifest( - dynamic_import_entries, - client_relative_path, - node_root.join( - format!( - "server/app{}/react-loadable-manifest.json", - &app_entry.original_name - ) - .into(), - ), - ); - server_assets.extend(loadable_manifest_output.await?.iter().copied()); + if emit_manifests { + // create app paths manifest + let app_paths_manifest_output = create_app_paths_manifest( + node_root, + &app_entry.original_name, + server_path + .await? + .get_path_to(&*rsc_chunk.ident().path().await?) + .context( + "RSC chunk path should be within app paths manifest directory", + )? + .into(), + ) + .await?; + server_assets.insert(app_paths_manifest_output); + + // create react-loadable-manifest for next/dynamic + let availability_info = Value::new(AvailabilityInfo::Root); + let mut dynamic_import_modules = collect_next_dynamic_imports( + vec![*ResolvedVc::upcast(app_entry.rsc_entry)], + Vc::upcast(this.app_project.client_module_context()), + VisitedDynamicImportModules::empty(), + ) + .await? + .client_dynamic_imports + .clone(); + dynamic_import_modules.extend(client_dynamic_imports.into_iter().flatten()); + let dynamic_import_entries = collect_chunk_group( + Vc::upcast(client_chunking_context), + dynamic_import_modules, + availability_info, + ) + .await?; + let loadable_manifest_output = create_react_loadable_manifest( + dynamic_import_entries, + client_relative_path, + node_root.join( + format!( + "server/app{}/react-loadable-manifest.json", + &app_entry.original_name + ) + .into(), + ), + ); + server_assets.extend(loadable_manifest_output.await?.iter().copied()); + } if this .app_project diff --git a/crates/next-api/src/pages.rs b/crates/next-api/src/pages.rs index 7d2ed05c20892..be23f53ade9df 100644 --- a/crates/next-api/src/pages.rs +++ b/crates/next-api/src/pages.rs @@ -988,11 +988,12 @@ impl PageEndpoint { .collect(), }; let manifest_path_prefix = get_asset_prefix_from_pathname(&self.pathname.await?); - Ok(Vc::upcast(VirtualOutputAsset::new( + let asset = Vc::upcast(VirtualOutputAsset::new( node_root .join(format!("server/pages{manifest_path_prefix}/pages-manifest.json",).into()), AssetContent::file(File::from(serde_json::to_string_pretty(&pages_manifest)?).into()), - ))) + )); + Ok(asset) } #[turbo_tasks::function] @@ -1078,6 +1079,7 @@ impl PageEndpoint { PageEndpointType::Api => self.api_chunk(), PageEndpointType::SsrOnly => self.ssr_chunk(), }; + let emit_manifests = !matches!(this.ty, PageEndpointType::Data); let pathname = this.pathname.await?; let original_name = &*this.original_name.await?; @@ -1125,15 +1127,19 @@ impl PageEndpoint { dynamic_import_entries, nft, } => { - let pages_manifest = self.pages_manifest(*entry).to_resolved().await?; - server_assets.push(pages_manifest); server_assets.push(entry); if let Some(nft) = &*nft.await? { server_assets.push(*nft); } - let loadable_manifest_output = self.react_loadable_manifest(dynamic_import_entries); - server_assets.extend(loadable_manifest_output.await?.iter().copied()); + if emit_manifests { + let pages_manifest = self.pages_manifest(*entry).to_resolved().await?; + server_assets.push(pages_manifest); + + let loadable_manifest_output = + self.react_loadable_manifest(dynamic_import_entries); + server_assets.extend(loadable_manifest_output.await?.iter().copied()); + } PageEndpointOutput::NodeJs { entry_chunk: entry, @@ -1146,79 +1152,83 @@ impl PageEndpoint { dynamic_import_entries, } => { let node_root = this.pages_project.project().node_root(); - let files_value = files.await?; - if let Some(&file) = files_value.first() { - let pages_manifest = self.pages_manifest(*file).to_resolved().await?; - server_assets.push(pages_manifest); + if emit_manifests { + let files_value = files.await?; + if let Some(&file) = files_value.first() { + let pages_manifest = self.pages_manifest(*file).to_resolved().await?; + server_assets.push(pages_manifest); + } + server_assets.extend(files_value.iter().copied()); + + // the next-edge-ssr-loader templates expect the manifests to be stored in + // global variables defined in these files + // + // they are created in `setup-dev-bundler.ts` + let mut file_paths_from_root = vec![ + "server/server-reference-manifest.js".into(), + "server/middleware-build-manifest.js".into(), + "server/middleware-react-loadable-manifest.js".into(), + "server/next-font-manifest.js".into(), + ]; + let mut wasm_paths_from_root = vec![]; + + let node_root_value = node_root.await?; + + file_paths_from_root + .extend(get_js_paths_from_root(&node_root_value, &files_value).await?); + + let all_output_assets = all_assets_from_entries(files).await?; + + wasm_paths_from_root.extend( + get_wasm_paths_from_root(&node_root_value, &all_output_assets).await?, + ); + + let all_assets = + get_paths_from_root(&node_root_value, &all_output_assets, |_asset| true) + .await?; + + let named_regex = get_named_middleware_regex(&pathname).into(); + let matchers = MiddlewareMatcher { + regexp: Some(named_regex), + original_source: pathname.clone_value(), + ..Default::default() + }; + let original_name = this.original_name.await?; + let edge_function_definition = EdgeFunctionDefinition { + files: file_paths_from_root, + wasm: wasm_paths_to_bindings(wasm_paths_from_root), + assets: paths_to_bindings(all_assets), + name: pathname.clone_value(), + page: original_name.clone_value(), + regions: None, + matchers: vec![matchers], + env: this.pages_project.project().edge_env().await?.clone_value(), + }; + let middleware_manifest_v2 = MiddlewaresManifestV2 { + sorted_middleware: vec![pathname.clone_value()], + functions: [(pathname.clone_value(), edge_function_definition)] + .into_iter() + .collect(), + ..Default::default() + }; + let manifest_path_prefix = + get_asset_prefix_from_pathname(&this.pathname.await?); + let middleware_manifest_v2 = VirtualOutputAsset::new( + node_root.join( + format!("server/pages{manifest_path_prefix}/middleware-manifest.json") + .into(), + ), + AssetContent::file( + FileContent::Content(File::from(serde_json::to_string_pretty( + &middleware_manifest_v2, + )?)) + .cell(), + ), + ) + .to_resolved() + .await?; + server_assets.push(ResolvedVc::upcast(middleware_manifest_v2)); } - server_assets.extend(files_value.iter().copied()); - - // the next-edge-ssr-loader templates expect the manifests to be stored in - // global variables defined in these files - // - // they are created in `setup-dev-bundler.ts` - let mut file_paths_from_root = vec![ - "server/server-reference-manifest.js".into(), - "server/middleware-build-manifest.js".into(), - "server/middleware-react-loadable-manifest.js".into(), - "server/next-font-manifest.js".into(), - ]; - let mut wasm_paths_from_root = vec![]; - - let node_root_value = node_root.await?; - - file_paths_from_root - .extend(get_js_paths_from_root(&node_root_value, &files_value).await?); - - let all_output_assets = all_assets_from_entries(files).await?; - - wasm_paths_from_root - .extend(get_wasm_paths_from_root(&node_root_value, &all_output_assets).await?); - - let all_assets = - get_paths_from_root(&node_root_value, &all_output_assets, |_asset| true) - .await?; - - let named_regex = get_named_middleware_regex(&pathname).into(); - let matchers = MiddlewareMatcher { - regexp: Some(named_regex), - original_source: pathname.clone_value(), - ..Default::default() - }; - let original_name = this.original_name.await?; - let edge_function_definition = EdgeFunctionDefinition { - files: file_paths_from_root, - wasm: wasm_paths_to_bindings(wasm_paths_from_root), - assets: paths_to_bindings(all_assets), - name: pathname.clone_value(), - page: original_name.clone_value(), - regions: None, - matchers: vec![matchers], - env: this.pages_project.project().edge_env().await?.clone_value(), - }; - let middleware_manifest_v2 = MiddlewaresManifestV2 { - sorted_middleware: vec![pathname.clone_value()], - functions: [(pathname.clone_value(), edge_function_definition)] - .into_iter() - .collect(), - ..Default::default() - }; - let manifest_path_prefix = get_asset_prefix_from_pathname(&this.pathname.await?); - let middleware_manifest_v2 = VirtualOutputAsset::new( - node_root.join( - format!("server/pages{manifest_path_prefix}/middleware-manifest.json") - .into(), - ), - AssetContent::file( - FileContent::Content(File::from(serde_json::to_string_pretty( - &middleware_manifest_v2, - )?)) - .cell(), - ), - ) - .to_resolved() - .await?; - server_assets.push(ResolvedVc::upcast(middleware_manifest_v2)); let loadable_manifest_output = self.react_loadable_manifest(dynamic_import_entries); server_assets.extend(loadable_manifest_output.await?.iter().copied()); From 5b9681c2b220b8012fe75cd5acf7ba76d96a59ff Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 13 Nov 2024 17:24:15 +0100 Subject: [PATCH 2/3] add Invalidator dirty cause --- turbopack/crates/turbo-tasks-backend/src/backend/mod.rs | 2 +- .../turbo-tasks-backend/src/backend/operation/invalidate.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs index 82de596a9adce..97d7dbd134a5c 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs @@ -888,7 +888,7 @@ impl TurboTasksBackendInner { } operation::InvalidateOperation::run( smallvec![task_id], - TaskDirtyCause::Unknown, + TaskDirtyCause::Invalidator, self.execute_context(turbo_tasks), ); } diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs index 69dd5d5f98241..cf8fd450b359f 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs @@ -80,6 +80,7 @@ pub enum TaskDirtyCause { CellRemoved { value_type: ValueTypeId }, OutputChange, CollectiblesChange { collectible_type: TraitTypeId }, + Invalidator, Unknown, } @@ -109,6 +110,7 @@ impl Display for TaskDirtyCause { registry::get_trait(*collectible_type).name ) } + TaskDirtyCause::Invalidator => write!(f, "invalidator"), TaskDirtyCause::Unknown => write!(f, "unknown"), } } From 158f6e6b26f37732b748db0d9858529a09196c37 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 13 Nov 2024 17:21:34 +0100 Subject: [PATCH 3/3] fix write_version feature flag --- turbopack/crates/turbo-tasks-fs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turbopack/crates/turbo-tasks-fs/src/lib.rs b/turbopack/crates/turbo-tasks-fs/src/lib.rs index 82cf94fdb6f21..a7e8ed07320a3 100644 --- a/turbopack/crates/turbo-tasks-fs/src/lib.rs +++ b/turbopack/crates/turbo-tasks-fs/src/lib.rs @@ -735,7 +735,7 @@ impl FileSystem for DiskFileSystem { f.set_permissions(file.meta.permissions.into()).await?; #[cfg(feature = "write_version")] { - let mut full_path = full_path; + let mut full_path = full_path.into_owned(); let hash = hash_xxh3_hash64(file); let ext = full_path.extension(); let ext = if let Some(ext) = ext {