Usage

Webpack integration for Flask.

A simple example

Let’s create a basic Flask application inside app.py. Jinja templates will reside in the templates folder and the static files, to be included in the template, in the instance/static folder:

from flask import Flask

app = Flask(
    __name__,
    template_folder='templates',
    static_folder='instance/static'
)

Let’s add a template and an asset to our project:

src/
  js/
    main.js
templates/
  index.html
app.py

The asset will be a simple javascript file:

import $ from 'jquery'

$(document).ready(function(){
  $("#msg").html("I am webpacked.");
});

The content of the index.html we will define later.

To build our asset, let’s create the Webpack configuration in src/webpack.config.js:

var path = require('path');
var config = require('./config');
var ManifestPlugin = require('webpack-manifest-plugin');

module.exports = {
    context: config.build.context,
    entry: {
        app: "./js/main.js",
    },
    output: {
        path: config.build.assetsPath,
        filename: 'js/[name].[chunkhash].js',
        publicPath: config.build.assetsURL
    },
    plugins: [
        new ManifestPlugin({
            fileName: 'manifest.json',
            stripSrc: true,
            publicPath: config.build.assetsURL
        })
    ]
}

You might have noticed that an import of a file which we did not create has been added, config.js. This is because this file is generated by Flask-WebpackExt when we run the flask webpack create command and it will contain the following information:

{
  "build": {
      // Absolute path to the directory where Webpack outputs files
      // will be written to.
      "assetsPath": "/private/tmp/testproject/instance/static/dist",

      // URL to access the built assets.
      "assetsURL": "/static/dist/",

      // Absolute path to the generated assets directory.
      "context": "/private/tmp/testproject/instance/assets",

      // Boolean which represents if Flask debug is on.
      "debug": false,

      // Absolute path to the generated static directory.
      "staticPath": "/private/tmp/testproject/instance/static",

      // URL to access the static files.
      "staticURL": "/static/"
  }
}

This is really important since it is Flask, and only Flask, who knows where the application path for assets is, so through this configuration we tell Webpack where to move the bundles.

Also, we will need a package.json for the npm dependencies, with a run script build that executes webpack, in src/package.json and will be triggered by flask webpack build:

{
    "private": true,
    "name": "example",
    "version": "0.0.1",
    "author": "myself",
    "license": "WTFPL",
    "description": "example",
    "scripts": {
        "build": "webpack --config webpack.config.js"
    },
    "dependencies": {
        "jquery": "^3.2.1"
    },
    "devDependencies": {
        "webpack-manifest-plugin": "^2.0.4"
    }
}

We can now define, in app.py a WebpackTemplateProject to integrate Webpack with our Flask application and build our assets:

from flask_webpackext.project import WebpackTemplateProject
from flask_webpackext import FlaskWebpackExt

project = WebpackTemplateProject(
    __name__,
    project_folder='src',
    config_path='config.json',
)

app.config.update(dict(
    WEBPACKEXT_PROJECT=project,
))

# Initialize extension
FlaskWebpackExt(app)

Since Flask-WebpackExt creates a new template global function called webpack to inject the assets in templates, we can use it to include the assets in our template index.html.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <div id="msg"></div>
        {{ webpack['app.js'] }}
    </body>
</html>

Finally, we will expose our template through a Flask view:

from flask import render_template

@app.route('/')
def index():
    return render_template('index.html')

At this point we are ready to build the assets:

$ flask webpack buildall

The command will copy all files from the src folder to the application instance folder designated for the Webpack project, download the npm packages and run Webpack to build our assets.

Alternatively, we can execute each build step separately. To copy all sources to the working directory:

$ flask webpack create

To run npm install command and download all dependencies:

$ flask webpack install

To run npm run build and execute what you have defined in the package.json:

$ flask webpack build

Now you can run the application and see if the assets are loaded:

$ export FLASK_APP=app.py
$ flask run
$ firefox http://127.0.0.1:5000/

Assets for multiple Flask modules

When working with large applications, assets are usually split in different modules. Flask-WebpackExt uses pywebpack which allows to solve this problem by using:

An example project with two modules could be structured as follows:

buildconfig/
  package.json
  webpack.config.js
modules/
  module1/
    js/
    css/
  module2/
    js/
    css/
templates/
  index.html
app.py

Let’s start with the definition of the first bundle bundle1. bundle2 would be similar:

from flask_webpackext import WebpackBundle
bundle1 = WebpackBundle(
    __name__,
    './modules/module1/static',
    entry={
        'module1-app': './js/module1-app.js',
    },
    dependencies={
        'jquery': '^3.2.1'
    }
)

We now define a WebpackBundleProject to put together all the bundles and integrate with Flask:

from module1 import bundle1
from module2 import bundle2
from flask_webpackext import WebpackBundleProject

myproject = WebpackBundleProject(
    __name__,
    project_folder='assets',
    config_path='src/config.json',
    bundles=[bundle1, bundle2],
)

app.config.update(dict(
    WEBPACKEXT_PROJECT=myproject,
))

# ...

At this point, we can create the application and config.json will be generated. The main difference with the previous config.json is that Flask-WebpackExt now knows about the bundles, and it will add them under the key entry:

{
    "build": {...},
    "entry": {
        "module1": "./js/module1.js",
        "module2": "./js/module2.js"
    }
}

Because entries are now included in the config.json, we can dynamically load them into webpack.config.js:

var config = require('./config')

module.exports = {
    context: config.build.context,
    entry: config.entry,
    ...
};

Manifest

Flask-WebpackExt can load the manifest.json created by webpack when building the assets. You have to add the plugin in the webpack.config.js to generate it:

...
var ManifestPlugin = require('webpack-manifest-plugin');

module.exports = {
    ...
    plugins: [
        new ManifestPlugin({
            fileName: 'manifest.json',
            stripSrc: true,
            publicPath: config.build.assetsURL
        })
    ]
};

The generated manifest.json will look like :

{
"module1.js": "/static/dist/js/module1.4adb22699eb1a5698794.js",
"module2.js": "/static/dist/js/module2.85e58794420201dc1426.js"
}

By default, Flask-WebpackExt will look for the manifest file in WEBPACKEXT_MANIFEST_PATH config variable, and if it exists, it will load the file and make it available inside Jinja templates.

The injected asset in the generated html template will look similar to this:

<script src="/static/dist/mymodule-app.8817a9d4faccbc712aa7.js"></script>

You can read more about it on pywebpack documentation.