Open arcanis opened 3 years ago
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
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.
Oh I see, sorry. I thought you were specifically asking for esbuild to output the webpack variable for compatibility purposes with imported code. Nvm :)
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:
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__',
}
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.
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.
just wanted to add that without this feature, for me, this great project is unusable. hopefully will be added soon
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
@evanw Is there any chance this could be reevaluated if scoped exclusively to the JS world (like Webpack)?
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.
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.
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.
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?
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.
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.
➕ 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.
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 :)
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.
The workaround is to use
import.meta.resolve
This would resolve relative to the module.
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?
This issue blocks us migrating from webpack to esbuild as well :( Any news about this feature? @evanw 🙏🏻
We'd be in favor of this feature, too. 🙏
Related: https://github.com/evanw/esbuild/issues/459
In Webpack,
__webpack_public_path__
is a runtime variable that contains the public path used to load the assets. Unlike the staticpublicPath
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.