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

Proposal: script type="module" has module related properties #2235

Closed
rektide opened this issue Jan 4, 2017 · 17 comments
Closed

Proposal: script type="module" has module related properties #2235

rektide opened this issue Jan 4, 2017 · 17 comments
Labels
addition/proposal New features or enhancements

Comments

@rektide
Copy link

rektide commented Jan 4, 2017

Hi. Best I can tell, a <script type="module">...</script> html element does not in any way reveal any of the content of the module inside of it. I would like to see the html element in some way declaratively identify what the module is. Totally spit-baling but perhaps a document.getElementById('mymodule').exports and document.getElementById('mymodule').default properties to expose what the script has?

I could be totally missing some way that HTMLScriptElement reveal what their content is, but I can't see right now how it is via the DOM that they reveal their contents, and I think there should be some DOM means for ES6M to explain what they have in them.

Short of having this functionality, everyone and their mother is going to have to import(scriptEl.getAttribute("src")).then(/* blah blah blah*/). And that seems ridiculous. The actual scriptEl itself ought provide a listing of what it exports & it's defaults.

Please finish this missing part of the Js/DOM relationship.

@annevk
Copy link
Member

annevk commented Jan 4, 2017

I think the top-level script is supposed to only import, even though it's not syntactically constrained in that way.

@matthewp
Copy link

matthewp commented Jan 4, 2017

If/when HTML modules ever happen I think you'll want script modules to be able to define exports, that way the HTML module's exports could (maybe, potentially) be made up of its children script modules' exports.

Here's an old gist from @domenic that has an idea that includes script elements including a module property: https://gist.github.com/domenic/fd84ee5f4e2dc0278ab1#file-import-module-maybe-js

@domenic
Copy link
Member

domenic commented Jan 4, 2017

Yes, this may play a role in the future with HTML modules, but as modules are specced right now it's not useful, as <script type="module"> is only for top-level modules, which are at the root of the graph and thus do not export anything.

Closing, but we might reopen if we end up needing this for HTML modules or similar.

@domenic domenic closed this as completed Jan 4, 2017
@domenic domenic added the addition/proposal New features or enhancements label Jan 4, 2017
@rektide
Copy link
Author

rektide commented Jan 5, 2017

I'm not sure why the distinction between top level or not-top level should exist. What use does drawing this distinction make? This seems like an unnecessary and needless complexity that HTML programmers ought not have to cogitate deeply on. Why not allow "top-level" modules to have exports and why not have HTMLScriptElement able to have some kind of exports property?

I can definitely think of interesting circumstances where a parent element of some kind might want to consider it's children HTMLScriptElement's exports and do something with them. The fad these days is things like React Router, where an element has child compute elements in it- that's doable in React, and we could make more custom elements to also do that, but a <script type="module"> alone ought suffice IMO.

I strongly believe the distinction between top-level and not is actively detrimental to the HTML spec. I'm not sure how I can follow up on this belief. Having this issue closed abruptly simply because the current spec doesn't recognize patterns like React Router's is, IMO, not adequate cause.

@domenic
Copy link
Member

domenic commented Jan 5, 2017

If you can provide an example that cannot be rewritten without the new proposed feature then perhaps this would be worth reconsidering. However vague allusions to React Router are not helpful here.

@domenic
Copy link
Member

domenic commented Jan 5, 2017

Another way to think of this is with a Node.js analogy. <script type=module src=m.js></script> is equivalent to node m.js. The node binary does not provide any way of getting these top-level module exports. If you want to get them you have to use require (import in the browser) inside your dependency tree; the top of the dependency tree doesn't use them.

@rektide
Copy link
Author

rektide commented Jan 5, 2017

require.main.exports.anExportOfM in Node will get the export anExportOfM from m.js.

Rather than create new specific custom elements, I don't see any reason why scripts ought not simply make their exports properties for the elements. If that were the case, one could make a ReactishRouter of a custom element, and normal script tags inside-

<ReactishRouter>
  <script type="text/module" src="routeAbout.js"></script>
  <script type="text/module" src="routeTwo.js"></script>
</ReactishRouter>

With perhaps a routeAbout.js of:

export default function About(){ /* ... */ }
export const path = "about"

I see some discussion in the current spec about the top-level flag on scripts. I can understand better it's use. However, I can find nothing what-so-ever asserting that top-level modules can't or should not have exports. There's only 8 current uses of the word "export" or "exports" on the HTML spec, and nothing seems pertinent to this "top-level" designation. So I'm not sure where this assertion comes from:

right now it's not useful, as <script type="module"> is only for top-level modules, which are at the root of the graph and thus do not export anything.

This seems simply like a historical expectation and projection of how things have been. I don't see anything that says top level modules can't or shouldn't have exports, and it seems like a possible boon if, having exports, the HTMLScriptElement could expose them. Again this can be worked around (if/when we get dynamic import) via import(scriptEl.getAttribute("src")) but I'm used to the DOM giving me good information about the HTML element it describes, and providing a script elements's exports seems like a natural expectation for a developer to have.

@domenic
Copy link
Member

domenic commented Jan 5, 2017

Yes, the spec is definitely grounded in the historical expectation of how things have always been. We're not interested in catering to novel, unproven module system usages.

Your example is better rewritten using something like <reactish-router srcs="routeAbout.js routeTwo.js"></reactish-router>, as it isn't abusing the <script type="module"> tag for something it wasn't meant for (importing the top-level modules so they can execute).

@rektide
Copy link
Author

rektide commented Jan 10, 2017

I strongly reject that this is about "novel, unproven module system usages".

This is about fulfilling the basic purpose of the DOM. From the top- the DOM L1 abstract:

This specification defines the Document Object Model Level 1, a platform- and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure and style of documents. The Document Object Model provides a standard set of objects for representing HTML and XML documents, a standard model of how these objects can be combined, and a standard interface for accessing and manipulating them.

Up until now, the HTMLScriptElement has provided very scant tools for accessing what's inside of it. There's been very little that the DOM could report on for a script, because scripts are opaque and haven't exposed anything themselves.

Now that HTMLScriptElement modules exist, there is something sensible and logical for the DOM make accessible- the exports of the module. Just as we have DOM tools to talk about the CSS- HTMLStyleElement.sheet so too ought we have a HTMLScriptElement.exports. That would be a pleasing fulfillment of the DOM's stated purpose.

@tabatkins
Copy link
Contributor

I'd like to re-open this, as I think the underlying "top-level modules only need to import" statement is incorrect. The point of exporting from a top-level module would seem to be to make the export visible to the page's global object (which is, in a sense, the true "top-level module"; one that imports all of its inline modules).

Right now you have to "fake" exports from inline modules by explicitly setting window.foo = ...; from inside the module; while this works, it feels weird to not be able to just export things, and means you can't trivially move a module from inline to external or vice versa.

@domenic
Copy link
Member

domenic commented Oct 5, 2018

I don't think we should add new syntax for setting properties on the global object.

@rektide
Copy link
Author

rektide commented Aug 2, 2019

In WICG/import-maps#2 (comment) , @justinfagnani proposed having HTMLScriptElement expose a .module property when type=module. This would satisfy a lot of my desires to have script elements expose a useful object model.

@justinfagnani
Copy link

I think @domenic has been supportive of the idea in general in some other contexts. @domenic should we reopen with the newer use-cases in mind?

@domenic
Copy link
Member

domenic commented Aug 2, 2019

I'd be happy to reopen if someone can present use cases. The conversation as it stands previously still seems accurate to me. It's full of invocations of theoretical purity and abstract design goals like "have an object model", and not actual use cases (i.e. #2235 (comment)).

@justinfagnani
Copy link

Ok, I can add some of the cases we've talked about in relation to HTML modules.

@sgentle
Copy link

sgentle commented Aug 14, 2019

FWIW I did this and it worked fine*:

const importBlob = (text) => {
  const blob = new Blob([text], {type: 'text/javascript'})
  const src = URL.createObjectURL(blob)
  const prom = import(src)
  prom.then(() => URL.revokeObjectURL(src))

  return prom
}

class ModuleScript extends HTMLScriptElement {
  connectedCallback() {
    this.module = this.src ? import(this.src) : importBlob(this.textContent)
  }
}

customElements.define('module-script', ModuleScript, {extends: 'script'})
<script type="module" is="module-script" src="./hello-world.js"></script>

<script type="module" is="module-script">
  export default "Hello, world"
</script>

* where fine means it loads inline modules twice

@justinfagnani
Copy link

One of the main use cases for access to the module object is for declarative custom elements. We talked about a hypothetical <vue-element> definition element in Toronto:

In an HTML module:

<vue-element name="my-element">
  <template>
    <span v-if="isShowing">I'm shown</span>
  </template>
  <script type=module>
    export default {
      props: ['isShowing'],
      methods: {
        show() {
          this.isShowing = true;
        },
      },
    };
  </script>
</vue-element>

This can work if the <vue-element> element can get a reference to the module object created by it's child <script> tag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements
Development

No branches or pull requests

7 participants