@@ -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,40 +382,16 @@ Maybe<uv_file> CheckFile(const URL& search,
383
382
return Just (fd);
384
383
}
385
384
386
- enum ResolveExtensionsOptions {
387
- TRY_EXACT_NAME,
388
- ONLY_VIA_EXTENSIONS
389
- };
390
-
391
- template <ResolveExtensionsOptions options>
392
- Maybe<URL> ResolveExtensions (const URL& search) {
393
- if (options == TRY_EXACT_NAME) {
394
- Maybe<uv_file> check = CheckFile (search);
395
- if (!check.IsNothing ()) {
396
- return Just (search);
397
- }
398
- }
399
-
400
- for (const char * extension : EXTENSIONS) {
401
- URL guess (search.path () + extension, &search);
402
- Maybe<uv_file> check = CheckFile (guess);
403
- if (!check.IsNothing ()) {
404
- return Just (guess);
405
- }
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 ;
406
391
}
407
-
408
- return Nothing<URL>();
409
- }
410
-
411
- inline Maybe<URL> ResolveIndex (const URL& search) {
412
- return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL (" index" , search));
413
- }
414
-
415
- Maybe<URL> ResolveMain (Environment* env, const URL& search) {
416
- URL pkg (" package.json" , &search);
417
- Maybe<uv_file> check = CheckFile (pkg, LEAVE_OPEN_AFTER_CHECK);
392
+ Maybe<uv_file> check = CheckFile (path, LEAVE_OPEN_AFTER_CHECK);
418
393
if (check.IsNothing ()) {
419
- return Nothing<URL>( );
394
+ return (pjson_cache_[path] = emptyPackage );
420
395
}
421
396
422
397
Isolate* isolate = env->isolate ();
@@ -435,85 +410,156 @@ Maybe<URL> ResolveMain(Environment* env, const URL& search) {
435
410
pkg_src.c_str (),
436
411
v8::NewStringType::kNormal ,
437
412
pkg_src.length ()).ToLocal (&src)) {
438
- return Nothing<URL>( );
413
+ return (pjson_cache_[path] = emptyPackage );
439
414
}
440
415
441
416
Local<Value> pkg_json;
442
417
if (!JSON::Parse (context, src).ToLocal (&pkg_json) || !pkg_json->IsObject ())
443
- return Nothing<URL>( );
418
+ return (pjson_cache_[path] = emptyPackage );
444
419
Local<Value> pkg_main;
445
- if (!pkg_json.As <Object>()->Get (context, env->main_string ())
446
- .ToLocal (&pkg_main) || !pkg_main->IsString ()) {
447
- return Nothing<URL>();
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
+ Local<Value> pkg_esm;
430
+ bool esm = false ;
431
+ if (pkg_json.As <Object>()->Get (context, env->esm_string ())
432
+ .ToLocal (&pkg_esm) && pkg_esm->IsBoolean ()) {
433
+ esm = pkg_esm.As <v8::Boolean>()->Value ();
434
+ }
435
+
436
+ PackageJson pjson = { true , has_main, main_std, esm };
437
+ pjson_cache_[path] = pjson;
438
+ return pjson;
439
+ }
440
+
441
+ ModuleResolution ResolveFormat (Environment* env, const URL& search) {
442
+ URL pjsonPath (" package.json" , &search);
443
+ PackageJson pjson;
444
+ do {
445
+ pjson = GetPackageJson (env, pjsonPath.ToFilePath ());
446
+ if (pjson.exists ) {
447
+ break ;
448
+ }
449
+ URL lastPjsonPath = pjsonPath;
450
+ pjsonPath = URL (" ../package.json" , pjsonPath);
451
+ if (pjsonPath.path () == lastPjsonPath.path ()) {
452
+ break ;
453
+ }
454
+ } while (true );
455
+ ModuleResolution resolution = { search, pjson.exists && pjson.esm };
456
+ return resolution;
457
+ }
458
+
459
+ enum ResolveExtensionsOptions {
460
+ TRY_EXACT_NAME,
461
+ ONLY_VIA_EXTENSIONS
462
+ };
463
+
464
+ template <ResolveExtensionsOptions options>
465
+ Maybe<ModuleResolution> ResolveExtensions (Environment* env, const URL& search) {
466
+ if (options == TRY_EXACT_NAME) {
467
+ Maybe<uv_file> check = CheckFile (search.ToFilePath ());
468
+ if (!check.IsNothing ()) {
469
+ return Just (ResolveFormat (env, search));
470
+ }
448
471
}
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 , " ./" );
472
+
473
+ for (const char * extension : EXTENSIONS) {
474
+ URL guess (search.path () + extension, &search);
475
+ Maybe<uv_file> check = CheckFile (guess.ToFilePath ());
476
+ if (!check.IsNothing ()) {
477
+ return Just (ResolveFormat (env, guess));
478
+ }
453
479
}
454
- return Resolve (env, main_std, search);
480
+
481
+ return Nothing<ModuleResolution>();
482
+ }
483
+
484
+ inline Maybe<ModuleResolution> ResolveIndex (Environment* env,
485
+ const URL& search) {
486
+ return ResolveExtensions<ONLY_VIA_EXTENSIONS>(env, URL (" index" , search));
455
487
}
456
488
457
- Maybe<URL> ResolveModule (Environment* env,
489
+ Maybe<ModuleResolution> ResolveMain (Environment* env, const URL& search) {
490
+ URL pkg (" package.json" , &search);
491
+
492
+ PackageJson pjson = GetPackageJson (env, pkg.ToFilePath ());
493
+ if (!pjson.exists || !pjson.has_main ) {
494
+ return Nothing<ModuleResolution>();
495
+ }
496
+ if (!ShouldBeTreatedAsRelativeOrAbsolutePath (pjson.main )) {
497
+ return Resolve (env, " ./" + pjson.main , search);
498
+ }
499
+ return Resolve (env, pjson.main , search);
500
+ }
501
+
502
+ Maybe<ModuleResolution> ResolveModule (Environment* env,
458
503
const std::string& specifier,
459
504
const URL& base) {
460
505
URL parent (" ." , base);
461
506
URL dir (" " );
462
507
do {
463
508
dir = parent;
464
- Maybe<URL> check = Resolve (env, " ./node_modules/" + specifier, dir, true );
509
+ Maybe<ModuleResolution> check =
510
+ Resolve (env, " ./node_modules/" + specifier, dir, true );
465
511
if (!check.IsNothing ()) {
466
512
const size_t limit = specifier.find (' /' );
467
513
const size_t spec_len =
468
514
limit == std::string::npos ? specifier.length () :
469
515
limit + 1 ;
470
516
std::string chroot =
471
517
dir.path () + " node_modules/" + specifier.substr (0 , spec_len);
472
- if (check.FromJust ().path ().substr (0 , chroot.length ()) != chroot) {
473
- return Nothing<URL >();
518
+ if (check.FromJust ().url . path ().substr (0 , chroot.length ()) != chroot) {
519
+ return Nothing<ModuleResolution >();
474
520
}
475
521
return check;
476
522
} else {
477
523
// TODO(bmeck) PREVENT FALLTHROUGH
478
524
}
479
525
parent = URL (" .." , &dir);
480
526
} while (parent.path () != dir.path ());
481
- return Nothing<URL >();
527
+ return Nothing<ModuleResolution >();
482
528
}
483
529
484
- Maybe<URL > ResolveDirectory (Environment* env,
530
+ Maybe<ModuleResolution > ResolveDirectory (Environment* env,
485
531
const URL& search,
486
532
bool read_pkg_json) {
487
533
if (read_pkg_json) {
488
- Maybe<URL > main = ResolveMain (env, search);
534
+ Maybe<ModuleResolution > main = ResolveMain (env, search);
489
535
if (!main.IsNothing ())
490
536
return main;
491
537
}
492
- return ResolveIndex (search);
538
+ return ResolveIndex (env, search);
493
539
}
494
540
495
541
} // anonymous namespace
496
542
497
-
498
- Maybe<URL> Resolve (Environment* env,
543
+ Maybe<ModuleResolution> Resolve (Environment* env,
499
544
const std::string& specifier,
500
545
const URL& base,
501
546
bool read_pkg_json) {
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
- return Nothing<URL >();
552
+ return Nothing<ModuleResolution >();
508
553
}
509
- return Just (pure_url);
554
+ return Just (ResolveFormat (env, pure_url) );
510
555
}
511
556
if (specifier.length () == 0 ) {
512
- return Nothing<URL >();
557
+ return Nothing<ModuleResolution >();
513
558
}
514
559
if (ShouldBeTreatedAsRelativeOrAbsolutePath (specifier)) {
515
560
URL resolved (specifier, base);
516
- Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
561
+ Maybe<ModuleResolution> file =
562
+ ResolveExtensions<TRY_EXACT_NAME>(env, resolved);
517
563
if (!file.IsNothing ())
518
564
return file;
519
565
if (specifier.back () != ' /' ) {
@@ -556,14 +602,30 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
556
602
return ;
557
603
}
558
604
559
- Maybe<URL> result = node::loader::Resolve (env, specifier_std, url, true );
560
- if (result.IsNothing () || (result.FromJust ().flags () & URL_FLAGS_FAILED)) {
605
+ Maybe<ModuleResolution> result =
606
+ node::loader::Resolve (env, specifier_std, url, true );
607
+ if (result.IsNothing () ||
608
+ (result.FromJust ().url .flags () & URL_FLAGS_FAILED)) {
561
609
std::string msg = " Cannot find module " + specifier_std;
562
610
env->ThrowError (msg.c_str ());
563
611
return ;
564
612
}
565
613
566
- args.GetReturnValue ().Set (result.FromJust ().ToObject (env));
614
+ Local<Object> resolved = Object::New (env->isolate ());
615
+
616
+ resolved->DefineOwnProperty (
617
+ env->context (),
618
+ env->esm_string (),
619
+ v8::Boolean::New (env->isolate (), result.FromJust ().esm ),
620
+ v8::ReadOnly);
621
+
622
+ resolved->DefineOwnProperty (
623
+ env->context (),
624
+ env->url_string (),
625
+ result.FromJust ().url .ToObject (env),
626
+ v8::ReadOnly);
627
+
628
+ args.GetReturnValue ().Set (resolved);
567
629
}
568
630
569
631
static MaybeLocal<Promise> ImportModuleDynamically (
0 commit comments