diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index fea151a0ff..ea83079b9b 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -25,9 +25,7 @@ const legacyExtensionFormatMap = { '__proto__': null, '.cjs': 'cjs', '.js': 'cjs', - '.json': 'cjs', - '.mjs': 'esm', - '.node': 'cjs' + '.mjs': 'esm' }; function resolve(specifier, parentURL) { diff --git a/src/env.h b/src/env.h index 9b308006a5..6c9a002437 100644 --- a/src/env.h +++ b/src/env.h @@ -82,12 +82,16 @@ struct PackageConfig { struct HasMain { enum Bool { No, Yes }; }; + struct HasExports { + enum Bool { No, Yes }; + }; struct IsESM { enum Bool { No, Yes }; }; const Exists::Bool exists; const IsValid::Bool is_valid; const HasMain::Bool has_main; + const HasExports::Bool has_exports; const std::string main; const IsESM::Bool esm; }; diff --git a/src/module_wrap.cc b/src/module_wrap.cc index b059862e09..f47134d0f5 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -524,6 +524,7 @@ Maybe ReadIfFile(const std::string& path) { using Exists = PackageConfig::Exists; using IsValid = PackageConfig::IsValid; using HasMain = PackageConfig::HasMain; +using HasExports = PackageConfig::HasExports; using IsESM = PackageConfig::IsESM; Maybe GetPackageConfig(Environment* env, @@ -538,8 +539,8 @@ Maybe GetPackageConfig(Environment* env, if (source.IsNothing()) { auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", - IsESM::No }); + PackageConfig { Exists::No, IsValid::Yes, HasMain::No, HasExports::No, + "", IsESM::No }); return Just(&entry.first->second); } @@ -565,8 +566,8 @@ Maybe GetPackageConfig(Environment* env, if (!parsed) { (void)env->package_json_cache.emplace(path, - PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "", - IsESM::No }); + PackageConfig { Exists::Yes, IsValid::No, HasMain::No, HasExports::No, + "", IsESM::No }); std::string msg = "Invalid JSON in '" + path + "' imported from " + base.ToFilePath(); node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str()); @@ -575,8 +576,17 @@ Maybe GetPackageConfig(Environment* env, Local pkg_main; HasMain::Bool has_main = HasMain::No; + HasExports::Bool has_exports = HasExports::No; std::string main_std; - if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) { + if (pkg_json->Get(env->context(), env->exports_string()).ToLocal(&pkg_main)) { + if (pkg_main->IsString()) { + has_main = HasMain::Yes; + has_exports = HasExports::Yes; + } + Utf8Value main_utf8(isolate, pkg_main); + main_std.assign(std::string(*main_utf8, main_utf8.length())); + } else if (pkg_json->Get(env->context(), + env->main_string()).ToLocal(&pkg_main)) { if (pkg_main->IsString()) { has_main = HasMain::Yes; } @@ -592,24 +602,9 @@ Maybe GetPackageConfig(Environment* env, } } - Local exports_v; - if (pkg_json->Get(env->context(), - env->exports_string()).ToLocal(&exports_v) && - (exports_v->IsObject() || exports_v->IsString() || - exports_v->IsBoolean())) { - Persistent exports; - // esm = IsESM::Yes; - exports.Reset(env->isolate(), exports_v); - - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std, - esm }); - return Just(&entry.first->second); - } - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std, - esm }); + PackageConfig { Exists::Yes, IsValid::Yes, has_main, has_exports, + main_std, esm }); return Just(&entry.first->second); } @@ -630,8 +625,8 @@ Maybe GetPackageBoundaryConfig(Environment* env, // (can't just check "/package.json" for Windows support). if (pjson_url.path() == last_pjson_url.path()) { auto entry = env->package_json_cache.emplace(pjson_url.ToFilePath(), - PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", - IsESM::No }); + PackageConfig { Exists::No, IsValid::Yes, HasMain::No, + HasExports::No, "", IsESM::No }); return Just(&entry.first->second); } } @@ -757,15 +752,20 @@ Maybe PackageMainResolve(Environment* env, node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); return Nothing(); } - if (pcfg.has_main == HasMain::Yes && - pcfg.main.substr(pcfg.main.length() - 4, 4) == ".mjs") { - return FinalizeResolution(env, URL(pcfg.main, pjson_url), base, true); - } - if (pcfg.esm == IsESM::Yes && - pcfg.main.substr(pcfg.main.length() - 3, 3) == ".js") { + if (pcfg.has_exports == HasExports::Yes) { + const size_t main_len = pcfg.main.length(); + if (main_len > 4 && pcfg.main.substr(main_len - 4, 4) == ".cjs" || + (pcfg.esm == IsESM::No && + main_len > 3 && pcfg.main.substr(main_len - 3, 3) == ".js")) { + std::string msg = "Cannot load exports entry point '" + + pcfg.main + "' in " + URL(".", pjson_url).ToFilePath() + + " imported from " + base.ToFilePath() + + " as it would be loaded as CommonJS."; + node::THROW_ERR_FORMAT_MISMATCH(env, msg.c_str()); + return Nothing(); + } return FinalizeResolution(env, URL(pcfg.main, pjson_url), base, true); } - Maybe resolved = LegacyMainResolve(pjson_url, pcfg); // Legacy main resolution error if (resolved.IsNothing()) { diff --git a/src/node_errors.h b/src/node_errors.h index bcf4154ff5..02d2b58bfb 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -62,6 +62,7 @@ void FatalException(const v8::FunctionCallbackInfo& args); V(ERR_STRING_TOO_LONG, Error) \ V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \ V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ + V(ERR_FORMAT_MISMATCH, Error) \ #define V(code, type) \ inline v8::Local code(v8::Isolate* isolate, \