Pages

Tuesday, February 20, 2018

Building daycare website [Part 5]: Configure WebPack to build a bundle for the application code and assets

In the last post of this series I wrote about the webpack.config.vendor.js file that is auto-generated by the dotnet new template that configures how the vendor bundle is created. In this post I will look at the webpack.config.js file that creates the application bundle. The full contents are below, followed by an explanation of each major part:

const path = require('path');
const webpack = require('webpack');
const { AureliaPlugin } = require('aurelia-webpack-plugin');
const bundleOutputDir = './wwwroot/dist';

module.exports = (env) => {
    const isDevBuild = !(env && env.prod);
    return [{
        stats: { modules: false },
        entry: { 'app': 'aurelia-bootstrapper' },
        resolve: {
            extensions: ['.ts', '.js'],
            modules: ['ClientApp', 'node_modules'],
        },
        output: {
            path: path.resolve(bundleOutputDir),
            publicPath: 'dist/',
            filename: '[name].js'
        },
        module: {
            rules: [
                { test: /\.ts$/i, include: /ClientApp/, use: 'ts-loader?silent=true' },
                { test: /\.html$/i, use: 'html-loader' },
                { test: /\.css$/i, use: isDevBuild ? 'css-loader' : 'css-loader?minimize' },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
            ]
        },
        plugins: [
            new webpack.DefinePlugin({ IS_DEV_BUILD: JSON.stringify(isDevBuild) }),
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./wwwroot/dist/vendor-manifest.json')
            }),
            new AureliaPlugin({ aureliaApp: 'boot' })
        ].concat(isDevBuild ? [
            new webpack.SourceMapDevToolPlugin({
                filename: '[file].map', // Remove this line if you prefer inline source maps
                moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]')  // Point sourcemap entries to the original file locations on disk
            })
        ] : [
            new webpack.optimize.UglifyJsPlugin()
        ])
    }];
}

  • Lines #6 and #7: the exported function is called by WebPack and receives the command line arguments (if any). This is used later on to perform small tweaks depending if we are creating debug or production bundles.
  • Line #10 declares a single bundle named ‘app’ and declares the entry point as ‘aurelia-bootstraper’. This is a separate npm module from the aurelia-framework, which is in charge of starting the application on the client. This is the module that is in charge of locating the ‘aurelia-app’ element in the page as described on this previous post. In short, all the Aurelia npm modules are included in the ‘vendor’ bundle, except for this one which is included in the ‘app’ bundle.
  • Line #11 declares both .ts and .js as known extensions, so that our code can issue import statements without needing to define an extension. Additionally it declares that WebPack should search for modules in the ‘node_modules’ directory (the default) and in the ‘ClientApp’ directory (which is where all our client side code will live inside the project directory).
  • Line #15 defines that all the assets will be written to the ‘www/dist’ folder and the bundle will be named ‘app.js’.
  • Line #20 defines how to load all the modules that are not javascript files:
    • All typescript files will use the ‘ts-loader’ which will compile the files down to javascript using the ‘tsconfig.json’ file that is included on the root of the project.
    • All html files will use the ‘html-loader’, which can process html files so that any images loaded by the <img> tags are loaded using a module.
    • Related to the previous bullet, all images use the ‘url-loader’, which can automatically move the image to the output directory and depending on the image size it can inline the content.
    • Finally, the CSS files use the ‘css-loader’ and depending if we are running a debug or production build, it will optionally minimize the CSS.
  • Line #28 defines all the plugins to use:
    • First it uses the DefinePlugin to write a global variable named ‘IS_DEV_BUILD’ so that client side code can read it to make decisions whether it is running on debug or production.
    • Then it uses the DllReferencePlugin to consume the manifest that was created as part of the vendor bundle, as described on my previous post.
    • Next comes the AureliaPlugin… which is shrouded in mystery. I don’t know exactly what this plugin does. My guess is that this is how we tell WebPack where to start discovering the application modules. Remember that the entry point was defined just as the ‘aurelia-bootstrapper’ module, so here we tell the plugin that our code graph begins in a module called ‘boot’.
    • Then it checks if this is running in debug mode and optionally uses the SourceMapDevToolPlugin to write script maps to the output folder to enable source code stepping from browser.
    • Lastly, if this is running in production mode, it will minify and compress the generated javascript bundle.

Phew. At this point we are done with WebPack, we have the app running and can start to develop the application. However, before doing that let’s make a big pit stop and talk about how the application is going to be deployed.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.