Today I wasted over 3 hours frantically looking for explanation why the hell POI's publicPath
setting thinks I am an idiot and it knows better what I want. So this is a quick post to let you know what you should do if you really want to change the output.publicPath
in a POI-managed project.
The problem
Imagine you're working on some project that has pre-existing sources and their location is (surprisingly) not in the root of your server. That's the case every single time when you're working on existing apps trying to induce some build system on them to bundle up the scripts and stylesheets.
Usually, for those cases Webpack has a special configuration option output.publicPath
that handles everything for you and life's good. POI, on the other hand, has that option exposed at the top level - but with a twist: it only works in final build and not in development mode.
The problem is, however, that if you'd like to work against an existing server that produces HTML and other artifacts you need to comply with that structure regardless of the mode. Unfortunately, POI fucking knows better! And here's the relevant point in POI's code that does it:
function getPublicPath(command, publicPath) {
if (command === 'build' && typeof publicPath === 'string') {
return /\/$/.test(publicPath) || publicPath === ''
? publicPath
: publicPath + '/'
}
return '/'
}
module.exports = getPublicPath
I have no idea what was going through EGOIST's head at the time of writing but it must have been something really, really strong. Not good for opensource as it breaks the fundamental rule of least surrprise.
The fix
Instead of setting the publicPath
in poi.config.js
or setting it via command-line parameter override it in the resulting Webpack configuration like so:
module.exports = {
configureWebpack(config, context) {
// we're not setting publicPath as the general configuration option
// because some sick bastard decided this will only have an effect
// in production mode and we need it everywhere. Talk about predictability...
config.output.publicPath = '/path/where/everything/lives/'
return config
},
With that life's good again (even thought it was so damn frustrating Today)
The architecture
Working on projects where the content is delivered from some sort of CMS or other app that has a fat backend isn't easy. It usually boils down to having everything running locally, even if we don't need it. There is, however, a better way of doing it! Use some reverse proxy to route traffic to your browser from 2 different sources: the backend and the frontend in development mode. Here's an example configuration with Apache2 but you can do much much more than that:
<VirtualHost :80>
ServerName my-app.local
# Make sure no caching is induced so that refresh always brings the latest version
# run "sudo a2enmod headers" to enable headers module
Header set Cache-Control no-cache
# run "sudo a2enmod proxy_http" to enable http proxy
ProxyRequests off
ProxyPreserveHost off
# Serve all theme files from local development server
ProxyPassMatch "^\/path\/where\/everything\/lives\/(.*)$" "http://localhost:10001/path/where/everything/lives/$1"
# Required by Hot Reload (run "sudo a2enmod proxy_wstunnel" to enable ws proxy)
ProxyPassMatch "^/(.+).hot-update.js$" "http://localhost:10001/$1.hot-update.js"
ProxyPassMatch "^/(.+).hot-update.json$" "http://localhost:10001/$1.hot-update.json"
ProxyPassMatch "^/sockjs-node/(.+)/websocket" "ws://localhost:10001/sockjs-node/$1/websocket"
ProxyPassMatch "^/sockjs-node/(.+)$" "http://localhost:10001/sockjs-node/$1"
# Serve everything else from remote server
ProxyPass / http://dev-machine.somedomain.com/
ProxyPassReverse / http://dev-machine.somedomain.com/
</VirtualHost :80>
Also, don't forget to set NameVitualHost *:80
or the name-based virtual hosting just won't work. Now with that in place add the following entry to your /etc/hosts
file:
127.0.0.1 my-app.local
and you're all set.
You can of course fiddle with it, add SSL support if your backend server requires it, but the point here is that it is possible and you can work without installing heavy backend to work on frontend!
Happy coding!