Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code frame and sourcemapped error support for Turbopack #56727

Merged
merged 23 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
998c03d
[WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 11, 2023
b6f5e22
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 11, 2023
ed6c1ad
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 12, 2023
e193bfa
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 12, 2023
964b756
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 12, 2023
75d6d32
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 16, 2023
da8d778
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 16, 2023
14f9129
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 16, 2023
ede8ad8
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 16, 2023
f2069e4
Merge remote-tracking branch 'origin/canary' into wbinnssmith/codeframe
wbinnssmith Oct 16, 2023
8d93a00
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 16, 2023
d70a8f6
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 17, 2023
a3d8856
Revert "fixup! [WIP] Code frame and sourcemapped error support for Tu…
wbinnssmith Oct 17, 2023
f76097e
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 17, 2023
f2da815
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 17, 2023
ced85d6
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 17, 2023
a4ad562
Merge remote-tracking branch 'origin/canary' into wbinnssmith/codeframe
wbinnssmith Oct 17, 2023
3fb2a2c
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 17, 2023
fe432d3
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 18, 2023
8af63bc
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 18, 2023
3307238
Merge branch 'canary' into wbinnssmith/codeframe
kodiakhq[bot] Oct 19, 2023
a9da295
Merge branch 'canary' into wbinnssmith/codeframe
kodiakhq[bot] Oct 19, 2023
002571d
fixup! [WIP] Code frame and sourcemapped error support for Turbopack
wbinnssmith Oct 19, 2023
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/next-swc/crates/napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ turbopack-binding = { workspace = true, features = [
"__turbopack",
"__turbopack_ecmascript_hmr_protocol",
] }
url = {workspace = true}

[target.'cfg(not(all(target_os = "linux", target_env = "musl", target_arch = "aarch64")))'.dependencies]
turbopack-binding = { workspace = true, features = ["__turbo_tasks_malloc"] }
Expand Down
131 changes: 129 additions & 2 deletions packages/next-swc/crates/napi/src/next_api/project.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{path::PathBuf, sync::Arc, time::Duration};

use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use napi::{
bindgen_prelude::External,
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
Expand All @@ -18,7 +18,10 @@ use tracing_subscriber::{
};
use turbo_tasks::{TransientInstance, TurboTasks, UpdateInfo, Vc};
use turbopack_binding::{
turbo::tasks_memory::MemoryBackend,
turbo::{
tasks_fs::{FileContent, FileSystem},
tasks_memory::MemoryBackend,
},
turbopack::{
cli_utils::{
exit::ExitGuard,
Expand All @@ -28,11 +31,13 @@ use turbopack_binding::{
},
core::{
error::PrettyPrintError,
source_map::{GenerateSourceMap, Token},
version::{PartialUpdate, TotalUpdate, Update},
},
ecmascript_hmr_protocol::{ClientUpdateInstruction, ResourceIdentifier},
},
};
use url::Url;

use super::{
endpoint::ExternalEndpoint,
Expand Down Expand Up @@ -607,3 +612,125 @@ pub fn project_update_info_subscribe(
});
Ok(())
}

#[turbo_tasks::value]
#[derive(Debug)]
#[napi(object)]
pub struct StackFrame {
pub file: String,
pub method_name: Option<String>,
pub line: u32,
pub column: Option<u32>,
}

#[napi]
pub async fn project_trace_source(
#[napi(ts_arg_type = "{ __napiType: \"Project\" }")] project: External<ProjectInstance>,
frame: StackFrame,
) -> napi::Result<Option<StackFrame>> {
let turbo_tasks = project.turbo_tasks.clone();
let traced_frame = turbo_tasks
.run_once(async move {
let file = match Url::parse(&frame.file) {
Ok(url) => match url.scheme() {
"file" => url.path().to_string(),
_ => bail!("Unknown url scheme"),
},
Err(_) => frame.file.to_string(),
};

let Some(chunk_base) = file.strip_prefix(
&(format!(
"{}/{}",
project.container.project().await?.root_path,
project.container.project().dist_dir().await?
)),
) else {
// File doesn't exist within the dist dir
return Ok(None);
};

let chunk_path = format!(
"{}{}",
project
.container
.project()
.client_relative_path()
.await?
.path,
chunk_base
);

let path = project
.container
.project()
.client_root()
.fs()
.root()
.join(chunk_path);

let Some(generatable): Option<Vc<Box<dyn GenerateSourceMap>>> =
Vc::try_resolve_sidecast(project.container.get_versioned_content(path)).await?
else {
return Ok(None);
};

let map = generatable
.generate_source_map()
.await?
.context("Chunk is missing a sourcemap")?;

let token = map
.lookup_token(frame.line as usize, frame.column.unwrap_or(0) as usize)
.await?
.clone_value()
.context("Unable to trace token from sourcemap")?;

let Token::Original(token) = token else {
return Ok(None);
};

let Some(source_file) = token.original_file.strip_prefix("/turbopack/[project]/")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be derived form somewhere from wherever has these names set, instead of us hardcoding again?

else {
bail!("Original file outside project")
};

Ok(Some(StackFrame {
file: source_file.to_string(),
method_name: token.name,
line: token.original_line as u32,
column: Some(token.original_column as u32),
}))
})
.await
.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string()))?;
Ok(traced_frame)
}

