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

Feature request: __webpack_public_path__-like variable #1441

Open
arcanis opened this issue Jul 12, 2021 · 25 comments
Open

Feature request: __webpack_public_path__-like variable #1441

arcanis opened this issue Jul 12, 2021 · 25 comments

Comments

@arcanis
Copy link

arcanis commented Jul 12, 2021

Related: #459

In Webpack, __webpack_public_path__ is a runtime variable that contains the public path used to load the assets. Unlike the static publicPath configuration (or what's --asset-path for esbuild), it can be freely modified by the program.

Having this variable is handy when the public path is expected to change from one environment to another (for example when the same assets are meant to be uploaded inside both an internal + external cdn). Without it, one has to rebuild the assets for each environment.

@lukeed
Copy link
Contributor

lukeed commented Jul 12, 2021

Shouldn't this just be userland? I wouldn't want this, ever, for example. Might be as simple as:

globalThis.__webpack_public_path__ = ASSET_PATH;

With a define for ASSET_PATH

@arcanis
Copy link
Author

arcanis commented Jul 12, 2021

I don't see how the code you mention would do anything unless ESBuild was generating code that uses __webpack_public_path__ (or whatever name it'd end up being) inside the urls path. For instance, it currently generates:

const pathToImg = './assets/icon-a456dfe.svg';

Since this path is static, it's impossible to change its public path at runtime. Instead, ESBuild would need to generate something like:

globalThis.__webpack_public_path__ = './assets';

const pathToImg = globalThis.__webpack_public_path__ + '/icon-a456dfe.svg';

The only other option would be for every single place that makes use of the imported images to add the public path themselves - which would go against the point of having a public path.

@lukeed
Copy link
Contributor

lukeed commented Jul 12, 2021

Oh I see, sorry. I thought you were specifically asking for esbuild to output the webpack variable for compatibility purposes with imported code. Nvm :)

@arcanis arcanis changed the title Feature request: __webpack_public_path__ Feature request: __webpack_public_path__-like variable Jul 12, 2021
@rtsao
Copy link
Contributor

rtsao commented Jul 12, 2021

This can be implemented as a plugin, but it feels a bit clunky. There may be a better way, but this is what I came up with:

let dynamicPublicPathPlugin = {
  name: "dynamic-public-path",
  setup(build) {
    build.onResolve({ filter: /\.txt/ }, (args) => {
      return {
        path: path.join(args.resolveDir, args.path),
        namespace:
          args.pluginData === "via-dynamic-file-loader"
            ? "file"
            : "dynamic-asset-path",
      };
    });

    build.onLoad(
      { filter: /\.*/, namespace: "dynamic-asset-path" },
      async (args) => {
        return {
          pluginData: "via-dynamic-file-loader",
          contents: `
          import assetPath from ${JSON.stringify(args.path)};
          const path = __webpack_public_path__ + assetPath;
          export default path;
        `,
          loader: "js",
        };
      }
    );
  },
};

A few thoughts:

  • It is a bit unfortunate to have to declare the file extensions both in the esbuild loader config as well as the resolve filter regex. It would be nicer if there was "loader" filter.
  • Having some single arbitrarily named global variable seems a bit odd. It would be a breaking change, but if publicPath worked similarly to "define" (i.e. could be a quoted string literal or plain identifier) this would solve this use case.
{
  publicPath: '"http://example.com/v1"',
  // or 
  publicPath: '__webpack_public_path__',
}

@evanw
Copy link
Owner

evanw commented Jul 17, 2021

Public paths can also end up in CSS files. This proposal essentially means esbuild would have to completely change its approach to CSS bundling from what it currently does (CSS goes in separate files) to something Webpack-like (CSS is generated at run-time using JavaScript) so that the run-time public path variable is respected. That's not a change that I'm planning to make, so this proposal could be considered out of scope.

@arcanis
Copy link
Author

arcanis commented Jul 17, 2021

CSS is much less of a problem than JS. The reason why __webpack_public_path__ (or similar) is needed is to account for dynamic CDNs; in the case of JS, since the relative paths are resolved relative to the webpage, it's critical to have a way to "reroute" static asset paths - this is traditionally done by avoiding relative paths in JS bundles, and instead using an absolute public path.

By contrast, relative paths in CSS files are relative to the CSS file itself, so they'd already work even when switching from a CDN to another. Having __webpack_public_path__ wouldn't enable much that isn't already possible anyway.

@giladv
Copy link

giladv commented Oct 1, 2021

just wanted to add that without this feature, for me, this great project is unusable.
hopefully will be added soon

@fregante
Copy link

This proposal essentially means esbuild would have to completely change its approach to CSS bundling

Would it? To me it sounds like esbuild could use a base_path global that defaults to . and could be replaced by the user with esbuild --define:base_path=https://mycdn

@arcanis
Copy link
Author

arcanis commented Nov 9, 2021

@evanw Is there any chance this could be reevaluated if scoped exclusively to the JS world (like Webpack)?

@silverwind
Copy link

