@@ -558,7 +558,7 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
558558 if (source.IsNothing ()) {
559559 auto entry = env->package_json_cache .emplace (path,
560560 PackageConfig { Exists::No, IsValid::Yes, HasMain::No, " " ,
561- PackageType::None });
561+ PackageType::None, Global<Value>() });
562562 return Just (&entry.first ->second );
563563 }
564564
@@ -578,7 +578,7 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
578578 !pkg_json_v->ToObject (context).ToLocal (&pkg_json)) {
579579 env->package_json_cache .emplace (path,
580580 PackageConfig { Exists::Yes, IsValid::No, HasMain::No, " " ,
581- PackageType::None });
581+ PackageType::None, Global<Value>() });
582582 std::string msg = " Invalid JSON in '" + path +
583583 " ' imported from " + base.ToFilePath ();
584584 node::THROW_ERR_INVALID_PACKAGE_CONFIG (env, msg.c_str ());
@@ -609,22 +609,22 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
609609 }
610610
611611 Local<Value> exports_v;
612- if (pkg_json->Get (env->context (),
612+ if (env->options ()->experimental_exports &&
613+ pkg_json->Get (env->context (),
613614 env->exports_string ()).ToLocal (&exports_v) &&
614- (exports_v->IsObject () || exports_v->IsString () ||
615- exports_v->IsBoolean ())) {
615+ !exports_v->IsNullOrUndefined ()) {
616616 Global<Value> exports;
617617 exports.Reset (env->isolate (), exports_v);
618618
619619 auto entry = env->package_json_cache .emplace (path,
620620 PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
621- pkg_type });
621+ pkg_type, std::move (exports) });
622622 return Just (&entry.first ->second );
623623 }
624624
625625 auto entry = env->package_json_cache .emplace (path,
626626 PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
627- pkg_type });
627+ pkg_type, Global<Value>() });
628628 return Just (&entry.first ->second );
629629}
630630
@@ -800,6 +800,66 @@ Maybe<URL> PackageMainResolve(Environment* env,
800800 return Nothing<URL>();
801801}
802802
803+ Maybe<URL> PackageExportsResolve (Environment* env,
804+ const URL& pjson_url,
805+ const std::string& pkg_subpath,
806+ const PackageConfig& pcfg,
807+ const URL& base) {
808+ CHECK (env->options ()->experimental_exports );
809+ Isolate* isolate = env->isolate ();
810+ Local<Context> context = env->context ();
811+ Local<Value> exports = pcfg.exports .Get (isolate);
812+ if (exports->IsObject ()) {
813+ Local<Object> exports_obj = exports.As <Object>();
814+ Local<String> subpath = String::NewFromUtf8 (isolate,
815+ pkg_subpath.c_str (), v8::NewStringType::kNormal ).ToLocalChecked ();
816+
817+ auto target = exports_obj->Get (context, subpath).ToLocalChecked ();
818+ if (target->IsString ()) {
819+ Utf8Value target_utf8 (isolate, target.As <v8::String>());
820+ std::string target (*target_utf8, target_utf8.length ());
821+ if (target.substr (0 , 2 ) == " ./" ) {
822+ URL target_url (target, pjson_url);
823+ return FinalizeResolution (env, target_url, base);
824+ }
825+ }
826+
827+ Local<String> best_match;
828+ std::string best_match_str = " " ;
829+ Local<Array> keys =
830+ exports_obj->GetOwnPropertyNames (context).ToLocalChecked ();
831+ for (uint32_t i = 0 ; i < keys->Length (); ++i) {
832+ Local<String> key = keys->Get (context, i).ToLocalChecked ().As <String>();
833+ Utf8Value key_utf8 (isolate, key);
834+ std::string key_str (*key_utf8, key_utf8.length ());
835+ if (key_str.back () != ' /' ) continue ;
836+ if (pkg_subpath.substr (0 , key_str.length ()) == key_str &&
837+ key_str.length () > best_match_str.length ()) {
838+ best_match = key;
839+ best_match_str = key_str;
840+ }
841+ }
842+
843+ if (best_match_str.length () > 0 ) {
844+ auto target = exports_obj->Get (context, best_match).ToLocalChecked ();
845+ if (target->IsString ()) {
846+ Utf8Value target_utf8 (isolate, target.As <v8::String>());
847+ std::string target (*target_utf8, target_utf8.length ());
848+ if (target.back () == ' /' && target.substr (0 , 2 ) == " ./" ) {
849+ std::string subpath = pkg_subpath.substr (best_match_str.length ());
850+ URL target_url (target + subpath, pjson_url);
851+ return FinalizeResolution (env, target_url, base);
852+ }
853+ }
854+ }
855+ }
856+ std::string msg = " Package exports for '" +
857+ URL (" ." , pjson_url).ToFilePath () + " ' do not define a '" + pkg_subpath +
858+ " ' subpath, imported from " + base.ToFilePath ();
859+ node::THROW_ERR_MODULE_NOT_FOUND (env, msg.c_str ());
860+ return Nothing<URL>();
861+ }
862+
803863Maybe<URL> PackageResolve (Environment* env,
804864 const std::string& specifier,
805865 const URL& base) {
@@ -847,7 +907,12 @@ Maybe<URL> PackageResolve(Environment* env,
847907 if (!pkg_subpath.length ()) {
848908 return PackageMainResolve (env, pjson_url, *pcfg.FromJust (), base);
849909 } else {
850- return FinalizeResolution (env, URL (pkg_subpath, pjson_url), base);
910+ if (!pcfg.FromJust ()->exports .IsEmpty ()) {
911+ return PackageExportsResolve (env, pjson_url, pkg_subpath,
912+ *pcfg.FromJust (), base);
913+ } else {
914+ return FinalizeResolution (env, URL (pkg_subpath, pjson_url), base);
915+ }
851916 }
852917 CHECK (false );
853918 // Cross-platform root check.
0 commit comments