Introduce pointer to <script> element in module scripts #1013

Open
annevk opened this Issue Apr 8, 2016 · 86 comments

Comments

@annevk
Member

annevk commented Apr 8, 2016

There was an offline discussion about document.currentScript and shadow trees and the suggestion came up to solve it in a different way for module scripts so that the global object would not be involved and the value would only be accessible to the script currently executing.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Apr 8, 2016

Member

I think this would belong in the "module meta" idea. It was detailed in more detail somewhere, but alluded to in whatwg/loader#38. @dherman might know more. The idea would be something like:

import { currentScript, url } from this module;

At other times people have suggested using metaproperties on import (currently the only metaproperty in the language is new.target; this would introduce a few more):

import.meta.currentScript; // or just import.currentScript?

Unfortunately the base extensibility hook here kind of needs a TC39 proposal for someone to champion, which is a whole process.

Member

domenic commented Apr 8, 2016

I think this would belong in the "module meta" idea. It was detailed in more detail somewhere, but alluded to in whatwg/loader#38. @dherman might know more. The idea would be something like:

import { currentScript, url } from this module;

At other times people have suggested using metaproperties on import (currently the only metaproperty in the language is new.target; this would introduce a few more):

import.meta.currentScript; // or just import.currentScript?

Unfortunately the base extensibility hook here kind of needs a TC39 proposal for someone to champion, which is a whole process.

domenic added a commit that referenced this issue Apr 11, 2016

Make module scripts modify less global script state
They no longer set document.currentScript, or fire beforescriptexecute
and afterscriptexecute events.

Closes #997. See #1013 for a future alternative to
document.currentScript, and #943 for more discussion of the events.

domenic added a commit that referenced this issue Apr 13, 2016

Make module scripts modify less global script state
They set document.currentScript to null, and no longer fire
beforescriptexecute and afterscriptexecute events.

Closes #997. See #1013 for a future alternative to
document.currentScript, and #943 for more discussion of the events.

domenic added a commit that referenced this issue Apr 14, 2016

Make module scripts modify less global script state
They set document.currentScript to null, and no longer fire
beforescriptexecute and afterscriptexecute events.

Closes #997. See #1013 for a future alternative to
document.currentScript, and #943 for more discussion of the events.

annevk added a commit that referenced this issue Apr 15, 2016

Don't update document.currentScript for module scripts
Make module scripts modify less global script state. They set document.currentScript to null, and no longer fire beforescriptexecute and afterscriptexecute events.

Closes #997. See #1013 for a future alternative to document.currentScript, and #943 for more discussion of the events.

annevk added a commit that referenced this issue Apr 22, 2016

Execute <script> in shadow trees
This makes <script> elements work when used in shadow trees. The
beforescriptexecute and afterscriptexecute events won’t work, since
they are already disabled for modules and only makes sense together
with currentScript, which cannot be made to work for shadow scripts.
See #1013 for details.

Fixes #762.

@annevk annevk referenced this issue Apr 22, 2016

Merged

Shadow script #1100

domenic added a commit that referenced this issue Apr 22, 2016

Execute <script> in shadow trees
This makes <script> elements work when used in shadow trees. The
beforescriptexecute and afterscriptexecute events won’t work, since
they are already disabled for modules and only makes sense together
with currentScript, which cannot be made to work for shadow scripts.
See #1013 for details.

Fixes #762.

annevk added a commit that referenced this issue Apr 23, 2016

Execute <script> in shadow trees
This makes <script> elements work when used in shadow trees. The
beforescriptexecute and afterscriptexecute events won’t work, since
they are already disabled for modules and only makes sense together
with currentScript, which cannot be made to work for shadow scripts.
See #1013 for details.

Fixes #762.

domenic added a commit that referenced this issue Apr 26, 2016

Execute <script> in shadow trees
This makes <script> elements work when used in shadow trees.

Note that document.currentScript is set to null while running a <script>
in a shadow tree; see #1013 for details.

This takes care of most of #762, but it remains to make the "load" event
scoped, so we'll leave that issue open for now.
@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Oct 26, 2016

Is it correct to say that document.currentScript is not set, even for module scripts with no imports like:

<script type="module">
  document.currentScript == null;
</script>

Even if it were set in this case, I'd still want something similar to currentScript within imported modules. One use-case I have is to insert a template directly after the module script.

I like a variant of import.currentScript personally; I think introducing a special meta would invite questions like whether it is mutable or not. Maybe a better name came be thought of than currentScript (is it really current?) like ownerScript perhaps.

Is it correct to say that document.currentScript is not set, even for module scripts with no imports like:

<script type="module">
  document.currentScript == null;
</script>

Even if it were set in this case, I'd still want something similar to currentScript within imported modules. One use-case I have is to insert a template directly after the module script.

I like a variant of import.currentScript personally; I think introducing a special meta would invite questions like whether it is mutable or not. Maybe a better name came be thought of than currentScript (is it really current?) like ownerScript perhaps.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Oct 26, 2016

Member

That's correct.

It should really be current; I don't see why it wouldn't be. We're still kind of waiting on TC39 to figure out the import metaproperty stuff though.

Member

domenic commented Oct 26, 2016

That's correct.

It should really be current; I don't see why it wouldn't be. We're still kind of waiting on TC39 to figure out the import metaproperty stuff though.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Nov 10, 2016

Member

It's worth mentioning that we have a path forward here without waiting on TC39, which is to reserve a special module name. So then we'd do something like

import { currentScript, url } from "js:context";
Member

domenic commented Nov 10, 2016

It's worth mentioning that we have a path forward here without waiting on TC39, which is to reserve a special module name. So then we'd do something like

import { currentScript, url } from "js:context";
@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Jan 12, 2017

import.currentScript sounds ideal here. With the dynamic import landed, perhaps a spec here can start to gain momentum. Landing features such as these soon will help significantly to avoid fragmentation of the adoption path features.

Note also feature detection here also seems tricky. I assume typeof import.currentScript wouldn't work, so it would just fall back to a syntax error if it isn't supported?

import.currentScript sounds ideal here. With the dynamic import landed, perhaps a spec here can start to gain momentum. Landing features such as these soon will help significantly to avoid fragmentation of the adoption path features.

Note also feature detection here also seems tricky. I assume typeof import.currentScript wouldn't work, so it would just fall back to a syntax error if it isn't supported?

@domenic domenic referenced this issue in tc39/proposal-dynamic-import Feb 9, 2017

Closed

import default operator #37

@Constellation

This comment has been minimized.

Show comment
Hide comment
@Constellation

Constellation Feb 9, 2017

Member

It's not directly related to this, I would like to note that I would like to have the similar thing in the module script in workers. See detailed thing why I would like to get it even in the workers. in tc39/proposal-dynamic-import#37.

Member

Constellation commented Feb 9, 2017

It's not directly related to this, I would like to note that I would like to have the similar thing in the module script in workers. See detailed thing why I would like to get it even in the workers. in tc39/proposal-dynamic-import#37.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 22, 2017

Member

I think we should spec something here soon that people are willing to ship. Node.js is also interested in getting agreement on this.

I'd like to propose

import { url, currentScript } from "js:context";

to get the current script URL (which could be that of a HTML file for inline scripts), and the current <script> element (which could be null if no <script> is currently executing).

How does that sound to implementers? /cc @whatwg/modules

Member

domenic commented Feb 22, 2017

I think we should spec something here soon that people are willing to ship. Node.js is also interested in getting agreement on this.

I'd like to propose

import { url, currentScript } from "js:context";

to get the current script URL (which could be that of a HTML file for inline scripts), and the current <script> element (which could be null if no <script> is currently executing).

How does that sound to implementers? /cc @whatwg/modules

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Feb 22, 2017

"js:context" looks fine but we are open to bikeshedding from Node.js side.

bmeck commented Feb 22, 2017

"js:context" looks fine but we are open to bikeshedding from Node.js side.

@bicknellr

This comment has been minimized.

Show comment
Hide comment
@bicknellr

bicknellr Feb 22, 2017

Maybe it would be worth carving out the entire js: protocol-like-prefix for future stuff like this?

Maybe it would be worth carving out the entire js: protocol-like-prefix for future stuff like this?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 22, 2017

Member

It effectively already is carved out, since fetching any URL that starts with js: will fail per today's specs.

Member

domenic commented Feb 22, 2017

It effectively already is carved out, since fetching any URL that starts with js: will fail per today's specs.

@ajklein

This comment has been minimized.

Show comment
Hide comment
@ajklein

ajklein Feb 22, 2017

Collaborator

Hate to tie this in to other bike-shedding, but it would be good if whatever naming scheme we choose matches TC39's naming scheme for internal modules (if they do arise).

Collaborator

ajklein commented Feb 22, 2017

Hate to tie this in to other bike-shedding, but it would be good if whatever naming scheme we choose matches TC39's naming scheme for internal modules (if they do arise).

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 22, 2017

Collaborator

That seems like such a hack. There is a proposal for global which is encountering some compatibility issue. Perhaps global should also live there?

Collaborator

rniwa commented Feb 22, 2017

That seems like such a hack. There is a proposal for global which is encountering some compatibility issue. Perhaps global should also live there?

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Feb 22, 2017

@rniwa how is it a hack if it needs context to the environment it was invoked from (in this case, the location of import or import())?

bmeck commented Feb 22, 2017

@rniwa how is it a hack if it needs context to the environment it was invoked from (in this case, the location of import or import())?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 22, 2017

Member

I don't think global makes sense to put in the context-specific module, but people have proposed putting it in a built-in module before. I'd suggest discussing that in the global repo.

@rniwa, putting global aside, I can't tell if you think the import { url } from "js:context" proposal is good or not?

Member

domenic commented Feb 22, 2017

I don't think global makes sense to put in the context-specific module, but people have proposed putting it in a built-in module before. I'd suggest discussing that in the global repo.

@rniwa, putting global aside, I can't tell if you think the import { url } from "js:context" proposal is good or not?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 23, 2017

Collaborator

I don't like it. Conceptually, what you're trying to figure out the information about the current module, not about "js:context". Why is the url of "js:context" not "js:context"?

Collaborator

rniwa commented Feb 23, 2017

I don't like it. Conceptually, what you're trying to figure out the information about the current module, not about "js:context". Why is the url of "js:context" not "js:context"?

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Feb 23, 2017

@rniwa you are grabbing an export of "js:context" named url, I don't understand why you would think it is the url of "js:context" given people can do things like export let url = "foo" in modules. Would a name different from url be sufficient?

bmeck commented Feb 23, 2017

@rniwa you are grabbing an export of "js:context" named url, I don't understand why you would think it is the url of "js:context" given people can do things like export let url = "foo" in modules. Would a name different from url be sufficient?

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Feb 23, 2017

"js:context" is a contextual module specifier. I can see the confusion because it looks like a URL and not a specifier. Given that it is a specifier distinct to the module loading it, what does it resolve to? What's the algorithm?

Let's pretend for a second that the algorithm resolves to "js:context:https://example.com/foo.js" where "https://example.com/foo.js" is the importing module. Would this mean that, I could do:

import { url, currentScript } from "js:context:https://example.com/bar.js" 

To grab another module's url/currentScript? Or would this be restricted?

"js:context" is a contextual module specifier. I can see the confusion because it looks like a URL and not a specifier. Given that it is a specifier distinct to the module loading it, what does it resolve to? What's the algorithm?

Let's pretend for a second that the algorithm resolves to "js:context:https://example.com/foo.js" where "https://example.com/foo.js" is the importing module. Would this mean that, I could do:

