fix(es/loader): Fix traversal of node modules resolver (#4327)

This commit is contained in:
Julien Karst 2022-04-13 21:51:17 +02:00 committed by GitHub
parent bef183e74a
commit 780de7095e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 13 deletions

2
Cargo.lock generated
View File

@ -3228,11 +3228,13 @@ dependencies = [
"ahash",
"anyhow",
"dashmap",
"lazy_static",
"lru",
"normpath",
"once_cell",
"parking_lot",
"path-clean",
"pathdiff",
"serde",
"serde_json",
"swc_cached",

View File

@ -27,18 +27,20 @@ tsc = ["dashmap", "once_cell", "swc_cached"]
[dependencies]
ahash = "0.7.4"
anyhow = "1.0.41"
dashmap = {version = "5.1.0", optional = true}
lru = {version = "0.7.1", optional = true}
once_cell = {version = "1.9.0", optional = true}
parking_lot = {version = "0.12.0", optional = true}
path-clean = {version = "=0.1.0", optional = true}
serde = {version = "1", features = ["derive"]}
serde_json = {version = "1.0.64", optional = true}
swc_cached = {version = "0.1.0", optional = true, path = "../swc_cached"}
swc_common = { version = "0.17.20", path = "../swc_common"}
dashmap = { version = "5.1.0", optional = true }
lru = { version = "0.7.1", optional = true }
once_cell = { version = "1.9.0", optional = true }
parking_lot = { version = "0.12.0", optional = true }
path-clean = { version = "=0.1.0", optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1.0.64", optional = true }
swc_cached = { version = "0.1.0", optional = true, path = "../swc_cached" }
swc_common = { version = "0.17.20", path = "../swc_common" }
tracing = "0.1.32"
pathdiff = "0.2.1"
[dev-dependencies]
lazy_static = "1.4.0"
[target.'cfg(windows)'.dependencies]
normpath = {version = "0.2", optional = true}
normpath = { version = "0.2", optional = true }

View File

@ -3,6 +3,7 @@
//! See: https://github.com/goto-bus-stop/node-resolve
use std::{
env::current_dir,
fs::File,
io::BufReader,
path::{Component, Path, PathBuf},
@ -14,6 +15,7 @@ use dashmap::{DashMap, DashSet};
use normpath::BasePath;
use once_cell::sync::Lazy;
use path_clean::PathClean;
use pathdiff::diff_paths;
use serde::Deserialize;
use swc_common::{collections::AHashMap, FileName};
use tracing::debug;
@ -53,6 +55,19 @@ fn find_package_root(path: &Path) -> Option<PathBuf> {
None
}
pub fn to_absolute_path(path: impl AsRef<Path>) -> Result<PathBuf, Error> {
let path = path.as_ref();
let absolute_path = if path.is_absolute() {
path.to_path_buf()
} else {
current_dir()?.join(path)
}
.clean();
Ok(absolute_path)
}
pub(crate) fn is_core_module(s: &str) -> bool {
NODE_BUILTINS.contains(&s)
}
@ -294,14 +309,17 @@ impl NodeModulesResolver {
base_dir: &Path,
target: &str,
) -> Result<Option<PathBuf>, Error> {
let mut path = Some(base_dir);
let absolute_path = to_absolute_path(base_dir)?;
let mut path = Some(&*absolute_path);
while let Some(dir) = path {
let node_modules = dir.join("node_modules");
if node_modules.is_dir() {
let path = node_modules.join(target);
if let Some(result) = self
.resolve_as_file(&path)
.or_else(|_| self.resolve_as_directory(&path))?
.ok()
.or_else(|| self.resolve_as_directory(&path).ok())
.flatten()
{
return Ok(Some(result));
}
@ -391,7 +409,12 @@ impl Resolve for NodeModulesResolver {
.and_then(|p| self.wrap(p))
} else {
self.resolve_node_modules(base_dir, target)
.and_then(|p| self.wrap(p))
.and_then(|path| {
let file_path = path.context("Impossible to get the node_modules path");
let current_directory = current_dir()?;
let relative_path = diff_paths(file_path?, current_directory);
self.wrap(relative_path)
})
}
}
}

View File

@ -0,0 +1 @@
!node_modules

View File

@ -0,0 +1 @@
console.log("hello world!");

View File

@ -0,0 +1,4 @@
{
"name": "jquery",
"main": "index.js"
}

View File

@ -0,0 +1 @@
console.log("hello world!");

View File

@ -0,0 +1,4 @@
{
"name": "jquery",
"main": "index.js"
}

View File

@ -0,0 +1 @@
console.log("hello world!");

View File

@ -0,0 +1,4 @@
{
"name": "example",
"main": "index.js"
}

View File

@ -0,0 +1,4 @@
{
"name": "app",
"main": "file.js"
}

View File

@ -0,0 +1,68 @@
#![cfg(feature = "node")]
use std::{
collections::HashMap,
env::{current_dir, set_current_dir},
path::PathBuf,
sync::{Arc, Mutex},
};
use anyhow::{anyhow, Error};
use lazy_static::lazy_static;
use swc_common::{collections::AHashMap, FileName};
extern crate swc_ecma_loader;
use swc_ecma_loader::{resolve::Resolve, resolvers::node::NodeModulesResolver, TargetEnv};
lazy_static! {
static ref UPDATE_DIR_MUTEX: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}
fn inside_directory(dir: &str, callback: fn()) {
let arc = Arc::clone(&UPDATE_DIR_MUTEX);
let _lock = arc.lock().expect("could not lock");
let initial_current_dir = current_dir().unwrap();
let new_current_dir = format!("{}{}", initial_current_dir.display(), dir);
set_current_dir(new_current_dir).unwrap();
callback();
set_current_dir(initial_current_dir).unwrap();
}
#[test]
fn basic_import() {
inside_directory("/tests/basic_import", || {
// Given
let node_resolver = NodeModulesResolver::new(TargetEnv::Node, Default::default(), true);
// When
let resolved = node_resolver
.resolve(&FileName::Real(PathBuf::from(&"jquery")), &"jquery")
.expect("should resolve");
// Expect
assert_eq!(
resolved,
FileName::Real(PathBuf::from("node_modules/jquery/index.js"))
);
});
}
#[test]
fn hoisting() {
inside_directory("/tests/hoisting/packages/app", || {
// Given
let node_resolver = NodeModulesResolver::new(TargetEnv::Node, Default::default(), true);
// When
let resolved = node_resolver
.resolve(&FileName::Real(PathBuf::from(&"jquery")), &"jquery")
.expect("should resolve");
// Expect
assert_eq!(
resolved,
FileName::Real(PathBuf::from("../../node_modules/jquery/index.js"))
);
});
}