@@ -350,10 +350,9 @@ enum CheckFileOptions {
350
350
CLOSE_AFTER_CHECK
351
351
};
352
352
353
- Maybe<uv_file> CheckFile (const URL& search ,
353
+ Maybe<uv_file> CheckFile (const std::string& path ,
354
354
CheckFileOptions opt = CLOSE_AFTER_CHECK) {
355
355
uv_fs_t fs_req;
356
- std::string path = search.ToFilePath ();
357
356
if (path.empty ()) {
358
357
return Nothing<uv_file>();
359
358
}
@@ -383,23 +382,103 @@ Maybe<uv_file> CheckFile(const URL& search,
383
382
return Just (fd);
384
383
}
385
384
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
+
386
464
enum ResolveExtensionsOptions {
387
465
TRY_EXACT_NAME,
388
466
ONLY_VIA_EXTENSIONS
389
467
};
390
468
391
469
template <ResolveExtensionsOptions options>
392
- Maybe<URL> ResolveExtensions (const URL& search) {
470
+ Maybe<URL> ResolveExtensions (Environment* env, const URL& search) {
393
471
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);
395
474
if (!check.IsNothing ()) {
396
475
return Just (search);
397
476
}
398
477
}
399
478
400
479
for (const char * extension : EXTENSIONS) {
401
480
URL guess (search.path () + extension, &search);
402
- Maybe<uv_file> check = CheckFile (guess);
481
+ Maybe<uv_file> check = CheckFile (guess. ToFilePath () );
403
482
if (!check.IsNothing ()) {
404
483
return Just (guess);
405
484
}
@@ -408,50 +487,21 @@ Maybe<URL> ResolveExtensions(const URL& search) {
408
487
return Nothing<URL>();
409
488
}
410
489
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));
413
492
}
414
493
415
494
Maybe<URL> ResolveMain (Environment* env, const URL& search) {
416
495
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
- }
440
496
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 ) {
447
499
return Nothing<URL>();
448
500
}
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);
453
503
}
454
- return Resolve (env, main_std , search);
504
+ return Resolve (env, pjson. main , search);
455
505
}
456
506
457
507
Maybe<URL> ResolveModule (Environment* env,
@@ -461,7 +511,8 @@ Maybe<URL> ResolveModule(Environment* env,
461
511
URL dir (" " );
462
512
do {
463
513
dir = parent;
464
- Maybe<URL> check = Resolve (env, " ./node_modules/" + specifier, dir, true );
514
+ Maybe<URL> check =
515
+ Resolve (env, " ./node_modules/" + specifier, dir);
465
516
if (!check.IsNothing ()) {
466
517
const size_t limit = specifier.find (' /' );
467
518
const size_t spec_len =
@@ -481,28 +532,22 @@ Maybe<URL> ResolveModule(Environment* env,
481
532
return Nothing<URL>();
482
533
}
483
534
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);
493
540
}
494
541
495
542
} // anonymous namespace
496
543
497
-
498
544
Maybe<URL> Resolve (Environment* env,
499
545
const std::string& specifier,
500
- const URL& base,
501
- bool read_pkg_json) {
546
+ const URL& base) {
502
547
URL pure_url (specifier);
503
548
if (!(pure_url.flags () & URL_FLAGS_FAILED)) {
504
549
// just check existence, without altering
505
- Maybe<uv_file> check = CheckFile (pure_url);
550
+ Maybe<uv_file> check = CheckFile (pure_url. ToFilePath () );
506
551
if (check.IsNothing ()) {
507
552
return Nothing<URL>();
508
553
}
@@ -513,13 +558,14 @@ Maybe<URL> Resolve(Environment* env,
513
558
}
514
559
if (ShouldBeTreatedAsRelativeOrAbsolutePath (specifier)) {
515
560
URL resolved (specifier, base);
516
- Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
561
+ Maybe<URL> file =
562
+ ResolveExtensions<TRY_EXACT_NAME>(env, resolved);
517
563
if (!file.IsNothing ())
518
564
return file;
519
565
if (specifier.back () != ' /' ) {
520
566
resolved = URL (specifier + " /" , base);
521
567
}
522
- return ResolveDirectory (env, resolved, read_pkg_json );
568
+ return ResolveDirectory (env, resolved);
523
569
} else {
524
570
return ResolveModule (env, specifier, base);
525
571
}
@@ -532,8 +578,9 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
532
578
env->ThrowError (" resolve() must not be called as a constructor" );
533
579
return ;
534
580
}
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)" );
537
584
return ;
538
585
}
539
586
@@ -556,14 +603,43 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
556
603
return ;
557
604
}
558
605
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)) {
561
615
std::string msg = " Cannot find module " + specifier_std;
562
616
env->ThrowError (msg.c_str ());
563
617
return ;
564
618
}
565
619
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);
567
643
}
568
644
569
645
static MaybeLocal<Promise> ImportModuleDynamically (
0 commit comments