import { url, currentScript } from "js:context:https://example.com/bar.js" 

To grab another module's url/currentScript? Or would this be restricted?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 23, 2017

Member

Yeah, I think given that you can do import { url } from "./foo.js" and the result can be anything (not just foo.js), it shouldn't be surprising that import { url } from "js:context" does not give "js:context".

We could consider names like currentURL or contextURL but I kind of feel that instead of prefixing everything with such prefixes it'd be better to just put it in the name of the place you're importing from.


@matthewp

Given that it is a specifier distinct to the module loading it, what does it resolve to? What's the algorithm?

Specifiers resolve to modules, not to URLs. One of the ways they can do this is via first transforming the specifier into a URL, then looking it up in the module map. That's not what's going on here; we're going directly from specifier to module.

So let's not pretend it resolves to some weird URL like js:context:https://example.com/foo.js. That is definitely not what's going on here.

Member

domenic commented Feb 23, 2017

Yeah, I think given that you can do import { url } from "./foo.js" and the result can be anything (not just foo.js), it shouldn't be surprising that import { url } from "js:context" does not give "js:context".

We could consider names like currentURL or contextURL but I kind of feel that instead of prefixing everything with such prefixes it'd be better to just put it in the name of the place you're importing from.


@matthewp

Given that it is a specifier distinct to the module loading it, what does it resolve to? What's the algorithm?

Specifiers resolve to modules, not to URLs. One of the ways they can do this is via first transforming the specifier into a URL, then looking it up in the module map. That's not what's going on here; we're going directly from specifier to module.

So let's not pretend it resolves to some weird URL like js:context:https://example.com/foo.js. That is definitely not what's going on here.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 23, 2017

Member

@rniwa given

Conceptually, what you're trying to figure out the information about the current module, not about "js:context"

would maybe calling it "js:currentmodule" be better? We've all indicated we're open to bikeshedding on the name; context seemed nice and short, but nobody insists on it.

Member

domenic commented Feb 23, 2017

@rniwa given

Conceptually, what you're trying to figure out the information about the current module, not about "js:context"

would maybe calling it "js:currentmodule" be better? We've all indicated we're open to bikeshedding on the name; context seemed nice and short, but nobody insists on it.

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Feb 23, 2017

I'm not super keen on "module" being in there if it also works in Scripts via import()

bmeck commented Feb 23, 2017

I'm not super keen on "module" being in there if it also works in Scripts via import()

@caridy

This comment has been minimized.

Show comment
Hide comment
@caridy

caridy Mar 17, 2017

Last time we discussed this (a while ago), import ... from this module; didn't make the cut. We were more interested in something like import.something as a way to access meta information about the current module. Specifying what goes there should not be a big endeavor, we just need a champion :)

caridy commented Mar 17, 2017

Last time we discussed this (a while ago), import ... from this module; didn't make the cut. We were more interested in something like import.something as a way to access meta information about the current module. Specifying what goes there should not be a big endeavor, we just need a champion :)

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Mar 28, 2017

@caridy I am not sure we need a new syntax for this. It also is kind of nice not to need to generate the values unless they are demanded (even if they are demanded late like import("js:context")).

bmeck commented Mar 28, 2017

@caridy I am not sure we need a new syntax for this. It also is kind of nice not to need to generate the values unless they are demanded (even if they are demanded late like import("js:context")).

@Pauan

This comment has been minimized.

Show comment
Hide comment
@Pauan

Pauan Mar 28, 2017

I don't like import { url } from "js:context";

Consider this code:

// foo.js
import { url } from "js:context";
// bar.js
import { url } from "js:context";
<script type="module" src="foo.js"></script>
<script type="module" src="bar.js"></script>

Even though modules are supposed to be cached (and therefore importing the same module multiple times returns the same module object), in this situation we would actually be importing different modules with a different value for url.

There's also another concern, similar to the concern raised with the dynamic import proposal: how does the "js:context" module know which module is importing it? Obviously this requires hidden information inside the JS engine.

It all feels very magical and inconsistent with how regular modules work.

So I prefer something like import.url because it doesn't break the programmer's mental model of how module imports work.

Pauan commented Mar 28, 2017

I don't like import { url } from "js:context";

Consider this code:

// foo.js
import { url } from "js:context";
// bar.js
import { url } from "js:context";
<script type="module" src="foo.js"></script>
<script type="module" src="bar.js"></script>

Even though modules are supposed to be cached (and therefore importing the same module multiple times returns the same module object), in this situation we would actually be importing different modules with a different value for url.

There's also another concern, similar to the concern raised with the dynamic import proposal: how does the "js:context" module know which module is importing it? Obviously this requires hidden information inside the JS engine.

It all feels very magical and inconsistent with how regular modules work.

So I prefer something like import.url because it doesn't break the programmer's mental model of how module imports work.

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Mar 28, 2017

@Pauan as per Ecma262 modules are idempotent per Source Text / Import Specifier pair. They are not idempotent per global environment / realm.

bmeck commented Mar 28, 2017

@Pauan as per Ecma262 modules are idempotent per Source Text / Import Specifier pair. They are not idempotent per global environment / realm.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Mar 28, 2017

Member

@Pauan it's actually extremely consistent with how modules work, and the programmer's mental model. The information is not implicit or hidden or concerning. (Certainly not as implicit as in import.url!).

To see this, consider a slight modification of your example:

// foo/foo.js
import { baz } from "./baz.js";
// bar/bar.js
import { baz } from "./baz.js";
<script type="module" src="foo/foo.js"></script>
<script type="module" src="bar/bar.js"></script>

Now consider your statement:

Even though modules are supposed to be cached (and therefore importing the same module multiple times returns the same module object), in this situation we would actually be importing different modules with a different value for url.

The same applies here. The modules are cached. Importing the same module multiple times returns the same module object. But our two import statements import different modules with different values for baz.

That's just how imports work: they always take into account the current module, and work relative to that. js:context would be exactly like that.

In contrast, something like import.url is not at all clear how it works. There's nothing in that syntax that says it's relative to the current module, whereas it's very clear in any syntax that imports (both import declarations and import()), since imports are always relative to the current module.

Member

domenic commented Mar 28, 2017

@Pauan it's actually extremely consistent with how modules work, and the programmer's mental model. The information is not implicit or hidden or concerning. (Certainly not as implicit as in import.url!).

To see this, consider a slight modification of your example:

// foo/foo.js
import { baz } from "./baz.js";
// bar/bar.js
import { baz } from "./baz.js";
<script type="module" src="foo/foo.js"></script>
<script type="module" src="bar/bar.js"></script>

Now consider your statement:

Even though modules are supposed to be cached (and therefore importing the same module multiple times returns the same module object), in this situation we would actually be importing different modules with a different value for url.

The same applies here. The modules are cached. Importing the same module multiple times returns the same module object. But our two import statements import different modules with different values for baz.

That's just how imports work: they always take into account the current module, and work relative to that. js:context would be exactly like that.

In contrast, something like import.url is not at all clear how it works. There's nothing in that syntax that says it's relative to the current module, whereas it's very clear in any syntax that imports (both import declarations and import()), since imports are always relative to the current module.

@Pauan

This comment has been minimized.

Show comment
Hide comment
@Pauan

Pauan Mar 28, 2017

@domenic I thought about it further, and you are correct. Even with Node, import { foo } from "foo" can have different behavior depending on where the importing module is (because your program can have multiple node_modules/foo folders).

In that case, I can't quite explain why I think import { foo } from "foo" is okay, but import { url } from "js:context" feels wrong. Perhaps it is just because of my familiarity and expectations with existing systems.

Pauan commented Mar 28, 2017

@domenic I thought about it further, and you are correct. Even with Node, import { foo } from "foo" can have different behavior depending on where the importing module is (because your program can have multiple node_modules/foo folders).

In that case, I can't quite explain why I think import { foo } from "foo" is okay, but import { url } from "js:context" feels wrong. Perhaps it is just because of my familiarity and expectations with existing systems.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Mar 30, 2017

Collaborator

In contrast, something like import.url is not at all clear how it works. There's nothing in that syntax that says it's relative to the current module, whereas it's very clear in any syntax that imports (both import declarations and import()), since imports are always relative to the current module.

import.url makes it clear that it's a builtin language feature (although I don't think that's the best name). import X from "js:context" makes it look like it's loading a module by the name of context by js scheme. It's very strange to hijack a protocol name like that. Given modern OS lets native (and Web) apps register their own scheme including js:, I don't think we should do this.

Collaborator

rniwa commented Mar 30, 2017

In contrast, something like import.url is not at all clear how it works. There's nothing in that syntax that says it's relative to the current module, whereas it's very clear in any syntax that imports (both import declarations and import()), since imports are always relative to the current module.

import.url makes it clear that it's a builtin language feature (although I don't think that's the best name). import X from "js:context" makes it look like it's loading a module by the name of context by js scheme. It's very strange to hijack a protocol name like that. Given modern OS lets native (and Web) apps register their own scheme including js:, I don't think we should do this.

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck May 23, 2017

@benjamn it does act like an import to everything I can see:

  1. it is contextual
  2. it is created prior to evaluation
  3. it comes from host env

I am curious how import.module.* acts different from a module namespace. Also note, CJS module is not in ESM impl for node due to oddities, I would be curious what that point was towards.

bmeck commented May 23, 2017

@benjamn it does act like an import to everything I can see:

  1. it is contextual
  2. it is created prior to evaluation
  3. it comes from host env

I am curious how import.module.* acts different from a module namespace. Also note, CJS module is not in ESM impl for node due to oddities, I would be curious what that point was towards.

@benjamn

This comment has been minimized.

Show comment
Hide comment
@benjamn

benjamn May 23, 2017

I was raising a concern that motivating the "js:context" pseudo-module to the Node community may be harder than you think (even though you yourself are happy with it), while also casting a bikeshed vote in favor of import.module.* as opposed to import.meta.* or import.context.* (though I would not TC39 block consensus for any of those import.{module,meta,context,...}.* options).

As for the differences from the CommonJS module namespace, I imagine that properties of import.module would be read-only from the perspective of module code, at the very least. Also, import.module is inherently more reliable than a free module variable, which can be shadowed by user-defined variables, which I think is an easy answer to folks who wonder why we don't just reuse the CommonJS module variable.

benjamn commented May 23, 2017

I was raising a concern that motivating the "js:context" pseudo-module to the Node community may be harder than you think (even though you yourself are happy with it), while also casting a bikeshed vote in favor of import.module.* as opposed to import.meta.* or import.context.* (though I would not TC39 block consensus for any of those import.{module,meta,context,...}.* options).

As for the differences from the CommonJS module namespace, I imagine that properties of import.module would be read-only from the perspective of module code, at the very least. Also, import.module is inherently more reliable than a free module variable, which can be shadowed by user-defined variables, which I think is an easy answer to folks who wonder why we don't just reuse the CommonJS module variable.

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck May 23, 2017

@benjamn it is in the EP rewrite so it would need a vote to not accept it currently.

nodejs/node-eps#39

As for the differences with CJS, there are other more problematic things like frozen view of exports, parent doesn't work when parallel loading occurs, etc. Even with a read-only object it will not be exactly like CJS.

bmeck commented May 23, 2017

@benjamn it is in the EP rewrite so it would need a vote to not accept it currently.

nodejs/node-eps#39

As for the differences with CJS, there are other more problematic things like frozen view of exports, parent doesn't work when parallel loading occurs, etc. Even with a read-only object it will not be exactly like CJS.

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp May 24, 2017

Looks like the committee preferred import.meta: https://github.com/domenic/proposal-import-meta

Looks like the committee preferred import.meta: https://github.com/domenic/proposal-import-meta

@dcleao

This comment has been minimized.

Show comment
Hide comment
@dcleao

dcleao May 27, 2017

I'm glad with the result. The "js:context" was obviously a desire to bypass the TC39 lengthy process. By what I read here, I worry some of you — very important people — just seem to be pushing for your personal/company-specific interests, ahead of the common good.
I thank to @rniwa (and @Pauan) for having been there to force things to a good outcome.

dcleao commented May 27, 2017

I'm glad with the result. The "js:context" was obviously a desire to bypass the TC39 lengthy process. By what I read here, I worry some of you — very important people — just seem to be pushing for your personal/company-specific interests, ahead of the common good.
I thank to @rniwa (and @Pauan) for having been there to force things to a good outcome.

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck May 27, 2017

@dcleao I would disagree with that conclusion, if the goal was to bypass TC39 entirely it would not have been brought up at all in TC39 since this is host provided meta-data.

bmeck commented May 27, 2017

@dcleao I would disagree with that conclusion, if the goal was to bypass TC39 entirely it would not have been brought up at all in TC39 since this is host provided meta-data.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic May 27, 2017

Member

@dcleao I don't think such accusations are fair or accurate; please mind your conduct going forward.

Member

domenic commented May 27, 2017

@dcleao I don't think such accusations are fair or accurate; please mind your conduct going forward.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford May 27, 2017

@domenic great work, very pleased to see this moving forward! Out of interest what exactly did @dcleao do to be seen as misconduct here? Surely opinions as to interests (even if they are wrong) are valid discussion points?

@domenic great work, very pleased to see this moving forward! Out of interest what exactly did @dcleao do to be seen as misconduct here? Surely opinions as to interests (even if they are wrong) are valid discussion points?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic May 27, 2017

Member

Ascribing selfish or corporate-greed motivated interests to individuals who are all working to make the web better in good faith, especially with loaded descriptors such as "obviously", is not welcome in this community. Similarly, encouraging others to "force" an outcome is pretty borderline as well.

Conduct discussions have a way of derailing otherwise technical threads; if this needs to be discussed further, please contact me directly by email, and do not engage here.

Member

domenic commented May 27, 2017

Ascribing selfish or corporate-greed motivated interests to individuals who are all working to make the web better in good faith, especially with loaded descriptors such as "obviously", is not welcome in this community. Similarly, encouraging others to "force" an outcome is pretty borderline as well.

Conduct discussions have a way of derailing otherwise technical threads; if this needs to be discussed further, please contact me directly by email, and do not engage here.

@apparebit

This comment has been minimized.

Show comment
Hide comment
@apparebit

apparebit May 31, 2017

Commenting as a user and former PL researcher, the import.meta syntax is a bit jarring because that same syntax usually references properties. In my expectation a language statement doesn't usually have them. As to special URL, what about about:, which most users already expect to reveal metadata?

Commenting as a user and former PL researcher, the import.meta syntax is a bit jarring because that same syntax usually references properties. In my expectation a language statement doesn't usually have them. As to special URL, what about about:, which most users already expect to reveal metadata?

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb May 31, 2017

@grimmr new.target, super.foo, etc, are existing precedents in the language (as of ES6/ES2015).

ljharb commented May 31, 2017

@grimmr new.target, super.foo, etc, are existing precedents in the language (as of ES6/ES2015).

@apparebit

This comment has been minimized.

Show comment
Hide comment
@apparebit

apparebit Jun 1, 2017

Yup, and my first encounter with new.target left me feeling a bit 😜. It does provide reasonable precedent but it also differs in attaching the metadata to a directly related language construct. That doesn't quite hold for import.meta, which inquires about the current module through the syntax for linking to another module, which makes for a more awkward fit.

Yup, and my first encounter with new.target left me feeling a bit 😜. It does provide reasonable precedent but it also differs in attaching the metadata to a directly related language construct. That doesn't quite hold for import.meta, which inquires about the current module through the syntax for linking to another module, which makes for a more awkward fit.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jun 1, 2017

So import.meta gives you info about how it was imported (how import was used), just like new.target gives you info about how it was constructed (how new was used)? Seems consistent to me :-)

ljharb commented Jun 1, 2017

So import.meta gives you info about how it was imported (how import was used), just like new.target gives you info about how it was constructed (how new was used)? Seems consistent to me :-)

@bmeck

This comment has been minimized.

Show comment
Hide comment
@bmeck

bmeck Jun 1, 2017

how is a bit off here, it could be mutated later by the environment and might have things that mutate over time on purpose (pendingDeps or something for circular deps, isTopLevel, etc.).

bmeck commented Jun 1, 2017

how is a bit off here, it could be mutated later by the environment and might have things that mutate over time on purpose (pendingDeps or something for circular deps, isTopLevel, etc.).

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jun 1, 2017

Collaborator

about:blank is a new page, not some meta data about the document which loaded it so that doesn't work either. Please read the section about Using a particular module specifier, and raise your concern in TC39 if you have a very compelling reason to object to import.meta.

Collaborator

rniwa commented Jun 1, 2017

about:blank is a new page, not some meta data about the document which loaded it so that doesn't work either. Please read the section about Using a particular module specifier, and raise your concern in TC39 if you have a very compelling reason to object to import.meta.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2017

Member

OK, so, import.meta is a thing! I have a tentative design for import.meta.scriptElement at https://github.com/tc39/proposal-import-meta/blob/master/HTML%20Integration.md . However, I wanted to check the design with this audience.

In particular, I think the biggest question is what is the value of import.meta.scriptElement inside descendants of the top-level module script. I think there are two possible answers:

  • The script element of the first-instantiated module script graph they appeared in. This can be nondeterministic based on what fetches first. (This is what is in the current HTML Integration draft linked above.)
  • null

I am strongly leaning toward null. In particular consider the situation

<script type=module src=a.mjs id=a></script>
<script type=module src=b.mjs id=b></script>

where both a.mjs and b.mjs do import "./c.mjs". Inside c.mjs, what is import.meta.scriptElement? I think it's very confusing for it to be nondeterministically either the a or b script elements. So just null seems better.

The counterargument is that in some larger percentage of cases, module scripts will not be shared among multiple graphs, and in such cases, it's really nice to be able to transparently refactor code into a sub-module without having to pass through import.meta explicitly.

Member

domenic commented Sep 14, 2017

OK, so, import.meta is a thing! I have a tentative design for import.meta.scriptElement at https://github.com/tc39/proposal-import-meta/blob/master/HTML%20Integration.md . However, I wanted to check the design with this audience.

In particular, I think the biggest question is what is the value of import.meta.scriptElement inside descendants of the top-level module script. I think there are two possible answers:

  • The script element of the first-instantiated module script graph they appeared in. This can be nondeterministic based on what fetches first. (This is what is in the current HTML Integration draft linked above.)
  • null

I am strongly leaning toward null. In particular consider the situation

<script type=module src=a.mjs id=a></script>
<script type=module src=b.mjs id=b></script>

where both a.mjs and b.mjs do import "./c.mjs". Inside c.mjs, what is import.meta.scriptElement? I think it's very confusing for it to be nondeterministically either the a or b script elements. So just null seems better.

The counterargument is that in some larger percentage of cases, module scripts will not be shared among multiple graphs, and in such cases, it's really nice to be able to transparently refactor code into a sub-module without having to pass through import.meta explicitly.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 14, 2017

Collaborator

Somewhat relatedly, what happens if a.mjs was imported twice as in:

<script type=module src=a.mjs></script>
<script type=module src=a.mjs></script>

Does module.meta.scriptElement refers to the first element? (I think this is what happens in your proposal).

What happens if we had:

<script type=module src=a.mjs></script>
<script type=module src=c.mjs></script>

where a.mjs imports c.mjs. Would c.mjs's import.meta.scriptElement start to refer to the second script element after we had discovered that there's a script element loading it? Or would it be null because the initiator was a.mjs?

Collaborator

rniwa commented Sep 14, 2017

Somewhat relatedly, what happens if a.mjs was imported twice as in:

<script type=module src=a.mjs></script>
<script type=module src=a.mjs></script>

Does module.meta.scriptElement refers to the first element? (I think this is what happens in your proposal).

What happens if we had:

<script type=module src=a.mjs></script>
<script type=module src=c.mjs></script>

where a.mjs imports c.mjs. Would c.mjs's import.meta.scriptElement start to refer to the second script element after we had discovered that there's a script element loading it? Or would it be null because the initiator was a.mjs?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2017

Member

what happens if a.mjs was imported twice as in:

Oh, wow, you're right, that's an even simpler case where things get messy :(. In my proposal as written right now I think it's nondeterministic since either one of those will be instantiated first... that's terrible, of course, and it should be the first one unless that's somehow infeasible with the spec/impl architecture.

What happens if we had:

I will ignore my proposal as it stands, as clearly I have not thought through all these cases very well, so any answers the proposal gives are accidental. Instead let's talk about what we want to be true.

I lean toward null, because to me the simplest mental model is that import.meta.scriptElement gets frozen the first time a script is evaluated. What do you (and others) think?

Member

domenic commented Sep 14, 2017

what happens if a.mjs was imported twice as in:

Oh, wow, you're right, that's an even simpler case where things get messy :(. In my proposal as written right now I think it's nondeterministic since either one of those will be instantiated first... that's terrible, of course, and it should be the first one unless that's somehow infeasible with the spec/impl architecture.

What happens if we had:

I will ignore my proposal as it stands, as clearly I have not thought through all these cases very well, so any answers the proposal gives are accidental. Instead let's talk about what we want to be true.

I lean toward null, because to me the simplest mental model is that import.meta.scriptElement gets frozen the first time a script is evaluated. What do you (and others) think?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 14, 2017

Collaborator

I guess we need to understand what the use cases of import.meta.scriptElement are. One recurring use case is that people want to insert some DOM nodes into the document at where the importing script element appears. If that's the one we'd like to support, then it seems like we'd always have to provide some script element as long as it's pulled in by a script element.

Let's say you have c.js which inserts a DOM node at where it's imported. Then it seems strange that when a.js imports c.js, it can no longer insert the same DOM node at where c.js is imported. More broadly, if b.js also imports c.js and both a.js and b.js are imported by script elements, it conceptually makes sense for c.js to insert the same DOM node to both places for the cases whereZ a.js and b.js's script elements might be inserted into two different shadow trees. But that really depends on what kind of DOM node is getting inserted by c.js as well.

We need to enumerate concrete use cases and study them more carefully before we can judge which proposal/behavior makes sense.

Collaborator

rniwa commented Sep 14, 2017

I guess we need to understand what the use cases of import.meta.scriptElement are. One recurring use case is that people want to insert some DOM nodes into the document at where the importing script element appears. If that's the one we'd like to support, then it seems like we'd always have to provide some script element as long as it's pulled in by a script element.

Let's say you have c.js which inserts a DOM node at where it's imported. Then it seems strange that when a.js imports c.js, it can no longer insert the same DOM node at where c.js is imported. More broadly, if b.js also imports c.js and both a.js and b.js are imported by script elements, it conceptually makes sense for c.js to insert the same DOM node to both places for the cases whereZ a.js and b.js's script elements might be inserted into two different shadow trees. But that really depends on what kind of DOM node is getting inserted by c.js as well.

We need to enumerate concrete use cases and study them more carefully before we can judge which proposal/behavior makes sense.

@domenic domenic referenced this issue in tc39/proposal-import-meta Sep 14, 2017

Closed

Should be during instantiation, not evaluation #5

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2017

Member

Agreed use cases will be useful. I've asked @justinfagnani to comment for Polymer which has plans to use this.

I anticipate most people won't be reusing a module script within multiple graphs on the page, so for their cases these issues won't matter that much. But I welcome correction from users.


Another issue raised by @nyaxt is that any design of import.meta.scriptElement implies holding a reference to the script element basically forever. Since the script element then has a pointer back to the Document (and that to Window), it keeps the entire DOM tree alive. I guess we could make it throw/return null if the page is navigated...

Member

domenic commented Sep 14, 2017

Agreed use cases will be useful. I've asked @justinfagnani to comment for Polymer which has plans to use this.

I anticipate most people won't be reusing a module script within multiple graphs on the page, so for their cases these issues won't matter that much. But I welcome correction from users.


Another issue raised by @nyaxt is that any design of import.meta.scriptElement implies holding a reference to the script element basically forever. Since the script element then has a pointer back to the Document (and that to Window), it keeps the entire DOM tree alive. I guess we could make it throw/return null if the page is navigated...

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Sep 14, 2017

Is it being an Array not an option?

Is it being an Array not an option?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2017

Member

@matthewp one that mutates dynamically over time? (Also, what if someone pushes into it?) It seems a bit strange, especially since the script is only evaluated once, so it doesn't really have multiple script elements that actually affected the world, just one that did and a bunch of other no-ops.

Do you have a use case where a mutating-over-time array would work much better?

Member

domenic commented Sep 14, 2017

@matthewp one that mutates dynamically over time? (Also, what if someone pushes into it?) It seems a bit strange, especially since the script is only evaluated once, so it doesn't really have multiple script elements that actually affected the world, just one that did and a bunch of other no-ops.

Do you have a use case where a mutating-over-time array would work much better?

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Sep 14, 2017

Probably a live NodeList instead of an array. I don't have any use-cases in favor of a list here, just the normal use cases for currentScript like @rniwa mentioned above. I use it to get the ownerDocument some times, but this is probably overly defensive.

I only mentioned a list structure to solve the problem of scriptElement possibly changing. I'd prefer a list that mutates over the reference changing to a different element. If it referred to only the first element, that would make sense to me as well.

matthewp commented Sep 14, 2017

Probably a live NodeList instead of an array. I don't have any use-cases in favor of a list here, just the normal use cases for currentScript like @rniwa mentioned above. I use it to get the ownerDocument some times, but this is probably overly defensive.

I only mentioned a list structure to solve the problem of scriptElement possibly changing. I'd prefer a list that mutates over the reference changing to a different element. If it referred to only the first element, that would make sense to me as well.

@justinfagnani

This comment has been minimized.

Show comment
Hide comment
@justinfagnani

justinfagnani Sep 16, 2017

So my use-cases would be around finding <template>s and other DOM resources associated with an element, test, etc. We have at times used more declarative styles of element and test definition where a script automatically finds its container or sibling. I know other projects have used currentScript to read attributes off the script element itself.

So my use-cases would be around finding <template>s and other DOM resources associated with an element, test, etc. We have at times used more declarative styles of element and test definition where a script automatically finds its container or sibling. I know other projects have used currentScript to read attributes off the script element itself.

domenic added a commit that referenced this issue Oct 18, 2017

Add import.meta.url
This integrates with the stage 3 import.meta proposal located at
https://github.com/tc39/proposal-import-meta/. This is based on
https://github.com/tc39/proposal-import-meta/blob/f5d39bc471a5bf2791708f9a3fec943380d9e3ee/HTML%20Integration.md
although it only includes the easier part, import.meta.url.
import.meta.scriptElement is still being discussed, at #1013, and as
such is excluded for now.
@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Oct 19, 2017

Collaborator

For finding templates, do you only care about the first script element which imports a given module script? Or do you care about every script element that imports your script?

Collaborator

rniwa commented Oct 19, 2017

For finding templates, do you only care about the first script element which imports a given module script? Or do you care about every script element that imports your script?

@justinfagnani

This comment has been minimized.

Show comment
Hide comment
@justinfagnani

justinfagnani Oct 19, 2017

Every script. The case would look something like this, simplified:

<x-declarative-element>
  <template>...</template>
  <script type="module">
    import {findTemplate} from '../x-element/x-element.js';
    const template = findTemplate(import.meta);
  </script>
</x-declarative-element>

Where findTemplate would use import.meta.scriptElement to find it's container, or sibling, etc.

Every script. The case would look something like this, simplified:

<x-declarative-element>
  <template>...</template>
  <script type="module">
    import {findTemplate} from '../x-element/x-element.js';
    const template = findTemplate(import.meta);
  </script>
</x-declarative-element>

Where findTemplate would use import.meta.scriptElement to find it's container, or sibling, etc.

@domenic domenic referenced this issue in w3ctag/design-reviews Oct 20, 2017

Open

import.meta.url, and import.meta generally #208

3 of 5 tasks complete

domenic added a commit that referenced this issue Nov 7, 2017

Add import.meta.url
This integrates with the stage 3 import.meta proposal located at
https://github.com/tc39/proposal-import-meta/. This is based on
https://github.com/tc39/proposal-import-meta/blob/f5d39bc471a5bf2791708f9a3fec943380d9e3ee/HTML%20Integration.md
although it only includes the easier part, import.meta.url.
import.meta.scriptElement is still being discussed, at #1013, and as
such is excluded for now.
@tomalec

This comment has been minimized.

Show comment
Hide comment
@tomalec

tomalec Dec 14, 2017

I have also a use case for Shadow DOM. To do any DOM related scripting scoped to the shadow root <script> element is in.
And to let the script "avoid affecting the document tree" and work with actually encapsulated one.

<button>Pink like a deco umbrella</button>
<div>
  <!--shadowroot-->
    <red-door>I want it painted black</red-door>
    <script type="module">
      import {paintitblack} from '../stones.js';
      paintitblack(import.meta.scriptElement.getRootNode().querySelector('*'));
    </script>
  <!--/shadow-root-->
</div>

More at w3c/webcomponents#717

tomalec commented Dec 14, 2017

I have also a use case for Shadow DOM. To do any DOM related scripting scoped to the shadow root <script> element is in.
And to let the script "avoid affecting the document tree" and work with actually encapsulated one.

<button>Pink like a deco umbrella</button>
<div>
  <!--shadowroot-->
    <red-door>I want it painted black</red-door>
    <script type="module">
      import {paintitblack} from '../stones.js';
      paintitblack(import.meta.scriptElement.getRootNode().querySelector('*'));
    </script>
  <!--/shadow-root-->
</div>

More at w3c/webcomponents#717

@Jamesernator

This comment has been minimized.

Show comment
Hide comment
@Jamesernator

Jamesernator Dec 21, 2017

Something that seems to be the prevailing use-case is to access things near the script tag (whether shadow DOM/document/siblings/etc), but given that modules are only executed once per unique URL I think we'd also need another flag on script[type="module"] tags for these sort've cases.

For example perhaps a unique attribute that causes the script to be executed as if it were a unique script (similar to how every inline script is unique).

e.g. In this example the script would be executed twice, once with the first script tag and again with the second script tag, effectively as if they were both written as inline script tags containing the contents of the script they were loading (although a nice bonus is it would only need to fetch once):

<script unique type="module" src="./someScriptToBeExecutedAgainstCurrentDocumentOrShadowRoot.js"></script>

<div>
   <!--Shadowroot-->
   <script unique type="module" src="./someScriptToBe..."></script>
   <!--/ShadowRoot-->
</div>

Of course this could just be solved by using inline scripts directly as that's effectively what my suggestion is equivalent to, but it leads to quite a bit of boilerplate e.g.:

<script type="module">
    import someCodeToBeExecuted from "./someCodeToBeExecuted.js";
    someCodeToBeExecuted(import.meta.scriptElement);
</script>
<div>
   <!--ShadowRoot-->
   <script type="module">
      import someCodeToBeExecuted from "./someCodeToBeExecuted.js";
      someCodeToBeExecuted(import.meta.scriptElement);
   </script>
   <!--/ShadowRoot-->
</div>

Something that seems to be the prevailing use-case is to access things near the script tag (whether shadow DOM/document/siblings/etc), but given that modules are only executed once per unique URL I think we'd also need another flag on script[type="module"] tags for these sort've cases.

For example perhaps a unique attribute that causes the script to be executed as if it were a unique script (similar to how every inline script is unique).

e.g. In this example the script would be executed twice, once with the first script tag and again with the second script tag, effectively as if they were both written as inline script tags containing the contents of the script they were loading (although a nice bonus is it would only need to fetch once):

<script unique type="module" src="./someScriptToBeExecutedAgainstCurrentDocumentOrShadowRoot.js"></script>

<div>
   <!--Shadowroot-->
   <script unique type="module" src="./someScriptToBe..."></script>
   <!--/ShadowRoot-->
</div>

Of course this could just be solved by using inline scripts directly as that's effectively what my suggestion is equivalent to, but it leads to quite a bit of boilerplate e.g.:

<script type="module">
    import someCodeToBeExecuted from "./someCodeToBeExecuted.js";
    someCodeToBeExecuted(import.meta.scriptElement);
</script>
<div>
   <!--ShadowRoot-->
   <script type="module">
      import someCodeToBeExecuted from "./someCodeToBeExecuted.js";
      someCodeToBeExecuted(import.meta.scriptElement);
   </script>
   <!--/ShadowRoot-->
</div>
@mk-pmb

This comment has been minimized.

Show comment
Hide comment
@mk-pmb

mk-pmb Dec 27, 2017

I often use a reference to the script tag to

  • read its innerHTML where I put configuration data in a block comment.
  • insert DOM nodes before the script tag
  • then remove the script tag to work around some edge cases when saving the page and loading it from disk again.

Since I like to use weak netbooks while traveling, I fear that having a long-lived reference to the script tag will invite implementation scenarios where memory is wasted without good reason. I'd thus prefer some getter function so scripts have to opt-in to the reference, and any script that doesn't care, won't stall garbage collection.

mk-pmb commented Dec 27, 2017

I often use a reference to the script tag to

  • read its innerHTML where I put configuration data in a block comment.
  • insert DOM nodes before the script tag
  • then remove the script tag to work around some edge cases when saving the page and loading it from disk again.

Since I like to use weak netbooks while traveling, I fear that having a long-lived reference to the script tag will invite implementation scenarios where memory is wasted without good reason. I'd thus prefer some getter function so scripts have to opt-in to the reference, and any script that doesn't care, won't stall garbage collection.

@mk-pmb

This comment has been minimized.

Show comment
Hide comment
@mk-pmb

mk-pmb Dec 27, 2017

For example perhaps a unique attribute

I consider that name confusing, as it's conceptually near "once", so it could just as well mean its opposite. How about "again", "run-again", "init-again" or sth. like that?
Update: Nope, that's confusing when it appears on the first script tag for that URL. Can't use "nocache" either because that might be interpreted as being about the transport layer.

mk-pmb commented Dec 27, 2017

For example perhaps a unique attribute

I consider that name confusing, as it's conceptually near "once", so it could just as well mean its opposite. How about "again", "run-again", "init-again" or sth. like that?
Update: Nope, that's confusing when it appears on the first script tag for that URL. Can't use "nocache" either because that might be interpreted as being about the transport layer.

@tomalec tomalec referenced this issue in w3c/webcomponents Feb 23, 2018

Open

Scoped Custom Element Registries #716

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment