django-webpack/django-webpack-loader

`get_chunk_url` always uses `publicPath`, prevents cdn host.

nitsujri opened this issue · 3 comments

Problem

Currently if using a custom_domain on something that extends S3ManifestStaticStorage for a separate cdn for assets like cdn.example.com and in the webpack configuration defines uses publicPath, the custom_domain never gets loaded:

https://github.com/django-webpack/django-webpack-loader/blob/master/webpack_loader/loaders.py#L81

Bad Solution

In Webpack, for any deployed env (like prod), one could set output: { publicPath: '', }, in the webpack config. This prevents publicPath being created for any asset in the manifest.json.

We tried this but it forces img, font assets to be inside the css folder due to the way references are built without a publicPath.

Best Work Around

Currently, we monkey patch get_chunk_url (works perfectly well for us):

import os

from django.contrib.staticfiles.storage import staticfiles_storage
from webpack_loader.loader import WebpackLoader

def get_chunk_url(self, chunk_file):
    # Removed by Monkey Patch
    # public_path = chunk_file.get('publicPath')
    # if public_path:
    #     return public_path

    # Use os.path.normpath for Windows paths
    relpath = os.path.normpath(
        os.path.join(self.config["BUNDLE_DIR_NAME"], chunk_file["name"])
    )
    return staticfiles_storage.url(relpath)

WebpackLoader.get_chunk_url = get_chunk_url

Solution

Honestly, I don't know what a good solution is here? Possible:

  • Carry a variable render_bundle? Seems very distant.
  • settings.WEBPACK_USE_PUBLICPATH_FROM_CHUNK_FILE - feels like the best of poisons?
fjsj commented

For the current 2.0.1 version, you need to repeat your STATIC_URL in your webpack.config.js. Like:

const path = require("path");

const STATIC_URL = process.env.STATIC_URL || '/';

module.exports = {
    // ...
    output: {
        path: path.resolve("./frontend/webpack_bundles/"),
        publicPath: `${STATIC_URL}webpack_bundles/`,
        filename: "[name]-[chunkhash].js",
    },
};

To build and collect static during your production deploys, run something like:

NODE_ENV=production STATIC_URL="your-custom-cdn-domain.com/" webpack --progress --bail --mode=production
python manage.py collectstatic --noinput

If you already have staticfiles properly configured in your Django-side, there's a way to indirectly pass the STATIC_URL to Webpack assets at runtime, which is publicPath: "auto". See: #350 (comment)

Closing this for now, let me know if you can't solve the issue and I will re-open it.

Thanks for the reply and appreciate the depth. I also recognize that the recommendation probably works for others well.

For us though, the suggested solution isnt ideal as the build is agnostic to the deployment environment. One Docker file builds as single container that can be deployed for production, uat and/or staging. The build env doesn't know where it will end up and I argue it shouldn't. The builder simply builds "for some production-like destination".

Hence we like Django writing the asset's URL.

fjsj commented

@nitsujri makes sense, but I guess publicPath: "auto" will work for your case. Please let me know if #350 (comment) works for you or not.