Deploy Wagtail CMS to PythonAnywhere (part 2)

Set up Webpack and client side code compilation

This is a minimal setup for compiling client side code with Webpack.

It will compile all files in client/styles css, client/scripts javascript and output them to webapp/static as bundle.css and bundle.js.

Setup node and NPM

First lets make sure the node version used will always be the intended version 18.

In the root of your project run:

echo 18 > .nvmrc
bash

Install NVM (node version manager)

Instructions can be viewed at https://github.com/nvm-sh/nvm where you can choose your preferred installation method. I used brew on MacOS.

brew install nvm
bash

Use node version 18

nvm use
bash

If you don't have the correct version of node installed a message will tell you that and you can install it with:

nvm install 18
bash

Initialize NPM

npm init
bash

This will create a package.json file in the root of your project.

Then install the required packages by running:

npm install --save-dev \
    @babel/preset-env \
    babel-loader \
    browser-sync-webpack-plugin \
    css-loader \
    mini-css-extract-plugin \
    sass \
    sass-loader \
    webpack \
    webpack-cli \
    webpack-dev-server
bash

This will have created a node_modules directory in the root of your project. It's not a folder we need to commit to the repository so add it to the .gitignore file.

echo "node_modules" >> .gitignore
bash

Add Webpack configuration

You can read more about Webpack at https://webpack.js.org/.

Create a file webpack.config.js in the root of your project with the following content:

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const BrowserSyncPlugin = require("browser-sync-webpack-plugin");

module.exports = {
  mode: "production", // or "development"
  entry: path.resolve(__dirname, "./client/scripts/index.js"),
  output: {
    path: path.resolve(__dirname, "./webapp/static/webapp"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "bundle.css",
    }),
    new BrowserSyncPlugin({
      host: "localhost",
      port: 3000,
      proxy: "http://127.0.0.1:8000/", // the port your django app will be running on in development
      files: ["./**/*.html"],
    }),
  ],
};
javascript

Add client side code

Create a directory called client in the root of your project with the following folders:

mkdir -p client/{scripts,styles}
bash

Add styles

Create a file client/styles/index.scss with the following content:

body {
  background-color: grey;
}
css

Add javascript

Create a file client/scripts/index.js with the following content:

console.log("Awesome Wagtail !");
javascript

Update the scripts section in the package.json file to:

"scripts": {
  "build": "webpack --mode production",
  "start": "webpack --mode development --watch"
},
json

Run Webpack

To compile the client side code during development run:

npm start # which will compile and watch for changes
bash

To compile the client side code for production run:

npm run build # which will compile and minify the code
bash

Running either of these commands will create a webapp/static/webapp directory containing bundle.css and bundle.js.

Update the base template to use the compiled files

Open the webapp/templates/base.html file and update the head section to include the compiled css and javascript files:

<!-- change -->
<link rel="stylesheet" type="text/css" href="{% static 'css/webapp.css' %}">
<!-- to -->
<link rel="stylesheet" type="text/css" href="{% static 'webapp/bundle.css' %}">
<!-- change -->
<script src="{% static 'js/webapp.js' %}"></script>
<!-- to -->
<script src="{% static 'webapp/bundle.js' %}"></script>
html

You can delete the webapp/static/css and webapp/static/js directories and files as they are no longer needed.

Run the development server

In a new console run the development server (after activating the virtualenv):

python manage.py runserver
bash

As long as you are running the npm start command in the other console...

Open your browser at http://localhost:3000 and you should see the home page with a grey background and a message in the console.

Try changing the background color in client/styles/index.scss and you should see the changes in the browser without having to refresh the page.

Just for fun :-)

Create a file client/scripts/logo.js with the following content:

class Logo {
  constructor() {
        this.logo = document.getElementsByClassName('logo')[0];
        this.header = document.getElementsByClassName('header')[0];
    }

    init() {
      setTimeout(() => {
        this.logo.classList.add('logo--fade-in');
            this.header.classList.add('header--slope');
        }, 1000);
    }
}

module.exports = Logo;
javascript

Update client/scripts/index.js to the following:

import "../styles/index.scss";
import Logo from "./logo.js";

const logo = new Logo();
logo.init();
javascript

Create a file client/styles/_vars.scss with the following content:

$background-color: #43b1b0;
$foreground-color: #fff;
css

Alter the client/styles/index.scss file to the following:

@import "./vars";

body {
  background-color: $background-color;
}

h2, a {
  color: $foreground-color !important;
}

svg .egg {
  fill: $foreground-color;
}

.logo--fade-in {
  animation: fade-in 1s ease-out;
  animation-fill-mode: forwards;
  svg g {
    fill: white;
  }
}

@keyframes fade-in {
  0% {
    opacity: 0.2;
    transform: scale(1);
  }
  75% {
    opacity: 0.8;
    transform: scale(1.8) translateX(10%);
  }
  100% {
    opacity: 1;
    transform: scale(1.4) translateX(0) translateY(20px) rotate(0deg);
  }
}

.header--slope {
  animation: slope 1s ease-in-out;
  animation-fill-mode: forwards;  
}

@keyframes slope {
  0% {
    transform: rotate(0deg);
  }
  90% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(-2deg);
  }
}
css

With npm start still running: If you view the Wagtail start page (home) it's going to look a little different.