Add support for Webpack as frontend pipeline

This commit is contained in:
Bruno Alla 2022-03-06 12:18:20 +00:00
parent b67b528636
commit db99d77010
26 changed files with 272 additions and 33 deletions

View File

@ -49,8 +49,12 @@ jobs:
script: script:
- name: Basic - name: Basic
args: "" args: ""
- name: Extended - name: Celery & DRF
args: "use_celery=y use_drf=y frontend_pipeline=Gulp" args: "use_celery=y use_drf=y"
- name: Gulp
args: "frontend_pipeline=Gulp"
- name: Webpack
args: "frontend_pipeline=Webpack"
name: "${{ matrix.script.name }} Docker" name: "${{ matrix.script.name }} Docker"
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -77,7 +81,9 @@ jobs:
- name: With Celery - name: With Celery
args: "use_celery=y frontend_pipeline='Django Compressor'" args: "use_celery=y frontend_pipeline='Django Compressor'"
- name: With Gulp - name: With Gulp
args: "frontend_pipeline='Gulp'" args: "frontend_pipeline=Gulp"
- name: With Webpack
args: "frontend_pipeline=Webpack"
name: "${{ matrix.script.name }} Bare metal" name: "${{ matrix.script.name }} Bare metal"
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -45,7 +45,8 @@
"frontend_pipeline": [ "frontend_pipeline": [
"None", "None",
"Django Compressor", "Django Compressor",
"Gulp" "Gulp",
"Webpack"
], ],
"use_celery": "n", "use_celery": "n",
"use_mailhog": "n", "use_mailhog": "n",

View File

@ -109,10 +109,10 @@ Or add the DSN for your account, if you already have one:
.. _Sentry add-on: https://elements.heroku.com/addons/sentry .. _Sentry add-on: https://elements.heroku.com/addons/sentry
Gulp & Bootstrap compilation Gulp or Webpack
++++++++++++++++++++++++++++ +++++++++++++++
If you've opted for Gulp, you'll most likely need to setup If you've opted for Gulp or Webpack as frontend pipeline, you'll most likely need to setup
your app to use `multiple buildpacks`_: one for Python & one for Node.js: your app to use `multiple buildpacks`_: one for Python & one for Node.js:
.. code-block:: bash .. code-block:: bash
@ -121,7 +121,7 @@ your app to use `multiple buildpacks`_: one for Python & one for Node.js:
At time of writing, this should do the trick: during deployment, At time of writing, this should do the trick: during deployment,
the Heroku should run ``npm install`` and then ``npm build``, the Heroku should run ``npm install`` and then ``npm build``,
which runs Gulp in cookiecutter-django. which run the SASS compilation & JS bundling.
If things don't work, please refer to the Heroku docs. If things don't work, please refer to the Heroku docs.

View File

