summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGuy Bedford <guybedford@gmail.com>2019-01-16 03:11:10 +0200
committerMichaƫl Zasso <targos@protonmail.com>2019-07-20 11:29:59 +0200
commitb379c0e8b6b1f67fb7985d3c51f6200e2e3f2290 (patch)
tree6aeb2110697e652f2437f08a01b5a4e8cd535603 /src
parentff432c8ef68b064940a809b70ad32238f6c29bba (diff)
downloadandroid-node-v8-b379c0e8b6b1f67fb7985d3c51f6200e2e3f2290.tar.gz
android-node-v8-b379c0e8b6b1f67fb7985d3c51f6200e2e3f2290.tar.bz2
android-node-v8-b379c0e8b6b1f67fb7985d3c51f6200e2e3f2290.zip
esm: implement "pkg-exports" proposal
Refs: https://github.com/jkrems/proposal-pkg-exports/issues/36 PR-URL: https://github.com/nodejs/node/pull/28568 Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'src')
-rw-r--r--src/env.h2
-rw-r--r--src/module_wrap.cc81
-rw-r--r--src/node_options.cc4
-rw-r--r--src/node_options.h1
4 files changed, 80 insertions, 8 deletions
diff --git a/src/env.h b/src/env.h
index 29b9a036cc..1356f0bbbc 100644
--- a/src/env.h
+++ b/src/env.h
@@ -99,6 +99,8 @@ struct PackageConfig {
const HasMain has_main;
const std::string main;
const PackageType type;
+
+ v8::Global<v8::Value> exports;
};
} // namespace loader
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index e104afb736..f1c819874a 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -558,7 +558,7 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
if (source.IsNothing()) {
auto entry = env->package_json_cache.emplace(path,
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "",
- PackageType::None });
+ PackageType::None, Global<Value>() });
return Just(&entry.first->second);
}
@@ -578,7 +578,7 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
!pkg_json_v->ToObject(context).ToLocal(&pkg_json)) {
env->package_json_cache.emplace(path,
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "",
- PackageType::None });
+ PackageType::None, Global<Value>() });
std::string msg = "Invalid JSON in '" + path +
"' imported from " + base.ToFilePath();
node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str());
@@ -609,22 +609,22 @@ Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
}
Local<Value> exports_v;
- if (pkg_json->Get(env->context(),
+ if (env->options()->experimental_exports &&
+ pkg_json->Get(env->context(),
env->exports_string()).ToLocal(&exports_v) &&
- (exports_v->IsObject() || exports_v->IsString() ||
- exports_v->IsBoolean())) {
+ !exports_v->IsNullOrUndefined()) {
Global<Value> exports;
exports.Reset(env->isolate(), exports_v);
auto entry = env->package_json_cache.emplace(path,
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
- pkg_type });
+ pkg_type, std::move(exports) });
return Just(&entry.first->second);
}
auto entry = env->package_json_cache.emplace(path,
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
- pkg_type });
+ pkg_type, Global<Value>() });
return Just(&entry.first->second);
}
@@ -800,6 +800,66 @@ Maybe<URL> PackageMainResolve(Environment* env,
return Nothing<URL>();
}
+Maybe<URL> PackageExportsResolve(Environment* env,
+ const URL& pjson_url,
+ const std::string& pkg_subpath,
+ const PackageConfig& pcfg,
+ const URL& base) {
+ CHECK(env->options()->experimental_exports);
+ Isolate* isolate = env->isolate();
+ Local<Context> context = env->context();
+ Local<Value> exports = pcfg.exports.Get(isolate);
+ if (exports->IsObject()) {
+ Local<Object> exports_obj = exports.As<Object>();
+ Local<String> subpath = String::NewFromUtf8(isolate,
+ pkg_subpath.c_str(), v8::NewStringType::kNormal).ToLocalChecked();
+
+ auto target = exports_obj->Get(context, subpath).ToLocalChecked();
+ if (target->IsString()) {
+ Utf8Value target_utf8(isolate, target.As<v8::String>());
+ std::string target(*target_utf8, target_utf8.length());
+ if (target.substr(0, 2) == "./") {
+ URL target_url(target, pjson_url);
+ return FinalizeResolution(env, target_url, base);
+ }
+ }
+
+ Local<String> best_match;
+ std::string best_match_str = "";
+ Local<Array> keys =
+ exports_obj->GetOwnPropertyNames(context).ToLocalChecked();
+ for (uint32_t i = 0; i < keys->Length(); ++i) {
+ Local<String> key = keys->Get(context, i).ToLocalChecked().As<String>();
+ Utf8Value key_utf8(isolate, key);
+ std::string key_str(*key_utf8, key_utf8.length());
+ if (key_str.back() != '/') continue;
+ if (pkg_subpath.substr(0, key_str.length()) == key_str &&
+ key_str.length() > best_match_str.length()) {
+ best_match = key;
+ best_match_str = key_str;
+ }
+ }
+
+ if (best_match_str.length() > 0) {
+ auto target = exports_obj->Get(context, best_match).ToLocalChecked();
+ if (target->IsString()) {
+ Utf8Value target_utf8(isolate, target.As<v8::String>());
+ std::string target(*target_utf8, target_utf8.length());
+ if (target.back() == '/' && target.substr(0, 2) == "./") {
+ std::string subpath = pkg_subpath.substr(best_match_str.length());
+ URL target_url(target + subpath, pjson_url);
+ return FinalizeResolution(env, target_url, base);
+ }
+ }
+ }
+ }
+ std::string msg = "Package exports for '" +
+ URL(".", pjson_url).ToFilePath() + "' do not define a '" + pkg_subpath +
+ "' subpath, imported from " + base.ToFilePath();
+ node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
+ return Nothing<URL>();
+}
+
Maybe<URL> PackageResolve(Environment* env,
const std::string& specifier,
const URL& base) {
@@ -847,7 +907,12 @@ Maybe<URL> PackageResolve(Environment* env,
if (!pkg_subpath.length()) {
return PackageMainResolve(env, pjson_url, *pcfg.FromJust(), base);
} else {
- return FinalizeResolution(env, URL(pkg_subpath, pjson_url), base);
+ if (!pcfg.FromJust()->exports.IsEmpty()) {
+ return PackageExportsResolve(env, pjson_url, pkg_subpath,
+ *pcfg.FromJust(), base);
+ } else {
+ return FinalizeResolution(env, URL(pkg_subpath, pjson_url), base);
+ }
}
CHECK(false);
// Cross-platform root check.
diff --git a/src/node_options.cc b/src/node_options.cc
index b6ea82158c..829154c3bf 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -304,6 +304,10 @@ DebugOptionsParser::DebugOptionsParser() {
}
EnvironmentOptionsParser::EnvironmentOptionsParser() {
+ AddOption("--experimental-exports",
+ "experimental support for exports in package.json",
+ &EnvironmentOptions::experimental_exports,
+ kAllowedInEnvironment);
AddOption("--experimental-modules",
"experimental ES Module support and caching modules",
&EnvironmentOptions::experimental_modules,
diff --git a/src/node_options.h b/src/node_options.h
index 280de40c0a..c55aaf17a0 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -100,6 +100,7 @@ class DebugOptions : public Options {
class EnvironmentOptions : public Options {
public:
bool abort_on_uncaught_exception = false;
+ bool experimental_exports = false;
bool experimental_modules = false;
std::string es_module_specifier_resolution;
bool experimental_wasm_modules = false;