Skip to content

Commit d8a4963

Browse files
committed
fix: resolve "browser" field when "exports" is present
closes #58 Failed to resolve `path` in browser mode for `postcss` when ``` "exports": { ... } "browser": { "./lib/terminal-highlight": false, "source-map-js": false, "path": false, "url": false, "fs": false }, ``` Are both present There are no such test cases for this type of combination in enhanced_resolve.
1 parent 7a8e5c4 commit d8a4963

File tree

6 files changed

+57
-34
lines changed

6 files changed

+57
-34
lines changed

fixtures/enhanced_resolve/test/fixtures/browser-module/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
2+
"exports": {
3+
},
24
"browser": {
35
"./lib/ignore.js": false,
46
"./lib/replaced.js": "./lib/browser",

fixtures/pnpm8/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"packageManager": "[email protected]",
99
"devDependencies": {
1010
"axios": "1.6.2",
11-
"styled-components": "6.1.1"
11+
"styled-components": "6.1.1",
12+
"postcss": "8.4.33"
1213
}
1314
}

fixtures/pnpm8/pnpm-lock.yaml

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

napi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ test = false
99
doctest = false
1010

1111
[dependencies]
12-
oxc_resolver = { path = "../../oxc_resolver" }
12+
oxc_resolver = { path = ".." }
1313
napi = { version = "2", default-features = false, features = ["napi3", "serde-json"] }
1414
napi-derive = { version = "2" }
1515

src/lib.rs

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -722,36 +722,34 @@ impl<Fs: FileSystem + Default> ResolverGeneric<Fs> {
722722
return Ok(None);
723723
};
724724
// 3. If the SCOPE/package.json "exports" is null or undefined, return.
725-
if package_json.exports.is_empty() {
726-
return self.load_browser_field(cached_path, Some(specifier), &package_json, ctx);
727-
}
728-
// 4. If the SCOPE/package.json "name" is not the first segment of X, return.
729-
let Some(subpath) = package_json
730-
.name
731-
.as_ref()
732-
.and_then(|package_name| Self::strip_package_name(specifier, package_name))
733-
else {
734-
return Ok(None);
735-
};
736-
// 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
737-
// "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
738-
// defined in the ESM resolver.
739-
let package_url = package_json.directory();
740-
// Note: The subpath is not prepended with a dot on purpose
741-
// because `package_exports_resolve` matches subpath without the leading dot.
742-
for exports in &package_json.exports {
743-
if let Some(cached_path) = self.package_exports_resolve(
744-
package_url,
745-
subpath,
746-
exports,
747-
&self.options.condition_names,
748-
ctx,
749-
)? {
750-
// 6. RESOLVE_ESM_MATCH(MATCH)
751-
return self.resolve_esm_match(specifier, &cached_path, &package_json, ctx);
725+
if !package_json.exports.is_empty() {
726+
// 4. If the SCOPE/package.json "name" is not the first segment of X, return.
727+
if let Some(subpath) = package_json
728+
.name
729+
.as_ref()
730+
.and_then(|package_name| Self::strip_package_name(specifier, package_name))
731+
{
732+
// 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
733+
// "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
734+
// defined in the ESM resolver.
735+
let package_url = package_json.directory();
736+
// Note: The subpath is not prepended with a dot on purpose
737+
// because `package_exports_resolve` matches subpath without the leading dot.
738+
for exports in &package_json.exports {
739+
if let Some(cached_path) = self.package_exports_resolve(
740+
package_url,
741+
subpath,
742+
exports,
743+
&self.options.condition_names,
744+
ctx,
745+
)? {
746+
// 6. RESOLVE_ESM_MATCH(MATCH)
747+
return self.resolve_esm_match(specifier, &cached_path, &package_json, ctx);
748+
}
749+
}
752750
}
753751
}
754-
Ok(None)
752+
self.load_browser_field(cached_path, Some(specifier), &package_json, ctx)
755753
}
756754

757755
/// RESOLVE_ESM_MATCH(MATCH)

tests/resolve_test.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{env, path::PathBuf};
22

3-
use oxc_resolver::{ResolveOptions, Resolver};
3+
use oxc_resolver::{ResolveError, ResolveOptions, Resolver};
44

55
fn dir() -> PathBuf {
66
env::current_dir().unwrap()
@@ -74,3 +74,22 @@ fn axios() {
7474
let resolution = Resolver::new(options).resolve(&path, specifier);
7575
assert_eq!(resolution.map(|r| r.into_path_buf()), Ok(module_path.join("dist/node/axios.cjs")));
7676
}
77+
78+
#[test]
79+
fn postcss() {
80+
let dir = dir();
81+
let path = dir.join("fixtures/pnpm8");
82+
let module_path = path.join("node_modules/postcss");
83+
let resolver = Resolver::new(ResolveOptions {
84+
alias_fields: vec![vec!["browser".into()]],
85+
..ResolveOptions::default()
86+
});
87+
88+
// should ignore "path"
89+
let resolution = resolver.resolve(&module_path, "path");
90+
assert_eq!(resolution, Err(ResolveError::Ignored(module_path.clone())));
91+
92+
// should ignore "./lib/terminal-highlight"
93+
let resolution = resolver.resolve(&module_path, "./lib/terminal-highlight");
94+
assert_eq!(resolution, Err(ResolveError::Ignored(module_path.join("lib/terminal-highlight"))));
95+
}

0 commit comments

Comments
 (0)