src: improve module loader readability
Various improvements on readability, performance and conformity to the Node core coding style in the ESM loader C++ code: - `isolate` for the `Isolate*`, `context` for the `Local<Context>` - Less reliance on `auto` where it’s unnecessary/increases cognitive overhead - Shorter paths to failure via `.ToLocal()` & co - Do not keep pending exceptions around e.g. for failed `JSON` parsing - Use `Maybe` types to get explicit error status forwarding - Remove an unnecessary handle scope - Add `nullptr` checks for unwrapped host objects - Remove unused code - Use `CamelCase` for function names - Use `const Foo&` instead of copying values whenever possible - Pass along the `Environment*` explicitly PR-URL: https://github.com/nodejs/node/pull/16536 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
This commit is contained in:
parent
89b3228f4d
commit
e22a8f17a5
@ -185,6 +185,7 @@ class ModuleWrap;
|
||||
V(kill_signal_string, "killSignal") \
|
||||
V(length_string, "length") \
|
||||
V(mac_string, "mac") \
|
||||
V(main_string, "main") \
|
||||
V(max_buffer_string, "maxBuffer") \
|
||||
V(message_string, "message") \
|
||||
V(minttl_string, "minttl") \
|
||||
|
@ -14,7 +14,6 @@ namespace loader {
|
||||
using node::url::URL;
|
||||
using node::url::URL_FLAGS_FAILED;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@ -23,15 +22,18 @@ using v8::Integer;
|
||||
using v8::IntegrityLevel;
|
||||
using v8::Isolate;
|
||||
using v8::JSON;
|
||||
using v8::Just;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Module;
|
||||
using v8::Nothing;
|
||||
using v8::Object;
|
||||
using v8::Promise;
|
||||
using v8::ScriptCompiler;
|
||||
using v8::ScriptOrigin;
|
||||
using v8::String;
|
||||
using v8::TryCatch;
|
||||
using v8::Value;
|
||||
|
||||
static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
|
||||
@ -61,7 +63,7 @@ ModuleWrap::~ModuleWrap() {
|
||||
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
Isolate* iso = args.GetIsolate();
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
if (!args.IsConstructCall()) {
|
||||
env->ThrowError("constructor must be called using new");
|
||||
@ -79,7 +81,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto source_text = args[0].As<String>();
|
||||
Local<String> source_text = args[0].As<String>();
|
||||
|
||||
if (!args[1]->IsString()) {
|
||||
env->ThrowError("second argument is not a string");
|
||||
@ -88,49 +90,44 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
Local<String> url = args[1].As<String>();
|
||||
|
||||
Local<Module> mod;
|
||||
Local<Module> module;
|
||||
|
||||
// compile
|
||||
{
|
||||
ScriptOrigin origin(url,
|
||||
Integer::New(iso, 0),
|
||||
Integer::New(iso, 0),
|
||||
False(iso),
|
||||
Integer::New(iso, 0),
|
||||
FIXED_ONE_BYTE_STRING(iso, ""),
|
||||
False(iso),
|
||||
False(iso),
|
||||
True(iso));
|
||||
Integer::New(isolate, 0), // line offset
|
||||
Integer::New(isolate, 0), // column offset
|
||||
False(isolate), // is cross origin
|
||||
Local<Integer>(), // script id
|
||||
Local<Value>(), // source map URL
|
||||
False(isolate), // is opaque (?)
|
||||
False(isolate), // is WASM
|
||||
True(isolate)); // is ES6 module
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
auto maybe_mod = ScriptCompiler::CompileModule(iso, &source);
|
||||
if (maybe_mod.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
mod = maybe_mod.ToLocalChecked();
|
||||
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module))
|
||||
return;
|
||||
}
|
||||
|
||||
auto that = args.This();
|
||||
auto ctx = that->CreationContext();
|
||||
auto url_str = FIXED_ONE_BYTE_STRING(iso, "url");
|
||||
Local<Object> that = args.This();
|
||||
Local<Context> context = that->CreationContext();
|
||||
Local<String> url_str = FIXED_ONE_BYTE_STRING(isolate, "url");
|
||||
|
||||
if (!that->Set(ctx, url_str, url).FromMaybe(false)) {
|
||||
if (!that->Set(context, url_str, url).FromMaybe(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModuleWrap* obj =
|
||||
new ModuleWrap(Environment::GetCurrent(ctx), that, mod, url);
|
||||
ModuleWrap* obj = new ModuleWrap(env, that, module, url);
|
||||
|
||||
env->module_map.emplace(mod->GetIdentityHash(), obj);
|
||||
env->module_map.emplace(module->GetIdentityHash(), obj);
|
||||
Wrap(that, obj);
|
||||
|
||||
that->SetIntegrityLevel(ctx, IntegrityLevel::kFrozen);
|
||||
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
|
||||
args.GetReturnValue().Set(that);
|
||||
}
|
||||
|
||||
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* iso = args.GetIsolate();
|
||||
EscapableHandleScope handle_scope(iso);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
if (!args[0]->IsFunction()) {
|
||||
env->ThrowError("first argument is not a function");
|
||||
return;
|
||||
@ -138,18 +135,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
Local<Function> resolver_arg = args[0].As<Function>();
|
||||
|
||||
auto that = args.This();
|
||||
Local<Object> that = args.This();
|
||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||
auto mod_context = that->CreationContext();
|
||||
CHECK_NE(obj, nullptr);
|
||||
Local<Context> mod_context = that->CreationContext();
|
||||
if (obj->linked_) return;
|
||||
obj->linked_ = true;
|
||||
Local<Module> mod(obj->module_.Get(iso));
|
||||
Local<Module> module(obj->module_.Get(isolate));
|
||||
|
||||
// call the dependency resolve callbacks
|
||||
for (int i = 0; i < mod->GetModuleRequestsLength(); i++) {
|
||||
Local<String> specifier = mod->GetModuleRequest(i);
|
||||
Utf8Value specifier_utf(env->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf, specifier_utf.length());
|
||||
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
|
||||
Local<String> specifier = module->GetModuleRequest(i);
|
||||
Utf8Value specifier_utf8(env->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
Local<Value> argv[] = {
|
||||
specifier
|
||||
@ -169,17 +167,19 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(handle_scope.Escape(that));
|
||||
args.GetReturnValue().Set(that);
|
||||
}
|
||||
|
||||
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||
auto iso = args.GetIsolate();
|
||||
auto that = args.This();
|
||||
auto ctx = that->CreationContext();
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Object> that = args.This();
|
||||
Local<Context> context = that->CreationContext();
|
||||
|
||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||
Local<Module> mod = obj->module_.Get(iso);
|
||||
Maybe<bool> ok = mod->InstantiateModule(ctx, ModuleWrap::ResolveCallback);
|
||||
CHECK_NE(obj, nullptr);
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
Maybe<bool> ok =
|
||||
module->InstantiateModule(context, ModuleWrap::ResolveCallback);
|
||||
|
||||
// clear resolve cache on instantiate
|
||||
for (auto& entry : obj->resolve_cache_)
|
||||
@ -192,28 +192,28 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||
auto iso = args.GetIsolate();
|
||||
auto that = args.This();
|
||||
auto ctx = that->CreationContext();
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Object> that = args.This();
|
||||
Local<Context> context = that->CreationContext();
|
||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||
auto result = obj->module_.Get(iso)->Evaluate(ctx);
|
||||
CHECK_NE(obj, nullptr);
|
||||
MaybeLocal<Value> result = obj->module_.Get(isolate)->Evaluate(context);
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ret = result.ToLocalChecked();
|
||||
args.GetReturnValue().Set(ret);
|
||||
args.GetReturnValue().Set(result.ToLocalChecked());
|
||||
}
|
||||
|
||||
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
auto isolate = args.GetIsolate();
|
||||
auto that = args.This();
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Object> that = args.This();
|
||||
ModuleWrap* obj = Unwrap<ModuleWrap>(that);
|
||||
CHECK_NE(obj, nullptr);
|
||||
|
||||
auto module = obj->module_.Get(isolate);
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
|
||||
switch (module->GetStatus()) {
|
||||
default:
|
||||
@ -225,7 +225,7 @@ void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = module->GetModuleNamespace();
|
||||
Local<Value> result = module->GetModuleNamespace();
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
||||
Local<String> specifier,
|
||||
Local<Module> referrer) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Isolate* iso = Isolate::GetCurrent();
|
||||
Isolate* isolate = env->isolate();
|
||||
if (env->module_map.count(referrer->GetIdentityHash()) == 0) {
|
||||
env->ThrowError("linking error, unknown module");
|
||||
return MaybeLocal<Module>();
|
||||
@ -253,8 +253,8 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
Utf8Value specifier_utf(env->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf, specifier_utf.length());
|
||||
Utf8Value specifier_utf8(env->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
if (dependent->resolve_cache_.count(specifier_std) != 1) {
|
||||
env->ThrowError("linking error, not in local cache");
|
||||
@ -262,7 +262,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
||||
}
|
||||
|
||||
Local<Promise> resolve_promise =
|
||||
dependent->resolve_cache_[specifier_std].Get(iso);
|
||||
dependent->resolve_cache_[specifier_std].Get(isolate);
|
||||
|
||||
if (resolve_promise->State() != Promise::kFulfilled) {
|
||||
env->ThrowError("linking error, dependency promises must be resolved on "
|
||||
@ -270,176 +270,177 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
auto module_object = resolve_promise->Result().As<Object>();
|
||||
Local<Object> module_object = resolve_promise->Result().As<Object>();
|
||||
if (module_object.IsEmpty() || !module_object->IsObject()) {
|
||||
env->ThrowError("linking error, expected a valid module object from "
|
||||
"resolver");
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
ModuleWrap* mod;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal<Module>());
|
||||
return mod->module_.Get(env->isolate());
|
||||
ModuleWrap* module;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
|
||||
return module->module_.Get(env->isolate());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
URL __init_cwd() {
|
||||
std::string specifier = "file://";
|
||||
#ifdef _WIN32
|
||||
// MAX_PATH is in characters, not bytes. Make sure we have enough headroom.
|
||||
char buf[MAX_PATH * 4];
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
#endif
|
||||
|
||||
size_t cwd_len = sizeof(buf);
|
||||
int err = uv_cwd(buf, &cwd_len);
|
||||
if (err) {
|
||||
return URL("");
|
||||
}
|
||||
specifier += buf;
|
||||
specifier += "/";
|
||||
return URL(specifier);
|
||||
}
|
||||
static URL INITIAL_CWD(__init_cwd());
|
||||
inline bool is_relative_or_absolute_path(std::string specifier) {
|
||||
auto len = specifier.length();
|
||||
if (len <= 0) {
|
||||
// Tests whether a path starts with /, ./ or ../
|
||||
// In WhatWG terminology, the alternative case is called a "bare" specifier
|
||||
// (e.g. in `import "jquery"`).
|
||||
inline bool ShouldBeTreatedAsRelativeOrAbsolutePath(
|
||||
const std::string& specifier) {
|
||||
size_t len = specifier.length();
|
||||
if (len == 0)
|
||||
return false;
|
||||
} else if (specifier[0] == '/') {
|
||||
if (specifier[0] == '/') {
|
||||
return true;
|
||||
} else if (specifier[0] == '.') {
|
||||
if (len == 1 || specifier[1] == '/') {
|
||||
if (len == 1 || specifier[1] == '/')
|
||||
return true;
|
||||
} else if (specifier[1] == '.') {
|
||||
if (len == 2 || specifier[2] == '/') {
|
||||
if (specifier[1] == '.') {
|
||||
if (len == 2 || specifier[2] == '/')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
struct read_result {
|
||||
bool had_error = false;
|
||||
std::string source;
|
||||
} read_result;
|
||||
inline const struct read_result read_file(uv_file file) {
|
||||
struct read_result ret;
|
||||
std::string src;
|
||||
|
||||
std::string ReadFile(uv_file file) {
|
||||
std::string contents;
|
||||
uv_fs_t req;
|
||||
void* base = malloc(4096);
|
||||
if (base == nullptr) {
|
||||
ret.had_error = true;
|
||||
return ret;
|
||||
}
|
||||
uv_buf_t buf = uv_buf_init(static_cast<char*>(base), 4096);
|
||||
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, 0, nullptr);
|
||||
while (req.result > 0) {
|
||||
src += std::string(static_cast<const char*>(buf.base), req.result);
|
||||
uv_fs_read(uv_default_loop(), &req, file, &buf, 1, src.length(), nullptr);
|
||||
}
|
||||
ret.source = src;
|
||||
return ret;
|
||||
char buffer_memory[4096];
|
||||
uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory));
|
||||
do {
|
||||
uv_fs_read(uv_default_loop(),
|
||||
&req,
|
||||
file,
|
||||
&buf,
|
||||
1,
|
||||
contents.length(), // offset
|
||||
nullptr);
|
||||
if (req.result <= 0)
|
||||
break;
|
||||
contents.append(buf.base, req.result);
|
||||
} while (true);
|
||||
return contents;
|
||||
}
|
||||
struct file_check {
|
||||
bool failed = true;
|
||||
uv_file file = -1;
|
||||
|
||||
enum CheckFileOptions {
|
||||
LEAVE_OPEN_AFTER_CHECK,
|
||||
CLOSE_AFTER_CHECK
|
||||
};
|
||||
inline const struct file_check check_file(const URL& search,
|
||||
bool close = false,
|
||||
bool allow_dir = false) {
|
||||
struct file_check ret;
|
||||
|
||||
Maybe<uv_file> CheckFile(const URL& search,
|
||||
CheckFileOptions opt = CLOSE_AFTER_CHECK) {
|
||||
uv_fs_t fs_req;
|
||||
std::string path = search.ToFilePath();
|
||||
if (path.empty()) {
|
||||
return ret;
|
||||
return Nothing<uv_file>();
|
||||
}
|
||||
uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
|
||||
auto fd = fs_req.result;
|
||||
uv_file fd = fs_req.result;
|
||||
if (fd < 0) {
|
||||
return ret;
|
||||
return Nothing<uv_file>();
|
||||
}
|
||||
if (!allow_dir) {
|
||||
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
||||
if (fs_req.statbuf.st_mode & S_IFDIR) {
|
||||
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
||||
if (fs_req.statbuf.st_mode & S_IFDIR) {
|
||||
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||
return Nothing<uv_file>();
|
||||
}
|
||||
ret.failed = false;
|
||||
ret.file = fd;
|
||||
if (close) uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||
return ret;
|
||||
|
||||
if (opt == CLOSE_AFTER_CHECK)
|
||||
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||
return Just(fd);
|
||||
}
|
||||
URL resolve_extensions(const URL& search, bool check_exact = true) {
|
||||
if (check_exact) {
|
||||
auto check = check_file(search, true);
|
||||
if (!check.failed) {
|
||||
return search;
|
||||
|
||||
enum ResolveExtensionsOptions {
|
||||
TRY_EXACT_NAME,
|
||||
ONLY_VIA_EXTENSIONS
|
||||
};
|
||||
|
||||
template<ResolveExtensionsOptions options>
|
||||
Maybe<URL> ResolveExtensions(const URL& search) {
|
||||
if (options == TRY_EXACT_NAME) {
|
||||
Maybe<uv_file> check = CheckFile(search);
|
||||
if (!check.IsNothing()) {
|
||||
return Just(search);
|
||||
}
|
||||
}
|
||||
for (auto extension : EXTENSIONS) {
|
||||
|
||||
for (const char* extension : EXTENSIONS) {
|
||||
URL guess(search.path() + extension, &search);
|
||||
auto check = check_file(guess, true);
|
||||
if (!check.failed) {
|
||||
return guess;
|
||||
Maybe<uv_file> check = CheckFile(guess);
|
||||
if (!check.IsNothing()) {
|
||||
return Just(guess);
|
||||
}
|
||||
}
|
||||
return URL("");
|
||||
|
||||
return Nothing<URL>();
|
||||
}
|
||||
inline URL resolve_index(const URL& search) {
|
||||
return resolve_extensions(URL("index", &search), false);
|
||||
|
||||
inline Maybe<URL> ResolveIndex(const URL& search) {
|
||||
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
|
||||
}
|
||||
URL resolve_main(const URL& search) {
|
||||
|
||||
Maybe<URL> ResolveMain(Environment* env, const URL& search) {
|
||||
URL pkg("package.json", &search);
|
||||
auto check = check_file(pkg);
|
||||
if (!check.failed) {
|
||||
auto iso = Isolate::GetCurrent();
|
||||
auto ctx = iso->GetCurrentContext();
|
||||
auto read = read_file(check.file);
|
||||
uv_fs_t fs_req;
|
||||
// if we fail to close :-/
|
||||
uv_fs_close(nullptr, &fs_req, check.file, nullptr);
|
||||
if (read.had_error) return URL("");
|
||||
std::string pkg_src = read.source;
|
||||
Local<String> src =
|
||||
String::NewFromUtf8(iso, pkg_src.c_str(),
|
||||
String::kNormalString, pkg_src.length());
|
||||
if (src.IsEmpty()) return URL("");
|
||||
auto maybe_pkg_json = JSON::Parse(ctx, src);
|
||||
if (maybe_pkg_json.IsEmpty()) return URL("");
|
||||
auto pkg_json_obj = maybe_pkg_json.ToLocalChecked().As<Object>();
|
||||
if (!pkg_json_obj->IsObject()) return URL("");
|
||||
auto maybe_pkg_main = pkg_json_obj->Get(
|
||||
ctx, FIXED_ONE_BYTE_STRING(iso, "main"));
|
||||
if (maybe_pkg_main.IsEmpty()) return URL("");
|
||||
auto pkg_main_str = maybe_pkg_main.ToLocalChecked().As<String>();
|
||||
if (!pkg_main_str->IsString()) return URL("");
|
||||
Utf8Value main_utf8(iso, pkg_main_str);
|
||||
std::string main_std(*main_utf8, main_utf8.length());
|
||||
if (!is_relative_or_absolute_path(main_std)) {
|
||||
main_std.insert(0, "./");
|
||||
}
|
||||
return Resolve(main_std, &search);
|
||||
Maybe<uv_file> check = CheckFile(pkg, LEAVE_OPEN_AFTER_CHECK);
|
||||
if (check.IsNothing()) {
|
||||
return Nothing<URL>();
|
||||
}
|
||||
return URL("");
|
||||
|
||||
Isolate* isolate = env->isolate();
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
std::string pkg_src = ReadFile(check.FromJust());
|
||||
uv_fs_t fs_req;
|
||||
uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr);
|
||||
|
||||
// It's not okay for the called of this method to not be able to tell
|
||||
// whether an exception is pending or not.
|
||||
TryCatch try_catch(isolate);
|
||||
|
||||
Local<String> src;
|
||||
if (!String::NewFromUtf8(isolate,
|
||||
pkg_src.c_str(),
|
||||
v8::NewStringType::kNormal,
|
||||
pkg_src.length()).ToLocal(&src)) {
|
||||
return Nothing<URL>();
|
||||
}
|
||||
|
||||
Local<Value> pkg_json;
|
||||
if (!JSON::Parse(context, src).ToLocal(&pkg_json) || !pkg_json->IsObject())
|
||||
return Nothing<URL>();
|
||||
Local<Value> pkg_main;
|
||||
if (!pkg_json.As<Object>()->Get(context, env->main_string())
|
||||
.ToLocal(&pkg_main) || !pkg_main->IsString()) {
|
||||
return Nothing<URL>();
|
||||
}
|
||||
Utf8Value main_utf8(isolate, pkg_main.As<String>());
|
||||
std::string main_std(*main_utf8, main_utf8.length());
|
||||
if (!ShouldBeTreatedAsRelativeOrAbsolutePath(main_std)) {
|
||||
main_std.insert(0, "./");
|
||||
}
|
||||
return Resolve(env, main_std, search);
|
||||
}
|
||||
URL resolve_module(std::string specifier, const URL* base) {
|
||||
|
||||
Maybe<URL> ResolveModule(Environment* env,
|
||||
const std::string& specifier,
|
||||
const URL& base) {
|
||||
URL parent(".", base);
|
||||
URL dir("");
|
||||
do {
|
||||
dir = parent;
|
||||
auto check = Resolve("./node_modules/" + specifier, &dir, true);
|
||||
if (!(check.flags() & URL_FLAGS_FAILED)) {
|
||||
const auto limit = specifier.find('/');
|
||||
const auto spec_len = limit == std::string::npos ?
|
||||
specifier.length() :
|
||||
limit + 1;
|
||||
Maybe<URL> check = Resolve(env, "./node_modules/" + specifier, dir, true);
|
||||
if (!check.IsNothing()) {
|
||||
const size_t limit = specifier.find('/');
|
||||
const size_t spec_len =
|
||||
limit == std::string::npos ? specifier.length() :
|
||||
limit + 1;
|
||||
std::string chroot =
|
||||
dir.path() + "node_modules/" + specifier.substr(0, spec_len);
|
||||
if (check.path().substr(0, chroot.length()) != chroot) {
|
||||
return URL("");
|
||||
if (check.FromJust().path().substr(0, chroot.length()) != chroot) {
|
||||
return Nothing<URL>();
|
||||
}
|
||||
return check;
|
||||
} else {
|
||||
@ -447,45 +448,51 @@ URL resolve_module(std::string specifier, const URL* base) {
|
||||
}
|
||||
parent = URL("..", &dir);
|
||||
} while (parent.path() != dir.path());
|
||||
return URL("");
|
||||
return Nothing<URL>();
|
||||
}
|
||||
|
||||
URL resolve_directory(const URL& search, bool read_pkg_json) {
|
||||
Maybe<URL> ResolveDirectory(Environment* env,
|
||||
const URL& search,
|
||||
bool read_pkg_json) {
|
||||
if (read_pkg_json) {
|
||||
auto main = resolve_main(search);
|
||||
if (!(main.flags() & URL_FLAGS_FAILED)) return main;
|
||||
Maybe<URL> main = ResolveMain(env, search);
|
||||
if (!main.IsNothing())
|
||||
return main;
|
||||
}
|
||||
return resolve_index(search);
|
||||
return ResolveIndex(search);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
URL Resolve(std::string specifier, const URL* base, bool read_pkg_json) {
|
||||
Maybe<URL> Resolve(Environment* env,
|
||||
const std::string& specifier,
|
||||
const URL& base,
|
||||
bool read_pkg_json) {
|
||||
URL pure_url(specifier);
|
||||
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
|
||||
// just check existence, without altering
|
||||
auto check = check_file(pure_url, true);
|
||||
if (check.failed) {
|
||||
return URL("");
|
||||
Maybe<uv_file> check = CheckFile(pure_url);
|
||||
if (check.IsNothing()) {
|
||||
return Nothing<URL>();
|
||||
}
|
||||
return pure_url;
|
||||
return Just(pure_url);
|
||||
}
|
||||
if (specifier.length() == 0) {
|
||||
return URL("");
|
||||
return Nothing<URL>();
|
||||
}
|
||||
if (is_relative_or_absolute_path(specifier)) {
|
||||
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
|
||||
URL resolved(specifier, base);
|
||||
auto file = resolve_extensions(resolved);
|
||||
if (!(file.flags() & URL_FLAGS_FAILED)) return file;
|
||||
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
|
||||
if (!file.IsNothing())
|
||||
return file;
|
||||
if (specifier.back() != '/') {
|
||||
resolved = URL(specifier + "/", base);
|
||||
}
|
||||
return resolve_directory(resolved, read_pkg_json);
|
||||
return ResolveDirectory(env, resolved, read_pkg_json);
|
||||
} else {
|
||||
return resolve_module(specifier, base);
|
||||
return ResolveModule(env, specifier, base);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
||||
@ -504,34 +511,34 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
||||
env->ThrowError("first argument is not a string");
|
||||
return;
|
||||
}
|
||||
Utf8Value specifier_utf(env->isolate(), args[0]);
|
||||
Utf8Value specifier_utf8(env->isolate(), args[0]);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
if (!args[1]->IsString()) {
|
||||
env->ThrowError("second argument is not a string");
|
||||
return;
|
||||
}
|
||||
Utf8Value url_utf(env->isolate(), args[1]);
|
||||
URL url(*url_utf, url_utf.length());
|
||||
Utf8Value url_utf8(env->isolate(), args[1]);
|
||||
URL url(*url_utf8, url_utf8.length());
|
||||
|
||||
if (url.flags() & URL_FLAGS_FAILED) {
|
||||
env->ThrowError("second argument is not a URL string");
|
||||
return;
|
||||
}
|
||||
|
||||
URL result = node::loader::Resolve(*specifier_utf, &url, true);
|
||||
if (result.flags() & URL_FLAGS_FAILED) {
|
||||
std::string msg = "Cannot find module ";
|
||||
msg += *specifier_utf;
|
||||
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url, true);
|
||||
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
|
||||
std::string msg = "Cannot find module " + specifier_std;
|
||||
env->ThrowError(msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(result.ToObject(env));
|
||||
args.GetReturnValue().Set(result.FromJust().ToObject(env));
|
||||
}
|
||||
|
||||
void ModuleWrap::Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context) {
|
||||
Local<Value> unused,
|
||||
Local<Context> context) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Isolate* isolate = env->isolate();
|
||||
|
||||
|
@ -12,8 +12,10 @@
|
||||
namespace node {
|
||||
namespace loader {
|
||||
|
||||
node::url::URL Resolve(std::string specifier, const node::url::URL* base,
|
||||
bool read_pkg_json = false);
|
||||
v8::Maybe<url::URL> Resolve(Environment* env,
|
||||
const std::string& specifier,
|
||||
const url::URL& base,
|
||||
bool read_pkg_json = false);
|
||||
|
||||
class ModuleWrap : public BaseObject {
|
||||
public:
|
||||
@ -23,7 +25,7 @@ class ModuleWrap : public BaseObject {
|
||||
v8::Local<v8::Context> context);
|
||||
|
||||
private:
|
||||
ModuleWrap(node::Environment* env,
|
||||
ModuleWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
v8::Local<v8::Module> module,
|
||||
v8::Local<v8::String> url);
|
||||
|
@ -118,6 +118,9 @@ class URL {
|
||||
URL(std::string input, const URL* base) :
|
||||
URL(input.c_str(), input.length(), base) {}
|
||||
|
||||
URL(std::string input, const URL& base) :
|
||||
URL(input.c_str(), input.length(), &base) {}
|
||||
|
||||
URL(std::string input, std::string base) :
|
||||
URL(input.c_str(), input.length(), base.c_str(), base.length()) {}
|
||||
|
||||
@ -168,6 +171,13 @@ class URL {
|
||||
|
||||
const Local<Value> ToObject(Environment* env) const;
|
||||
|
||||
URL(const URL&) = default;
|
||||
URL& operator=(const URL&) = default;
|
||||
URL(URL&&) = default;
|
||||
URL& operator=(URL&&) = default;
|
||||
|
||||
URL() : URL("") {}
|
||||
|
||||
private:
|
||||
struct url_data context_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user