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
51 changes: 32 additions & 19 deletions crates/pack-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use pack_core::{
config::{Config, ModuleIds as ModuleIdStrategyConfig},
emit_assets,
mode::Mode,
util::{Runtime, convert_to_project_relative},
util::{Runtime, convert_to_project_relative, strip_leading_separator, to_unix_path},
};
use serde::Deserialize;
use std::{
fs,
path::{MAIN_SEPARATOR, Path, PathBuf},
path::{Path, PathBuf},
time::Duration,
};
use tracing::Instrument;
Expand All @@ -25,7 +25,7 @@ use turbo_tasks_fs::{
DirectoryContent, DirectoryEntry, DiskFileSystem, FileContent, FileSystem, FileSystemEntryType,
FileSystemPath, VirtualFileSystem, invalidation,
};
use turbo_unix_path::normalize_path;
use turbo_unix_path::{join_path, normalize_path, unix_to_sys};
use turbopack::global_module_ids::get_global_module_id_strategy;
use turbopack_core::{
file_source::FileSource,
Expand Down Expand Up @@ -584,7 +584,7 @@ impl Project {

if self.pack_path.starts_with(&*self.root_path) {
let relative_pack_path = self.pack_path.strip_prefix(&*self.root_path).unwrap();
let relative_pack_path = relative_pack_path.trim_start_matches(MAIN_SEPARATOR);
let relative_pack_path = strip_leading_separator(relative_pack_path);
let turbopack_path = Path::new(relative_pack_path).join(".turbopack");
if let Some(path_str) = turbopack_path.to_str() {
let turbopack_path_normalized =
Expand Down Expand Up @@ -619,6 +619,24 @@ impl Project {
DiskFileSystem::new(rcstr!("output"), self.root_path.clone())
}

#[turbo_tasks::function]
pub async fn dist_dir_absolute(self: Vc<Self>) -> Result<Vc<RcStr>> {
let this = self.await?;
let dist_dir = self.dist_dir().await?;
Ok(Vc::cell(
format!(
"{}{}{}",
this.root_path,
std::path::MAIN_SEPARATOR,
unix_to_sys(
&join_path(&this.project_path, dist_dir.as_str())
.context("expected project_path to be inside of root_path")?
)
)
.into(),
))
}

#[turbo_tasks::function]
pub async fn project_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
Ok(self.project_fs(self.dist_dir()).root())
Expand All @@ -635,10 +653,14 @@ impl Project {
.clone()
.unwrap_or("dist".into());

let relative_dist_path = convert_to_project_relative(&dist_path, &this.project_path)?;
let relative_dist_path = relative_dist_path
let mut relative_dist_path = convert_to_project_relative(&dist_path, &this.project_path)?;
// Strip "./" or ".\" prefix before converting to unix path (Windows compatibility)
relative_dist_path = relative_dist_path
.strip_prefix("./")
.unwrap_or(&relative_dist_path);
.or_else(|| relative_dist_path.strip_prefix(".\\"))
.unwrap_or(&relative_dist_path)
.into();
let relative_dist_path = to_unix_path(&relative_dist_path);

Ok(Vc::cell(relative_dist_path.into()))
}
Expand All @@ -651,10 +673,7 @@ impl Project {
} else {
"./"
};
let pack_relative = pack_relative
.strip_prefix(MAIN_SEPARATOR)
.unwrap_or(pack_relative)
.replace(MAIN_SEPARATOR, "/");
let pack_relative = to_unix_path(strip_leading_separator(pack_relative));

Ok(self
.output_fs()
Expand All @@ -671,10 +690,7 @@ impl Project {
let dist_dir = self.dist_dir().await?;

let project_relative = this.project_path.strip_prefix(&*this.root_path).unwrap();
let project_relative = project_relative
.strip_prefix(MAIN_SEPARATOR)
.unwrap_or(project_relative)
.replace(MAIN_SEPARATOR, "/");
let project_relative = to_unix_path(strip_leading_separator(project_relative));

Ok(self
.output_fs()
Expand Down Expand Up @@ -705,10 +721,7 @@ impl Project {
let this = self.await?;
let root = self.project_root().await?;
let project_relative = this.project_path.strip_prefix(&*this.root_path).unwrap();
let project_relative = project_relative
.strip_prefix(MAIN_SEPARATOR)
.unwrap_or(project_relative)
.replace(MAIN_SEPARATOR, "/");
let project_relative = to_unix_path(strip_leading_separator(project_relative));
Ok(root.join(&project_relative)?.cell())
}

Expand Down
56 changes: 36 additions & 20 deletions crates/pack-api/src/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ use anyhow::{Result, bail};
use turbo_rcstr::RcStr;
use turbo_tasks::Vc;
use turbo_tasks_fs::FileContent;
use turbo_unix_path::sys_to_unix;
use url::Url;

use crate::project::ProjectContainer;

#[turbo_tasks::function]
pub async fn get_source_map_rope(
container: Vc<ProjectContainer>,
file_path: RcStr,
source_url: RcStr,
) -> Result<Vc<FileContent>> {
let (file, module) = match Url::parse(&file_path) {
let (file_path_sys, module) = match Url::parse(&source_url) {
Ok(url) => match url.scheme() {
"file" => {
let path = urlencoding::decode(url.path())?.to_string();
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
let path: String = match url.to_file_path() {
Ok(path) => path.to_string_lossy().into(),
Err(_) => {
bail!("Failed to convert file URL to file path: {url}");
}
};
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
let path: String = urlencoding::decode(url.path())?.into_owned();

let module = url.query_pairs().find(|(k, _)| k == "id");
(
path,
Expand All @@ -24,36 +34,42 @@ pub async fn get_source_map_rope(
},
)
}
_ => bail!("Unknown url scheme"),
_ => bail!("Unknown url scheme '{}'", url.scheme()),
},
Err(_) => (file_path.to_string(), None),
Err(_) => (source_url.to_string(), None),
};

let Some(chunk_base) = file.strip_prefix(
&(format!(
"{}/{}/",
container.project().await?.project_path,
container.project().dist_dir().await?
)),
) else {
// File doesn't exist within the dist dir
return Ok(FileContent::NotFound.cell());
};
let chunk_base_unix =
match file_path_sys.strip_prefix(container.project().dist_dir_absolute().await?.as_str()) {
Some(relative_path) => sys_to_unix(relative_path),
None => {
// File doesn't exist within the dist dir
return Ok(FileContent::NotFound.cell());
}
};

let server_path = container.project().node_root().await?.join(chunk_base)?;
let server_path = container
.project()
.node_root()
.await?
.join(&chunk_base_unix)?;

let client_path = container.project().client_root().await?.join(chunk_base)?;
let client_path = container
.project()
.client_root()
.await?
.join(&chunk_base_unix)?;

let mut map = container.get_source_map(server_path, module.clone());

if map.await?.is_content() {
if !map.await?.is_content() {
// If the chunk doesn't exist as a server chunk, try a client chunk.
// TODO: Properly tag all server chunks and use the `isServer` query param.
// Currently, this is inaccurate as it does not cover RSC server
// chunks.
map = container.get_source_map(client_path, module);
if map.await?.is_content() {
bail!("chunk/module '{}' is missing a sourcemap", file_path);
if !map.await?.is_content() {
bail!("chunk/module '{}' is missing a sourcemap", source_url);
}
}

Expand Down
30 changes: 27 additions & 3 deletions crates/pack-core/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::path::{MAIN_SEPARATOR, Path};
use std::{borrow::Cow, path::Path, sync::LazyLock};

use anyhow::{Context, Result};
use bincode::{Decode, Encode};
use dunce::{canonicalize, simplified};
use regex::Regex;
use serde::Deserialize;
use turbo_rcstr::RcStr;
use turbo_tasks::{NonLocalValue, TaskInput, Vc, trace::TraceRawVcs};
Expand All @@ -11,6 +12,29 @@ use turbopack::{condition::ContextCondition, module_options::RuleCondition};

use crate::config::Config;

static WINDOWS_PATH: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[A-Za-z]:[/\\]|^\\\\").unwrap());

pub fn is_absolute_path(path: &str) -> bool {
if Path::new(path).is_absolute() {
return true;
}

WINDOWS_PATH.is_match(path)
}

pub fn to_unix_path(path: &str) -> Cow<'_, str> {
if path.contains('\\') {
Cow::Owned(path.replace('\\', "/"))
} else {
Cow::Borrowed(path)
}
}

pub fn strip_leading_separator(path: &str) -> &str {
path.trim_start_matches(['/', '\\'])
}

#[derive(
Default,
PartialEq,
Expand Down Expand Up @@ -91,10 +115,10 @@ pub async fn internal_assets_conditions() -> Result<ContextCondition> {
}

pub fn convert_to_project_relative(project_inside_path: &str, project_path: &str) -> Result<RcStr> {
if project_inside_path.starts_with(MAIN_SEPARATOR) {
if is_absolute_path(project_inside_path) {
pathdiff::diff_paths(
simplified(Path::new(project_inside_path)),
canonicalize(if project_path.starts_with(MAIN_SEPARATOR) {
canonicalize(if is_absolute_path(project_path) {
project_path.into()
} else {
let current_dir = std::env::current_dir().unwrap();
Expand Down
Loading