Skip to content

Commit

Permalink
consolidate handling for well known objects across refs and analysis (#…
Browse files Browse the repository at this point in the history
…69111)

### What?

We have a set of known functions with special handling to fix problematic or nonstandard packages. This PR ensures the logic for mapping string package names to these well known functions is in one place and one place only.

### Why?

We had two slowly diverging implementations.

### How?

Utility function. I opted not to implement TryInto as we do not have a surjective mapping into the codomain.
  • Loading branch information
arlyon committed Aug 27, 2024
1 parent 53cf8e2 commit 2e74ed6
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 67 deletions.
24 changes: 12 additions & 12 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3685,15 +3685,18 @@ pub mod test_utils {
builtin::early_replace_builtin, well_known::replace_well_known, JsValue, ModuleValue,
WellKnownFunctionKind, WellKnownObjectKind,
};
use crate::analyzer::{
builtin::replace_builtin, imports::ImportAnnotations, parse_require_context,
use crate::{
analyzer::{builtin::replace_builtin, imports::ImportAnnotations, parse_require_context},
utils::module_value_to_well_known_object,
};

pub async fn early_visitor(mut v: JsValue) -> Result<(JsValue, bool)> {
let m = early_replace_builtin(&mut v);
Ok((v, m))
}

/// Visitor that replaces well known functions and objects with their
/// corresponding values. Returns the new value and whether it was modified.
pub async fn visitor(
v: JsValue,
compile_time_info: Vc<CompileTimeInfo>,
Expand Down Expand Up @@ -3745,16 +3748,13 @@ pub mod test_utils {
"process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess),
_ => v.into_unknown(true, "unknown global"),
},
JsValue::Module(ModuleValue {
module: ref name, ..
}) => match name.as_ref() {
"path" => JsValue::WellKnownObject(WellKnownObjectKind::PathModule),
"os" => JsValue::WellKnownObject(WellKnownObjectKind::OsModule),
"process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess),
"@mapbox/node-pre-gyp" => JsValue::WellKnownObject(WellKnownObjectKind::NodePreGyp),
"node-pre-gyp" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeGypBuild),
_ => return Ok((v, false)),
},
JsValue::Module(ref mv) => {
if let Some(wko) = module_value_to_well_known_object(mv) {
wko
} else {
return Ok((v, false));
}
}
_ => {
let (mut v, m1) = replace_well_known(v, compile_time_info).await?;
let m2 = replace_builtin(&mut v);
Expand Down
57 changes: 10 additions & 47 deletions turbopack/crates/turbopack-ecmascript/src/references/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ use crate::{
imports::{ImportAnnotations, ImportedSymbol, Reexport},
parse_require_context,
top_level_await::has_top_level_await,
ConstantNumber, ConstantString, ModuleValue, RequireContextValue,
ConstantNumber, ConstantString, RequireContextValue,
},
chunk::EcmascriptExports,
code_gen::{CodeGen, CodeGenerateable, CodeGenerateableWithAsyncModuleInfo, CodeGenerateables},
Expand All @@ -136,7 +136,7 @@ use crate::{
type_issue::SpecifiedModuleTypeIssue,
},
tree_shake::{find_turbopack_part_id_in_asserts, part_of_module, split},
utils::AstPathRange,
utils::{module_value_to_well_known_object, AstPathRange},
EcmascriptInputTransforms, EcmascriptModuleAsset, EcmascriptParsable, SpecifiedModuleType,
TreeShakingMode,
};
Expand Down Expand Up @@ -2401,51 +2401,14 @@ async fn value_visitor_inner(
"Buffer" => JsValue::WellKnownObject(WellKnownObjectKind::NodeBuffer),
_ => return Ok((v, false)),
},
JsValue::Module(ModuleValue {
module: ref name, ..
}) => {
if *compile_time_info.environment().node_externals().await? {
// TODO check externals
match &**name {
"node:path" | "path" => {
JsValue::WellKnownObject(WellKnownObjectKind::PathModule)
}
"node:fs/promises" | "fs/promises" => {
JsValue::WellKnownObject(WellKnownObjectKind::FsModule)
}
"node:fs" | "fs" => JsValue::WellKnownObject(WellKnownObjectKind::FsModule),
"node:child_process" | "child_process" => {
JsValue::WellKnownObject(WellKnownObjectKind::ChildProcess)
}
"node:os" | "os" => JsValue::WellKnownObject(WellKnownObjectKind::OsModule),
"node:process" | "process" => {
JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess)
}
"@mapbox/node-pre-gyp" => {
JsValue::WellKnownObject(WellKnownObjectKind::NodePreGyp)
}
"node-gyp-build" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeGypBuild)
}
"node:bindings" | "bindings" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeBindings)
}
"express" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeExpress),
"strong-globalize" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalize)
}
"resolve-from" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom)
}
"@grpc/proto-loader" => {
JsValue::WellKnownObject(WellKnownObjectKind::NodeProtobufLoader)
}
_ => v.into_unknown(true, "cross module analyzing is not yet supported"),
}
} else {
v.into_unknown(true, "cross module analyzing is not yet supported")
}
}
JsValue::Module(ref mv) => compile_time_info
.environment()
.node_externals()
.await?
// TODO check externals
.then(|| module_value_to_well_known_object(mv))
.flatten()
.unwrap_or_else(|| v.into_unknown(true, "cross module analyzing is not yet supported")),
JsValue::Argument(..) => {
v.into_unknown(true, "cross function analyzing is not yet supported")
}
Expand Down
33 changes: 32 additions & 1 deletion turbopack/crates/turbopack-ecmascript/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use swc_core::{
};
use turbopack_core::{chunk::ModuleId, resolve::pattern::Pattern};

