Skip to content

Commit

Permalink
Merge branch 'canary' into update-with-web-worker-example
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam-Phillemon9493 authored Sep 13, 2024
2 parents 7a64044 + 1bcf0be commit 0c0e41c
Show file tree
Hide file tree
Showing 17 changed files with 140 additions and 67 deletions.
31 changes: 19 additions & 12 deletions crates/napi/src/next_api/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,8 @@ pub fn project_hmr_events(
async move {
let project = project.project().resolve().await?;
let state = project.hmr_version_state(identifier.clone(), session);
let update = hmr_update(project, identifier, state)

let update = hmr_update(project, identifier.clone(), state)
.strongly_consistent()
.await
.inspect_err(|e| log_panic_and_inform(e))?;
Expand All @@ -743,15 +744,15 @@ pub fn project_hmr_events(
diagnostics,
} = &*update;
match &**update {
Update::None => {}
Update::Missing | Update::None => {}
Update::Total(TotalUpdate { to }) => {
state.set(to.clone()).await?;
}
Update::Partial(PartialUpdate { to, .. }) => {
state.set(to.clone()).await?;
}
}
Ok((update.clone(), issues.clone(), diagnostics.clone()))
Ok((Some(update.clone()), issues.clone(), diagnostics.clone()))
}
.instrument(tracing::info_span!(
"HMR subscription",
Expand All @@ -775,14 +776,16 @@ pub fn project_hmr_events(
path: identifier.clone(),
headers: None,
};
let update = match &*update {
Update::Total(_) => ClientUpdateInstruction::restart(&identifier, &update_issues),
Update::Partial(update) => ClientUpdateInstruction::partial(
let update = match update.as_deref() {
None | Some(Update::Missing) | Some(Update::Total(_)) => {
ClientUpdateInstruction::restart(&identifier, &update_issues)
}
Some(Update::Partial(update)) => ClientUpdateInstruction::partial(
&identifier,
&update.instruction,
&update_issues,
),
Update::None => ClientUpdateInstruction::issues(&identifier, &update_issues),
Some(Update::None) => ClientUpdateInstruction::issues(&identifier, &update_issues),
};

Ok(vec![TurbopackResult {
Expand Down Expand Up @@ -1030,18 +1033,22 @@ pub async fn project_trace_source(
.client_relative_path()
.join(chunk_base.into());

let mut map_result = project
let mut map = project
.container
.get_source_map(server_path, module.clone())
.await;
if map_result.is_err() {
.await?;

if map.is_none() {
// 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_result = project.container.get_source_map(client_path, module).await;
map = project
.container
.get_source_map(client_path, module)
.await?;
}
let map = map_result?.context("chunk/module is missing a sourcemap")?;
let map = map.context("chunk/module is missing a sourcemap")?;

let Some(line) = frame.line else {
return Ok(None);
Expand Down
33 changes: 21 additions & 12 deletions crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ use turbopack_core::{
output::{OutputAsset, OutputAssets},
resolve::{find_context_file, FindContextFileResult},
source_map::OptionSourceMap,
version::{Update, Version, VersionState, VersionedContent},
version::{
NotFoundVersion, OptionVersionedContent, Update, Version, VersionState, VersionedContent,
},
PROJECT_FILESYSTEM_NAME,
};
use turbopack_node::execution_context::ExecutionContext;
Expand Down Expand Up @@ -1155,22 +1157,23 @@ impl Project {
}

#[turbo_tasks::function]
async fn hmr_content(
self: Vc<Self>,
identifier: RcStr,
) -> Result<Vc<Box<dyn VersionedContent>>> {
async fn hmr_content(self: Vc<Self>, identifier: RcStr) -> Result<Vc<OptionVersionedContent>> {
if let Some(map) = self.await?.versioned_content_map {
Ok(map.get(self.client_relative_path().join(identifier)))
let content = map.get(self.client_relative_path().join(identifier.clone()));
Ok(content)
} else {
bail!("must be in dev mode to hmr")
}
}

#[turbo_tasks::function]
async fn hmr_version(self: Vc<Self>, identifier: RcStr) -> Result<Vc<Box<dyn Version>>> {
let content = self.hmr_content(identifier);

Ok(content.version())
let content = self.hmr_content(identifier).await?;
if let Some(content) = &*content {
Ok(content.version())
} else {
Ok(Vc::upcast(NotFoundVersion::new()))
}
}

/// Get the version state for a session. Initialized with the first seen
Expand All @@ -1190,12 +1193,13 @@ impl Project {
// INVALIDATION: This is intentionally untracked to avoid invalidating this
// function completely. We want to initialize the VersionState with the
// first seen version of the session.
VersionState::new(
let state = VersionState::new(
version
.into_trait_ref_strongly_consistent_untracked()
.await?,
)
.await
.await?;
Ok(state)
}

/// Emits opaque HMR events whenever a change is detected in the chunk group
Expand All @@ -1207,7 +1211,12 @@ impl Project {
from: Vc<VersionState>,
) -> Result<Vc<Update>> {
let from = from.get();
Ok(self.hmr_content(identifier).update(from))
let content = self.hmr_content(identifier).await?;
if let Some(content) = *content {
Ok(content.update(from))
} else {
Ok(Update::Missing.cell())
}
}

/// Gets a list of all HMR identifiers that can be subscribed to. This is
Expand Down
34 changes: 24 additions & 10 deletions crates/next-api/src/versioned_content_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use turbo_tasks::{
use turbo_tasks_fs::FileSystemPath;
use turbopack_core::{
asset::Asset,
output::{OutputAsset, OutputAssets},
output::{OptionOutputAsset, OutputAsset, OutputAssets},
source_map::{GenerateSourceMap, OptionSourceMap},
version::VersionedContent,
version::OptionVersionedContent,
};

/// An unresolved output assets operation. We need to pass an operation here as
Expand All @@ -26,13 +26,15 @@ pub struct OutputAssetsOperation(Vc<OutputAssets>);
struct MapEntry {
assets_operation: Vc<OutputAssets>,
side_effects: Vc<Completion>,
/// Precomputed map for quick access to output asset by filepath
path_to_asset: HashMap<Vc<FileSystemPath>, Vc<Box<dyn OutputAsset>>>,
}

#[turbo_tasks::value(transparent)]
struct OptionMapEntry(Option<MapEntry>);

type PathToOutputOperation = HashMap<Vc<FileSystemPath>, IndexSet<Vc<OutputAssets>>>;
// A precomputed map for quick access to output asset by filepath
type OutputOperationToComputeEntry = HashMap<Vc<OutputAssets>, Vc<OptionMapEntry>>;

#[turbo_tasks::value]
Expand Down Expand Up @@ -67,6 +69,7 @@ impl VersionedContentMap {
#[turbo_tasks::function]
pub async fn insert_output_assets(
self: Vc<Self>,
// Output assets to emit
assets_operation: Vc<OutputAssetsOperation>,
node_root: Vc<FileSystemPath>,
client_relative_path: Vc<FileSystemPath>,
Expand All @@ -88,6 +91,8 @@ impl VersionedContentMap {
Ok(entry.side_effects)
}

/// Creates a ComputEntry (a pre-computed map for optimized lookup) for an output assets
/// operation. When assets change, map_path_to_op is updated.
#[turbo_tasks::function]
async fn compute_entry(
self: Vc<Self>,
Expand Down Expand Up @@ -148,8 +153,13 @@ impl VersionedContentMap {
}

#[turbo_tasks::function]
pub fn get(self: Vc<Self>, path: Vc<FileSystemPath>) -> Vc<Box<dyn VersionedContent>> {
self.get_asset(path).versioned_content()
pub async fn get(
self: Vc<Self>,
path: Vc<FileSystemPath>,
) -> Result<Vc<OptionVersionedContent>> {
Ok(Vc::cell(
(*self.get_asset(path).await?).map(|a| a.versioned_content()),
))
}

#[turbo_tasks::function]
Expand All @@ -158,8 +168,12 @@ impl VersionedContentMap {
path: Vc<FileSystemPath>,
section: Option<RcStr>,
) -> Result<Vc<OptionSourceMap>> {
let Some(asset) = &*self.get_asset(path).await? else {
return Ok(Vc::cell(None));
};

if let Some(generate_source_map) =
Vc::try_resolve_sidecast::<Box<dyn GenerateSourceMap>>(self.get_asset(path)).await?
Vc::try_resolve_sidecast::<Box<dyn GenerateSourceMap>>(*asset).await?
{
Ok(if let Some(section) = section {
generate_source_map.by_section(section)
Expand All @@ -176,7 +190,7 @@ impl VersionedContentMap {
pub async fn get_asset(
self: Vc<Self>,
path: Vc<FileSystemPath>,
) -> Result<Vc<Box<dyn OutputAsset>>> {
) -> Result<Vc<OptionOutputAsset>> {
let result = self.raw_get(path).await?;
if let Some(MapEntry {
assets_operation: _,
Expand All @@ -187,17 +201,17 @@ impl VersionedContentMap {
side_effects.await?;

if let Some(asset) = path_to_asset.get(&path) {
return Ok(*asset);
return Ok(Vc::cell(Some(*asset)));
} else {
let path = path.to_string().await?;
bail!(
"could not find asset for path {} (asset has been removed)",
path
path,
);
}
}
let path = path.to_string().await?;
bail!("could not find asset for path {}", path);

Ok(Vc::cell(None))
}

#[turbo_tasks::function]
Expand Down
8 changes: 2 additions & 6 deletions crates/next-build-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,8 @@ async fn hmr(tt: &TurboTasks<MemoryBackend>, project: Vc<ProjectContainer>) -> R
let session = session.clone();
async move {
let project = project.project();
project
.hmr_update(
ident.clone(),
project.hmr_version_state(ident.clone(), session),
)
.await?;
let state = project.hmr_version_state(ident.clone(), session);
project.hmr_update(ident.clone(), state).await?;
Ok(Vc::<()>::cell(()))
}
});
Expand Down
7 changes: 5 additions & 2 deletions crates/next-core/src/next_server/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,17 @@ pub async fn get_next_server_transforms_rules(
// optimize_use_state))

rules.push(get_next_image_rule());
}

if let NextRuntime::Edge = next_runtime {
rules.push(get_middleware_dynamic_assert_rule(mdx_rs));
if let NextRuntime::Edge = next_runtime {
rules.push(get_middleware_dynamic_assert_rule(mdx_rs));

if !foreign_code {
rules.push(next_edge_node_api_assert(
mdx_rs,
matches!(context_ty, ServerContextType::Middleware { .. })
&& matches!(*mode.await?, NextMode::Build),
matches!(*mode.await?, NextMode::Build),
));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ use super::module_rule_match_js_no_url;
pub fn next_edge_node_api_assert(
enable_mdx_rs: bool,
should_error_for_node_apis: bool,
is_production: bool,
) -> ModuleRule {
let transformer = EcmascriptInputTransform::Plugin(Vc::cell(Box::new(NextEdgeNodeApiAssert {
should_error_for_node_apis,
is_production,
}) as _));
ModuleRule::new(
module_rule_match_js_no_url(enable_mdx_rs),
Expand All @@ -30,6 +32,7 @@ pub fn next_edge_node_api_assert(
#[derive(Debug)]
struct NextEdgeNodeApiAssert {
should_error_for_node_apis: bool,
is_production: bool,
}

#[async_trait]
Expand All @@ -43,6 +46,7 @@ impl CustomTransformer for NextEdgeNodeApiAssert {
unresolved_ctxt: SyntaxContext::empty().apply_mark(ctx.unresolved_mark),
},
self.should_error_for_node_apis,
self.is_production,
);
program.visit_with(&mut visitor);
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub fn warn_for_edge_runtime(
cm: Arc<SourceMap>,
ctx: ExprCtx,
should_error_for_node_apis: bool,
is_production: bool,
) -> impl Visit {
WarnForEdgeRuntime {
cm,
Expand All @@ -26,6 +27,7 @@ pub fn warn_for_edge_runtime(
should_add_guards: false,
guarded_symbols: Default::default(),
guarded_process_props: Default::default(),
is_production,
}
}

Expand All @@ -39,6 +41,7 @@ struct WarnForEdgeRuntime {
/// `if(typeof clearImmediate !== "function") clearImmediate();`
guarded_symbols: FxHashSet<Atom>,
guarded_process_props: FxHashSet<Atom>,
is_production: bool,
}

const EDGE_UNSUPPORTED_NODE_APIS: &[&str] = &[
Expand Down Expand Up @@ -232,6 +235,18 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime",
_ => (),
}
}

fn emit_dynamic_not_allowed_error(&self, span: Span) {
if self.is_production {
let msg = "Dynamic Code Evaluation (e. g. 'eval', 'new Function', \
'WebAssembly.compile') not allowed in Edge Runtime"
.to_string();

HANDLER.with(|h| {
h.struct_span_err(span, &msg).emit();
});
}
}
}

impl Visit for WarnForEdgeRuntime {
Expand Down Expand Up @@ -266,6 +281,11 @@ impl Visit for WarnForEdgeRuntime {
fn visit_expr(&mut self, n: &Expr) {
if let Expr::Ident(ident) = n {
if ident.ctxt == self.ctx.unresolved_ctxt {
if ident.sym == "eval" {
self.emit_dynamic_not_allowed_error(ident.span);
return;
}

for api in EDGE_UNSUPPORTED_NODE_APIS {
if self.is_in_middleware_layer() && ident.sym == *api {
self.emit_unsupported_api_error(ident.span, api);
Expand Down
1 change: 1 addition & 0 deletions crates/next-custom-transforms/tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ fn test_edge_assert(input: PathBuf) {
is_unresolved_ref_safe: false,
unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
},
true,
true
))
)
Expand Down
4 changes: 3 additions & 1 deletion test/integration/dynamic-routing/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1542,7 +1542,9 @@ function runTests({ dev }) {
'/d/[id]': 'pages/d/[id].html',
'/dash/[hello-world]': 'pages/dash/[hello-world].html',
'/': 'pages/index.html',
'/index/[...slug]': 'pages/index/[...slug].html',
'/index/[...slug]': process.env.TURBOPACK
? 'pages/index/index/[...slug].html'
: 'pages/index/[...slug].html',
'/on-mount/[post]': 'pages/on-mount/[post].html',
'/p1/p2/all-ssg/[...rest]': 'pages/p1/p2/all-ssg/[...rest].js',
'/p1/p2/all-ssr/[...rest]': 'pages/p1/p2/all-ssr/[...rest].js',
Expand Down
Loading

0 comments on commit 0c0e41c

Please sign in to comment.