Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src,lib: implement import.meta #18368

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ Only the CLI argument for the main entry point to the program can be an entry
point into an ESM graph. Dynamic import can also be used to create entry points
into ESM graphs at runtime.

#### `import.meta`

The `import.meta` metaproperty is an `Object` that contains the following
property:
* `url` {string} The absolute `file:` URL of the module

### Unsupported

| Feature | Reason |
| --- | --- |
| `require('./foo.mjs')` | ES Modules have differing resolution and timing, use dynamic import |
| `import.meta` | pending V8 implementation |

## Notable differences between `import` and `require`

Expand Down
1 change: 1 addition & 0 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
process.emitWarning(
'The ESM module loader is experimental.',
'ExperimentalWarning', undefined);
NativeModule.require('internal/process/modules').setup();
}


Expand Down
17 changes: 17 additions & 0 deletions lib/internal/process/modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const {
setInitializeImportMetaObjectCallback
} = internalBinding('module_wrap');

function initializeImportMetaObject(wrap, meta) {
meta.url = wrap.url;
}

function setupModules() {
setInitializeImportMetaObjectCallback(initializeImportMetaObject);
}

module.exports = {
setup: setupModules
};
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
'lib/internal/net.js',
'lib/internal/module.js',
'lib/internal/os.js',
'lib/internal/process/modules.js',
'lib/internal/process/next_tick.js',
'lib/internal/process/promises.js',
'lib/internal/process/stdio.js',
Expand Down
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ class ModuleWrap;
V(type_string, "type") \
V(uid_string, "uid") \
V(unknown_string, "<unknown>") \
V(url_string, "url") \
V(user_string, "user") \
V(username_string, "username") \
V(valid_from_string, "valid_from") \
Expand Down Expand Up @@ -278,6 +279,7 @@ class ModuleWrap;
V(context, v8::Context) \
V(domain_callback, v8::Function) \
V(host_import_module_dynamically_callback, v8::Function) \
V(host_initialize_import_meta_object_callback, v8::Function) \
V(http2ping_constructor_template, v8::ObjectTemplate) \
V(http2stream_constructor_template, v8::ObjectTemplate) \
V(http2settings_constructor_template, v8::ObjectTemplate) \
Expand Down
64 changes: 53 additions & 11 deletions src/module_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::String;
using v8::TryCatch;
using v8::Undefined;
using v8::Value;

static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
Expand Down Expand Up @@ -64,6 +65,19 @@ ModuleWrap::~ModuleWrap() {
context_.Reset();
}

ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
Local<Module> module) {
ModuleWrap* ret = nullptr;
auto range = env->module_map.equal_range(module->GetIdentityHash());
for (auto it = range.first; it != range.second; ++it) {
if (it->second->module_ == module) {
ret = it->second;
break;
}
}
return ret;
}

void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -133,9 +147,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
}
}

Local<String> url_str = FIXED_ONE_BYTE_STRING(isolate, "url");

if (!that->Set(context, url_str, url).FromMaybe(false)) {
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
return;
}

Expand Down Expand Up @@ -361,14 +373,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
return MaybeLocal<Module>();
}

ModuleWrap* dependent = nullptr;
auto range = env->module_map.equal_range(referrer->GetIdentityHash());
for (auto it = range.first; it != range.second; ++it) {
if (it->second->module_ == referrer) {
dependent = it->second;
break;
}
}
ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer);

if (dependent == nullptr) {
env->ThrowError("linking error, null dep");
Expand Down Expand Up @@ -728,6 +733,40 @@ void ModuleWrap::SetImportModuleDynamicallyCallback(
iso->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
}

void ModuleWrap::HostInitializeImportMetaObjectCallback(
Local<Context> context, Local<Module> module, Local<Object> meta) {
Isolate* isolate = context->GetIsolate();
Environment* env = Environment::GetCurrent(context);
ModuleWrap* module_wrap = ModuleWrap::GetFromModule(env, module);

if (module_wrap == nullptr) {
return;
}

Local<Object> wrap = module_wrap->object();
Local<Function> callback =
env->host_initialize_import_meta_object_callback();
Local<Value> args[] = { wrap, meta };
callback->Call(context, Undefined(isolate), arraysize(args), args)
.ToLocalChecked();
}

void ModuleWrap::SetInitializeImportMetaObjectCallback(
const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
if (!args[0]->IsFunction()) {
env->ThrowError("first argument is not a function");
return;
}

Local<Function> import_meta_callback = args[0].As<Function>();
env->set_host_initialize_import_meta_object_callback(import_meta_callback);

isolate->SetHostInitializeImportMetaObjectCallback(
HostInitializeImportMetaObjectCallback);
}

void ModuleWrap::Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Expand All @@ -752,6 +791,9 @@ void ModuleWrap::Initialize(Local<Object> target,
env->SetMethod(target,
"setImportModuleDynamicallyCallback",
node::loader::ModuleWrap::SetImportModuleDynamicallyCallback);
env->SetMethod(target,
"setInitializeImportMetaObjectCallback",
ModuleWrap::SetInitializeImportMetaObjectCallback);

#define V(name) \
target->Set(context, \
Expand Down
8 changes: 8 additions & 0 deletions src/module_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class ModuleWrap : public BaseObject {
static void Initialize(v8::Local<v8::Object> target,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context);
static void HostInitializeImportMetaObjectCallback(
v8::Local<v8::Context> context,
v8::Local<v8::Module> module,
v8::Local<v8::Object> meta);

private:
ModuleWrap(Environment* env,
Expand All @@ -44,10 +48,14 @@ class ModuleWrap : public BaseObject {
static void Resolve(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetImportModuleDynamicallyCallback(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetInitializeImportMetaObjectCallback(
const v8::FunctionCallbackInfo<v8::Value>& args);
static v8::MaybeLocal<v8::Module> ResolveCallback(
v8::Local<v8::Context> context,
v8::Local<v8::String> specifier,
v8::Local<v8::Module> referrer);
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);


v8::Persistent<v8::Module> module_;
v8::Persistent<v8::String> url_;
Expand Down
2 changes: 2 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3680,6 +3680,8 @@ static void ParseArgs(int* argc,
config_experimental_modules = true;
new_v8_argv[new_v8_argc] = "--harmony-dynamic-import";
new_v8_argc += 1;
new_v8_argv[new_v8_argc] = "--harmony-import-meta";
new_v8_argc += 1;
} else if (strcmp(arg, "--experimental-vm-modules") == 0) {
config_experimental_vm_modules = true;
} else if (strcmp(arg, "--loader") == 0) {
Expand Down
22 changes: 22 additions & 0 deletions test/es-module/test-esm-import-meta.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Flags: --experimental-modules

import '../common';
import assert from 'assert';

assert.strictEqual(Object.getPrototypeOf(import.meta), null);

const keys = ['url'];
assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);

const descriptors = Object.getOwnPropertyDescriptors(import.meta);
for (const descriptor of Object.values(descriptors)) {
delete descriptor.value; // Values are verified below.
assert.deepStrictEqual(descriptor, {
enumerable: true,
writable: true,
configurable: true
});
}

const urlReg = /^file:\/\/\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
assert(import.meta.url.match(urlReg));