silverwind commented Nov 24, 2021

Webpack-like (CSS is generated at run-time using JavaScript)

Normally, one would "extract" CSS in Webpack using mini-css-extract-plugin, which results in CSS loaded as CSS. I'm under the impression that a runtime-set __webpack_public_path__ also works for such extracted CSS. I think this is because the webpack runtime code performs loading of all resources in JS.

Thought I'm not totally sure whether a feature like __webpack_public_path__ is really necessary to have in esbuild. The main challenge is that some apps need to support changing the base URL at runtime without recompilation. For example changing the base URL from / to /sub/ should result in all resources being loaded from /sub/ thereafter, including splitted chunks.

For JS-initated loading, it should be possible to determine the correct path based on import.meta.url, but I think this would have to force all loading to go through JS to work and compiled CSS must not emit @import statements because those would go to the wrong URL.

@giladv
Copy link

giladv commented Nov 27, 2021

another use case:
when having chunks, you would like to have control over where the loaded from.
for instance on a micro FE architecture bundles are loaded remotely and those bundles might have chunks.
so unless the container app will be able to set the correct public url dynamically all chunks will try to load relative to the
container app instead of relative to the micro app.

@doberkofler
Copy link

We have the same requirement for something like __webpack_public_path__ as we are using dynamic import for custom code splitting purposes and our base path for the JavaScript resources is dynamic.

@doberkofler
Copy link

This can be implemented as a plugin, but it feels a bit clunky. There may be a better way, but this is what I came up with:

@rtsao I'm having a hard time to fully understand your plugin and just wanted to ask, if you have successfully used your plugin approach before I dig into it?

@Donorlin
Copy link

Donorlin commented Dec 2, 2022

any news about this?
We as well need this feature pretty bad, because we know public path at runtime. Our paths are configured per environment.

@Ketec
Copy link

Ketec commented Jan 4, 2023

End up here on a similar issue - using __webpack_public_path__ to load static assets (images, json, worker scripts) in remotes in microfrontend setup.
In this case the assets are available/served from the remote path and remote code needs to know what it is.

import.meta.url will not work here because it returns the shell/host domain that is serving that micro frontend.

@amogower
Copy link

➕ 1 on this 🙏

Being unable to set the public path at runtime means we have to break Rule III of the Twelve-Factor App and re-build our JS for each environment rather than having it simply adapt to the environment in which it's deployed.

@SPWizard01
Copy link

This is the one thing that actually blocks me from migrating from webpack to esbuild my entire app so I had to do compromises and split it, for now :)

Would love to see it, my scenario is hosting app inside another app (i.e. SharePoint) which has many different site collections that are independent of each other and hold assets on their own(/sites/siteA, /site/anothersite) so I am pretty much stuck with webpack.

But I love the speed of esbuild, amazing.
Plus this variable enables you to debug in production if you will, i.e. setting public path to your localhost :)

@Iworb
Copy link

Iworb commented Apr 1, 2024

I have the same issue with retrieving the public path for my microfrontends. Recently I've used Webpack to bundle the applications and server them on the different ports. __webpack_public_path__ helped the microfrontend to define where it should look for the assets to be loaded. Is there some workaround for the ESBuild to get the script path rather than the host application?

@doberkofler I think you had the same issue. Have you found the workaround? Because I don't believe it will be implemented. It's a big downside for ESBuild as for me.

@SPWizard01
Copy link

SPWizard01 commented Apr 2, 2024

The workaround is to use

import.meta.resolve

This would resolve relative to the module.

@aleesaan
Copy link

We wanted to migrate from webpack to esbuild but we're also blocked because of this issue, as we're using different CDNs in different environments. Is there any plan to add this feature @evanw? Or do you perhaps have suggestions about how this could be achieved currently?

@vojtesaak
Copy link

This issue blocks us migrating from webpack to esbuild as well :( Any news about this feature? @evanw 🙏🏻

@ondrejpesat
Copy link

We'd be in favor of this feature, too. 🙏

@where-picturesque
Copy link

where-picturesque commented Oct 29, 2024

My resources have been deployed on two CDNs, one server in China and one overseas. When accessing the website, determine the resource path based on the user's IP address. If the access address is overseas, use the overseas CDN. So we need a variable similar to webpack_public_cth.
Another way is to redirect

@BurnedToast
Copy link

BurnedToast commented Dec 17, 2024

We also build our applications once and deploy them to multiple environments. This requires some sort of public path configuration.

Not having this feature would result in having to build the applications for each environment.
This would be hard to organize and maintain, and would increase the load on the build server for no good reason (recompiling, testing, etc.).

I hope there is a way to support this, as it prevents us from upgrading to a current version of angular.

@bart-rosseel-axa
Copy link

Adding to previous calls to have some kind of public path configuration. We have a need to decouple the domains from which resources are loaded and the page context in which they are executed. This is sth we can achieve with Webpack, and not with ESBuild. The limitation prevents us from migrating to the newer and better builder for our Angular components.

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