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

Conversation

targos
Copy link
Member

@targos targos commented Jan 25, 2018

The feature is still behind the --harmony-import-meta V8 flag.
This commit implements the C++ callback that is required to configure
the import.meta object and adds two properties one property:

  • url: absolute URL of the module
    - require: CommonJS require function

/cc @nodejs/esm @MylesBorins @nodejs/tsc

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

ESM

@targos targos added dont-land-on-v4.x esm Issues and PRs related to the ECMAScript Modules implementation. labels Jan 25, 2018
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Jan 25, 2018
doc/api/esm.md Outdated

Available with the flag `--harmony-import-meta`, the `import.meta` metaproperty
is an `Object` that contains the following properties:
* `url` {String} The absolute `file:` URL of the module
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit: we usually lowercase primitive types in docs (i.e. {string})

@guybedford
Copy link
Contributor

Is the idea with import.meta.require to then implement defaulting ".js" loads from ES modules as being treated as ES modules themselves? I think we should be clear exactly what that proposal is, so it can be adequately reviewed. It could also make sense to separate this out into a separate PR with the full proposal to help focus discussion.

doc/api/esm.md Outdated
Available with the flag `--harmony-import-meta`, the `import.meta` metaproperty
is an `Object` that contains the following properties:
* `url` {string} The absolute `file:` URL of the module
* `require` {Function} The same `require` function that is usually available in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link to the require() docs here.

@bmeck
Copy link
Member

bmeck commented Jan 25, 2018

@targos can we split out import.meta.require for now since thats a bigger talk than the non-controversial import.meta.url

@targos
Copy link
Member Author

targos commented Jan 25, 2018

@bmeck done. I kept the JS initialization function for later use.

Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@targos
Copy link
Member Author

targos commented Jan 25, 2018

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this add some tests that import.meta.nothing is an error, as well as import.meta.toString?

@bmeck
Copy link
Member

bmeck commented Jan 25, 2018

@ljharb is the following check fine?

assert.stictEqual(Object.getPrototypeOf(import.meta), null);
assert.deepEqual(Object.keys(Object.getOwnPropertyDescriptors(import.meta)), ['url']);

