Files

240 lines
8.5 KiB
Rust

//! Test resolving real-world packages from pnpm node_modules.
use std::{env, path::PathBuf};
use unrs_resolver::{ModuleType, ResolveError, ResolveOptions, Resolver};
fn dir() -> PathBuf {
env::current_dir().unwrap()
}
#[test]
fn styled_components() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path = dir
.join("node_modules")
.join(".pnpm")
.join("[email protected][email protected][email protected][email protected]")
.join("node_modules")
.join("styled-components");
let specifier = "styled-components";
// cjs
let options =
ResolveOptions { alias_fields: vec![vec!["browser".into()]], ..ResolveOptions::default() };
let resolution = Resolver::new(options).resolve(&path, specifier);
assert_eq!(
resolution.map(unrs_resolver::Resolution::into_path_buf),
Ok(module_path.join("dist").join("styled-components.browser.cjs.js"))
);
// esm
let options = ResolveOptions {
alias_fields: vec![vec!["browser".into()]],
main_fields: vec!["module".into()],
..ResolveOptions::default()
};
let resolution = Resolver::new(options).resolve(&path, specifier);
assert_eq!(
resolution.map(unrs_resolver::Resolution::into_path_buf),
Ok(module_path.join("dist").join("styled-components.browser.esm.js"))
);
}
#[test]
fn axios() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path = dir.join("node_modules/.pnpm/[email protected]/node_modules/axios");
let specifier = "axios";
// default
let options = ResolveOptions::default();
let resolution = Resolver::new(options).resolve(&path, specifier);
assert_eq!(
resolution.map(unrs_resolver::Resolution::into_path_buf),
Ok(module_path.join("index.js"))
);
// browser
let options = ResolveOptions {
condition_names: vec!["browser".into(), "require".into()],
..ResolveOptions::default()
};
let resolution = Resolver::new(options).resolve(&path, specifier);
assert_eq!(
resolution.map(unrs_resolver::Resolution::into_path_buf),
Ok(module_path.join("dist/browser/axios.cjs"))
);
// cjs
let options = ResolveOptions {
condition_names: vec!["node".into(), "require".into()],
..ResolveOptions::default()
};
let resolution = Resolver::new(options).resolve(&path, specifier);
assert_eq!(
resolution.map(unrs_resolver::Resolution::into_path_buf),
Ok(module_path.join("dist/node/axios.cjs"))
);
}
#[test]
fn postcss() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path = path.join("node_modules/postcss");
let resolver = Resolver::new(ResolveOptions {
alias_fields: vec![vec!["browser".into()]],
symlinks: false,
..ResolveOptions::default()
});
// should ignore "path"
let resolution = resolver.resolve(&module_path, "path");
assert_eq!(resolution, Err(ResolveError::Ignored(module_path.clone())));
// should ignore "./lib/terminal-highlight"
let resolution = resolver.resolve(&module_path, "./lib/terminal-highlight");
assert_eq!(resolution, Err(ResolveError::Ignored(module_path.join("lib/terminal-highlight"))));
}
#[test]
fn ipaddr_js() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path =
dir.join("node_modules/.pnpm/[email protected]/node_modules/ipaddr.js/lib/ipaddr.js");
let resolvers = [
// with `extension_alias`
Resolver::new(ResolveOptions {
extension_alias: vec![(".js".into(), vec![".js".into(), ".ts".into(), ".tsx".into()])],
..ResolveOptions::default()
}),
// with `extensions` should still resolve to module main
Resolver::new(ResolveOptions {
extensions: vec![(".ts".into())],
..ResolveOptions::default()
}),
// default
Resolver::default(),
];
for resolver in resolvers {
let resolution = resolver.resolve(&path, "ipaddr.js").map(|r| r.full_path());
assert_eq!(resolution, Ok(module_path.clone()));
}
}
#[test]
fn decimal_js() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path =
dir.join("node_modules/.pnpm/[email protected]/node_modules/decimal.js/decimal.mjs");
let resolvers = [
// with `extension_alias`
Resolver::new(ResolveOptions {
extension_alias: vec![(".js".into(), vec![".js".into(), ".ts".into(), ".tsx".into()])],
condition_names: vec!["import".into()],
..ResolveOptions::default()
}),
// default
Resolver::new(ResolveOptions {
condition_names: vec!["import".into()],
..ResolveOptions::default()
}),
];
for resolver in resolvers {
let resolution = resolver.resolve(&path, "decimal.js").map(|r| r.full_path());
assert_eq!(resolution, Ok(module_path.clone()));
}
}
#[test]
fn decimal_js_from_mathjs() {
let dir = dir();
let path = dir.join("node_modules/.pnpm/[email protected]/node_modules/mathjs/lib/esm");
let module_path =
dir.join("node_modules/.pnpm/[email protected]/node_modules/decimal.js/decimal.mjs");
let resolvers = [
// with `extension_alias`
Resolver::new(ResolveOptions {
extension_alias: vec![(".js".into(), vec![".js".into(), ".ts".into(), ".tsx".into()])],
condition_names: vec!["import".into()],
..ResolveOptions::default()
}),
// default
Resolver::new(ResolveOptions {
condition_names: vec!["import".into()],
..ResolveOptions::default()
}),
];
for resolver in resolvers {
let resolution = resolver.resolve(&path, "decimal.js").map(|r| r.full_path());
assert_eq!(resolution, Ok(module_path.clone()));
}
}
#[test]
fn minimatch() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let esm_resolver = Resolver::new(ResolveOptions {
condition_names: vec!["import".into()],
module_type: true,
..ResolveOptions::default()
});
let resolution = esm_resolver.resolve(&path, "minimatch").unwrap();
assert_eq!(
resolution.full_path(),
dir.join("node_modules/.pnpm/[email protected]/node_modules/minimatch/dist/esm/index.js")
);
assert_eq!(resolution.module_type(), Some(ModuleType::Module));
let cjs_resolver = esm_resolver.clone_with_options(ResolveOptions {
condition_names: vec!["require".into()],
module_type: true,
..ResolveOptions::default()
});
let resolution = cjs_resolver.resolve(&path, "minimatch").unwrap();
assert_eq!(
resolution.full_path(),
dir.join(
"node_modules/.pnpm/[email protected]/node_modules/minimatch/dist/commonjs/index.js"
)
);
assert_eq!(resolution.module_type(), Some(ModuleType::CommonJs));
}
// NOTE: pnpm v10 shortens windows directory path.
// `virtualStoreDirMaxLength: 1024` is set in pnpm-workspace.yaml to keep the long name.
#[test]
fn windows_symlinked_longfilename() {
let dir = dir();
let path = dir.join("fixtures/pnpm");
let module_path = dir.join("node_modules")
.join(".pnpm")
.join("@oxc-resolver+test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@file+fixtures+pnpm+longfilename")
.join("node_modules")
.join("@oxc-resolver")
.join("test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
.join("index.js");
// Note: dunce::canonicalize seems to only trim \\?\ when the path is shorter than 260 chars.
// Our implementation should mitigate that.
// <https://gitlab.com/kornelski/dunce/-/blob/1ee29a83526c9f4c3618e1335f0454c878a54dcf/src/lib.rs#L176-180>
assert!(module_path.as_os_str().len() > 260, "Windows path must be super long.");
let specifier = "@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let resolution =
Resolver::new(ResolveOptions::default()).resolve(&path, specifier).map(|r| r.full_path());
assert_eq!(resolution, Ok(module_path));
}