@ -155,7 +155,7 @@ To run Celery locally, make sure redis-server is installed (instructions are ava
Sass Compilation & Live Reloading Sass Compilation & Live Reloading
--------------------------------- ---------------------------------
If you've opted for Gulp as front-end pipeline, the project comes configured with `Sass`_ compilation and `live reloading`_. As you change you Sass/JS source files, the task runner will automatically rebuild the corresponding CSS and JS assets and reload them in your browser without refreshing the page. If you've opted for Gulp or Webpack as front-end pipeline, the project comes configured with `Sass`_ compilation and `live reloading`_. As you change you Sass/JS source files, the task runner will automatically rebuild the corresponding CSS and JS assets and reload them in your browser without refreshing the page.
#. Make sure that `Node.js`_ v16 is installed on your machine. #. Make sure that `Node.js`_ v16 is installed on your machine.
#. In the project root, install the JS dependencies with:: #. In the project root, install the JS dependencies with::

View File

@ -94,7 +94,10 @@ frontend_pipeline:
1. None 1. None
2. `Django Compressor`_ 2. `Django Compressor`_
3. `Gulp`_: support Bootstrap recompilation with real-time variables alteration. 3. `Gulp`_
4. `Webpack`_
Both Gulp and Webpack support Bootstrap recompilation with real-time variables alteration.
use_celery: use_celery:
Indicates whether the project should be configured to use Celery_. Indicates whether the project should be configured to use Celery_.
@ -144,6 +147,7 @@ debug:
.. _PostgreSQL: https://www.postgresql.org/docs/ .. _PostgreSQL: https://www.postgresql.org/docs/
.. _Gulp: https://github.com/gulpjs/gulp .. _Gulp: https://github.com/gulpjs/gulp
.. _Webpack: https://webpack.js.org
.. _AWS: https://aws.amazon.com/s3/ .. _AWS: https://aws.amazon.com/s3/
.. _GCP: https://cloud.google.com/storage/ .. _GCP: https://cloud.google.com/storage/

View File

@ -10,6 +10,7 @@ TODO: restrict Cookiecutter Django project initialization to
""" """
from __future__ import print_function from __future__ import print_function
import json
import os import os
import random import random
import shutil import shutil
@ -98,12 +99,90 @@ def remove_sass_files():
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass")) shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass"))
def remove_webpack_files():
shutil.rmtree("webpack")
remove_vendors_js()
def remove_vendors_js():
vendors_js_path = os.path.join(
"{{ cookiecutter.project_slug }}",
"static",
"js",
"vendors.js",
)
if os.path.exists(vendors_js_path):
os.remove(vendors_js_path)
def remove_packagejson_file(): def remove_packagejson_file():
file_names = ["package.json"] file_names = ["package.json"]
for file_name in file_names: for file_name in file_names:
os.remove(file_name) os.remove(file_name)
def update_package_json(remove_dev_deps=None, remove_keys=None, scripts=None):
remove_dev_deps = remove_dev_deps or []
remove_keys = remove_keys or []
scripts = scripts or {}
with open("package.json", mode="r") as fd:
content = json.load(fd)
for package_name in remove_dev_deps:
content["devDependencies"].pop(package_name)
for key in remove_keys:
content.pop(key)
content["scripts"].update(scripts)
with open("package.json", mode="w") as fd:
json.dump(content, fd, ensure_ascii=False, indent=2)
fd.write("\n")
def handle_js_runner(choice):
if choice == "Gulp":
update_package_json(
remove_dev_deps=[
"@babel/core",
"@babel/preset-env",
"babel-loader",
"css-loader",
"mini-css-extract-plugin",
"postcss-loader",
"postcss-preset-env",
"sass-loader",
"webpack",
"webpack-bundle-tracker",
"webpack-cli",
"webpack-dev-server",
"webpack-merge",
],
remove_keys=["babel"],
scripts={
"dev": "gulp",
"build": "gulp generate-assets",
},
)
remove_webpack_files()
elif choice == "Webpack":
update_package_json(
remove_dev_deps=[
"browser-sync",
"cssnano",
"gulp",
"gulp-imagemin",
"gulp-plumber",
"gulp-postcss",
"gulp-rename",
"gulp-sass",
"gulp-uglify-es",
],
scripts={
"dev": "webpack serve --config webpack/dev.config.js ",
"build": "webpack --config webpack/prod.config.js",
},
)
remove_gulp_files()
def remove_celery_files(): def remove_celery_files():
file_names = [ file_names = [
os.path.join("config", "celery_app.py"), os.path.join("config", "celery_app.py"),
@ -383,13 +462,16 @@ def main():
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y": if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
append_to_gitignore_file("!.envs/.local/") append_to_gitignore_file("!.envs/.local/")
if "{{ cookiecutter.frontend_pipeline }}" != "Gulp": if "{{ cookiecutter.frontend_pipeline }}".lower() in ["none", "django compressor"]:
remove_gulp_files() remove_gulp_files()
remove_webpack_files()
remove_packagejson_file() remove_packagejson_file()
if "{{ cookiecutter.use_docker }}".lower() == "y": if "{{ cookiecutter.use_docker }}".lower() == "y":
remove_node_dockerfile() remove_node_dockerfile()
else:
handle_js_runner("{{ cookiecutter.frontend_pipeline }}")
if "{{ cookiecutter.cloud_provider}}" == "None": if "{{ cookiecutter.cloud_provider }}" == "None":
print( print(
WARNING + "You chose not to use a cloud provider, " WARNING + "You chose not to use a cloud provider, "
"media files won't be served in production." + TERMINATOR "media files won't be served in production." + TERMINATOR

View File

@ -32,13 +32,11 @@ pytest
# Make sure the check doesn't raise any warnings # Make sure the check doesn't raise any warnings
python manage.py check --fail-level WARNING python manage.py check --fail-level WARNING
# Run npm build script if package.json is present
if [ -f "package.json" ] if [ -f "package.json" ]
then then
npm install npm install
if [ -f "gulpfile.js" ] npm run build
then
npm run build
fi
fi fi
# Generate the HTML for the documentation # Generate the HTML for the documentation

View File

@ -90,6 +90,7 @@ SUPPORTED_COMBINATIONS = [
{"frontend_pipeline": "None"}, {"frontend_pipeline": "None"},
{"frontend_pipeline": "django-compressor"}, {"frontend_pipeline": "django-compressor"},
{"frontend_pipeline": "Gulp"}, {"frontend_pipeline": "Gulp"},
{"frontend_pipeline": "Webpack"},
{"use_celery": "y"}, {"use_celery": "y"},
{"use_celery": "n"}, {"use_celery": "n"},
{"use_mailhog": "y"}, {"use_mailhog": "y"},

View File

@ -41,3 +41,9 @@ docker-compose -f local.yml run django python manage.py check --fail-level WARNI
# Generate the HTML for the documentation # Generate the HTML for the documentation
docker-compose -f local.yml run docs make html docker-compose -f local.yml run docs make html
# Run npm build script if package.json is present
if [ -f "package.json" ]
then
docker-compose -f local.yml run node npm run build
fi

View File

@ -344,3 +344,7 @@ project.min.css
vendors.js vendors.js
*.min.js *.min.js
{%- endif %} {%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
{{ cookiecutter.project_slug }}/static/webpack_bundles/
webpack-stats.json
{%- endif %}

View File

@ -10,7 +10,7 @@
<option value="celeryworker"/> <option value="celeryworker"/>
<option value="celerybeat"/> <option value="celerybeat"/>
{%- endif %} {%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Gulp' %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
<option value="node"/> <option value="node"/>
{%- endif %} {%- endif %}
</list> </list>

View File

@ -13,7 +13,7 @@
</facet> </facet>
</component> </component>
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
{% if cookiecutter.frontend_pipeline == 'Gulp' %} {% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/node_modules" /> <excludeFolder url="file://$MODULE_DIR$/node_modules" />
</content> </content>

View File

@ -1,6 +1,6 @@
ARG PYTHON_VERSION=3.9-slim-bullseye ARG PYTHON_VERSION=3.9-slim-bullseye
{% if cookiecutter.frontend_pipeline == 'Gulp' -%} {% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] -%}
FROM node:16-bullseye-slim as client-builder FROM node:16-bullseye-slim as client-builder
ARG APP_HOME=/app ARG APP_HOME=/app
@ -99,7 +99,7 @@ RUN chmod +x /start-flower
# copy application code to WORKDIR # copy application code to WORKDIR
{%- if cookiecutter.frontend_pipeline == 'Gulp' %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
COPY --from=client-builder --chown=django:django ${APP_HOME} ${APP_HOME} COPY --from=client-builder --chown=django:django ${APP_HOME} ${APP_HOME}
{% else %} {% else %}
COPY --chown=django:django . ${APP_HOME} COPY --chown=django:django . ${APP_HOME}

View File

@ -89,6 +89,9 @@ THIRD_PARTY_APPS = [
"corsheaders", "corsheaders",
"drf_spectacular", "drf_spectacular",
{%- endif %} {%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
"webpack_loader",
{%- endif %}
] ]
LOCAL_APPS = [ LOCAL_APPS = [
@ -351,6 +354,19 @@ SPECTACULAR_SETTINGS = {
{"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"}, {"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"},
], ],
} }
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
# django-webpack-loader
# ------------------------------------------------------------------------------
WEBPACK_LOADER = {
"DEFAULT": {
"CACHE": not DEBUG,
"STATS_FILE": ROOT_DIR / "webpack-stats.json",
"POLL_INTERVAL": 0.1,
"IGNORE": [r".+\.hot-update.js", r".+\.map"],
}
}
{%- endif %} {%- endif %}
# Your stuff... # Your stuff...
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -69,7 +69,7 @@ if env("USE_DOCKER") == "yes":
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips] INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
{%- if cookiecutter.frontend_pipeline == 'Gulp' %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
try: try:
_, _, ips = socket.gethostbyname_ex("node") _, _, ips = socket.gethostbyname_ex("node")
INTERNAL_IPS.extend(ips) INTERNAL_IPS.extend(ips)
@ -94,6 +94,12 @@ CELERY_TASK_ALWAYS_EAGER = True
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-eager-propagates # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-eager-propagates
CELERY_TASK_EAGER_PROPAGATES = True CELERY_TASK_EAGER_PROPAGATES = True
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
# django-webpack-loader
# ------------------------------------------------------------------------------
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG # noqa F405
{%- endif %} {%- endif %}
# Your stuff... # Your stuff...
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -10,6 +10,7 @@ const pjson = require('./package.json')
const autoprefixer = require('autoprefixer') const autoprefixer = require('autoprefixer')
const browserSync = require('browser-sync').create() const browserSync = require('browser-sync').create()
const concat = require('gulp-concat') const concat = require('gulp-concat')
const tildeImporter = require('node-sass-tilde-importer');
const cssnano = require ('cssnano') const cssnano = require ('cssnano')
const imagemin = require('gulp-imagemin') const imagemin = require('gulp-imagemin')
const pixrem = require('pixrem') const pixrem = require('pixrem')
@ -27,7 +28,6 @@ function pathsConfig(appName) {
const vendorsRoot = 'node_modules' const vendorsRoot = 'node_modules'
return { return {
bootstrapSass: `${vendorsRoot}/bootstrap/scss`,
vendorsJs: [ vendorsJs: [
`${vendorsRoot}/@popperjs/core/dist/umd/popper.js`, `${vendorsRoot}/@popperjs/core/dist/umd/popper.js`,
`${vendorsRoot}/bootstrap/dist/js/bootstrap.js`, `${vendorsRoot}/bootstrap/dist/js/bootstrap.js`,
@ -61,8 +61,8 @@ function styles() {
return src(`${paths.sass}/project.scss`) return src(`${paths.sass}/project.scss`)
.pipe(sass({ .pipe(sass({
importer: tildeImporter,
includePaths: [ includePaths: [
paths.bootstrapSass,
paths.sass paths.sass
] ]
}).on('error', sass.logError)) }).on('error', sass.logError))

View File

@ -107,7 +107,7 @@ services:
command: /start-flower command: /start-flower
{%- endif %} {%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Gulp' %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
node: node:
build: build:

View File

@ -1,13 +1,16 @@
{ {
"name": "{{cookiecutter.project_slug}}", "name": "{{cookiecutter.project_slug}}",
"version": "{{ cookiecutter.version }}", "version": "{{ cookiecutter.version }}",
"dependencies": {},
"devDependencies": { "devDependencies": {
"bootstrap": "^5.1.3", "@babel/core": "^7.16.5",
"gulp-concat": "^2.6.1", "@babel/preset-env": "^7.16.5",
"@popperjs/core": "^2.10.2", "@popperjs/core": "^2.10.2",
"autoprefixer": "^10.4.0", "autoprefixer": "^10.4.0",
"babel-loader": "^8.2.3",
"bootstrap": "^5.1.3",
"browser-sync": "^2.27.7", "browser-sync": "^2.27.7",
"css-loader": "^6.5.1",
"gulp-concat": "^2.6.1",
"cssnano": "^5.0.11", "cssnano": "^5.0.11",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-imagemin": "^7.1.0", "gulp-imagemin": "^7.1.0",
@ -16,9 +19,19 @@
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0", "gulp-sass": "^5.0.0",
"gulp-uglify-es": "^3.0.0", "gulp-uglify-es": "^3.0.0",
"mini-css-extract-plugin": "^2.4.5",
"node-sass-tilde-importer": "^1.0.2",
"pixrem": "^5.0.0", "pixrem": "^5.0.0",
"postcss": "^8.3.11", "postcss": "^8.3.11",
"sass": "^1.43.4" "postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.0.2",
"sass": "^1.43.4",
"sass-loader": "^12.4.0",
"webpack": "^5.65.0",
"webpack-bundle-tracker": "^1.4.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.6.0",
"webpack-merge": "^5.8.0"
}, },
"engines": { "engines": {
"node": "16" "node": "16"
@ -26,8 +39,11 @@
"browserslist": [ "browserslist": [
"last 2 versions" "last 2 versions"
], ],
"babel": {
"presets": ["@babel/preset-env"]
},
"scripts": { "scripts": {
"dev": "gulp", "dev": "",
"build": "gulp generate-assets" "build": ""
} }
} }

View File

@ -46,3 +46,6 @@ django-cors-headers==3.11.0 # https://github.com/adamchainz/django-cors-headers
# DRF-spectacular for api documentation # DRF-spectacular for api documentation
drf-spectacular==0.21.2 # https://github.com/tfranzel/drf-spectacular drf-spectacular==0.21.2 # https://github.com/tfranzel/drf-spectacular
{%- endif %} {%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
django-webpack-loader==1.4.1 # https://github.com/django-webpack/django-webpack-loader
{%- endif %}

View File

@ -0,0 +1,55 @@
const path = require('path');
const BundleTracker = require('webpack-bundle-tracker');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
target: "web",
context: path.join(__dirname, '../'),
entry: {
'project': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/project'),
'vendors': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/vendors'),
},
output: {
path: path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/webpack_bundles/'),
publicPath: '/static/webpack_bundles/',
filename: 'js/[name]-[fullhash].js',
chunkFilename: 'js/[name]-[hash].js'
},
plugins: [
new BundleTracker({filename: path.resolve(__dirname, '../webpack-stats.json')}),
new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css' }),
],
module: {
rules: [
// we pass the output from babel loader to react-hot loader
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env',
'autoprefixer',
'pixrem',
],
},
},
},
'sass-loader',
],
}
],
},
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.jsx']
},
}

View File

@ -0,0 +1,16 @@
const { merge } = require('webpack-merge');
const commonConfig = require('./common.config');
module.exports = merge(commonConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
port: 3000,
proxy: {
'/': 'http://0.0.0.0:8000',
},
// We need hot=false (Disable HMR) to set liveReload=true
hot: false,
liveReload: true,
},
})

View File

@ -0,0 +1,8 @@
const { merge } = require('webpack-merge');
const devConfig = require('./common.config');
module.exports = merge(devConfig, {
mode: 'production',
devtool: 'source-map',
bail: true,
})

View File

@ -1 +1,5 @@
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
import '../sass/project.scss';
{%- endif %}
/* Project specific Javascript goes here. */ /* Project specific Javascript goes here. */

View File

@ -0,0 +1,2 @@
import '@popperjs/core';
import 'bootstrap';

View File

@ -1,5 +1,5 @@
@import "custom_bootstrap_vars"; @import "custom_bootstrap_vars";
@import "bootstrap"; @import "~bootstrap/scss/bootstrap";
// project specific CSS goes here // project specific CSS goes here

View File

@ -1,4 +1,8 @@
{% raw %}{% load static i18n {% endraw %}{% if cookiecutter.frontend_pipeline == 'Django Compressor' %}compress{% endif %}{% raw %}%}<!DOCTYPE html> {% raw %}{% load static i18n {% endraw %}
{%- if cookiecutter.frontend_pipeline == 'Django Compressor' %}compress
{%- endif %}{% raw %}%}{% endraw %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}{% raw %}{% load render_bundle from webpack_loader %}{% endraw %}
{%- endif %}{% raw %}<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %} {% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE }}"> <html lang="{{ LANGUAGE_CODE }}">
<head> <head>
@ -31,6 +35,8 @@
{% endcompress %} {% endcompress %}
{%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<link href="{% static 'css/project.min.css' %}" rel="stylesheet"> <link href="{% static 'css/project.min.css' %}" rel="stylesheet">
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
{% render_bundle 'project' 'css' %}
{%- endraw %}{% endif %}{% raw %} {%- endraw %}{% endif %}{% raw %}
{% endblock %} {% endblock %}
<!-- Le javascript <!-- Le javascript
@ -38,8 +44,11 @@
{# Placed at the top of the document so pages load faster with defer #} {# Placed at the top of the document so pages load faster with defer #}
{% block javascript %} {% block javascript %}
{%- endraw %}{% if cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} {%- endraw %}{% if cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<!-- Vendor dependencies bundled as one file--> <!-- Vendor dependencies bundled as one file -->
<script defer src="{% static 'js/vendors.min.js' %}"></script> <script defer src="{% static 'js/vendors.min.js' %}"></script>
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
<!-- Vendor dependencies bundled as one file -->
{% render_bundle 'vendors' 'js' attrs='defer' %}
{%- endraw %}{% else %}{% raw %} {%- endraw %}{% else %}{% raw %}
<!-- Bootstrap JS --> <!-- Bootstrap JS -->
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.min.js" integrity="sha512-OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.min.js" integrity="sha512-OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@ -55,6 +64,8 @@
{% endcompress %} {% endcompress %}
{%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<script defer src="{% static 'js/project.min.js' %}"></script> <script defer src="{% static 'js/project.min.js' %}"></script>
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
{% render_bundle 'project' 'js' attrs='defer' %}
{%- endraw %}{% endif %}{% raw %} {%- endraw %}{% endif %}{% raw %}
{% endblock javascript %} {% endblock javascript %}