mirror of
				https://github.com/cookiecutter/cookiecutter-django.git
				synced 2025-10-31 07:57:42 +03:00 
			
		
		
		
	Add Webpack support (#3623)
* Add support for Webpack as frontend pipeline * Rename CI jobs * Fix a couple of issues with Webpack + Docker * Don't include Boostrap CSS from CDN with Webpack * Rename variable * Set publicPath in prod webpack config * Fix removal of SASS files in post-gen hooks * Add Webpack to readme usage section * Run Django + Webpack dev server concurrently without Docker * Fix async runserver command with Gulp/Webpack * Upgrade django-webpack-loader to 1.5.0 * Pass variables required by Webpack at build time * Upgrade django-webpack-loader to 1.7.0 * Add missing condition * Add support for Azure Storage + Webpack * Whitespaces * Rename ROOT_DIR -> BASE_DIR * Rename jobs * Bump django-webpack-loader to latest * Document limitation of Docker + Webpack + no Whitenoise * Update section on custom Bootstrap compilation in generated readme
This commit is contained in:
		
							parent
							
								
									977ffd91a3
								
							
						
					
					
						commit
						cbb0e19de7
					
				
							
								
								
									
										22
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -29,7 +29,7 @@ jobs: | |||
|           - windows-latest | ||||
|           - macOS-latest | ||||
| 
 | ||||
|     name: "Run tests" | ||||
|     name: "pytest ${{ matrix.os }}" | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|  | @ -49,10 +49,14 @@ jobs: | |||
|         script: | ||||
|           - name: Basic | ||||
|             args: "" | ||||
|           - name: Extended | ||||
|             args: "use_celery=y use_drf=y frontend_pipeline=Gulp" | ||||
|           - name: Celery & DRF | ||||
|             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: "Docker ${{ matrix.script.name }}" | ||||
|     runs-on: ubuntu-latest | ||||
|     env: | ||||
|       DOCKER_BUILDKIT: 1 | ||||
|  | @ -74,12 +78,14 @@ jobs: | |||
|       fail-fast: false | ||||
|       matrix: | ||||
|         script: | ||||
|           - name: With Celery | ||||
|           - name: Celery | ||||
|             args: "use_celery=y frontend_pipeline='Django Compressor'" | ||||
|           - name: With Gulp | ||||
|             args: "frontend_pipeline='Gulp'" | ||||
|           - name: Gulp | ||||
|             args: "frontend_pipeline=Gulp" | ||||
|           - name: Webpack | ||||
|             args: "frontend_pipeline=Webpack" | ||||
| 
 | ||||
|     name: "${{ matrix.script.name }} Bare metal" | ||||
|     name: "Bare metal ${{ matrix.script.name }}" | ||||
|     runs-on: ubuntu-latest | ||||
|     services: | ||||
|       redis: | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ production-ready Django projects quickly. | |||
| -   Registration via [django-allauth](https://github.com/pennersr/django-allauth) | ||||
| -   Comes with custom user model ready to go | ||||
| -   Optional basic ASGI setup for Websockets | ||||
| -   Optional custom static build using Gulp and livereload | ||||
| -   Optional custom static build using Gulp or Webpack | ||||
| -   Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable) | ||||
| -   Media storage using Amazon S3, Google Cloud Storage or Azure Storage | ||||
| -   Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support) | ||||
|  | @ -149,6 +149,7 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re | |||
|     1 - None | ||||
|     2 - Django Compressor | ||||
|     3 - Gulp | ||||
|     4 - Webpack | ||||
|     Choose from 1, 2, 3, 4 [1]: 1 | ||||
|     use_celery [n]: y | ||||
|     use_mailhog [n]: n | ||||
|  |  | |||
|  | @ -46,7 +46,8 @@ | |||
|   "frontend_pipeline": [ | ||||
|     "None", | ||||
|     "Django Compressor", | ||||
|     "Gulp" | ||||
|     "Gulp", | ||||
|     "Webpack" | ||||
|   ], | ||||
|   "use_celery": "n", | ||||
|   "use_mailhog": "n", | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
| 
 | ||||
| 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: | ||||
| 
 | ||||
| .. 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, | ||||
| 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. | ||||
| 
 | ||||
|  |  | |||
|  | @ -84,6 +84,32 @@ You can read more about this feature and how to configure it, at `Automatic HTTP | |||
| 
 | ||||
| .. _Automatic HTTPS: https://docs.traefik.io/https/acme/ | ||||
| 
 | ||||
| .. _webpack-whitenoise-limitation: | ||||
| 
 | ||||
| Webpack without Whitenoise limitation | ||||
| ------------------------------------- | ||||
| 
 | ||||
| If you opt for Webpack without Whitenoise, Webpack needs to know the static URL at build time, when running ``docker-compose build`` (See ``webpack/prod.config.js``). Depending on your setup, this URL may come from the following environment variables: | ||||
| 
 | ||||
| - ``AWS_STORAGE_BUCKET_NAME`` | ||||
| - ``DJANGO_AWS_S3_CUSTOM_DOMAIN`` | ||||
| - ``DJANGO_GCP_STORAGE_BUCKET_NAME`` | ||||
| - ``DJANGO_AZURE_CONTAINER_NAME`` | ||||
| 
 | ||||
| The Django settings are getting these values at runtime via the ``.envs/.production/.django`` file , but Docker does not read this file at build time, it only look for a ``.env`` in the root of the project. Failing to pass the values correctly will result in a page without CSS styles nor javascript. | ||||
| 
 | ||||
| To solve this, you can either: | ||||
| 
 | ||||
| 1. merge all the env files into ``.env`` by running:: | ||||
| 
 | ||||
|      merge_production_dotenvs_in_dotenv.py | ||||
| 
 | ||||
| 2. create a ``.env`` file in the root of the project with just variables you need. You'll need to also define them in ``.envs/.production/.django`` (hence duplicating them). | ||||
| 3. set these variables when running the build command:: | ||||
| 
 | ||||
|      DJANGO_AWS_S3_CUSTOM_DOMAIN=example.com docker-compose -f production.yml build``. | ||||
| 
 | ||||
| None of these options are ideal, we're open to suggestions on how to improve this. If you think you have one, please open an issue or a pull request. | ||||
| 
 | ||||
| (Optional) Postgres Data Volume Modifications | ||||
| --------------------------------------------- | ||||
|  |  | |||
|  | @ -172,7 +172,7 @@ You can also use Django admin to queue up tasks, thanks to the `django-celerybea | |||
| 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. | ||||
| #. In the project root, install the JS dependencies with:: | ||||
|  |  | |||
|  | @ -95,7 +95,10 @@ frontend_pipeline: | |||
| 
 | ||||
|     1. None | ||||
|     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: | ||||
|     Indicates whether the project should be configured to use Celery_. | ||||
|  | @ -145,6 +148,7 @@ debug: | |||
| .. _PostgreSQL: https://www.postgresql.org/docs/ | ||||
| 
 | ||||
| .. _Gulp: https://github.com/gulpjs/gulp | ||||
| .. _Webpack: https://webpack.js.org | ||||
| 
 | ||||
| .. _AWS: https://aws.amazon.com/s3/ | ||||
| .. _GCP: https://cloud.google.com/storage/ | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| Troubleshooting | ||||
| ===================================== | ||||
| =============== | ||||
| 
 | ||||
| This page contains some advice about errors and problems commonly encountered during the development of Cookiecutter Django applications. | ||||
| 
 | ||||
|  | @ -38,6 +38,16 @@ To fix this, you can either: | |||
| .. _rm: https://docs.docker.com/engine/reference/commandline/volume_rm/ | ||||
| .. _prune: https://docs.docker.com/v17.09/engine/reference/commandline/system_prune/ | ||||
| 
 | ||||
| Variable is not set. Defaulting to a blank string | ||||
| ------------------------------------------------- | ||||
| 
 | ||||
| Example:: | ||||
| 
 | ||||
|     WARN[0000] The "DJANGO_AWS_STORAGE_BUCKET_NAME" variable is not set. Defaulting to a blank string. | ||||
|     WARN[0000] The "DJANGO_AWS_S3_CUSTOM_DOMAIN" variable is not set. Defaulting to a blank string. | ||||
| 
 | ||||
| You have probably opted for Docker + Webpack without Whitenoise. This is a know limitation of the combination, which needs a little bit of manual intervention. See the :ref:`dedicated section about it <webpack-whitenoise-limitation>`. | ||||
| 
 | ||||
| Others | ||||
| ------ | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ TODO: restrict Cookiecutter Django project initialization to | |||
| """ | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import json | ||||
| import os | ||||
| import random | ||||
| import shutil | ||||
|  | @ -87,15 +88,30 @@ def remove_heroku_build_hooks(): | |||
|     shutil.rmtree("bin") | ||||
| 
 | ||||
| 
 | ||||
| def remove_sass_files(): | ||||
|     shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass")) | ||||
| 
 | ||||
| 
 | ||||
| def remove_gulp_files(): | ||||
|     file_names = ["gulpfile.js"] | ||||
|     for file_name in file_names: | ||||
|         os.remove(file_name) | ||||
|     remove_sass_files() | ||||
| 
 | ||||
| 
 | ||||
| def remove_sass_files(): | ||||
|     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(): | ||||
|  | @ -104,6 +120,83 @@ def remove_packagejson_file(): | |||
|         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, use_docker, use_async): | ||||
|     if choice == "Gulp": | ||||
|         update_package_json( | ||||
|             remove_dev_deps=[ | ||||
|                 "@babel/core", | ||||
|                 "@babel/preset-env", | ||||
|                 "babel-loader", | ||||
|                 "concurrently", | ||||
|                 "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": | ||||
|         scripts = { | ||||
|             "dev": "webpack serve --config webpack/dev.config.js", | ||||
|             "build": "webpack --config webpack/prod.config.js", | ||||
|         } | ||||
|         remove_dev_deps = [ | ||||
|             "browser-sync", | ||||
|             "cssnano", | ||||
|             "gulp", | ||||
|             "gulp-imagemin", | ||||
|             "gulp-plumber", | ||||
|             "gulp-postcss", | ||||
|             "gulp-rename", | ||||
|             "gulp-sass", | ||||
|             "gulp-uglify-es", | ||||
|         ] | ||||
|         if not use_docker: | ||||
|             dev_django_cmd = ( | ||||
|                 "uvicorn config.asgi:application --reload" | ||||
|                 if use_async | ||||
|                 else "python manage.py runserver_plus" | ||||
|             ) | ||||
|             scripts.update( | ||||
|                 { | ||||
|                     "dev": "concurrently npm:dev:*", | ||||
|                     "dev:webpack": "webpack serve --config webpack/dev.config.js", | ||||
|                     "dev:django": dev_django_cmd, | ||||
|                 } | ||||
|             ) | ||||
|         else: | ||||
|             remove_dev_deps.append("concurrently") | ||||
|         update_package_json(remove_dev_deps=remove_dev_deps, scripts=scripts) | ||||
|         remove_gulp_files() | ||||
| 
 | ||||
| 
 | ||||
| def remove_celery_files(): | ||||
|     file_names = [ | ||||
|         os.path.join("config", "celery_app.py"), | ||||
|  | @ -384,13 +477,21 @@ def main(): | |||
|         if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y": | ||||
|             append_to_gitignore_file("!.envs/.local/") | ||||
| 
 | ||||
|     if "{{ cookiecutter.frontend_pipeline }}" != "Gulp": | ||||
|     if "{{ cookiecutter.frontend_pipeline }}" in ["None", "Django Compressor"]: | ||||
|         remove_gulp_files() | ||||
|         remove_webpack_files() | ||||
|         remove_sass_files() | ||||
|         remove_packagejson_file() | ||||
|         if "{{ cookiecutter.use_docker }}".lower() == "y": | ||||
|             remove_node_dockerfile() | ||||
|     else: | ||||
|         handle_js_runner( | ||||
|             "{{ cookiecutter.frontend_pipeline }}", | ||||
|             use_docker=("{{ cookiecutter.use_docker }}".lower() == "y"), | ||||
|             use_async=("{{ cookiecutter.use_async }}".lower() == "y"), | ||||
|         ) | ||||
| 
 | ||||
|     if "{{ cookiecutter.cloud_provider}}" == "None": | ||||
|     if "{{ cookiecutter.cloud_provider }}" == "None": | ||||
|         print( | ||||
|             WARNING + "You chose not to use a cloud provider, " | ||||
|             "media files won't be served in production." + TERMINATOR | ||||
|  |  | |||
|  | @ -32,13 +32,11 @@ pytest | |||
| # Make sure the check doesn't raise any warnings | ||||
| python manage.py check --fail-level WARNING | ||||
| 
 | ||||
| # Run npm build script if package.json is present | ||||
| if [ -f "package.json" ] | ||||
| then | ||||
|     npm install | ||||
|     if [ -f "gulpfile.js" ] | ||||
|     then | ||||
|         npm run build | ||||
|     fi | ||||
|     npm run build | ||||
| fi | ||||
| 
 | ||||
| # Generate the HTML for the documentation | ||||
|  |  | |||
|  | @ -101,6 +101,7 @@ SUPPORTED_COMBINATIONS = [ | |||
|     {"frontend_pipeline": "None"}, | ||||
|     {"frontend_pipeline": "Django Compressor"}, | ||||
|     {"frontend_pipeline": "Gulp"}, | ||||
|     {"frontend_pipeline": "Webpack"}, | ||||
|     {"use_celery": "y"}, | ||||
|     {"use_celery": "n"}, | ||||
|     {"use_mailhog": "y"}, | ||||
|  |  | |||
|  | @ -41,3 +41,9 @@ docker-compose -f local.yml run django python manage.py check --fail-level WARNI | |||
| 
 | ||||
| # Generate the HTML for the documentation | ||||
| 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 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								{{cookiecutter.project_slug}}/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								{{cookiecutter.project_slug}}/.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -348,3 +348,7 @@ vendors.js | |||
| *.min.js | ||||
| *.min.js.map | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
| {{ cookiecutter.project_slug }}/static/webpack_bundles/ | ||||
| webpack-stats.json | ||||
| {%- endif %} | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
|             <option value="celeryworker"/> | ||||
|             <option value="celerybeat"/> | ||||
|             {%- endif %} | ||||
|             {%- if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
|             {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} | ||||
|             <option value="node"/> | ||||
|             {%- endif %} | ||||
|           </list> | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
|     </facet> | ||||
|   </component> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     {% if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
|     {% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/node_modules" /> | ||||
|     </content> | ||||
|  |  | |||
|  | @ -128,13 +128,14 @@ See detailed [cookiecutter-django Heroku documentation](http://cookiecutter-djan | |||
| See detailed [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html). | ||||
| 
 | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
| {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} | ||||
| 
 | ||||
| ### Custom Bootstrap Compilation | ||||
| 
 | ||||
| The generated CSS is set up with automatic Bootstrap recompilation with variables of your choice. | ||||
| Bootstrap v5 is installed using npm and customised by tweaking your variables in `static/sass/custom_bootstrap_vars`. | ||||
| 
 | ||||
| You can find a list of available variables [in the bootstrap source](https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss), or get explanations on them in the [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/). | ||||
| You can find a list of available variables [in the bootstrap source](https://github.com/twbs/bootstrap/blob/v5.1.3/scss/_variables.scss), or get explanations on them in the [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/). | ||||
| 
 | ||||
| Bootstrap's javascript as well as its dependencies is concatenated into a single file: `static/js/vendors.js`. | ||||
| Bootstrap's javascript as well as its dependencies are concatenated into a single file: `static/js/vendors.js`. | ||||
| {%- endif %} | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| ARG PYTHON_VERSION=3.10-slim-bullseye | ||||
| 
 | ||||
| {% if cookiecutter.frontend_pipeline == 'Gulp' -%} | ||||
| {% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] -%} | ||||
| FROM node:16-bullseye-slim as client-builder | ||||
| 
 | ||||
| ARG APP_HOME=/app | ||||
|  | @ -9,6 +9,20 @@ WORKDIR ${APP_HOME} | |||
| COPY ./package.json ${APP_HOME} | ||||
| RUN npm install && npm cache clean --force | ||||
| COPY . ${APP_HOME} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' and cookiecutter.use_whitenoise == 'n' %} | ||||
| {%- if cookiecutter.cloud_provider == 'AWS' %} | ||||
| ARG DJANGO_AWS_STORAGE_BUCKET_NAME | ||||
| ENV DJANGO_AWS_STORAGE_BUCKET_NAME=${DJANGO_AWS_STORAGE_BUCKET_NAME} | ||||
| ARG DJANGO_AWS_S3_CUSTOM_DOMAIN | ||||
| ENV DJANGO_AWS_S3_CUSTOM_DOMAIN=${DJANGO_AWS_S3_CUSTOM_DOMAIN} | ||||
| {%- elif cookiecutter.cloud_provider == 'GCP' %} | ||||
| ARG DJANGO_GCP_STORAGE_BUCKET_NAME | ||||
| ENV DJANGO_GCP_STORAGE_BUCKET_NAME=${DJANGO_GCP_STORAGE_BUCKET_NAME} | ||||
| {%- elif cookiecutter.cloud_provider == 'Azure' %} | ||||
| ARG DJANGO_AZURE_ACCOUNT_NAME | ||||
| ENV DJANGO_AZURE_ACCOUNT_NAME=${DJANGO_AZURE_ACCOUNT_NAME} | ||||
| {%- endif %} | ||||
| {%- endif %} | ||||
| RUN npm run build | ||||
| 
 | ||||
| {%- endif %} | ||||
|  | @ -99,7 +113,7 @@ RUN chmod +x /start-flower | |||
| 
 | ||||
| 
 | ||||
| # 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} | ||||
| {% else %} | ||||
| COPY --chown=django:django . ${APP_HOME} | ||||
|  |  | |||
|  | @ -87,6 +87,9 @@ THIRD_PARTY_APPS = [ | |||
|     "corsheaders", | ||||
|     "drf_spectacular", | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
|     "webpack_loader", | ||||
| {%- endif %} | ||||
| ] | ||||
| 
 | ||||
| LOCAL_APPS = [ | ||||
|  | @ -355,6 +358,19 @@ SPECTACULAR_SETTINGS = { | |||
|     "VERSION": "1.0.0", | ||||
|     "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"], | ||||
| } | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
| # django-webpack-loader | ||||
| # ------------------------------------------------------------------------------ | ||||
| WEBPACK_LOADER = { | ||||
|     "DEFAULT": { | ||||
|         "CACHE": not DEBUG, | ||||
|         "STATS_FILE": BASE_DIR / "webpack-stats.json", | ||||
|         "POLL_INTERVAL": 0.1, | ||||
|         "IGNORE": [r".+\.hot-update.js", r".+\.map"], | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| {%- endif %} | ||||
| # Your stuff... | ||||
| # ------------------------------------------------------------------------------ | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ if env("USE_DOCKER") == "yes": | |||
| 
 | ||||
|     hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) | ||||
|     INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips] | ||||
|     {%- if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
|     {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} | ||||
|     try: | ||||
|         _, _, ips = socket.gethostbyname_ex("node") | ||||
|         INTERNAL_IPS.extend(ips) | ||||
|  | @ -94,6 +94,12 @@ CELERY_TASK_ALWAYS_EAGER = True | |||
| # https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates | ||||
| CELERY_TASK_EAGER_PROPAGATES = True | ||||
| 
 | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
| # django-webpack-loader | ||||
| # ------------------------------------------------------------------------------ | ||||
| WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG  # noqa F405 | ||||
| 
 | ||||
| {%- endif %} | ||||
| # Your stuff... | ||||
| # ------------------------------------------------------------------------------ | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ const pjson = require('./package.json') | |||
| const autoprefixer = require('autoprefixer') | ||||
| const browserSync = require('browser-sync').create() | ||||
| const concat = require('gulp-concat') | ||||
| const tildeImporter = require('node-sass-tilde-importer'); | ||||
| const cssnano = require ('cssnano') | ||||
| const imagemin = require('gulp-imagemin') | ||||
| const pixrem = require('pixrem') | ||||
|  | @ -27,7 +28,6 @@ function pathsConfig(appName) { | |||
|   const vendorsRoot = 'node_modules' | ||||
| 
 | ||||
|   return { | ||||
|     bootstrapSass: `${vendorsRoot}/bootstrap/scss`, | ||||
|     vendorsJs: [ | ||||
|       `${vendorsRoot}/@popperjs/core/dist/umd/popper.js`, | ||||
|       `${vendorsRoot}/bootstrap/dist/js/bootstrap.js`, | ||||
|  | @ -61,8 +61,8 @@ function styles() { | |||
| 
 | ||||
|   return src(`${paths.sass}/project.scss`) | ||||
|     .pipe(sass({ | ||||
|       importer: tildeImporter, | ||||
|       includePaths: [ | ||||
|         paths.bootstrapSass, | ||||
|         paths.sass | ||||
|       ] | ||||
|     }).on('error', sass.logError)) | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ services: | |||
|     command: /start-flower | ||||
| 
 | ||||
|   {%- endif %} | ||||
|   {%- if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
|   {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} | ||||
| 
 | ||||
|   node: | ||||
|     build: | ||||
|  | @ -122,7 +122,9 @@ services: | |||
|     command: npm run dev | ||||
|     ports: | ||||
|       - "3000:3000" | ||||
|       {%- if cookiecutter.frontend_pipeline == 'Gulp' %} | ||||
|       # Expose browsersync UI: https://www.browsersync.io/docs/options/#option-ui | ||||
|       - "3001:3001" | ||||
|       {%- endif %} | ||||
| 
 | ||||
|   {%- endif %} | ||||
|  |  | |||
|  | @ -1,13 +1,17 @@ | |||
| { | ||||
|   "name": "{{cookiecutter.project_slug}}", | ||||
|   "version": "{{ cookiecutter.version }}", | ||||
|   "dependencies": {}, | ||||
|   "devDependencies": { | ||||
|     "bootstrap": "^5.1.3", | ||||
|     "gulp-concat": "^2.6.1", | ||||
|     "@babel/core": "^7.16.5", | ||||
|     "@babel/preset-env": "^7.16.5", | ||||
|     "@popperjs/core": "^2.10.2", | ||||
|     "autoprefixer": "^10.4.0", | ||||
|     "babel-loader": "^8.2.3", | ||||
|     "bootstrap": "^5.1.3", | ||||
|     "browser-sync": "^2.27.7", | ||||
|     "css-loader": "^6.5.1", | ||||
|     "gulp-concat": "^2.6.1", | ||||
|     "concurrently": "^7.0.0", | ||||
|     "cssnano": "^5.0.11", | ||||
|     "gulp": "^4.0.2", | ||||
|     "gulp-imagemin": "^7.1.0", | ||||
|  | @ -16,9 +20,19 @@ | |||
|     "gulp-rename": "^2.0.0", | ||||
|     "gulp-sass": "^5.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", | ||||
|     "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": { | ||||
|     "node": "16" | ||||
|  | @ -26,8 +40,11 @@ | |||
|   "browserslist": [ | ||||
|     "last 2 versions" | ||||
|   ], | ||||
|   "babel": { | ||||
|     "presets": ["@babel/preset-env"] | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "gulp", | ||||
|     "build": "gulp generate-assets" | ||||
|     "dev": "", | ||||
|     "build": "" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -10,6 +10,19 @@ services: | |||
|     build: | ||||
|       context: . | ||||
|       dockerfile: ./compose/production/django/Dockerfile | ||||
|       {%- if cookiecutter.frontend_pipeline == 'Webpack' and cookiecutter.use_whitenoise == 'n' %} | ||||
|       args: | ||||
|         # These variable can be defined in an .env file in the root of the repo | ||||
|         {%- if cookiecutter.cloud_provider == 'AWS' %} | ||||
|         DJANGO_AWS_STORAGE_BUCKET_NAME: ${DJANGO_AWS_STORAGE_BUCKET_NAME} | ||||
|         DJANGO_AWS_S3_CUSTOM_DOMAIN: ${DJANGO_AWS_S3_CUSTOM_DOMAIN} | ||||
|         {%- elif cookiecutter.cloud_provider == 'GCP' %} | ||||
|         DJANGO_GCP_STORAGE_BUCKET_NAME: ${DJANGO_GCP_STORAGE_BUCKET_NAME} | ||||
|         {%- elif cookiecutter.cloud_provider == 'Azure' %} | ||||
|         DJANGO_AZURE_ACCOUNT_NAME: ${DJANGO_AZURE_ACCOUNT_NAME} | ||||
|         {%- endif %} | ||||
|       {%- endif %} | ||||
| 
 | ||||
|     image: {{ cookiecutter.project_slug }}_production_django | ||||
|     depends_on: | ||||
|       - postgres | ||||
|  |  | |||
|  | @ -42,7 +42,10 @@ django-redis==5.2.0  # https://github.com/jazzband/django-redis | |||
| {%- if cookiecutter.use_drf == 'y' %} | ||||
| # Django REST Framework | ||||
| djangorestframework==3.14.0  # https://github.com/encode/django-rest-framework | ||||
| django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers | ||||
| django-cors-headers==3.13.0  # https://github.com/adamchainz/django-cors-headers | ||||
| # DRF-spectacular for api documentation | ||||
| drf-spectacular==0.25.1  # https://github.com/tfranzel/drf-spectacular  | ||||
| drf-spectacular==0.25.1  # https://github.com/tfranzel/drf-spectacular | ||||
| {%- endif %} | ||||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
| django-webpack-loader==1.8.0  # https://github.com/django-webpack/django-webpack-loader | ||||
| {%- endif %} | ||||
|  |  | |||
							
								
								
									
										55
									
								
								{{cookiecutter.project_slug}}/webpack/common.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								{{cookiecutter.project_slug}}/webpack/common.config.js
									
									
									
									
									
										Normal 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'], | ||||
|   }, | ||||
| }; | ||||
							
								
								
									
										20
									
								
								{{cookiecutter.project_slug}}/webpack/dev.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								{{cookiecutter.project_slug}}/webpack/dev.config.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| const { merge } = require('webpack-merge'); | ||||
| const commonConfig = require('./common.config'); | ||||
| 
 | ||||
| module.exports = merge(commonConfig, { | ||||
|   mode: 'development', | ||||
|   devtool: 'inline-source-map', | ||||
|   devServer: { | ||||
|     port: 3000, | ||||
|     proxy: { | ||||
|       {%- if cookiecutter.use_docker == 'n' %} | ||||
|       '/': 'http://0.0.0.0:8000', | ||||
|       {%- else %} | ||||
|       '/': 'http://django:8000', | ||||
|       {%- endif %} | ||||
|     }, | ||||
|     // We need hot=false (Disable HMR) to set liveReload=true
 | ||||
|     hot: false, | ||||
|     liveReload: true, | ||||
|   }, | ||||
| }); | ||||
							
								
								
									
										28
									
								
								{{cookiecutter.project_slug}}/webpack/prod.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								{{cookiecutter.project_slug}}/webpack/prod.config.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| const { merge } = require('webpack-merge'); | ||||
| const commonConfig = require('./common.config'); | ||||
| 
 | ||||
| // This variable should mirror the one from config/settings/production.py
 | ||||
| {%- if cookiecutter.use_whitenoise == 'n' %} | ||||
| {%- if cookiecutter.cloud_provider == 'AWS' %} | ||||
| const s3BucketName = process.env.DJANGO_AWS_STORAGE_BUCKET_NAME; | ||||
| const awsS3Domain = process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN ? | ||||
|     process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN | ||||
|     : `${s3BucketName}.s3.amazonaws.com`; | ||||
| const staticUrl = `https://${awsS3Domain}/static/`; | ||||
| {%- elif cookiecutter.cloud_provider == 'GCP' %} | ||||
| const staticUrl = `https://storage.googleapis.com/${process.env.DJANGO_GCP_STORAGE_BUCKET_NAME}/static/`; | ||||
| {%- elif cookiecutter.cloud_provider == 'Azure' %} | ||||
| const staticUrl = `https://${process.env.DJANGO_AZURE_ACCOUNT_NAME}.blob.core.windows.net/static/`; | ||||
| {%- endif %} | ||||
| {%- else %} | ||||
| const staticUrl = '/static/'; | ||||
| {%- endif %} | ||||
| 
 | ||||
| module.exports = merge(commonConfig, { | ||||
|   mode: 'production', | ||||
|   devtool: 'source-map', | ||||
|   bail: true, | ||||
|   output: { | ||||
|     publicPath: `${staticUrl}webpack_bundles/`, | ||||
|   }, | ||||
| }); | ||||
|  | @ -1 +1,5 @@ | |||
| {%- if cookiecutter.frontend_pipeline == 'Webpack' %} | ||||
| import '../sass/project.scss'; | ||||
| {%- endif %} | ||||
| 
 | ||||
| /* Project specific Javascript goes here. */ | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| import '@popperjs/core'; | ||||
| import 'bootstrap'; | ||||
|  | @ -1,5 +1,5 @@ | |||
| @import "custom_bootstrap_vars"; | ||||
| @import "bootstrap"; | ||||
| @import "~bootstrap/scss/bootstrap"; | ||||
| 
 | ||||
| 
 | ||||
| // project specific CSS goes here | ||||
|  |  | |||
|  | @ -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 %} | ||||
| <html lang="{{ LANGUAGE_CODE }}"> | ||||
|   <head> | ||||
|  | @ -13,7 +17,7 @@ | |||
| 
 | ||||
|     {% block css %} | ||||
|     {%- endraw %} | ||||
|     {%- if cookiecutter.frontend_pipeline != 'Gulp' %} | ||||
|     {%- if cookiecutter.frontend_pipeline in ['None', 'Django Compressor'] %} | ||||
|     {%- raw %} | ||||
|     <!-- Latest compiled and minified Bootstrap CSS --> | ||||
|     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css" integrity="sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | ||||
|  | @ -31,6 +35,8 @@ | |||
|     {% endcompress %} | ||||
|     {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} | ||||
|     <link href="{% static 'css/project.min.css' %}" rel="stylesheet"> | ||||
|     {%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %} | ||||
|     {% render_bundle 'project' 'css' %} | ||||
|     {%- endraw %}{% endif %}{% raw %} | ||||
|     {% endblock %} | ||||
|     <!-- Le javascript | ||||
|  | @ -38,8 +44,11 @@ | |||
|     {# Placed at the top of the document so pages load faster with defer #} | ||||
|     {% block javascript %} | ||||
|       {%- 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> | ||||
|       {%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %} | ||||
|       <!-- Vendor dependencies bundled as one file --> | ||||
|       {% render_bundle 'vendors' 'js' attrs='defer' %} | ||||
|       {%- endraw %}{% else %}{% raw %} | ||||
|       <!-- 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> | ||||
|  | @ -55,6 +64,8 @@ | |||
|       {% endcompress %} | ||||
|       {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} | ||||
|       <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 %} | ||||
| 
 | ||||
|     {% endblock javascript %} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user