#[napi]
pub async fn project_get_source_for_asset(
#[napi(ts_arg_type = "{ __napiType: \"Project\" }")] project: External<ProjectInstance>,
file_path: String,
) -> napi::Result<Option<String>> {
let turbo_tasks = project.turbo_tasks.clone();
let source = turbo_tasks
.run_once(async move {
let source_content = &*project
.container
.project()
.project_path()
.join(file_path.to_string())
.read()
.await?;

let FileContent::Content(source_content) = source_content else {
return Ok(None);
};

Ok(Some(source_content.content().to_str()?.to_string()))
})
.await
.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string()))?;

Ok(source)
}
24 changes: 19 additions & 5 deletions packages/next-swc/crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,22 @@ impl ProjectContainer {
pub fn hmr_identifiers(self: Vc<Self>) -> Vc<Vec<String>> {
self.project().hmr_identifiers()
}

#[turbo_tasks::function]
pub async fn get_versioned_content(
self: Vc<Self>,
file_path: Vc<FileSystemPath>,
) -> Result<Vc<Box<dyn VersionedContent>>> {
let this = self.await?;
Ok(this.versioned_content_map.get(file_path))
}
}

#[turbo_tasks::value]
pub struct Project {
/// A root path from which all files must be nested under. Trying to access
/// a file outside this root will fail. Think of this as a chroot.
root_path: String,
pub root_path: String,

/// A path where to emit the build outputs. next.config.js's distDir.
dist_dir: String,
Expand Down Expand Up @@ -374,13 +383,18 @@ impl Project {
}

#[turbo_tasks::function]
pub(super) async fn node_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
pub async fn dist_dir(self: Vc<Self>) -> Result<Vc<String>> {
Ok(Vc::cell(self.await?.dist_dir.to_string()))
}

#[turbo_tasks::function]
pub async fn node_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
let this = self.await?;
Ok(self.node_fs().root().join(this.dist_dir.to_string()))
}

#[turbo_tasks::function]
pub(super) fn client_root(self: Vc<Self>) -> Vc<FileSystemPath> {
pub fn client_root(self: Vc<Self>) -> Vc<FileSystemPath> {
self.client_fs().root()
}

Expand All @@ -399,7 +413,7 @@ impl Project {
}

#[turbo_tasks::function]
pub(super) async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
pub async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
let next_config = self.next_config().await?;
Ok(self.client_root().join(format!(
"{}/_next",
Expand All @@ -411,7 +425,7 @@ impl Project {
}

#[turbo_tasks::function]
pub(super) async fn project_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
pub async fn project_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
let this = self.await?;
let root = self.project_root_path();
let project_relative = this.project_path.strip_prefix(&this.root_path).unwrap();
Expand Down
17 changes: 17 additions & 0 deletions packages/next/src/build/swc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,13 @@ export interface HmrIdentifiers {
identifiers: string[]
}

export interface StackFrame {
file: string
methodName: string | null
line: number
column: number | null
}

export interface UpdateInfo {
duration: number
tasks: number
Expand All @@ -540,6 +547,8 @@ export interface Project {
hmrIdentifiersSubscribe(): AsyncIterableIterator<
TurbopackResult<HmrIdentifiers>
>
getSourceForAsset(filePath: string): Promise<string | null>
traceSource(stackFrame: StackFrame): Promise<StackFrame | null>
updateInfoSubscribe(): AsyncIterableIterator<TurbopackResult<UpdateInfo>>
}

Expand Down Expand Up @@ -916,6 +925,14 @@ function bindingToApi(binding: any, _wasm: boolean) {
return subscription
}

traceSource(stackFrame: StackFrame): Promise<StackFrame | null> {
return binding.projectTraceSource(this._nativeProject, stackFrame)
}

getSourceForAsset(filePath: string): Promise<string | null> {
return binding.projectGetSourceForAsset(this._nativeProject, filePath)
}

updateInfoSubscribe() {
const subscription = subscribe<TurbopackResult<UpdateInfo>>(
true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@ export function getOriginalStackFrame(
}

if (
!(
source.file?.startsWith('webpack-internal:') ||
source.file?.startsWith('file:')
)
source.file === '<anonymous>' ||
source.file?.match(/^node:/) ||
source.file?.match(/https?:\/\//)
) {
return Promise.resolve({
error: false,
Expand Down
Loading
Loading