Possible to control placement order of base tags?
I currently have to support proxying to multiple apps via sub-document paths (via nginx) and the solution that I've found works the best is to set the base tag to / and then for sub-document path setting sub_filter '<base href="/">' '<base href="/mysubpath/">'; which has been working great so far (as long as my webpack is configured without a publicPath, all ajax requests are relative, and i set react router's basename prop to new URL(document.baseURI}.pathname)
The only issue I'm running into is that when I use html-webpack-plugin to set the basename
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
base: '/',
favicon: 'src/static/favicon.svg',
})
]
it works, (and nginx is able to replace it with it's sub_filter directive, which is awesome) BUT the injected <base href="/"> tag is inserted after the existing <head> content in the template:
<!DOCTYPE html>
<html>
<head>
<script type="application/javascript" src="config.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
Which html-webpack-plugin ends up returning with the injected scripts and base tag as:
<!DOCTYPE html>
<html>
<head>
<script type="application/javascript" src="config.js"></script>
<base href="/"><link rel="icon" href="favicon.png"></head>
<body>
<div id="root"></div>
<script src="vendors.js"></script><script src="main.js"></script></body>
</html>
If you notice, I'm loading a js file called config.js (it's generated by the server, and serves to inject some public variables at runtime for the docker container, so I can assign some vars to window.config which allows me to change some env variables for the client without having to have webpack rebuild the whole bundle) and putting it in my index.html that i'm using for the template.
I kept seeing that if I loaded the page on it's path without a trailing slash, it would try and load http://my-proxy-server/config.js instead of http://my-proxy-server/subpath/config.js, which resulted in the config file not being loaded. Even though I was serving it (thanks to nginx's sub_filter) with <base href="/subpath/">
I thought it was my nginx config, but had a hunch and moved the <base href="/subpath/"> tag in the compiled index.html to the top of the <head> and boom, it suddenly worked.
I'm guessing that as it parses the index.html, it first sees the <script src="config.js> tag and loads it, then continues down and later finds the <base href="/subpath"> tag, and any scripts loaded after that point are relative to the base tag, but ones before it use the document's current path, which when I'm not using a trailing slash, is the server root and not my sub-document path's root.
All that being said, I've learned that the order matters for where the <base> tag is placed in the <head> and I need it to be placed before my <script> tag. Since the script is loaded from the server, it's not something I can (as far as I know) get webpack to pick up and let html-webpack-plugin to include (plus it needs to be before the bundles) are loaded, but as html-webpack-plugin injects it at the bottom of my existing template content, it's getting placed too late in the <head>
tl;dr
<head>
<base href="/subpath/">
<script src="file.js"></script>
</head>
works but
<head>
<script src"file.js"></script>
<base href="/subpath/">
</head>
doesn't, and html-webpack-plugin places the header content AFTER the template's existing content there.
What would be the best practice to fix? Somehow ensure the <base> tag generated by html-webpack-plugin is injected at the top of the <head>, or at least before the script I need to load? Is there a way to let the script be injected by html-webpack-plugin when it's not park of the webpack build? Create a placeholder config.js and include in the build, only to be replaced at runtime by my startup script?
My current workaround is to just omit base in the plugin's options and just set it manually on the template, but I'd like to use html-webpack-plugin to generate the base tag if possible, so my webpack configs can be consistent between projects. (especially for ones that may not have a template at all and use the defaults)
Any help is appreciated. I'd like to figure out the cleanest approach. My current workaround feels hacky.
You are absolutely right, we should generate <base> tag in root of <head>
I think you should inject your config.js using html-webpack-plugin mechanism with custom injecting.
Smth like this: https://github.com/jantimon/html-webpack-plugin/tree/main/examples/custom-insertion-position
You may push your tag <script src="config.js></script> in htmlWebpackPlugin.tags array
UPD: I've checked the source code and it looks like html-webpack-plugin doesn't support injection of custom tags in custom places (https://github.com/jantimon/html-webpack-plugin/blob/1e4262528ff02a83e1fc7739b42472680fd205c2/index.js#L920)
I think we need some hack in case with <base> tag
But now you may just rewrite your template.html
BTW: We've wrote the plugin to generate <base> tag with window.location.pathname. You may replace nginx with this
https://github.com/crutch12/base-href-runtime-webpack-plugin