Skip to content
Merged
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
62 changes: 46 additions & 16 deletions crates/napi/src/lockfile.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
fs::{File, OpenOptions},
io::Write,
mem::ManuallyDrop,
sync::Mutex,
};
Expand All @@ -24,10 +25,10 @@ pub struct LockfileInner {
}

#[napi(ts_return_type = "{ __napiType: \"Lockfile\" } | null")]
pub fn lockfile_try_acquire_sync(path: String) -> napi::Result<Option<External<JsLockfile>>> {
let mut open_options = OpenOptions::new();
open_options.write(true).create(true);

pub fn lockfile_try_acquire_sync(
path: String,
content: Option<String>,
) -> napi::Result<Option<External<JsLockfile>>> {
// On Windows, we don't use `File::lock` because that grabs a mandatory lock. That can break
// tools or code that read the contents of the `.next` directory because the mandatory lock
// file will fail with EBUSY when read. Instead, we open a file with write mode, but without
Expand All @@ -42,13 +43,24 @@ pub fn lockfile_try_acquire_sync(path: String) -> napi::Result<Option<External<J

use windows_sys::Win32::{Foundation, Storage::FileSystem};

// On Windows, opening with write mode without FILE_SHARE_WRITE acts as the lock.
// We use truncate(true) here because if we can open the file, we have the lock.
let mut open_options = OpenOptions::new();
open_options.write(true).create(true).truncate(true);
open_options
.share_mode(FileSystem::FILE_SHARE_READ | FileSystem::FILE_SHARE_DELETE)
.custom_flags(FileSystem::FILE_FLAG_DELETE_ON_CLOSE);
match open_options.open(&path) {
Ok(file) => Ok(Some(External::new(Mutex::new(ManuallyDrop::new(Some(
LockfileInner { file },
)))))),
Ok(mut file) => {
// Write content to the lockfile if provided
if let Some(ref data) = content {
file.write_all(data.as_bytes())?;
file.flush()?;
}
Ok(Some(External::new(Mutex::new(ManuallyDrop::new(Some(
LockfileInner { file },
))))))
}
Err(err)
if err.raw_os_error()
== Some(Foundation::ERROR_SHARING_VIOLATION.try_into().unwrap()) =>
Expand All @@ -61,25 +73,43 @@ pub fn lockfile_try_acquire_sync(path: String) -> napi::Result<Option<External<J

#[cfg(not(windows))]
return {
use std::fs::TryLockError;
use std::{fs::TryLockError, io::Seek};

// On Unix, we must NOT truncate on open because flock is advisory -
// opening with truncate would clear another process's lockfile content.
// Instead, open without truncate, acquire the lock, then truncate.
let mut open_options = OpenOptions::new();
open_options.write(true).create(true).read(true);

let file = open_options.open(&path)?;
match file.try_lock() {
Ok(_) => Ok(Some(External::new(Mutex::new(ManuallyDrop::new(Some(
LockfileInner {
file,
path: path.into(),
},
)))))),
Ok(_) => {
// We have the lock - now truncate and write content
file.set_len(0)?;
(&file).seek(std::io::SeekFrom::Start(0))?;
if let Some(ref data) = content {
(&file).write_all(data.as_bytes())?;
(&file).flush()?;
}
Ok(Some(External::new(Mutex::new(ManuallyDrop::new(Some(
LockfileInner {
file,
path: path.into(),
},
))))))
}
Err(TryLockError::WouldBlock) => Ok(None),
Err(TryLockError::Error(err)) => Err(err.into()),
}
};
}

#[napi(ts_return_type = "Promise<{ __napiType: \"Lockfile\" } | null>")]
pub async fn lockfile_try_acquire(path: String) -> napi::Result<Option<External<JsLockfile>>> {
tokio::task::spawn_blocking(move || lockfile_try_acquire_sync(path))
pub async fn lockfile_try_acquire(
path: String,
content: Option<String>,
) -> napi::Result<Option<External<JsLockfile>>> {
tokio::task::spawn_blocking(move || lockfile_try_acquire_sync(path, content))
.await
.context("panicked while attempting to acquire lockfile")?
}
Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ pub async fn get_client_module_options_context(
ecmascript: EcmascriptOptionsContext {
esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Relative),
enable_typeof_window_inlining: Some(TypeofWindow::Object),
enable_import_as_bytes: *next_config.turbopack_import_type_bytes().await?,
source_maps,
infer_module_side_effects: *next_config.turbopack_infer_module_side_effects().await?,
..Default::default()
Expand Down
15 changes: 4 additions & 11 deletions crates/next-core/src/next_client/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ use crate::{
next_client::context::ClientContextType,
next_config::NextConfig,
next_shared::transforms::{
debug_fn_name::get_debug_fn_name_rule, get_import_type_bytes_rule,
get_import_type_json_rule, get_next_dynamic_transform_rule, get_next_font_transform_rule,
get_next_image_rule, get_next_lint_transform_rule, get_next_modularize_imports_rule,
get_next_pages_transforms_rule, get_server_actions_transform_rule,
next_cjs_optimizer::get_next_cjs_optimizer_rule,
debug_fn_name::get_debug_fn_name_rule, get_next_dynamic_transform_rule,
get_next_font_transform_rule, get_next_image_rule, get_next_lint_transform_rule,
get_next_modularize_imports_rule, get_next_pages_transforms_rule,
get_server_actions_transform_rule, next_cjs_optimizer::get_next_cjs_optimizer_rule,
next_disallow_re_export_all_in_page::get_next_disallow_export_all_in_page_rule,
next_pure::get_next_pure_rule, server_actions::ActionsTransform,
},
Expand Down Expand Up @@ -113,11 +112,5 @@ pub async fn get_next_client_transforms_rules(
rules.push(get_next_image_rule().await?);
}

if *next_config.turbopack_import_type_bytes().await? {
rules.push(get_import_type_bytes_rule());
}

rules.push(get_import_type_json_rule());

Ok(rules)
}
7 changes: 7 additions & 0 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,11 +833,14 @@ impl TryFrom<ConfigConditionItem> for ConditionItem {
)]
#[serde(rename_all = "camelCase")]
pub struct RuleConfigItem {
#[serde(default)]
pub loaders: Vec<LoaderItem>,
#[serde(default, alias = "as")]
pub rename_as: Option<RcStr>,
#[serde(default)]
pub condition: Option<ConfigConditionItem>,
#[serde(default, alias = "type")]
pub module_type: Option<RcStr>,
}

#[derive(
Expand Down Expand Up @@ -1563,13 +1566,15 @@ impl NextConfig {
loaders: transform_loaders(&mut [loaders].into_iter()),
rename_as: None,
condition: None,
module_type: None,
},
));
}
RuleConfigCollectionItem::Full(RuleConfigItem {
loaders,
rename_as,
condition,
module_type,
}) => {
// If the extension contains a wildcard, and the rename_as does not,
// emit an issue to prevent users from encountering duplicate module
Expand Down Expand Up @@ -1618,6 +1623,7 @@ impl NextConfig {
loaders: transform_loaders(&mut loaders.iter()),
rename_as: rename_as.clone(),
condition,
module_type: module_type.clone(),
},
));
}
Expand Down Expand Up @@ -2172,6 +2178,7 @@ mod tests {
RuleConfigItem {
loaders: vec![],
rename_as: Some(rcstr!("*.js")),
module_type: None,
condition: Some(ConfigConditionItem::All(
[
ConfigConditionItem::Builtin(WebpackLoaderBuiltinCondition::Production),
Expand Down
32 changes: 15 additions & 17 deletions crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,11 +553,11 @@ pub async fn get_server_module_options_context(

// A set of custom ecma transform rules being applied to server context.
let source_transform_rules: Vec<ModuleRule> = vec![
get_swc_ecma_transform_plugin_rule(next_config, project_path.clone()).await?,
get_relay_transform_rule(next_config, project_path.clone()).await?,
get_emotion_transform_rule(next_config).await?,
get_react_remove_properties_transform_rule(next_config).await?,
get_remove_console_transform_rule(next_config).await?,
get_react_remove_properties_transform_rule(next_config).await?,
get_emotion_transform_rule(next_config).await?,
get_relay_transform_rule(next_config, project_path.clone()).await?,
get_swc_ecma_transform_plugin_rule(next_config, project_path.clone()).await?,
]
.into_iter()
.flatten()
Expand All @@ -578,6 +578,7 @@ pub async fn get_server_module_options_context(
let module_options_context = ModuleOptionsContext {
ecmascript: EcmascriptOptionsContext {
enable_typeof_window_inlining: Some(TypeofWindow::Undefined),
enable_import_as_bytes: *next_config.turbopack_import_type_bytes().await?,
import_externals: *next_config.import_externals().await?,
ignore_dynamic_requests: true,
source_maps,
Expand Down Expand Up @@ -620,15 +621,14 @@ pub async fn get_server_module_options_context(

let module_options_context = match ty {
ServerContextType::Pages { .. } | ServerContextType::PagesApi { .. } => {
next_server_rules.extend(page_transform_rules);
next_server_rules.extend(source_transform_rules);
if let ServerContextType::Pages { .. } = ty {
next_server_rules.push(
get_next_react_server_components_transform_rule(next_config, false, None)
.await?,
);
}

next_server_rules.extend(source_transform_rules);
next_server_rules.extend(page_transform_rules);

foreign_next_server_rules.extend(internal_custom_rules);

Expand Down Expand Up @@ -698,12 +698,12 @@ pub async fn get_server_module_options_context(
ServerContextType::AppSSR { app_dir, .. } => {
foreign_next_server_rules.extend(internal_custom_rules);

next_server_rules.extend(page_transform_rules.clone());
next_server_rules.extend(source_transform_rules);
next_server_rules.push(
get_next_react_server_components_transform_rule(next_config, false, Some(app_dir))
.await?,
);
next_server_rules.extend(source_transform_rules);
next_server_rules.extend(page_transform_rules.clone());

let module_options_context = ModuleOptionsContext {
ecmascript: EcmascriptOptionsContext {
Expand Down Expand Up @@ -762,8 +762,6 @@ pub async fn get_server_module_options_context(
ecmascript_client_reference_transition_name,
..
} => {
next_server_rules.extend(page_transform_rules);

let client_directive_transformer = ecmascript_client_reference_transition_name.map(
|ecmascript_client_reference_transition_name| {
get_ecma_transform_rule(
Expand All @@ -776,16 +774,16 @@ pub async fn get_server_module_options_context(
},
);

foreign_next_server_rules.extend(client_directive_transformer.clone());
foreign_next_server_rules.extend(internal_custom_rules);
foreign_next_server_rules.extend(client_directive_transformer.clone());

next_server_rules.extend(client_directive_transformer.clone());
next_server_rules.extend(source_transform_rules);
next_server_rules.push(
get_next_react_server_components_transform_rule(next_config, true, Some(app_dir))
.await?,
);

next_server_rules.extend(source_transform_rules);
next_server_rules.extend(client_directive_transformer.clone());
next_server_rules.extend(page_transform_rules);

let module_options_context = ModuleOptionsContext {
ecmascript: EcmascriptOptionsContext {
Expand Down Expand Up @@ -842,8 +840,6 @@ pub async fn get_server_module_options_context(
app_dir,
ecmascript_client_reference_transition_name,
} => {
next_server_rules.extend(source_transform_rules);

let mut common_next_server_rules = vec![
get_next_react_server_components_transform_rule(next_config, true, Some(app_dir))
.await?,
Expand All @@ -864,6 +860,8 @@ pub async fn get_server_module_options_context(
next_server_rules.extend(common_next_server_rules.iter().cloned());
internal_custom_rules.extend(common_next_server_rules);

next_server_rules.extend(source_transform_rules);

let module_options_context = ModuleOptionsContext {
ecmascript: EcmascriptOptionsContext {
esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Full),
Expand Down
15 changes: 4 additions & 11 deletions crates/next-core/src/next_server/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ use crate::{
next_config::NextConfig,
next_server::context::ServerContextType,
next_shared::transforms::{
get_import_type_bytes_rule, get_import_type_json_rule, get_next_dynamic_transform_rule,
get_next_font_transform_rule, get_next_image_rule, get_next_lint_transform_rule,
get_next_modularize_imports_rule, get_next_pages_transforms_rule,
get_next_track_dynamic_imports_transform_rule, get_server_actions_transform_rule,
next_cjs_optimizer::get_next_cjs_optimizer_rule,
get_next_dynamic_transform_rule, get_next_font_transform_rule, get_next_image_rule,
get_next_lint_transform_rule, get_next_modularize_imports_rule,
get_next_pages_transforms_rule, get_next_track_dynamic_imports_transform_rule,
get_server_actions_transform_rule, next_cjs_optimizer::get_next_cjs_optimizer_rule,
next_disallow_re_export_all_in_page::get_next_disallow_export_all_in_page_rule,
next_edge_node_api_assert::next_edge_node_api_assert,
next_middleware_dynamic_assert::get_middleware_dynamic_assert_rule,
Expand Down Expand Up @@ -216,12 +215,6 @@ pub async fn get_next_server_transforms_rules(
}
}

if *next_config.turbopack_import_type_bytes().await? {
rules.push(get_import_type_bytes_rule());
}

rules.push(get_import_type_json_rule());

Ok(rules)
}

Expand Down
23 changes: 1 addition & 22 deletions crates/next-core/src/next_shared/transforms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ pub use server_actions::get_server_actions_transform_rule;
use turbo_tasks::ResolvedVc;
use turbo_tasks_fs::FileSystemPath;
use turbopack::module_options::{ModuleRule, ModuleRuleEffect, ModuleType, RuleCondition};
use turbopack_core::reference_type::{
EcmaScriptModulesReferenceSubType, ReferenceType, UrlReferenceSubType,
};
use turbopack_core::reference_type::{ReferenceType, UrlReferenceSubType};
use turbopack_ecmascript::{CustomTransformer, EcmascriptInputTransform};

use crate::next_image::{StructuredImageModuleType, module::BlurPlaceholderMode};
Expand Down Expand Up @@ -125,25 +123,6 @@ pub(crate) fn module_rule_match_pages_page_file(
])
}

pub(crate) fn get_import_type_bytes_rule() -> ModuleRule {
// Move this into turbopack once the feature is standardized
ModuleRule::new(
RuleCondition::ReferenceType(ReferenceType::EcmaScriptModules(
EcmaScriptModulesReferenceSubType::ImportWithType("bytes".into()),
)),
vec![ModuleRuleEffect::ModuleType(ModuleType::InlinedBytesJs)],
)
}

pub(crate) fn get_import_type_json_rule() -> ModuleRule {
ModuleRule::new(
RuleCondition::ReferenceType(ReferenceType::EcmaScriptModules(
EcmaScriptModulesReferenceSubType::ImportWithType("json".into()),
)),
vec![ModuleRuleEffect::ModuleType(ModuleType::Json)],
)
}

pub(crate) enum EcmascriptTransformStage {
Preprocess,
Main,
Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_shared/webpack_rules/babel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub async fn get_babel_loader_rules(
}]),
rename_as: Some(rcstr!("*")),
condition: Some(ConditionItem::All(loader_conditions.into())),
module_type: None,
},
)])
}
Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_shared/webpack_rules/sass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub async fn get_sass_loader_rules(
loaders,
rename_as: Some(rename),
condition: None,
module_type: None,
},
));
}
Expand Down
Loading
Loading