forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Port edge assert plugin to turbopack (vercel#69041)
### What? Port `MiddlewarePlugin` to turbopack ### Why? To make turbopack behave identically to webpack mode. ### How? Closes PACK-3196
- Loading branch information
Showing
12 changed files
with
241 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
crates/next-core/src/next_shared/transforms/next_edge_node_api_assert.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use anyhow::Result; | ||
use async_trait::async_trait; | ||
use next_custom_transforms::transforms::warn_for_edge_runtime::warn_for_edge_runtime; | ||
use swc_core::{ | ||
common::SyntaxContext, | ||
ecma::{ast::*, utils::ExprCtx, visit::VisitWith}, | ||
}; | ||
use turbo_tasks::Vc; | ||
use turbopack::module_options::{ModuleRule, ModuleRuleEffect}; | ||
use turbopack_ecmascript::{CustomTransformer, EcmascriptInputTransform, TransformContext}; | ||
|
||
use super::module_rule_match_js_no_url; | ||
|
||
pub fn next_edge_node_api_assert(enable_mdx_rs: bool, should_error: bool) -> ModuleRule { | ||
let transformer = | ||
EcmascriptInputTransform::Plugin(Vc::cell( | ||
Box::new(NextEdgeNodeApiAssert { should_error }) as _, | ||
)); | ||
ModuleRule::new( | ||
module_rule_match_js_no_url(enable_mdx_rs), | ||
vec![ModuleRuleEffect::ExtendEcmascriptTransforms { | ||
prepend: Vc::cell(vec![]), | ||
append: Vc::cell(vec![transformer]), | ||
}], | ||
) | ||
} | ||
|
||
#[derive(Debug)] | ||
struct NextEdgeNodeApiAssert { | ||
should_error: bool, | ||
} | ||
|
||
#[async_trait] | ||
impl CustomTransformer for NextEdgeNodeApiAssert { | ||
#[tracing::instrument(level = tracing::Level::TRACE, name = "next_edge_node_api_assert", skip_all)] | ||
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> { | ||
let mut visitor = warn_for_edge_runtime( | ||
ctx.source_map.clone(), | ||
ExprCtx { | ||
is_unresolved_ref_safe: false, | ||
unresolved_ctxt: SyntaxContext::empty().apply_mark(ctx.unresolved_mark), | ||
}, | ||
self.should_error, | ||
); | ||
program.visit_with(&mut visitor); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
crates/next-custom-transforms/src/transforms/warn_for_edge_runtime.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
use std::sync::Arc; | ||
|
||
use swc_core::{ | ||
common::{errors::HANDLER, SourceMap, Span}, | ||
ecma::{ | ||
ast::{Expr, Ident, MemberExpr, MemberProp}, | ||
utils::{ExprCtx, ExprExt}, | ||
visit::{Visit, VisitWith}, | ||
}, | ||
}; | ||
|
||
pub fn warn_for_edge_runtime(cm: Arc<SourceMap>, ctx: ExprCtx, should_error: bool) -> impl Visit { | ||
WarnForEdgeRuntime { | ||
cm, | ||
ctx, | ||
should_error, | ||
} | ||
} | ||
|
||
struct WarnForEdgeRuntime { | ||
cm: Arc<SourceMap>, | ||
ctx: ExprCtx, | ||
should_error: bool, | ||
} | ||
|
||
const EDGE_UNSUPPORTED_NODE_APIS: &[&str] = &[ | ||
"clearImmediate", | ||
"setImmediate", | ||
"BroadcastChannel", | ||
"ByteLengthQueuingStrategy", | ||
"CompressionStream", | ||
"CountQueuingStrategy", | ||
"DecompressionStream", | ||
"DomException", | ||
"MessageChannel", | ||
"MessageEvent", | ||
"MessagePort", | ||
"ReadableByteStreamController", | ||
"ReadableStreamBYOBRequest", | ||
"ReadableStreamDefaultController", | ||
"TransformStreamDefaultController", | ||
"WritableStreamDefaultController", | ||
]; | ||
|
||
impl WarnForEdgeRuntime { | ||
fn build_unsupported_api_error(&self, span: Span, api_name: &str) -> Option<()> { | ||
let loc = self.cm.lookup_line(span.lo).ok()?; | ||
|
||
let msg=format!("A Node.js API is used ({api_name} at line: {}) which is not supported in the Edge Runtime. | ||
Learn more: https://nextjs.org/docs/api-reference/edge-runtime",loc.line+1); | ||
|
||
HANDLER.with(|h| { | ||
if self.should_error { | ||
h.struct_span_err(span, &msg).emit(); | ||
} else { | ||
h.struct_span_warn(span, &msg).emit(); | ||
} | ||
}); | ||
|
||
None | ||
} | ||
|
||
fn is_in_middleware_layer(&self) -> bool { | ||
true | ||
} | ||
|
||
fn warn_for_unsupported_process_api(&self, span: Span, prop: &Ident) { | ||
if !self.is_in_middleware_layer() || prop.sym == "env" { | ||
return; | ||
} | ||
|
||
self.build_unsupported_api_error(span, &format!("process.{}", prop.sym)); | ||
} | ||
} | ||
|
||
impl Visit for WarnForEdgeRuntime { | ||
fn visit_member_expr(&mut self, n: &MemberExpr) { | ||
if n.obj.is_global_ref_to(&self.ctx, "process") { | ||
if let MemberProp::Ident(prop) = &n.prop { | ||
self.warn_for_unsupported_process_api(n.span, prop); | ||
return; | ||
} | ||
} | ||
|
||
n.visit_children_with(self); | ||
} | ||
|
||
fn visit_expr(&mut self, n: &Expr) { | ||
if let Expr::Ident(ident) = n { | ||
if ident.span.ctxt == self.ctx.unresolved_ctxt { | ||
for api in EDGE_UNSUPPORTED_NODE_APIS { | ||
if self.is_in_middleware_layer() && ident.sym == *api { | ||
self.build_unsupported_api_error(ident.span, api); | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
|
||
n.visit_children_with(self); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
crates/next-custom-transforms/tests/fixture/edge-assert/process-api/input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
console.log(process.loadEnvFile()) |
1 change: 1 addition & 0 deletions
1
crates/next-custom-transforms/tests/fixture/edge-assert/process-api/output.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
console.log(process.loadEnvFile()); |
6 changes: 6 additions & 0 deletions
6
crates/next-custom-transforms/tests/fixture/edge-assert/process-api/output.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
x A Node.js API is used (process.loadEnvFile at line: 1) which is not supported in the Edge Runtime. | ||
| Learn more: https://nextjs.org/docs/api-reference/edge-runtime | ||
,-[input.js:1:1] | ||
1 | console.log(process.loadEnvFile()) | ||
: ^^^^^^^^^^^^^^^^^^^ | ||
`---- |
1 change: 1 addition & 0 deletions
1
crates/next-custom-transforms/tests/fixture/edge-assert/process-env/input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
console.log(process.env.NODE_ENV) |
1 change: 1 addition & 0 deletions
1
crates/next-custom-transforms/tests/fixture/edge-assert/process-env/output.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
console.log(process.env.NODE_ENV); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters