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

How should we handle import.meta #6719

Closed
TheLarkInn opened this issue Mar 10, 2018 · 63 comments
Closed

How should we handle import.meta #6719

TheLarkInn opened this issue Mar 10, 2018 · 63 comments

Comments

@TheLarkInn
Copy link
Member

Hey, @sokra @ooflorent, @Kovensky @ljharb:

It looks like import.meta has landed in Stage 3 and Babel has just landed it also. It looks like there is already an acorn plugin that allows for the syntax support.

First piece is: Should we add this or wait?

Second piece is: Are there webpack specific things we should consider for this syntax. I feel like platforms (Electron, etc.) are going to start shoving a bunch of crap (which use to be globals) in these objects that we would automatically use to treat as Externals. Is there specific handlings that we need to be concerned here.

@Jessidhia
Copy link
Member

I actually don't think import.meta will be that abused; at least I hope not.

It is mostly useful for things that are actually not global but still need to exist in every module anyway; for example their own filename, or even the require function as it needs to be bound to the module's dirname.

In webpack's use case, it'd be useful to put import.meta.hot in it. AFAIK it's our only extension to the module object, which we could then remove from scope entirely. We could also put import.meta.moduleId there if that would be useful.

__dirname and __filename will not be available, instead there will be a per-module import.meta.url.

The problem is how to deal with them -- they have to be absolute URLs that are parseable by using the WHATWG URL constructor (new URL(import.meta.url) will throw if it's not absolute, with protocol scheme and all).

This could mean that, if import.meta.url is used by a bundled module, that we would be forced to always enable the NamedModulesPlugin, or maybe something even more drastic such as supporting import()s using a pseudo webpack: protocol.

We don't necessarily have to put url there, though; it's not part of the JS spec. We could just say it's something that's only available when the file is not bundled, and instead use some other key if modules really need to know their dirname.

@ljharb
Copy link

ljharb commented Mar 10, 2018

I think that until node and all browsers have shipped it unflagged, webpack shouldn’t handle it. Users who want to use it early can use a custom babel transform for it - just like import()

@tilgovi
Copy link

tilgovi commented Mar 12, 2018

I just stumbled into module not being defined in an .mjs file, trying to use the HMR API. import.meta seems like a reasonable place for it.

@enjikaka
Copy link

enjikaka commented Apr 7, 2018

For my use case import.meta would be used to have clear isolation between HTML, CSS and JS in components;

components/
     fancy-button/
        fancy-button.js
        fancy-button.html
        fancy-button.css

Where fancy-button.js would do something like;

class FancyButton extends HTMLElement {
    async connectedCallback () {
        const sDOM = this.attachShadow({ mode: 'closed });
        const cssText = await fetch(new URL('fancy-button.css', import.meta.url)).then(r => r.text());
        const rawMarkup = await fetch(new URL('fancy-button.html', import.meta.url)).then(r => r.text());

        this.sDOM.innerHTML = `
            <style>${cssText}</style>
            ${rawMarkup}
        `;
    }
}

customElements.define('fancy-button', FancyButton);

Provided webpack would output this into a build folder keeping the component file structure intact (splitting chunks etc - still separate files, just the JS handled in webpack); I guess it would make sense for webpack to, in runtime, replace import.meta.url when not available in running browser with a path to where webpack wrote the component? When bundling I assume it would be more problematic maybe. 🤔I guess this kinda overlaps with regular html/css imports in webpack, but as only importing JS is the modules spec maybe it makes sense that webpack has this behvaiour for import.meta.url?

@web-padawan
Copy link

I'm working with the Polymer 3 preview and trying to use import.meta.url inside of the static property of the component like this:

static get importPath() {
  return import.meta.url
}

The use case is to build the component which can detect whether it has been loaded from it's file (as it is performed by Polymer CLI when serving locally) or from a bundle (either Webpack or Rollup, etc).

As a developer, I would expect the import.meta.url to contain a path ending with the filename (e. g. the name of the file where the component has been bundled into), and to be able to execute some logic depending on that, without having to rely on process.env stuff.

@ljharb
Copy link

ljharb commented Apr 16, 2018

Since files can be loaded in the browser too, that doesn’t seem like a robust detection mechanism.

@jdalton
Copy link
Contributor

jdalton commented Apr 17, 2018

I think a good first step would be to support parsing it at least. There are acorn plugins for it.

@Jessidhia
Copy link
Member

The existing plugin is not usable as it is AGPL, and loading it would make webpack itself AGPL at runtime.

@ljharb
Copy link

ljharb commented Apr 18, 2018

It looks dual-licensed as that and Apache 2, so it’d be fine.

@Jessidhia
Copy link
Member

Jessidhia commented Apr 18, 2018

It's dual-licensed with "and" so both licenses always apply. But this is getting into lawyer territory 🤔

@ljharb
Copy link

ljharb commented Apr 18, 2018

@jdalton
Copy link
Contributor

jdalton commented Apr 18, 2018

\cc @adrianheine – there's more license concerns deviating from the MIT license of Acorn proper.

@adrianheine
Copy link

Hi, if you want to use acorn-import-meta, you can choose under which license you want to use it. It is released under AGPLv3 and Apache2, so you can choose the one or the other :)

@rtsao
Copy link
Contributor

rtsao commented May 15, 2018

It would be nice to achieve consensus on how to use import.meta as a replacement for the current de facto standard of using static (build-time) process.env.NODE_ENV checks for dead-code elimination of in-browser development-only code.

Are there any existing efforts to create such a proposal?

@web-padawan
Copy link

web-padawan commented Jun 13, 2018

@Kovensky Is there any progress on this? As of now, Webpack is still failing to parse import.meta:

Module parse failed: Unexpected token (145:21)
You may need an appropriate loader to handle this file type.
|       key: "importMeta",
|       get: function get() {
>         return import.meta;
|       }
|     }, {

This makes import.meta totally unusable and it seems rather strange for the spec draft moved to Stage 3 already last September. Apparently not much people tried to use it so far, but it is important at least for Polymer developers, as Polymer 3 actively promotes this feature.

@ooflorent
Copy link
Member

@web-padawan Only stage 4 features are currently supported (except dynamic imports). We are waiting for import.meta to stabilize in node and in browsers before implementing it.

@daKmoR
Copy link

daKmoR commented Dec 2, 2018

hey, we just released a "temporary" loader - e.g. until webpack supports it out of the box that is.
Take it for a spin and use import.meta.url today. Let us know if you find it's useful for you or if you encounter any bugs.
npm: https://www.npmjs.com/package/@open-wc/webpack
github: https://github.com/open-wc/open-wc/tree/master/packages/webpack

@manucorporat
Copy link

Any update on this one? import.meta is already an important part of the platform and any dependency using it will make webpack automatically to crash.

Could at least update the webpack's parser to not crash?

@matthewp
Copy link

matthewp commented May 8, 2019

I think at this point basing decisions on stage version isn't the best way to go. import.meta is currently supported in Safari, Chrome, and Firefox. And in EdgeChromium. So we're at near 100% browser support. Unfortunately it seems that more goes into stage versions than just "stability", so its not the best signal.

@joeldenning
Copy link
Contributor

The SystemJS community would also enjoy seeing import.meta implemented, especially for webpack entry modules so that they can know from what URL they were downloaded from and dynamically set the public path accordingly. See systemjs/systemjs#1939

Is there consensus on what import.meta.url would be for both entry modules and for all other modules? Since webpack modules are compiled down by the time they hit the browser, does it even make sense for any module except the entry module to be able to use import.meta.url?

@joeldenning
Copy link
Contributor

I would be happy to look into what it would take to PR this -- parsing import.meta and replacing with whatever value is decided on.

@kaleidawave
Copy link

@joeldenning Okay, but would that variable need to be set manually by everyone who uses the library...?

@evilebottnawi ok true but the problem is that 99% of this lib is not environment specific. There is ~2 sections (quite high up the call stack) that need to make fs calls. My current thinking is that at this point can split whether window is defined. The following should work if webpack doesn't change import.meta.url

/// <reference lib="dom"/>
import { join, dirname } from "path";

if (typeof window !== "undefined") {
    // @ts-ignore ts does not like during node compiliation `import.meta.url` 
    fetch(join(dirname(import.meta.url), "/bundle/x.txt"))
        .then(x => x.text())
        .then(x => console.log(x));
} else {
    console.log(require("fs").readFileSync(join(__dirname, "/bundle/x.txt")).toString());
}

Creating a branch of the library to not rely on fs calls is not possible. A solution would be to send down a function down that handles fs access and have separate web and node entry point where that function argument is different....? But I want to know why import.meta.url is compiled away as according to mdn it seems pretty dynamic....?

@alexander-akait
Copy link
Member

import.meta.url woks according spec

@Pauan
Copy link

Pauan commented Oct 16, 2020

@kaleidawave If you're making a library, you generally won't compile it with Webpack. Your library will just use regular ES6 modules + dynamic import() + import.meta.url, and you leave it up to the application to use Webpack (or Rollup, or Browserify, or...)

The reason why import.meta.url is compiled is because all ES6 modules (including dynamic import()) are compiled by Webpack, because this is necessary in order to compile ES6 -> ES5.

It would help if you explained more what your goal is, so we can help you achieve that goal (rather than getting stuck on a particular implementation).

@joeldenning
Copy link
Contributor

Okay, but would that variable need to be set manually by everyone who uses the library...?

Libraries are generally not compiled by webpack, and generally there is no good way to guarantee that runtime assets will be downloaded from the correct URL when publishing a webpack library.

@kaleidawave
Copy link

Interesting, looks like webpack isn't for this project....

@evilebottnawi I would counter that:

Say I have /src/index.js:

console.log(import.meta.url)

And its webpack'd to /out/index.js. Then take some html file not compiled by webpack

<script type="module" src="/src/index.js?hello='x'"></script>
<script type="module" src="/out/index.js?hello='x'"></script>

Will log this:

> http://127.0.0.1:8080/src/index.js?hello=%27x%27
> file:///C:/Users/.../src/index.js

Unless I making the mistake that a webpack output is not to be used with non webpacked'd assets (as I need it to be) then it is not inline with the spec...

@Pauan
Copy link

Pauan commented Oct 16, 2020

@kaleidawave The point of Webpack is that all of your code is compiled with Webpack. It needs complete knowledge of all your code in order to compile the modules correctly. That's why the application is supposed to use Webpack, not libraries.

It sounds like you're misunderstanding what Webpack does. Webpack is intended to take all of your ES6 modules and bundle them up into a single file (I'm leaving out some details about chunks, but that's the general idea).

Webpack does not generate ES6 modules, Webpack generates raw ES5 code. So you don't use Webpack with <script type="module">, and you're not supposed to mix the compiled Webpack code with source code (unless they are completely separate).

If you instead want to generate ES6 modules, you should use something like Rollup instead.

Like I said, it would help if you explained what your goal is, so we can help you to achieve it.

@alexander-akait
Copy link
Member

alexander-akait commented Oct 16, 2020

Webpack does not generate ES6 modules, Webpack generates raw ES5 code.

We are working on it 😄 Hope it will be in near future

@sokra
Copy link
Member

sokra commented Oct 16, 2020

We recently added this syntax: new URL("bundle/x.js", import.meta.url) to reference assets. x.js will be copied into the output directory and webpack will generate code that will create an valid URL to that asset at runtime (using public path and the base URL).
Maybe this is something useful for your case.

Using import.meta.url on its own isn't that useful as it's unclear what the meaning should be: URL of the bundle or URL of the module. Since the individual modules doesn't exist anymore at runtime and there are usually multiple bundles we decided to go with the module URL.

@rjgotten
Copy link

rjgotten commented Oct 16, 2020

@sokra
Using import.meta.url on its own isn't that useful as it's unclear what the meaning should be: URL of the bundle or URL of the module.

There's nothing unclear there. Read the spec. It's supposed to be the runtime base URL from which the module was obtained, i.e. loaded, for external script files; or the document's URL for modules defined inline in an HTML page.

https://html.spec.whatwg.org/multipage/webappapis.html#hostgetimportmetaproperties
https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url

Anything else is wrong.

As I mentioned further back in this issue thread, the correct solution would've been to put Webpack-specific properties onto import.meta (which the spec allows !!) and then offer an import.meta.urlthat uses a hash fragment to separate the bundle URL and the module identifier, the last part retreading the path of how fragments work for e.g. pointing to subparts of SVG files.

import.meta.url // -> "my/bundle/url.js#relative/path/to/file.js"
import.meta.webpack.bundle // -> "my/bundle/url.js"
import.meta.webpack.resource // -> "relative/path/to/file.js"

This is completely in keeping with the spec and still has all necessary information to support every use-case, whether that is based on bundle URL or compile-time module path.

@Pauan
Copy link

Pauan commented Oct 17, 2020

@rjgotten You are confusing the bundle with modules. The bundle is not an ES6 module. import.meta.url must always refer to the current module, so it is incorrect to have it refer to the bundle.

In addition, that would create fragmentation within the ecosystem. Right now Webpack uses standard ES6 modules, standard ES6 dynamic import(), and standard import.meta.url. That means you can take ES6 code and run it directly in the browser, or you can compile it with Webpack and it will work correctly either way.

With your proposal, now there would be a difference in behavior with import.meta.url when it is used in Webpack (compared to running directly in the browser), which would break things and create a split between browser code and Webpack code.

So it would have to be the other way around: import.meta.url is always the module URL. And then Webpack could add a new import.meta.webpack.bundle which would point to the bundle file.

@rjgotten
Copy link

rjgotten commented Oct 17, 2020

You are confusing the bundle with modules. The bundle is not an ES6 module. import.meta.url must always refer to the current module, so it is incorrect to have it refer to the bundle.

Incorrect. The spec literally states that import.meta.url is the module script's base URL and clearly defines the base URL as "the URL from which the script was obtained." That means if you have a transcoded representation of a module woven into a bundle, then the bundle URL is where the script was obtained...

With your proposal, now there would be a difference in behavior with import.meta.url when it is used in Webpack (compared to running directly in the browser)

That difference is already there considering @kaleidawave showing that Webpack emits a file:// protocol url for import.meta.url which makes even less sense. Might as well emit the custom webpack:// protocol used for source maps at that point, which at least offers some modicum of 'closed environment' internal consistency.

@alexander-akait
Copy link
Member

alexander-akait commented Oct 17, 2020

import.meta.webpack

Where is it in the specification? 😄

@rjgotten What is the your problem, please stop arguing and show your problem

Incorrect. The spec literally states that import.meta.url is the module script's base URL and clearly defines the base URL as "the URL from which the script was obtained." That means if you have a transcoded representation of a module woven into a bundle, then the bundle URL is where the script was obtained...

Do you understand what it is bunder? Where should we store this? How? In runtime? Do you want a huge overhead? And why?

That difference is already there considering @kaleidawave showing that Webpack emits a file:// protocol url for import.meta.url which makes even less sense. Might as well emit the custom webpack:// protocol used for source maps at that point, which at least offers some modicum of 'closed environment' internal consistency.

What?

@Pauan
Copy link

Pauan commented Oct 17, 2020

@rjgotten Wrong. It is a syntax error to use import.meta.url in a script. Try it yourself in the browser if you don't believe me. Bundles are not the same as modules, period. It is simply incorrect to treat them as modules.

That difference is already there considering @kaleidawave showing that Webpack emits a file:// protocol url for import.meta.url which makes even less sense.

That is because they are not compiling their entire code with Webpack (which is incorrect). When all of the code and assets are compiled with Webpack (as it should), then using import.meta.url works the same. With your proposal import.meta.url would never work the same, ever.

Might as well emit the custom webpack:// protocol used for source maps at that point, which at least offers some modicum of 'closed environment' internal consistency.

That may be a good idea, perhaps @sokra could explain the reasoning for using file://. But it's somewhat irrelevant, since the assets and URLs are handled internally by Webpack, so it doesn't matter what the URL or protocol is.

@alexander-akait
Copy link
Member

That may be a good idea, perhaps @sokra could explain the reasoning for using file://. But it's somewhat irrelevant, since the assets and URLs are handled internally by Webpack, so it doesn't matter what the URL or protocol is.

Yep, also we support file:// protocol, you can use it in code, so we used it

@sokra
Copy link
Member

sokra commented Oct 17, 2020

The spec says

the URL from which the script was obtained

And thats exactly what we return. The module was obtained from the filesystem. That's where it's located.

The bundle is a kind of transport format.

Webpack also supports import("file://..."), so this is consistent with import.meta.url, e.g. you can do import(import.meta.url + "/../other.js") (please don't do that).

It also makes sense for console.error("error in " + import.meta.url). For other scenarios it doesn't make sense. I don't think there is a solution that makes everybody happy. We had similar discussions with __filename.

Webpack tries it's best to map urls to there public counterpart if some exist. E.g. assets have a public URL and we use it. JS modules are bundled so they don't have a public counterpart.

@alex-shamshurin
Copy link

We can handle with import.meta with this babel plugin

function () {
      return {
        visitor: {
          MetaProperty(path) {
            path.replaceWithSourceString('process')
          },
        },
      }
    },

@echofriend
Copy link

echofriend commented Jul 11, 2021

Webpack team,
Your import.meta support has issues.
Following your instructions to import web workers as per your documentation "https://webpack.js.org/guides/web-workers/#example" is not working for me.

The moment I add this line "const worker = new Worker(new URL('./myworker.js', import.meta.url));", I get this error on webpack-cli : "Critical dependency: Accessing import.meta directly is unsupported (only property access is supported)"

However, if I use it const worker = new Worker('myworker.js') , it works fine bypassing webpack.

The whole js fails to work when I try to use webworker with webpack 5 as per your documentation. Please fix this if its a bug.

@alexander-akait
Copy link
Member

@echofriend Please provide reproducible example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests