Skip to content

Commit 05d6b5d

Browse files
authored
feat(resolver): allow exports field in require('../directory') (#572)
closes #571 This is not part of the spec but some vite projects rely on this behavior. Opt-in by `allowPackageExportsInDirectoryResolve: true`. See * vitejs/vite#20252 * nodejs/node#58827
1 parent 9b112b4 commit 05d6b5d

File tree

6 files changed

+72
-1
lines changed

6 files changed

+72
-1
lines changed

napi/index.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ export interface NapiResolveOptions {
191191
* Default `false`
192192
*/
193193
moduleType?: boolean
194+
/**
195+
* Allow `exports` field in `require('../directory')`.
196+
*
197+
* This is not part of the spec but some vite projects rely on this behavior.
198+
* See
199+
* * <https://github.com/vitejs/vite/pull/20252>
200+
* * <https://github.com/nodejs/node/issues/58827>
201+
*
202+
* Default: `false`
203+
*/
204+
allowPackageExportsInDirectoryResolve?: boolean
194205
}
195206

196207
export interface ResolveResult {

napi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ impl ResolverFactory {
252252
module_type: op.module_type.unwrap_or(default.module_type),
253253
#[cfg(feature = "yarn_pnp")]
254254
pnp_manifest: default.pnp_manifest,
255+
allow_package_exports_in_directory_resolve: op
256+
.allow_package_exports_in_directory_resolve
257+
.unwrap_or(default.allow_package_exports_in_directory_resolve),
255258
}
256259
}
257260
}

napi/src/options.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ pub struct NapiResolveOptions {
149149
///
150150
/// Default `false`
151151
pub module_type: Option<bool>,
152+
153+
/// Allow `exports` field in `require('../directory')`.
154+
///
155+
/// This is not part of the spec but some vite projects rely on this behavior.
156+
/// See
157+
/// * <https://github.com/vitejs/vite/pull/20252>
158+
/// * <https://github.com/nodejs/node/issues/58827>
159+
///
160+
/// Default: `false`
161+
pub allow_package_exports_in_directory_resolve: Option<bool>,
152162
}
153163

154164
#[napi]

src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,21 @@ impl<C: Cache> ResolverGeneric<C> {
617617
}
618618
// f. LOAD_INDEX(X) DEPRECATED
619619
// g. THROW "not found"
620+
621+
// Allow `exports` field in `require('../directory')`.
622+
// This is not part of the spec but some vite projects rely on this behavior.
623+
// See
624+
// * <https://github.com/vitejs/vite/pull/20252>
625+
// * <https://github.com/nodejs/node/issues/58827>
626+
if self.options.allow_package_exports_in_directory_resolve {
627+
for exports in package_json.exports_fields(&self.options.exports_fields) {
628+
if let Some(path) =
629+
self.package_exports_resolve(cached_path, ".", &exports, ctx)?
630+
{
631+
return Ok(Some(path));
632+
}
633+
}
634+
}
620635
}
621636
// 2. LOAD_INDEX(X)
622637
self.load_index(cached_path, ctx)

src/options.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,16 @@ pub struct ResolveOptions {
162162
///
163163
/// Default: `false`
164164
pub module_type: bool,
165+
166+
/// Allow `exports` field in `require('../directory')`.
167+
///
168+
/// This is not part of the spec but some vite projects rely on this behavior.
169+
/// See
170+
/// * <https://github.com/vitejs/vite/pull/20252>
171+
/// * <https://github.com/nodejs/node/issues/58827>
172+
///
173+
/// Default: `false`
174+
pub allow_package_exports_in_directory_resolve: bool,
165175
}
166176

167177
impl ResolveOptions {
@@ -484,6 +494,7 @@ impl Default for ResolveOptions {
484494
symlinks: true,
485495
builtin_modules: false,
486496
module_type: false,
497+
allow_package_exports_in_directory_resolve: false,
487498
}
488499
}
489500
}
@@ -554,6 +565,13 @@ impl fmt::Display for ResolveOptions {
554565
if self.builtin_modules {
555566
write!(f, "builtin_modules:{:?},", self.builtin_modules)?;
556567
}
568+
if self.allow_package_exports_in_directory_resolve {
569+
write!(
570+
f,
571+
"allow_package_exports_in_directory_resolve:{:?},",
572+
self.allow_package_exports_in_directory_resolve
573+
)?;
574+
}
557575
Ok(())
558576
}
559577
}
@@ -604,10 +622,11 @@ mod test {
604622
restrictions: vec![Restriction::Path(PathBuf::from("restrictions"))],
605623
roots: vec![PathBuf::from("roots")],
606624
builtin_modules: true,
625+
allow_package_exports_in_directory_resolve: true,
607626
..ResolveOptions::default()
608627
};
609628

610-
let expected = r#"tsconfig:TsconfigOptions { config_file: "tsconfig.json", references: Auto },alias:[("a", [Ignore])],alias_fields:[["browser"]],condition_names:["require"],enforce_extension:Enabled,exports_fields:[["exports"]],imports_fields:[["imports"]],extension_alias:[(".js", [".ts"])],extensions:[".js", ".json", ".node"],fallback:[("fallback", [Ignore])],fully_specified:true,main_fields:["main"],main_files:["index"],modules:["node_modules"],resolve_to_context:true,prefer_relative:true,prefer_absolute:true,restrictions:[Path("restrictions")],roots:["roots"],symlinks:true,builtin_modules:true,"#;
629+
let expected = r#"tsconfig:TsconfigOptions { config_file: "tsconfig.json", references: Auto },alias:[("a", [Ignore])],alias_fields:[["browser"]],condition_names:["require"],enforce_extension:Enabled,exports_fields:[["exports"]],imports_fields:[["imports"]],extension_alias:[(".js", [".ts"])],extensions:[".js", ".json", ".node"],fallback:[("fallback", [Ignore])],fully_specified:true,main_fields:["main"],main_files:["index"],modules:["node_modules"],resolve_to_context:true,prefer_relative:true,prefer_absolute:true,restrictions:[Path("restrictions")],roots:["roots"],symlinks:true,builtin_modules:true,allow_package_exports_in_directory_resolve:true,"#;
611630
assert_eq!(format!("{options}"), expected);
612631

613632
let options = ResolveOptions {
@@ -635,6 +654,7 @@ mod test {
635654
symlinks: false,
636655
tsconfig: None,
637656
module_type: false,
657+
allow_package_exports_in_directory_resolve: false,
638658
};
639659

640660
assert_eq!(format!("{options}"), "");

src/tests/exports_field.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,18 @@ fn extension_alias_throw_error() {
287287
}
288288
}
289289

290+
#[test]
291+
fn directory() {
292+
let f = super::fixture();
293+
let resolver = Resolver::new(ResolveOptions {
294+
allow_package_exports_in_directory_resolve: true,
295+
..ResolveOptions::default()
296+
});
297+
let resolution = resolver.resolve(f.join("foo"), "../exports-field");
298+
let path = resolution.unwrap().full_path();
299+
assert_eq!(path, f.join("exports-field").join("a.js"));
300+
}
301+
290302
// Small script for generating the test cases from enhanced-resolve
291303
// for (c of testCases) {
292304
// console.log("TestCase {")

0 commit comments

Comments
 (0)