Package: django-webpack5-loader
django-webpack5-loaderUse webpack to generate your static bundles without django's staticfiles or opaque wrappers.Django webpack loader consumes the output generated
bywebpack-bundle-trackerand lets you use the generated bundles in
django.If you want to use entrypoints, please usewebpack4-bundle-tracker.If you want to leverage webpack critical css generation while maintaining
a Django-like structure in your project, have a look atwebpack-critical-pages.Achangelogis also available.Purpose of this ForkThis package aims not only to support webpack 4 and 5, introduce additional loading of entrypoints in templates,
but also adds a framework for structuring django projects into pages usingwebpack_loader.contrib.pages.
See below.Theinitial webpack 4 portwas made possible thanks toSvetoslav DekovAli FaroukNikolaus PiccolottoInstallnpminstall--save-devwebpack-bundle-tracker

pipinstalldjango-webpack-loaderConfigurationConfiguringwebpack-bundle-trackerBefore configuringdjango-webpack-loader, let's first configure what's necessary onwebpack-bundle-trackerside. Update your Webpack configuration file (it's usually onwebpack.config.jsin the project root). Make sure your file looks like this (adapt to your needs):constpath=require("path");constwebpack=require("webpack");constBundleTracker=require("webpack-bundle-tracker");module.exports={context:__dirname,entry:"./assets/js/index",output:{path:path.resolve("./assets/webpack_bundles/"),filename:"[name]-[hash].js",},plugins:[newBundleTracker({filename:"./webpack-stats.json"})],};The configuration above expects theindex.js(the app entrypoint file) to live inside the/assets/js/directory (this guide going forward will assume that all front-end related files are placed inside the/assets/directory, with the different kinds of files arranged within its subdirectories).The generated compiled files will be placed inside the/assets/webpack_bundles/directory and the file with the information regarding the bundles and assets (webpack-stats.json) will be stored in the project root.Compiling the front-end assetsYou must generate the front-end bundle usingwebpack-bundle-trackerbefore usingdjango-webpack-loader. You can compile the assets and generate the bundles by running:npxwebpack--configwebpack.config.js--watchThis will also generate the stats file. You can also refer to howdjango-react-boilerplateconfigure thepackage.jsonscripts for different situations.⚠️ Hot reload is available through a specific config. Checkthis section.⚠️ This is the recommended usage for the development environment. Forusage in production, please refer tothis sectionConfiguring the settings fileFirst of all, addwebpack_loadertoINSTALLED_APPS.INSTALLED_APPS=(...'webpack_loader',...)Below is the recommended setup for the Django settings file when usingdjango-webpack-loader.WEBPACK_LOADER={'DEFAULT':{'CACHE':notDEBUG,'STATS_FILE':os.path.join(BASE_DIR,'webpack-stats.json'),'POLL_INTERVAL':0.1,'IGNORE':[r'.+\.hot-update.js',r'.+\.map'],}}For that setup, we're using theDEBUGvariable provided by Django. Since in a production environment (DEBUG = False) the assets files won't constantly change, we can safely cache the results (CACHE=True) and optimize our flow, asdjango-webpack-loaderwill read the stats file only once and store the assets files paths in memory. From that point onwards, it will use these stored paths as the source of truth. IfCACHE=False, we'll always read the stats file to get the assets paths.⚠️ IfCACHE=True, any changes made in the assets files will only be read when the web workers are restarted.During development, when the stats file changes a lot, we want to always poll for its updated version (in our case, we'll fetch it every 0.1s, as defined onPOLL_INTERVAL).⚠️ In production (DEBUG=False), we'll only fetch the stats file once, soPOLL_INTERVALis ignored.WhileCACHEisn't directly related toPOLL_INTERVAL, it's interesting to keepCACHEbinded to theDEBUGlogic value (in this case, the negation of the logic value) in order to only cache the assets in production, as we'd not continuously poll the stats file in that environment.TheSTATS_FILEparameter represents the output file produced bywebpack-bundle-tracker. Since in the Webpack configuration file we've named itwebpack-stats.jsonand stored it on the project root, we must replicate that setting on the back-end side.IGNOREis a list of regular expressions. If a file generated by Webpack matches one of the expressions, the file will not be included in the template.Extra settingsTIMEOUTis the number of seconds webpack_loader should wait for webpack to finish compiling before raising an exception.0,Noneor leaving the value out of settings disables timeoutsINTEGRITYis flag enablingSubresource Integrityon rendered<script>and<link>tags. Integrity hash is get from stats file and configuration on side ofBundleTracker, where configuration optionintegrity: trueis required.LOADER_CLASSis the fully qualified name of a python class as a string that holds the custom webpack loader. This is where behavior can be customized as to how the stats file is loaded. Examples include loading the stats file from a database, cache, external url, etc. For convenience,webpack_loader.loader.WebpackLoadercan be extended. Theload_assetsmethod is likely where custom behavior will be added. This should return the stats file as an object.Here's a simple example of loading from an external url:importrequestsfromwebpack_loader.loaderimportWebpackLoaderclassExternalWebpackLoader(WebpackLoader):defload_assets(self):url=self.config['STATS_URL']returnrequests.get(url).json()RenderingIn order to render the front-end code into the Django templates, we use therender_bundletemplate tag.Its behavior is to accept a string with the name of an entrypoint from the stats file (in our case, we're usingmain, which isthe default) and it'll proceed to include all files under that entrypoint. You can read more about the entrypoints concepthere.⚠️ You can also check an example on how to use multipleentryvalueshere.Below is the basic usage forrender_bundlewithin a template:render_bundle{%loadrender_bundlefromwebpack_loader%}{%render_bundle'main'%}That will render the proper<script>and<link>tags needed in your template.Running in developmentFordjango-webpack-loaderto work, you must run the webpack pipeline. Please refer tothis section.In summary, you should do the following:# in one shellnpxwebpack--configwebpack.config.js--watch# in another shellpythonmanage.pyrunserver⚠️ You can also checkthis exampleon how to run a project withdjango-webpack-loaderandwebpack-bundle-track.Usage in productionWe recommend that you keep your local bundles and the stats file outside the version control, having a production pipeline that will compile and collect the assets during the deployment phase.You must addSTATICFILES_DIRSto your settings file, pointing to the directory where the static files are located. This will letcollectstaticknow where it should look at:STATICFILES_DIRS=(os.path.join(BASE_DIR,'assets'),)Below are the commands that should be run to compile and collect the static files (please note this may change from platform to platform):npm run build
python manage.py collectstatic --noinputFirst we build the assets and, since we havewebpack-bundle-trackerin our front-end building pipeline, the stats file will be populated. Then, we manually run collecstatic to collect the compiled assets.⚠️ Heroku is one platform that automatically runs collectstatic for you, so you need to setDISABLE_COLLECTSTATIC=1environment var. Instead, you must manually run collectstatic after running webpack. In Heroku, this is achieved with apost_compilehook. You can see an example on how to implement this flow ondjango-react-boilerplate.However, production usage for this package isfairly flexible. Other approaches may include keeping the production bundles in the version control and take that responsibility from the automatic pipeline. However, you must remember to always build the frontend and generate the bundle before pushing to remote.Usage in testsThere are 2 approaches for whenrender_bundleshows up in tests, since we don't havewebpack-bundle-trackerat that point to generate the stats file.The first approach is to have specific settings for them (which is how we approach on ourtests), such as donehere. Please note that it's necessary to have a pre-made stats file for the tests (which in general can be empty, such ashere).The second approach is to leverageLOADER_CLASSoverridingfor the test settings and customize theget_bundlemethod to return the url of a stats file. Note that, using this approach, the stats file doesn't have toexist.Advanced UsageRendering by file extensionrender_bundlealso takes a second argument which can be a file extension to match. This is useful when you want to
render different types for files in separately. For example, to render CSS in head and JS at bottom we can do something
like this,{%loadrender_bundlefromwebpack_loader%}<html><head>{%render_bundle'main''css'%}</head><body>....{%render_bundle'main''js'%}</body></head>render_entrypoint (Available only when using Webpack v.4 or newer){%loadrender_entrypointfromwebpack_loader%}{%render_entrypoint'index'%}render_entrypointwill render all the proper<script>and<link>tags needed in your template for that endpoint.
Using this, you can make use of webpack 4 code splitting features. Example webpack config:module.exports={...,entry:{index:"./myapp/static/src/pages/index.js",contact_us:"./myapp/static/src/pages/contact_us.js",},...,plugins:[newBundleTracker({filename:"./webpack-stats.json"})]};Just asrender_bundle,render_entrypointalso takes a second argument which can be a file extension to match, and
can be used in a similar way,{%loadrender_entrypointfromwebpack_loader%}<html><head>{%render_entrypoint'main''css'%}</head><body>....{%render_entrypoint'main''js'%}</body></head>Automatic page handling based on EntrypointsUsingwebpack_loader.contrib.pagesyou can register entrypoints for corresponding pages in templates.At the top of your individual page, do:{%extends"layout.jinja"%}{%doregister_entrypoint("myapp/dashboard")%}In the layout's (base template's) head, place the following:<!DOCTYPE html>{%doregister_entrypoint("main")%}<htmllang="{{LANGUAGE_CODE}}"><head>...{{render_css()}}</head><body>...{{render_js()}}</body>This will load the registered entrypoints in order (main, thenmyapp/dashboard) and automatically inject
the webpack-generated css and js. It also supports critical css injection upon first request visits.<html><head>{%render_bundle'main''css'is_preload=True%}{%render_bundle'main''js'is_preload=True%}{%render_bundle'main''css'%}</head><body>{%render_bundle'main''js'%}</body></html>Accessing other webpack assetswebpack_statictemplate tag provides facilities to load static assets managed by webpack in Django templates. It is like Django's built instatictag but for webpack assets instead.In the below example,logo.pngcan be any static asset shipped with any npm package.{%loadwebpack_staticfromwebpack_loader%}<!-- render full public path of logo.png --><imgsrc="{%webpack_static'logo.png'%}"/>The public path is based onwebpack.config.jsoutput.publicPath.Please note that this approach will use the original asset file, and not a post-processed one from the Webpack pipeline, in case that file had gone through such flow (i.e.: You've imported an image on the React side and used it there, the file used within the React components will probably have a hash string on its name, etc. This processed file will be different than the one you'll grab withwebpack_static).Useskip_common_chunksonrender_bundleYou can use the parameterskip_common_chunks=Trueto specify that you don't want an already generated chunk be included again in the same page. This should only happen if you use more than one entrypoint per Django template (multiplerender_bundlecalls). By usingskip_common_chunks=True, you can get the same default behavior of theHtmlWebpackPlugin.In order for this option to work,django-webpack-loaderrequires therequestobject to be in the context, to be able to keep track of the generated chunks.Therequestobject is passed by default via thedjango.template.context_processors.requestmiddleware with using the Django built-in templating system, and also with using Jinja2.If you don't haverequestin the context for some reason (e.g. usingTemplate.renderorrender_to_stringdirectly without passing the request), you'll get warnings on the console and the common chunks will remain duplicated.Appending file extensionsThesuffixoption can be used to append a string at the end of the file URL. For instance, it can be used if your webpack configuration emits compressed.gzfiles.
qwe{%loadrender_bundlefromwebpack_loader%}<html><head><metacharset="UTF-8"><title>Example</title>{%render_bundle'main''css'%}</head><body>{%render_bundle'main''js'suffix='.gz'%}</body></html>Multiple Webpack configurationsVersion 1.0 and up ofdjango-webpack-loaderalso supports multiple Webpack configurations. The following configuration defines 2 Webpack stats files in settings and uses theconfigargument in the template tags to influence which stats file to load the bundles from.WEBPACK_LOADER={'DEFAULT':{'BUNDLE_DIR_NAME':'bundles/','STATS_FILE':os.path.join(BASE_DIR,'webpack-stats.json'),},'DASHBOARD':{'BUNDLE_DIR_NAME':'dashboard_bundles/','STATS_FILE':os.path.join(BASE_DIR,'webpack-stats-dashboard.json'),}}{%loadrender_bundlefromwebpack_loader%}<html><body>....{%render_bundle'main''js''DEFAULT'%}{%render_bundle'main''js''DASHBOARD'%}<!-- or render all files from a bundle -->{%render_bundle'main'config='DASHBOARD'%}<!-- the following tags do the same thing -->{%render_bundle'main''css''DASHBOARD'%}{%render_bundle'main'extension='css'config='DASHBOARD'%}{%render_bundle'main'config='DASHBOARD'extension='css'%}<!-- add some extra attributes to the tag -->{%render_bundle'main''js''DEFAULT'attrs='async charset="UTF-8"'%}</body></head>File URLs instead of html tagsIf you need the URL to an asset without the HTML tags, theget_filestemplate tag can be used. A common use case is specifying the URL to a custom css file for a Javascript plugin.get_filesworks exactly likerender_bundleexcept it returns a list of matching files and lets you assign the list to a custom template variable.Each object in the returned list has 2 properties:name, which is the name of a chunk from the stats file;url, which can be:ThepublicPathif the asset has one;Thepathto the asset in the static files storage, if the asset doesn't have apublicPath.For example:{%loadget_filesfromwebpack_loader%}{%get_files'editor''css'aseditor_css_files%}CKEDITOR.config.contentsCss = '{{editor_css_files.0.url}}';<!-- or list down name and url for every file --><ul>{%forcss_fileineditor_css_files%}<li>{{css_file.name}}:{{css_file.url}}</li>{%endfor%}</ul>Code splittingIn case you wish to usecode-splitting, follow the recipe below on the Javascript side.Create your entrypoint file and add elements to the DOM, while leveraging the lazy imports.// src/principal.jsfunctiongetComponent(){returnimport(/* webpackChunkName: "lodash" */"lodash").then(({default:_})=>{constelement=document.createElement("div");element.innerHTML=_.join(["Hello","webpack"]," ");returnelement;}).catch((error)=>"An error occurred while loading the component");}getComponent().then((component)=>{document.body.appendChild(component);});On your configuration file, do the following:// webpack.config.jsmodule.exports={context:__dirname,entry:{principal:"./src/principal",},output:{path:path.resolve("./dist/"),// publicPath should match your STATIC_URL config.// This is required otherwise webpack will try to fetch// our chunk generated by the dynamic import from "/" instead of "/dist/".publicPath:"/dist/",chunkFilename:"[name].bundle.js",filename:"[name]-[hash].js",},plugins:[newBundleTracker({filename:"./webpack-stats.json"})],};If you're using Webpack 5 instead of 4, do the following:Changefilename: "[name]-[hash].js"tofilename: "[name]-[fullhash].js";Remove/* webpackChunkName: "lodash" */, which is not needed anymore.On your template, render the bundle as usual:<!-- index.html -->{%loadrender_bundlefromwebpack_loader%}<!DOCTYPE html><html><head><metacharset="utf-8"><title>My test page</title></head><body><p>This is my page</p>{%render_bundle'principal''js'%}</body></html>Hot reloadIn case you wish to enable hot reload for your project usingdjango-webpack-loaderandwebpack-bundle-tracker, please check outthis example, in particular howserver.jsandwebpack.config.jsare configured.Jinja2 ConfigurationIf you need to output your assets in a jinja template, we provide a Jinja2 extension that's compatible withdjango-jinja.To install the extension, add it to theTEMPLATESconfiguration in the["OPTIONS"]["extension"]list.fromdjango_jinja.builtinsimportDEFAULT_EXTENSIONSTEMPLATES=[{"BACKEND":"django_jinja.backend.Jinja2","OPTIONS":{"extensions":DEFAULT_EXTENSIONS+["webpack_loader.contrib.jinja2ext.WebpackExtension",],}}]Then in your base jinja template, do:{{ render_bundle('main') }}Migrating from version < 1.0.0In order to usedjango-webpack-loader>=1.0.0, you must ensure thatwebpack-bundle-tracker@1.0.0is being used on the JavaScript side. It's recommended that you always keep at least minor version parity across both packages, for full compatibility.This is necessary because the formatting ofwebpack-stats.jsonthatwebpack-bundle-trackeroutputs has changed starting at version1.0.0-alpha.1. Starting atdjango-webpack-loader==1.0.0, this is the only formatting accepted here, meaning that other versions of that package don't output compatible files anymore, thereby breaking compatibility with olderwebpack-bundle-trackerreleases.Commercial SupportThis project is maintained byVinta Softwareand is used in products of Vinta's clients. We are always looking for exciting work, so if you need any commercial support, feel free to get in touch:contact@vinta.com.br