@@ -350,10 +350,9 @@ enum CheckFileOptions {
350350 CLOSE_AFTER_CHECK
351351};
352352
353- Maybe<uv_file> CheckFile (const URL& search ,
353+ Maybe<uv_file> CheckFile (const std::string& path ,
354354 CheckFileOptions opt = CLOSE_AFTER_CHECK) {
355355 uv_fs_t fs_req;
356- std::string path = search.ToFilePath ();
357356 if (path.empty ()) {
358357 return Nothing<uv_file>();
359358 }
@@ -383,23 +382,103 @@ Maybe<uv_file> CheckFile(const URL& search,
383382 return Just (fd);
384383}
385384
385+ PackageJson emptyPackage = { false , false , " " , false };
386+ std::unordered_map<std::string, PackageJson> pjson_cache_;
387+ PackageJson GetPackageJson (Environment* env, const std::string path) {
388+ auto existing = pjson_cache_.find (path);
389+ if (existing != pjson_cache_.end ()) {
390+ return existing->second ;
391+ }
392+ Maybe<uv_file> check = CheckFile (path, LEAVE_OPEN_AFTER_CHECK);
393+ if (check.IsNothing ()) {
394+ return (pjson_cache_[path] = emptyPackage);
395+ }
396+
397+ Isolate* isolate = env->isolate ();
398+ Local<Context> context = isolate->GetCurrentContext ();
399+ std::string pkg_src = ReadFile (check.FromJust ());
400+ uv_fs_t fs_req;
401+ uv_fs_close (nullptr , &fs_req, check.FromJust (), nullptr );
402+ uv_fs_req_cleanup (&fs_req);
403+
404+ // It's not okay for the called of this method to not be able to tell
405+ // whether an exception is pending or not.
406+ TryCatch try_catch (isolate);
407+
408+ Local<String> src;
409+ if (!String::NewFromUtf8 (isolate,
410+ pkg_src.c_str (),
411+ v8::NewStringType::kNormal ,
412+ pkg_src.length ()).ToLocal (&src)) {
413+ return (pjson_cache_[path] = emptyPackage);
414+ }
415+
416+ Local<Value> pkg_json;
417+ if (!JSON::Parse (context, src).ToLocal (&pkg_json) || !pkg_json->IsObject ())
418+ return (pjson_cache_[path] = emptyPackage);
419+ Local<Value> pkg_main;
420+ bool has_main = false ;
421+ std::string main_std;
422+ if (pkg_json.As <Object>()->Get (context, env->main_string ())
423+ .ToLocal (&pkg_main) && pkg_main->IsString ()) {
424+ has_main = true ;
425+ Utf8Value main_utf8 (isolate, pkg_main.As <String>());
426+ main_std = std::string (*main_utf8, main_utf8.length ());
427+ }
428+
429+ bool esm = false ;
430+ Local<Value> pkg_mode;
431+ std::string pkg_mode_std;
432+ if (pkg_json.As <Object>()->Get (context, env->mode_string ())
433+ .ToLocal (&pkg_mode) && pkg_mode->IsString ()) {
434+ Utf8Value pkg_mode_utf8 (isolate, pkg_mode.As <String>());
435+ pkg_mode_std = std::string (*pkg_mode_utf8, pkg_mode_utf8.length ());
436+ if (pkg_mode_std == " esm" ) {
437+ esm = true ;
438+ }
439+ }
440+
441+ PackageJson pjson = { true , has_main, main_std, esm };
442+ pjson_cache_[path] = pjson;
443+ return pjson;
444+ }
445+
446+ bool CheckPjsonEsmMode (Environment* env, const URL& search) {
447+ URL pjsonPath (" package.json" , &search);
448+ PackageJson pjson;
449+ do {
450+ pjson = GetPackageJson (env, pjsonPath.ToFilePath ());
451+ if (pjson.exists ) {
452+ break ;
453+ }
454+ URL lastPjsonPath = pjsonPath;
455+ pjsonPath = URL (" ../package.json" , pjsonPath);
456+ if (pjsonPath.path () == lastPjsonPath.path ()) {
457+ break ;
458+ }
459+ } while (true );
460+ return pjson.exists && pjson.esm ;
461+ }
462+
463+
386464enum ResolveExtensionsOptions {
387465 TRY_EXACT_NAME,
388466 ONLY_VIA_EXTENSIONS
389467};
390468
391469template <ResolveExtensionsOptions options>
392- Maybe<URL> ResolveExtensions (const URL& search) {
470+ Maybe<URL> ResolveExtensions (Environment* env, const URL& search) {
393471 if (options == TRY_EXACT_NAME) {
394- Maybe<uv_file> check = CheckFile (search);
472+ std::string filePath = search.ToFilePath ();
473+ Maybe<uv_file> check = CheckFile (filePath);
395474 if (!check.IsNothing ()) {
396475 return Just (search);
397476 }
398477 }
399478
400479 for (const char * extension : EXTENSIONS) {
401480 URL guess (search.path () + extension, &search);
402- Maybe<uv_file> check = CheckFile (guess);
481+ Maybe<uv_file> check = CheckFile (guess. ToFilePath () );
403482 if (!check.IsNothing ()) {
404483 return Just (guess);
405484 }
@@ -408,50 +487,21 @@ Maybe<URL> ResolveExtensions(const URL& search) {
408487 return Nothing<URL>();
409488}
410489
411- inline Maybe<URL> ResolveIndex (const URL& search) {
412- return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL (" index" , search));
490+ inline Maybe<URL> ResolveIndex (Environment* env, const URL& search) {
491+ return ResolveExtensions<ONLY_VIA_EXTENSIONS>(env, URL (" index" , search));
413492}
414493
415494Maybe<URL> ResolveMain (Environment* env, const URL& search) {
416495 URL pkg (" package.json" , &search);
417- Maybe<uv_file> check = CheckFile (pkg, LEAVE_OPEN_AFTER_CHECK);
418- if (check.IsNothing ()) {
419- return Nothing<URL>();
420- }
421-
422- Isolate* isolate = env->isolate ();
423- Local<Context> context = isolate->GetCurrentContext ();
424- std::string pkg_src = ReadFile (check.FromJust ());
425- uv_fs_t fs_req;
426- uv_fs_close (nullptr , &fs_req, check.FromJust (), nullptr );
427- uv_fs_req_cleanup (&fs_req);
428-
429- // It's not okay for the called of this method to not be able to tell
430- // whether an exception is pending or not.
431- TryCatch try_catch (isolate);
432-
433- Local<String> src;
434- if (!String::NewFromUtf8 (isolate,
435- pkg_src.c_str (),
436- v8::NewStringType::kNormal ,
437- pkg_src.length ()).ToLocal (&src)) {
438- return Nothing<URL>();
439- }
440496
441- Local<Value> pkg_json;
442- if (!JSON::Parse (context, src).ToLocal (&pkg_json) || !pkg_json->IsObject ())
443- return Nothing<URL>();
444- Local<Value> pkg_main;
445- if (!pkg_json.As <Object>()->Get (context, env->main_string ())
446- .ToLocal (&pkg_main) || !pkg_main->IsString ()) {
497+ PackageJson pjson = GetPackageJson (env, pkg.ToFilePath ());
498+ if (!pjson.exists || !pjson.has_main ) {
447499 return Nothing<URL>();
448500 }
449- Utf8Value main_utf8 (isolate, pkg_main.As <String>());
450- std::string main_std (*main_utf8, main_utf8.length ());
451- if (!ShouldBeTreatedAsRelativeOrAbsolutePath (main_std)) {
452- main_std.insert (0 , " ./" );
501+ if (!ShouldBeTreatedAsRelativeOrAbsolutePath (pjson.main )) {
502+ return Resolve (env, " ./" + pjson.main , search);
453503 }
454- return Resolve (env, main_std , search);
504+ return Resolve (env, pjson. main , search);
455505}
456506
457507Maybe<URL> ResolveModule (Environment* env,
@@ -461,7 +511,8 @@ Maybe<URL> ResolveModule(Environment* env,
461511 URL dir (" " );
462512 do {
463513 dir = parent;
464- Maybe<URL> check = Resolve (env, " ./node_modules/" + specifier, dir, true );
514+ Maybe<URL> check =
515+ Resolve (env, " ./node_modules/" + specifier, dir);
465516 if (!check.IsNothing ()) {
466517 const size_t limit = specifier.find (' /' );
467518 const size_t spec_len =
@@ -481,28 +532,22 @@ Maybe<URL> ResolveModule(Environment* env,
481532 return Nothing<URL>();
482533}
483534
484- Maybe<URL> ResolveDirectory (Environment* env,
485- const URL& search,
486- bool read_pkg_json) {
487- if (read_pkg_json) {
488- Maybe<URL> main = ResolveMain (env, search);
489- if (!main.IsNothing ())
490- return main;
491- }
492- return ResolveIndex (search);
535+ Maybe<URL> ResolveDirectory (Environment* env, const URL& search) {
536+ Maybe<URL> main = ResolveMain (env, search);
537+ if (!main.IsNothing ())
538+ return main;
539+ return ResolveIndex (env, search);
493540}
494541
495542} // anonymous namespace
496543
497-
498544Maybe<URL> Resolve (Environment* env,
499545 const std::string& specifier,
500- const URL& base,
501- bool read_pkg_json) {
546+ const URL& base) {
502547 URL pure_url (specifier);
503548 if (!(pure_url.flags () & URL_FLAGS_FAILED)) {
504549 // just check existence, without altering
505- Maybe<uv_file> check = CheckFile (pure_url);
550+ Maybe<uv_file> check = CheckFile (pure_url. ToFilePath () );
506551 if (check.IsNothing ()) {
507552 return Nothing<URL>();
508553 }
@@ -513,13 +558,14 @@ Maybe<URL> Resolve(Environment* env,
513558 }
514559 if (ShouldBeTreatedAsRelativeOrAbsolutePath (specifier)) {
515560 URL resolved (specifier, base);
516- Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
561+ Maybe<URL> file =
562+ ResolveExtensions<TRY_EXACT_NAME>(env, resolved);
517563 if (!file.IsNothing ())
518564 return file;
519565 if (specifier.back () != ' /' ) {
520566 resolved = URL (specifier + " /" , base);
521567 }
522- return ResolveDirectory (env, resolved, read_pkg_json );
568+ return ResolveDirectory (env, resolved);
523569 } else {
524570 return ResolveModule (env, specifier, base);
525571 }
@@ -532,8 +578,9 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
532578 env->ThrowError (" resolve() must not be called as a constructor" );
533579 return ;
534580 }
535- if (args.Length () != 2 ) {
536- env->ThrowError (" resolve must have exactly 2 arguments (string, string)" );
581+ if (args.Length () != 3 ) {
582+ env->ThrowError (
583+ " resolve must have exactly 3 arguments (string, string, boolean)" );
537584 return ;
538585 }
539586
@@ -556,14 +603,43 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
556603 return ;
557604 }
558605
559- Maybe<URL> result = node::loader::Resolve (env, specifier_std, url, true );
560- if (result.IsNothing () || (result.FromJust ().flags () & URL_FLAGS_FAILED)) {
606+ if (!args[2 ]->IsBoolean ()) {
607+ env->ThrowError (" third argument is not a boolean" );
608+ return ;
609+ }
610+ bool check_pjson_mode = args[2 ]->ToBoolean ().As <v8::Boolean>()->Value ();
611+
612+ Maybe<URL> result = node::loader::Resolve (env, specifier_std, url);
613+ if (result.IsNothing () ||
614+ (result.FromJust ().flags () & URL_FLAGS_FAILED)) {
561615 std::string msg = " Cannot find module " + specifier_std;
562616 env->ThrowError (msg.c_str ());
563617 return ;
564618 }
565619
566- args.GetReturnValue ().Set (result.FromJust ().ToObject (env));
620+ bool esm = false ;
621+ if (check_pjson_mode) {
622+ std::string path = result.FromJust ().ToFilePath ();
623+ if (path.substr (path.length () - 3 , 3 ) == " .js" ) {
624+ esm = CheckPjsonEsmMode (env, result.FromJust ());
625+ }
626+ }
627+
628+ Local<Object> resolved = Object::New (env->isolate ());
629+
630+ resolved->DefineOwnProperty (
631+ env->context (),
632+ env->esm_string (),
633+ v8::Boolean::New (env->isolate (), esm),
634+ v8::ReadOnly);
635+
636+ resolved->DefineOwnProperty (
637+ env->context (),
638+ env->url_string (),
639+ result.FromJust ().ToObject (env),
640+ v8::ReadOnly);
641+
642+ args.GetReturnValue ().Set (resolved);
567643}
568644
569645static MaybeLocal<Promise> ImportModuleDynamically (
0 commit comments