Skip to content

Commit 21ce2c5

Browse files
committed
src,lib: implement import.meta
Implement the C++ callback that is required to configure the `import.meta` object and add one property: - url: absolute URL of the module
1 parent ac7f1e3 commit 21ce2c5

File tree

9 files changed

+112
-12
lines changed

9 files changed

+112
-12
lines changed

doc/api/esm.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ Only the CLI argument for the main entry point to the program can be an entry
3636
point into an ESM graph. Dynamic import can also be used to create entry points
3737
into ESM graphs at runtime.
3838

39+
#### `import.meta`
40+
41+
The `import.meta` metaproperty is an `Object` that contains the following
42+
property:
43+
* `url` {string} The absolute `file:` URL of the module
44+
3945
### Unsupported
4046

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

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

lib/internal/bootstrap_node.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
process.emitWarning(
105105
'The ESM module loader is experimental.',
106106
'ExperimentalWarning', undefined);
107+
NativeModule.require('internal/process/modules').setup();
107108
}
108109

109110

lib/internal/process/modules.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
const {
4+
setInitializeImportMetaObjectCallback
5+
} = internalBinding('module_wrap');
6+
7+
function initializeImportMetaObject(wrap, meta) {
8+
meta.url = wrap.url;
9+
}
10+
11+
function setupModules() {
12+
setInitializeImportMetaObjectCallback(initializeImportMetaObject);
13+
}
14+
15+
module.exports = {
16+
setup: setupModules
17+
};

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
'lib/internal/net.js',
113113
'lib/internal/module.js',
114114
'lib/internal/os.js',
115+
'lib/internal/process/modules.js',
115116
'lib/internal/process/next_tick.js',
116117
'lib/internal/process/promises.js',
117118
'lib/internal/process/stdio.js',

src/env.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ class ModuleWrap;
249249
V(type_string, "type") \
250250
V(uid_string, "uid") \
251251
V(unknown_string, "<unknown>") \
252+
V(url_string, "url") \
252253
V(user_string, "user") \
253254
V(username_string, "username") \
254255
V(valid_from_string, "valid_from") \
@@ -278,6 +279,7 @@ class ModuleWrap;
278279
V(context, v8::Context) \
279280
V(domain_callback, v8::Function) \
280281
V(host_import_module_dynamically_callback, v8::Function) \
282+
V(host_initialize_import_meta_object_callback, v8::Function) \
281283
V(http2ping_constructor_template, v8::ObjectTemplate) \
282284
V(http2stream_constructor_template, v8::ObjectTemplate) \
283285
V(http2settings_constructor_template, v8::ObjectTemplate) \

src/module_wrap.cc

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ using v8::ScriptCompiler;
3737
using v8::ScriptOrigin;
3838
using v8::String;
3939
using v8::TryCatch;
40+
using v8::Undefined;
4041
using v8::Value;
4142

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

68+
ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
69+
Local<Module> module) {
70+
ModuleWrap* ret = nullptr;
71+
auto range = env->module_map.equal_range(module->GetIdentityHash());
72+
for (auto it = range.first; it != range.second; ++it) {
73+
if (it->second->module_ == module) {
74+
ret = it->second;
75+
break;
76+
}
77+
}
78+
return ret;
79+
}
80+
6781
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
6882
Environment* env = Environment::GetCurrent(args);
6983

@@ -133,9 +147,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
133147
}
134148
}
135149

136-
Local<String> url_str = FIXED_ONE_BYTE_STRING(isolate, "url");
137-
138-
if (!that->Set(context, url_str, url).FromMaybe(false)) {
150+
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
139151
return;
140152
}
141153

@@ -361,14 +373,7 @@ MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context,
361373
return MaybeLocal<Module>();
362374
}
363375

364-
ModuleWrap* dependent = nullptr;
365-
auto range = env->module_map.equal_range(referrer->GetIdentityHash());
366-
for (auto it = range.first; it != range.second; ++it) {
367-
if (it->second->module_ == referrer) {
368-
dependent = it->second;
369-
break;
370-
}
371-
}
376+
ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer);
372377

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

