Skip to content

Commit 2cadbd3

Browse files
committed
feat(turbopack): improve compile-time define value to support more data types and expr evaluation
1 parent 1d79c9f commit 2cadbd3

File tree

14 files changed

+285
-72
lines changed

14 files changed

+285
-72
lines changed

crates/next-core/src/util.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::future::Future;
1+
use std::str::FromStr;
22

33
use anyhow::{Context, Result, bail};
44
use serde::{Deserialize, Serialize, de::DeserializeOwned};
@@ -60,12 +60,9 @@ pub fn defines(define_env: &FxIndexMap<RcStr, Option<RcStr>>) -> CompileTimeDefi
6060
)
6161
.or_insert_with(|| {
6262
if let Some(v) = v {
63-
let val = serde_json::from_str(v);
63+
let val = serde_json::Value::from_str(v);
6464
match val {
65-
Ok(serde_json::Value::Bool(v)) => CompileTimeDefineValue::Bool(v),
66-
Ok(serde_json::Value::String(v)) => {
67-
CompileTimeDefineValue::String(v.into())
68-
}
65+
Ok(v) => v.into(),
6966
_ => CompileTimeDefineValue::Evaluate(v.clone()),
7067
}
7168
} else {

turbopack/crates/turbo-rcstr/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ impl AsRef<[u8]> for RcStr {
233233
}
234234
}
235235

236+
impl From<RcStr> for BytesStr {
237+
fn from(value: RcStr) -> Self {
238+
Self::from_str_slice(value.as_str())
239+
}
240+
}
241+
236242
impl PartialEq<str> for RcStr {
237243
fn eq(&self, other: &str) -> bool {
238244
self.as_str() == other

turbopack/crates/turbopack-core/src/compile_time_info.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Result;
22
use serde::{Deserialize, Serialize};
33
use turbo_rcstr::RcStr;
4-
use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, TaskInput, Vc, trace::TraceRawVcs};
4+
use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
55
use turbo_tasks_fs::FileSystemPath;
66

77
use crate::environment::Environment;
@@ -102,12 +102,16 @@ macro_rules! free_var_references {
102102
// TODO: replace with just a `serde_json::Value`
103103
// https://linear.app/vercel/issue/WEB-1641/compiletimedefinevalue-should-just-use-serde-jsonvalue
104104
#[turbo_tasks::value]
105-
#[derive(Debug, Clone, Hash, TaskInput)]
105+
#[derive(Debug, Clone, Hash)]
106106
pub enum CompileTimeDefineValue {
107+
Null,
107108
Bool(bool),
109+
Number(RcStr),
108110
String(RcStr),
109-
JSON(RcStr),
111+
Array(Vec<CompileTimeDefineValue>),
112+
Object(Vec<(RcStr, CompileTimeDefineValue)>),
110113
Undefined,
114+
Evaluate(RcStr),
111115
}
112116

113117
impl From<bool> for CompileTimeDefineValue {
@@ -136,7 +140,16 @@ impl From<&str> for CompileTimeDefineValue {
136140

137141
impl From<serde_json::Value> for CompileTimeDefineValue {
138142
fn from(value: serde_json::Value) -> Self {
139-
Self::JSON(value.to_string().into())
143+
match value {
144+
serde_json::Value::Null => Self::Null,
145+
serde_json::Value::Bool(b) => Self::Bool(b),
146+
serde_json::Value::Number(n) => Self::Number(n.to_string().into()),
147+
serde_json::Value::String(s) => Self::String(s.into()),
148+
serde_json::Value::Array(a) => Self::Array(a.into_iter().map(|i| i.into()).collect()),
149+
serde_json::Value::Object(m) => {
150+
Self::Object(m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
151+
}
152+
}
140153
}
141154
}
142155

turbopack/crates/turbopack-ecmascript/benches/analyzer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub fn benchmark(c: &mut Criterion) {
5757
program.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
5858

5959
let eval_context = EvalContext::new(
60-
&program,
60+
Some(&program),
6161
unresolved_mark,
6262
top_level_mark,
6363
Default::default(),

turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ use std::{
44
sync::Arc,
55
};
66

7+
use anyhow::{Ok, Result};
78
use rustc_hash::{FxHashMap, FxHashSet};
89
use swc_core::{
910
atoms::Atom,
10-
common::{GLOBALS, Mark, Span, Spanned, SyntaxContext, comments::Comments, pass::AstNodePath},
11+
base::try_with_handler,
12+
common::{
13+
GLOBALS, Mark, SourceMap, Span, Spanned, SyntaxContext, comments::Comments,
14+
pass::AstNodePath, sync::Lrc,
15+
},
1116
ecma::{
1217
ast::*,
1318
atoms::atom,
@@ -26,6 +31,7 @@ use super::{
2631
use crate::{
2732
SpecifiedModuleType,
2833
analyzer::{WellKnownObjectKind, is_unresolved},
34+
references::constant_value::parse_single_expr_lit,
2935
utils::{AstPathRange, unparen},
3036
};
3137

@@ -311,7 +317,7 @@ impl EvalContext {
311317
/// webpackIgnore or turbopackIgnore comments, you must pass those in,
312318
/// since the AST does not include comments by default.
313319
pub fn new(
314-
module: &Program,
320+
module: Option<&Program>,
315321
unresolved_mark: Mark,
316322
top_level_mark: Mark,
317323
force_free_values: Arc<FxHashSet<Id>>,
@@ -321,7 +327,9 @@ impl EvalContext {
321327
Self {
322328
unresolved_mark,
323329
top_level_mark,
324-
imports: ImportMap::analyze(module, source, comments),
330+
imports: module.map_or(ImportMap::default(), |m| {
331+
ImportMap::analyze(m, source, comments)
332+
}),
325333
force_free_values,
326334
}
327335
}
@@ -720,6 +728,29 @@ impl EvalContext {
720728
_ => JsValue::unknown_empty(true, "unsupported expression"),
721729
}
722730
}
731+
732+
pub fn eval_single_expr_lit(expr_lit: RcStr) -> Result<JsValue> {
733+
let cm = Lrc::new(SourceMap::default());
734+
735+
let js_value = try_with_handler(cm, Default::default(), |_| {
736+
GLOBALS.set(&Default::default(), || {
737+
let expr = parse_single_expr_lit(expr_lit);
738+
let eval_context = EvalContext::new(
739+
None,
740+
Mark::new(),
741+
Mark::new(),
742+
Default::default(),
743+
None,
744+
None,
745+
);
746+
747+
Ok(eval_context.eval(&expr))
748+
})
749+
})
750+
.map_err(|e| e.to_pretty_error())?;
751+
752+
Ok(js_value)
753+
}
723754
}
724755

725756
enum EarlyReturn {

turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ use turbopack_core::compile_time_info::{
3333

3434
use self::imports::ImportAnnotations;
3535
pub(crate) use self::imports::ImportMap;
36-
use crate::{references::require_context::RequireContextMap, utils::StringifyJs};
36+
use crate::{
37+
analyzer::graph::EvalContext, references::require_context::RequireContextMap,
38+
utils::StringifyJs,
39+
};
3740

3841
pub mod builtin;
3942
pub mod graph;
@@ -581,38 +584,73 @@ impl From<ConstantValue> for JsValue {
581584
}
582585
}
583586

584-
impl From<&CompileTimeDefineValue> for JsValue {
585-
fn from(v: &CompileTimeDefineValue) -> Self {
586-
match v {
587-
CompileTimeDefineValue::String(s) => JsValue::Constant(s.as_str().into()),
588-
CompileTimeDefineValue::Bool(b) => JsValue::Constant((*b).into()),
589-
CompileTimeDefineValue::JSON(_) => {
590-
JsValue::unknown_empty(false, "compile time injected JSON")
587+
impl TryFrom<&CompileTimeDefineValue> for JsValue {
588+
type Error = anyhow::Error;
589+
590+
fn try_from(value: &CompileTimeDefineValue) -> Result<Self> {
591+
match value {
592+
CompileTimeDefineValue::Null => Ok(JsValue::Constant(ConstantValue::Null)),
593+
CompileTimeDefineValue::Bool(b) => Ok(JsValue::Constant((*b).into())),
594+
CompileTimeDefineValue::Number(n) => Ok(JsValue::Constant(ConstantValue::Num(
595+
ConstantNumber(n.as_str().parse::<f64>()?),
596+
))),
597+
CompileTimeDefineValue::String(s) => Ok(JsValue::Constant(s.as_str().into())),
598+
CompileTimeDefineValue::Array(a) => {
599+
let mut js_value = JsValue::Array {
600+
total_nodes: a.len() as u32,
601+
items: a.iter().map(|i| i.try_into()).collect::<Result<Vec<_>>>()?,
602+
mutable: false,
603+
};
604+
js_value.update_total_nodes();
605+
Ok(js_value)
591606
}
592-
CompileTimeDefineValue::Undefined => JsValue::Constant(ConstantValue::Undefined),
607+
CompileTimeDefineValue::Object(m) => {
608+
let mut js_value = JsValue::Object {
609+
total_nodes: m.len() as u32,
610+
parts: m
611+
.iter()
612+
.map(|(k, v)| {
613+
Ok::<ObjectPart, anyhow::Error>(ObjectPart::KeyValue(
614+
k.clone().into(),
615+
v.try_into()?,
616+
))
617+
})
618+
.collect::<Result<Vec<_>>>()?,
619+
mutable: false,
620+
};
621+
js_value.update_total_nodes();
622+
Ok(js_value)
623+
}
624+
CompileTimeDefineValue::Undefined => Ok(JsValue::Constant(ConstantValue::Undefined)),
625+
CompileTimeDefineValue::Evaluate(s) => EvalContext::eval_single_expr_lit(s.clone()),
593626
}
594627
}
595628
}
596629

597-
impl From<&FreeVarReference> for JsValue {
598-
fn from(v: &FreeVarReference) -> Self {
599-
match v {
600-
FreeVarReference::Value(v) => v.into(),
630+
impl TryFrom<&FreeVarReference> for JsValue {
631+
type Error = anyhow::Error;
632+
633+
fn try_from(value: &FreeVarReference) -> Result<Self> {
634+
match value {
635+
FreeVarReference::Value(v) => v.try_into(),
601636
FreeVarReference::Ident(_) => {
602-
JsValue::unknown_empty(false, "compile time injected ident")
603-
}
604-
FreeVarReference::Member(_, _) => {
605-
JsValue::unknown_empty(false, "compile time injected member")
606-
}
607-
FreeVarReference::EcmaScriptModule { .. } => {
608-
JsValue::unknown_empty(false, "compile time injected free var module")
609-
}
610-
FreeVarReference::Error(_) => {
611-
JsValue::unknown_empty(false, "compile time injected free var error")
637+
Ok(JsValue::unknown_empty(false, "compile time injected ident"))
612638
}
639+
FreeVarReference::Member(_, _) => Ok(JsValue::unknown_empty(
640+
false,
641+
"compile time injected member",
642+
)),
643+
FreeVarReference::EcmaScriptModule { .. } => Ok(JsValue::unknown_empty(
644+
false,
645+
"compile time injected free var module",
646+
)),
647+
FreeVarReference::Error(_) => Ok(JsValue::unknown_empty(
648+
false,
649+
"compile time injected free var error",
650+
)),
613651
FreeVarReference::InputRelative(kind) => {
614652
use turbopack_core::compile_time_info::InputRelativeConstant;
615-
JsValue::unknown_empty(
653+
Ok(JsValue::unknown_empty(
616654
false,
617655
match kind {
618656
InputRelativeConstant::DirName => {
@@ -622,7 +660,7 @@ impl From<&FreeVarReference> for JsValue {
622660
"compile time injected free var referencing the file name"
623661
}
624662
},
625-
)
663+
))
626664
}
627665
}
628666
}
@@ -4069,7 +4107,7 @@ mod tests {
40694107
m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
40704108

40714109
let eval_context = EvalContext::new(
4072-
&m,
4110+
Some(&m),
40734111
unresolved_mark,
40744112
top_level_mark,
40754113
Default::default(),

turbopack/crates/turbopack-ecmascript/src/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ async fn parse_file_content(
497497
});
498498

499499
let eval_context = EvalContext::new(
500-
&parsed_program,
500+
Some(&parsed_program),
501501
unresolved_mark,
502502
top_level_mark,
503503
Arc::new(var_with_ts_declare),

0 commit comments

Comments
 (0)