Local<Object> meta) {
Isolate* isolate = context->GetIsolate();
Environment* env = Environment::GetCurrent(isolate);
meta->CreateDataProperty(context, env->url_string(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm wondering if there's any perf hit with moving this to the js callback. if there isn't i would suggest doing that just for consistency.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean by passing the URL string to the JS callback?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no i mean like

importMetaCallback(wrap, meta) {
  // attach wrap.url to meta in a safe way here
}

@ljharb
Copy link
Member

ljharb commented Jan 25, 2018

@bmeck should probably be gOPDs, but sure, that sounds great!

import assert from 'assert';

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per #18368 (review) we can add

assert.stictEqual(Object.getPrototypeOf(import.meta), null);
assert.deepEqual(Object.keys(Object.getOwnPropertyDescriptors(import.meta)), ['url']);

Copy link
Member

@apapirovski apapirovski Jan 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the second line use Reflect.ownKeys(import.meta) instead? Or am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems fine

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change ['urls'] to [{ enumerable: true, writable: false, configurable: false, value: 'url' }], eg

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reflect.ownKeys is good because it also covers symbols; but it doesn't cover descriptors.

@MylesBorins
Copy link
Contributor

As import.meta will require a flag in V8 to turn on. Does it make sense to make the --experimental-modules flag also flip the appropriate V8 flag?

@devsnek
Copy link
Member

devsnek commented Jan 25, 2018

probs not, we don't do it for import() and the features are still experimental above node's proverbial head.

@MylesBorins
Copy link
Contributor

MylesBorins commented Jan 25, 2018

@devsnek I plan to open a PR to do the same for import() fwiw

edit: both features are shipping in chrome with the flag turned off... the flag currently is only turned on in V8 to embedders from having the apis called without the callback being set (would have an unhandled rejection)

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nit: Can you use Environment::GetCurrent(context)? Getting it from the isolate only looks up the context on the isolate and takes it from there

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks! I didn't know there was a Environment::GetCurrent(context)

@Fishrock123
Copy link
Contributor

Can we add meta.dirname, meta.filename, and meta.isMain?

@bmeck
Copy link
Member

bmeck commented Jan 26, 2018

@Fishrock123 you can get dirname and filename from the url, and they get normalized always when they go through becoming a URL (thank goodness). I'm neutral to isMain but feel like thare are various ways to figure it out as well, just nothing convenient like the dirname or filename solution.

@Fishrock123
Copy link
Contributor

you can get dirname and filename from the url

I'm not really sure that means we should force users to always parse the url when there is already a precedent set here.

I'd love to know how you can figure out inMain without doing it internally, but we'll still need it if doing so is difficult.

@devsnek
Copy link
Member

devsnek commented Jan 26, 2018

personally i'm -1 to dirname/filename. you have your url which is generally enough and to be honest the cases where you need the filename of the file you wrote are imo generally rare enough to warrant you having to parse the url yourself.

@bmeck
Copy link
Member

bmeck commented Jan 26, 2018

@Fishrock123

I'm not really sure that means we should force users to always parse the url when there is already a precedent set here.

The precedent exists but normalization of \\ to / simplifies things and fixes some common cross platform bugs, also .url is compatible between both Node and browser environments. Even if we supplied .filename there are also some problems of passing it around to import() etc. if you intended to preserve query and hash fragments. I see history in .dirname / .filename but don't think that is enough when the workflow exists with .url and avoids some compatibility both with OS and host environment considerations.

I'd love to know how you can figure out inMain without doing it internally, but we'll still need it if doing so is difficult.

Parsing out process.argv/process.cwd() vs the location identifier, we used to do this way back when at Nodejitsu and the technique still works. Very ugly to do though. Has a nice benefit of wrappers being able to setup a new "main" entry point without spawning a new process though.

@bmeck
Copy link
Member

bmeck commented Jan 29, 2018

@jdalton the main attraction to frozen things like that is that you can't bail out of static analysis if it becomes mutable. Bailing out is probably fine if people have to do it for browsers anyway I think, but still a little bit of a question on who is actively wanting to rewrite .url and why instead of doing some source code transform.

@ljharb
Copy link
Member

ljharb commented Jan 29, 2018

@jdalton I just said it'd be unfortunate; not that i'd "fight" for it. I could ask the same question of browsers - why would they ship a fully unlocked descriptor without consulting with node first? "Interoperable" could also mean that browsers ship a non-writable non-enumerable descriptor, for example.

@jdalton
Copy link
Member

jdalton commented Jan 29, 2018

@bmeck

The main attraction to frozen things like that is that you can't bail out of static analysis if it becomes mutable.

I think it's totally fine to bring those things up as additions or changes to proposals/specs/browsers-early-implementations. However, the ship has kinda sailed on the import.meta.url front for now and the PR should align with how it is in browser-land. If it's changed later, after spec/browser discussions or whatever, then great!

ModuleWrap* module_wrap = ModuleWrap::GetFromModule(env, module);

if (module_wrap == nullptr) {
env->ThrowError("ModuleWrap object not found in map");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we just return and leave the object empty? The upcoming vm.Module PR will make it possible to have Modules that are not in the core module map.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the vm.Module code just delete the property if it is configurable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but this line throws an error. We don’t want that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the throw, but we will probably have to come up with a solution to configure import.meta in vm.Module

@targos
Copy link
Member Author

targos commented Jan 30, 2018

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Jan 31, 2018
@jdalton
Copy link
Member

jdalton commented Jan 31, 2018

@targos Just curious, can you check that eval("import.meta") errors in ESM.

For example in Chrome:

eval("import.meta")
// throws "SyntaxError: Cannot use 'import.meta' outside a module

@targos
Copy link
Member Author

targos commented Jan 31, 2018

@jdalton This is already tested by V8:

assertThrows(() => eval('import.meta'), SyntaxError);

@jdalton
Copy link
Member

jdalton commented Jan 31, 2018

@targos Yep. I know Node had to do some extra work to make static import errors more clear, so I was curious if things were OK for import.meta.

@targos
Copy link
Member Author

targos commented Jan 31, 2018

@jdalton Here is the output:

$ cat test.mjs

eval("import.meta");


$ ./node --experimental-modules test.mjs
(node:16949) ExperimentalWarning: The ESM module loader is experimental.
SyntaxError: Cannot use 'import.meta' outside a module
    at file:///home/mzasso/git/nodejs/node/test.mjs:2:1
    at ModuleJob.run (internal/loader/ModuleJob.js:106:14)
    at <anonymous>

@targos
Copy link
Member Author

targos commented Jan 31, 2018

I had to rebase because of a conflict.
New CI: https://ci.nodejs.org/job/node-test-pull-request/12847/

@jdalton
Copy link
Member

jdalton commented Jan 31, 2018

Here is the output:

Thanks! It looks like it's missing the arrow stack decorator.

@TimothyGu
Copy link
Member

Other than the conflict, is there anything preventing this from getting merged?

Implement the C++ callback that is required to configure the
`import.meta` object and add one property:
- url: absolute URL of the module
@BridgeAR
Copy link
Member

BridgeAR commented Feb 1, 2018

One more CI before landing: https://ci.nodejs.org/job/node-test-pull-request/12877/

@BridgeAR
Copy link
Member

BridgeAR commented Feb 1, 2018

Landed in 9e1a6f8

@BridgeAR BridgeAR closed this Feb 1, 2018
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Feb 1, 2018
Implement the C++ callback that is required to configure the
`import.meta` object and add one property:
- url: absolute URL of the module

PR-URL: nodejs#18368
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
@targos targos deleted the import-meta branch February 1, 2018 12:56
@addaleax addaleax removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Feb 4, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Implement the C++ callback that is required to configure the
`import.meta` object and add one property:
- url: absolute URL of the module

PR-URL: nodejs#18368
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. esm Issues and PRs related to the ECMAScript Modules implementation. lib / src Issues and PRs related to general changes in the lib or src directory.
Projects
None yet
Development

Successfully merging this pull request may close these issues.