736+
void ModuleWrap::HostInitializeImportMetaObjectCallback(
737+
Local<Context> context, Local<Module> module, Local<Object> meta) {
738+
Isolate* isolate = context->GetIsolate();
739+
Environment* env = Environment::GetCurrent(context);
740+
ModuleWrap* module_wrap = ModuleWrap::GetFromModule(env, module);
741+
742+
if (module_wrap == nullptr) {
743+
return;
744+
}
745+
746+
Local<Object> wrap = module_wrap->object();
747+
Local<Function> callback =
748+
env->host_initialize_import_meta_object_callback();
749+
Local<Value> args[] = { wrap, meta };
750+
callback->Call(context, Undefined(isolate), arraysize(args), args)
751+
.ToLocalChecked();
752+
}
753+
754+
void ModuleWrap::SetInitializeImportMetaObjectCallback(
755+
const FunctionCallbackInfo<Value>& args) {
756+
Environment* env = Environment::GetCurrent(args);
757+
Isolate* isolate = env->isolate();
758+
if (!args[0]->IsFunction()) {
759+
env->ThrowError("first argument is not a function");
760+
return;
761+
}
762+
763+
Local<Function> import_meta_callback = args[0].As<Function>();
764+
env->set_host_initialize_import_meta_object_callback(import_meta_callback);
765+
766+
isolate->SetHostInitializeImportMetaObjectCallback(
767+
HostInitializeImportMetaObjectCallback);
768+
}
769+
731770
void ModuleWrap::Initialize(Local<Object> target,
732771
Local<Value> unused,
733772
Local<Context> context) {
@@ -752,6 +791,9 @@ void ModuleWrap::Initialize(Local<Object> target,
752791
env->SetMethod(target,
753792
"setImportModuleDynamicallyCallback",
754793
node::loader::ModuleWrap::SetImportModuleDynamicallyCallback);
794+
env->SetMethod(target,
795+
"setInitializeImportMetaObjectCallback",
796+
ModuleWrap::SetInitializeImportMetaObjectCallback);
755797

756798
#define V(name) \
757799
target->Set(context, \

src/module_wrap.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class ModuleWrap : public BaseObject {
2323
static void Initialize(v8::Local<v8::Object> target,
2424
v8::Local<v8::Value> unused,
2525
v8::Local<v8::Context> context);
26+
static void HostInitializeImportMetaObjectCallback(
27+
v8::Local<v8::Context> context,
28+
v8::Local<v8::Module> module,
29+
v8::Local<v8::Object> meta);
2630

2731
private:
2832
ModuleWrap(Environment* env,
@@ -44,10 +48,14 @@ class ModuleWrap : public BaseObject {
4448
static void Resolve(const v8::FunctionCallbackInfo<v8::Value>& args);
4549
static void SetImportModuleDynamicallyCallback(
4650
const v8::FunctionCallbackInfo<v8::Value>& args);
51+
static void SetInitializeImportMetaObjectCallback(
52+
const v8::FunctionCallbackInfo<v8::Value>& args);
4753
static v8::MaybeLocal<v8::Module> ResolveCallback(
4854
v8::Local<v8::Context> context,
4955
v8::Local<v8::String> specifier,
5056
v8::Local<v8::Module> referrer);
57+
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
58+
5159

5260
v8::Persistent<v8::Module> module_;
5361
v8::Persistent<v8::String> url_;

src/node.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3680,6 +3680,8 @@ static void ParseArgs(int* argc,
36803680
config_experimental_modules = true;
36813681
new_v8_argv[new_v8_argc] = "--harmony-dynamic-import";
36823682
new_v8_argc += 1;
3683+
new_v8_argv[new_v8_argc] = "--harmony-import-meta";
3684+
new_v8_argc += 1;
36833685
} else if (strcmp(arg, "--experimental-vm-modules") == 0) {
36843686
config_experimental_vm_modules = true;
36853687
} else if (strcmp(arg, "--loader") == 0) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Flags: --experimental-modules
2+
3+
import '../common';
4+
import assert from 'assert';
5+
6+
assert.strictEqual(Object.getPrototypeOf(import.meta), null);
7+
8+
const keys = ['url'];
9+
assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);
10+
11+
const descriptors = Object.getOwnPropertyDescriptors(import.meta);
12+
for (const descriptor of Object.values(descriptors)) {
13+
delete descriptor.value; // Values are verified below.
14+
assert.deepStrictEqual(descriptor, {
15+
enumerable: true,
16+
writable: true,
17+
configurable: true
18+
});
19+
}
20+
21+
const urlReg = /^file:\/\/\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
22+
assert(import.meta.url.match(urlReg));

0 commit comments

Comments
 (0)