use crate::analyzer::{ConstantNumber, ConstantValue, JsValue};
use crate::analyzer::{
ConstantNumber, ConstantValue, JsValue, ModuleValue, WellKnownFunctionKind, WellKnownObjectKind,
};

pub fn unparen(expr: &Expr) -> &Expr {
if let Some(expr) = expr.as_paren() {
Expand Down Expand Up @@ -139,3 +141,32 @@ pub enum AstPathRange {
/// specific ast path.
StartAfter(#[turbo_tasks(trace_ignore)] Vec<AstParentKind>),
}

/// Converts a module value (ie an import) to a well known object,
/// which we specifically handle.
pub fn module_value_to_well_known_object(module_value: &ModuleValue) -> Option<JsValue> {
Some(match &*module_value.module {
"node:path" | "path" => JsValue::WellKnownObject(WellKnownObjectKind::PathModule),
"node:fs/promises" | "fs/promises" => {
JsValue::WellKnownObject(WellKnownObjectKind::FsModule)
}
"node:fs" | "fs" => JsValue::WellKnownObject(WellKnownObjectKind::FsModule),
"node:child_process" | "child_process" => {
JsValue::WellKnownObject(WellKnownObjectKind::ChildProcess)
}
"node:os" | "os" => JsValue::WellKnownObject(WellKnownObjectKind::OsModule),
"node:process" | "process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess),
"@mapbox/node-pre-gyp" => JsValue::WellKnownObject(WellKnownObjectKind::NodePreGyp),
"node-gyp-build" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeGypBuild),
"node:bindings" | "bindings" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeBindings)
}
"express" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeExpress),
"strong-globalize" => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalize)
}
"resolve-from" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom),
"@grpc/proto-loader" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProtobufLoader),
_ => return None,
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,24 @@
0 -> 9 call = require*0*("fs/promises")
- *0* require: The require method from CommonJS

0 -> 11 member call = module<fs/promises, {}>["readFile"]("./hello.txt", "utf-8")
0 -> 11 member call = fs*0*["readFile"]("./hello.txt", "utf-8")
- *0* fs: The Node.js fs module: https://nodejs.org/api/fs.html

0 -> 14 member call = ???*0*["status"](200)
- *0* arguments[1]
⚠️ function calls are not analysed yet

0 -> 15 member call = ???*0*["json"](
{
"users": [{"id": 1}, {"id": 2}, {"id": 3}],
"hello": module<fs/promises, {}>["readFile"]("./hello.txt", "utf-8")
}
{"users": [{"id": 1}, {"id": 2}, {"id": 3}], "hello": ???*2*}
)
- *0* ???*1*["status"](200)
⚠️ unknown callee object
- *1* arguments[1]
⚠️ function calls are not analysed yet
- *2* fs.readFile*3*("./hello.txt", "utf-8")
⚠️ unsupported function
⚠️ This value might have side effects
- *3* fs.readFile: A file reading method from the Node.js fs module: https://nodejs.org/api/fs.html

0 -> 16 free var = FreeVar(require)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ exports = {}

handler = (...) => undefined

hello = module<fs/promises, {}>["readFile"]("./hello.txt", "utf-8")
hello = ???*0*
- *0* fs.readFile*1*("./hello.txt", "utf-8")
⚠️ unsupported function
⚠️ This value might have side effects
- *1* fs.readFile: A file reading method from the Node.js fs module: https://nodejs.org/api/fs.html

moduleId = ???*0*
- *0* arguments[0]
⚠️ function calls are not analysed yet

promises_namespaceObject = module<fs/promises, {}>
promises_namespaceObject = fs*0*
- *0* fs: The Node.js fs module: https://nodejs.org/api/fs.html

res = ???*0*
- *0* arguments[1]
Expand Down

0 comments on commit 2e74ed6

Please sign in to comment.