Skip to content

Commit

Permalink
allow to alias free vars to modules (#4368)
Browse files Browse the repository at this point in the history
### Description

Allows to e. g. alias `Buffer` to `import { Buffer } from "node:buffer"`

Basically implements the webpack `ProvidePlugin`

### Testing Instructions

Tests added in next.js PR
  • Loading branch information
sokra authored Mar 29, 2023
1 parent 07c4f1c commit ad2bf56
Show file tree
Hide file tree
Showing 84 changed files with 14,981 additions and 421,717 deletions.
8 changes: 2 additions & 6 deletions crates/node-file-trace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use turbopack::{
use turbopack_cli_utils::issue::{ConsoleUiVc, IssueSeverityCliOption, LogOptions};
use turbopack_core::{
asset::{Asset, AssetVc, AssetsVc},
compile_time_info::{CompileTimeDefinesVc, CompileTimeInfo},
compile_time_info::CompileTimeInfo,
context::{AssetContext, AssetContextVc},
environment::{EnvironmentIntention, EnvironmentVc, ExecutionEnvironment, NodeJsEnvironment},
issue::{IssueContextExt, IssueReporter, IssueSeverity, IssueVc},
Expand Down Expand Up @@ -646,11 +646,7 @@ async fn create_module_asset(
)),
Value::new(EnvironmentIntention::Api),
);
let compile_time_info = CompileTimeInfo {
environment: env,
defines: CompileTimeDefinesVc::empty(),
}
.cell();
let compile_time_info = CompileTimeInfo::builder(env).cell();
let glob_mappings = vec![
(
root,
Expand Down
93 changes: 84 additions & 9 deletions crates/turbopack-core/src/compile_time_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,65 @@ use std::collections::HashMap;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use turbo_tasks::trace::TraceRawVcs;
use turbo_tasks_fs::FileSystemPathVc;

use crate::environment::EnvironmentVc;

// TODO stringify split map collect could be optimized with a marco
#[macro_export]
macro_rules! compile_time_defines_internal {
macro_rules! definable_name_map_internal {
($map:ident, $($name:ident).+ = $value:expr) => {
$map.insert(
$crate::compile_time_defines_internal!($($name).+).into(),
$crate::definable_name_map_internal!($($name).+).into(),
$value.into()
);
};
($map:ident, $($name:ident).+ = $value:expr,) => {
$map.insert(
$crate::compile_time_defines_internal!($($name).+).into(),
$crate::definable_name_map_internal!($($name).+).into(),
$value.into()
);
};
($map:ident, $($name:ident).+ = $value:expr, $($more:tt)+) => {
$crate::compile_time_defines_internal!($map, $($name).+ = $value);
$crate::compile_time_defines_internal!($map, $($more)+);
$crate::definable_name_map_internal!($map, $($name).+ = $value);
$crate::definable_name_map_internal!($map, $($more)+);
};
($name:ident) => {
[stringify!($name).to_string()]
};
($name:ident . $($more:ident).+) => {
$crate::compile_time_defines_internal!($($more).+, [stringify!($name).to_string()])
$crate::definable_name_map_internal!($($more).+, [stringify!($name).to_string()])
};
($name:ident, [$($array:expr),+]) => {
[$($array),+, stringify!($name).to_string()]
};
($name:ident . $($more:ident).+, [$($array:expr),+]) => {
$crate::compile_time_defines_internal!($($more).+, [$($array),+, stringify!($name).to_string()])
$crate::definable_name_map_internal!($($more).+, [$($array),+, stringify!($name).to_string()])
};
}

// TODO stringify split map collect could be optimized with a marco
#[macro_export]
macro_rules! compile_time_defines {
($($more:tt)+) => {
{
let mut map = std::collections::HashMap::new();
$crate::compile_time_defines_internal!(map, $($more)+);
$crate::definable_name_map_internal!(map, $($more)+);
$crate::compile_time_info::CompileTimeDefines(map)
}
};
}

#[macro_export]
macro_rules! free_var_references {
($($more:tt)+) => {
{
let mut map = std::collections::HashMap::new();
$crate::definable_name_map_internal!(map, $($more)+);
$crate::compile_time_info::FreeVarReferences(map)
}
};
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs)]
pub enum CompileTimeDefineValue {
Bool(bool),
Expand Down Expand Up @@ -86,10 +97,41 @@ impl CompileTimeDefinesVc {
}
}

#[turbo_tasks::value]
pub enum FreeVarReference {
EcmaScriptModule {
request: String,
context: Option<FileSystemPathVc>,
export: Option<String>,
},
}

#[turbo_tasks::value(transparent)]
pub struct FreeVarReferences(pub HashMap<Vec<String>, FreeVarReference>);

#[turbo_tasks::value_impl]
impl FreeVarReferencesVc {
#[turbo_tasks::function]
pub fn empty() -> Self {
Self::cell(HashMap::new())
}
}

#[turbo_tasks::value(shared)]
pub struct CompileTimeInfo {
pub environment: EnvironmentVc,
pub defines: CompileTimeDefinesVc,
pub free_var_references: FreeVarReferencesVc,
}

impl CompileTimeInfo {
pub fn builder(environment: EnvironmentVc) -> CompileTimeInfoBuilder {
CompileTimeInfoBuilder {
environment,
defines: None,
free_var_references: None,
}
}
}

#[turbo_tasks::value_impl]
Expand All @@ -99,6 +141,7 @@ impl CompileTimeInfoVc {
CompileTimeInfo {
environment,
defines: CompileTimeDefinesVc::empty(),
free_var_references: FreeVarReferencesVc::empty(),
}
.cell()
}
Expand All @@ -108,3 +151,35 @@ impl CompileTimeInfoVc {
Ok(self.await?.environment)
}
}

pub struct CompileTimeInfoBuilder {
environment: EnvironmentVc,
defines: Option<CompileTimeDefinesVc>,
free_var_references: Option<FreeVarReferencesVc>,
}

impl CompileTimeInfoBuilder {
pub fn defines(mut self, defines: CompileTimeDefinesVc) -> Self {
self.defines = Some(defines);
self
}

pub fn free_var_references(mut self, free_var_references: FreeVarReferencesVc) -> Self {
self.free_var_references = Some(free_var_references);
self
}

pub fn build(self) -> CompileTimeInfo {
CompileTimeInfo {
environment: self.environment,
defines: self.defines.unwrap_or_else(CompileTimeDefinesVc::empty),
free_var_references: self
.free_var_references
.unwrap_or_else(FreeVarReferencesVc::empty),
}
}

pub fn cell(self) -> CompileTimeInfoVc {
self.build().cell()
}
}
25 changes: 11 additions & 14 deletions crates/turbopack-ecmascript/benches/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use swc_core::{
use turbo_tasks::Value;
use turbo_tasks_testing::VcStorage;
use turbopack_core::{
compile_time_info::{CompileTimeDefinesVc, CompileTimeInfo},
compile_time_info::CompileTimeInfo,
environment::{EnvironmentIntention, EnvironmentVc, ExecutionEnvironment, NodeJsEnvironment},
target::CompileTargetVc,
};
Expand Down Expand Up @@ -93,19 +93,16 @@ fn bench_link(b: &mut Bencher, input: &BenchInput) {
b.to_async(rt).iter(|| async {
for val in input.var_graph.values.values() {
VcStorage::with(async {
let compile_time_info = CompileTimeInfo {
environment: EnvironmentVc::new(
Value::new(ExecutionEnvironment::NodeJsLambda(
NodeJsEnvironment {
compile_target: CompileTargetVc::unknown(),
..Default::default()
}
.into(),
)),
Value::new(EnvironmentIntention::ServerRendering),
),
defines: CompileTimeDefinesVc::empty(),
}
let compile_time_info = CompileTimeInfo::builder(EnvironmentVc::new(
Value::new(ExecutionEnvironment::NodeJsLambda(
NodeJsEnvironment {
compile_target: CompileTargetVc::unknown(),
..Default::default()
}
.into(),
)),
Value::new(EnvironmentIntention::ServerRendering),
))
.cell();
link(
&input.var_graph,
Expand Down
5 changes: 3 additions & 2 deletions crates/turbopack-ecmascript/src/analyzer/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{mem::take, sync::Arc};

use swc_core::ecma::atoms::js_word;

use super::{ConstantNumber, ConstantValue, JsValue, LogicalOperator, ObjectPart};
use crate::analyzer::FreeVarKind;

/// Replaces some builtin values with their resulting values. Called early
/// without lazy nested values. This allows to skip a lot of work to process the
Expand Down Expand Up @@ -247,7 +248,7 @@ pub fn replace_builtin(value: &mut JsValue) -> bool {
}
}
if potential_values.is_empty() {
*value = JsValue::FreeVar(FreeVarKind::Other("undefined".into()));
*value = JsValue::FreeVar(js_word!("undefined"));
} else {
*value = potential_values_to_alternatives(
potential_values,
Expand Down
45 changes: 27 additions & 18 deletions crates/turbopack-ecmascript/src/analyzer/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ use swc_core::{
};

use super::{ConstantNumber, ConstantValue, ImportMap, JsValue, ObjectPart, WellKnownFunctionKind};
use crate::{
analyzer::{is_unresolved, FreeVarKind},
utils::unparen,
};
use crate::{analyzer::is_unresolved, utils::unparen};

#[derive(Debug, Clone, Default)]
pub struct EffectsBlock {
Expand Down Expand Up @@ -148,6 +145,13 @@ pub enum Effect {
ast_path: Vec<AstParentKind>,
span: Span,
},
/// A reference to a free var access.
FreeVar {
var: JsValue,
ast_path: Vec<AstParentKind>,
span: Span,
},
// TODO ImportMeta should be replaced with Member
/// A reference to `import.meta`.
ImportMeta {
ast_path: Vec<AstParentKind>,
Expand Down Expand Up @@ -207,6 +211,13 @@ impl Effect {
obj.normalize();
prop.normalize();
}
Effect::FreeVar {
var,
ast_path: _,
span: _,
} => {
var.normalize();
}
Effect::ImportedBinding {
esm_reference_index: _,
export: _,
Expand Down Expand Up @@ -345,15 +356,7 @@ impl EvalContext {
return imported;
}
if is_unresolved(i, self.unresolved_mark) {
match &*i.sym {
"require" => JsValue::FreeVar(FreeVarKind::Require),
"define" => JsValue::FreeVar(FreeVarKind::Define),
"__dirname" => JsValue::FreeVar(FreeVarKind::Dirname),
"__filename" => JsValue::FreeVar(FreeVarKind::Filename),
"process" => JsValue::FreeVar(FreeVarKind::NodeProcess),
"Object" => JsValue::FreeVar(FreeVarKind::Object),
_ => JsValue::FreeVar(FreeVarKind::Other(i.sym.clone())),
}
JsValue::FreeVar(i.sym.clone())
} else {
JsValue::Variable(id)
}
Expand Down Expand Up @@ -551,7 +554,7 @@ impl EvalContext {
}
let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();

let callee = box JsValue::FreeVar(FreeVarKind::Import);
let callee = box JsValue::FreeVar(js_word!("import"));

JsValue::call(callee, args)
}
Expand All @@ -566,7 +569,7 @@ impl EvalContext {
.iter()
.map(|e| match e {
Some(e) => self.eval(&e.expr),
_ => JsValue::FreeVar(FreeVarKind::Other(js_word!("undefined"))),
_ => JsValue::FreeVar(js_word!("undefined")),
})
.collect();
JsValue::array(arr)
Expand Down Expand Up @@ -867,7 +870,7 @@ impl Analyzer<'_> {
match &n.callee {
Callee::Import(_) => {
self.add_effect(Effect::Call {
func: JsValue::FreeVar(FreeVarKind::Import),
func: JsValue::FreeVar(js_word!("import")),
args,
ast_path: as_parent_path(ast_path),
span: n.span(),
Expand Down Expand Up @@ -933,7 +936,7 @@ impl Analyzer<'_> {
let values = self.cur_fn_return_values.take().unwrap();

match values.len() {
0 => box JsValue::FreeVar(FreeVarKind::Other(js_word!("undefined"))),
0 => box JsValue::FreeVar(js_word!("undefined")),
1 => box values.into_iter().next().unwrap(),
_ => box JsValue::alternatives(values),
}
Expand Down Expand Up @@ -1421,7 +1424,7 @@ impl VisitAstPath for Analyzer<'_> {
.arg
.as_deref()
.map(|e| self.eval_context.eval(e))
.unwrap_or(JsValue::FreeVar(FreeVarKind::Other(js_word!("undefined"))));
.unwrap_or(JsValue::FreeVar(js_word!("undefined")));

values.push(return_value);
}
Expand All @@ -1441,6 +1444,12 @@ impl VisitAstPath for Analyzer<'_> {
ast_path: as_parent_path(ast_path),
span: ident.span(),
})
} else if is_unresolved(ident, self.eval_context.unresolved_mark) {
self.add_effect(Effect::FreeVar {
var: JsValue::FreeVar(ident.sym.clone()),
ast_path: as_parent_path(ast_path),
span: ident.span(),
})
}
}

Expand Down
Loading

0 comments on commit ad2bf56

Please sign in to comment.