DEV Community

Dharmi Kumbhani
Dharmi Kumbhani

Posted on

Creating the webpack required for three.js

Step 1 - Basic setup:

  1. Go to directory
  2. npm init → Initialise npm
  3. create src folder and put script.js, index.html and style.css in it.

in index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document Name</title>
</head>
<body>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

in script.js add:

import ./style.css
Enter fullscreen mode Exit fullscreen mode

in style.css add:

* {
    margin: 0;
    padding: 0;
Enter fullscreen mode Exit fullscreen mode

PS: We adding just the basic boiler plate.

Step 2 - Webpack Setup

  1. yarn add webpack webpack-cli in CLI to add webpack dependencies: webpack and webpack-cli
  2. Create bundler folder at the same level as src
  3. Create webpack.common.js in it.
  4. Removie "main" from package.json → removing the entry point from package.json to avoid clashes.

Step 3 - Webpack Configuration:

  • Creating entry and output -> in webpack.common.js
const path = require('path');

module.exports = {
    entry: path.resolve(__dirname, '../src/script.js'),
    output:
    {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, '../dist')
    },
        devtool: 'source-map',
}
Enter fullscreen mode Exit fullscreen mode
  • For testing in package.json add the following scripts:
"scripts": {
    "test": "webpack --config ./bundler/webpack.common.js",
},
Enter fullscreen mode Exit fullscreen mode

here we are specifing that instead of having a webpack.config.js file in our root folder we have seperated it on to a bundler folder.

You can run npm run test any time in between to see the output in the dist folder

Step 4 - Installing loaders, plugins and writing corresponding rules for webpack configuration:

  • How to write rules?
module.exports = {
    entry: path.resolve(__dirname, '../src/script.js'),
    output:
    {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, '../dist')
    },
        devtool: 'source-map',
    module: {
        rules: [
            
        ]
    }
};
Enter fullscreen mode Exit fullscreen mode
  • Adding Plugins:
  1. Require them at the top of javascript file:
  2. Declaring the plugin: Add the plugin key as a property of the modules.export object and its value is an array where we declare the plugins along with configurations (if any)
module.exports = {
    entry: path.resolve(__dirname, '../src/script.js'),
    output:
    {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, '../dist')
    },
        devtool: 'source-map',
        plugins:[
        ...
    ],
    module: {
        rules: [
            
        ]
    }
};
Enter fullscreen mode Exit fullscreen mode
  • HTML support in webpack: yarn add html-webpack-plugin: This is a webpack plugin that simplifies creation of HTML files to serve your webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation.

Require the plugin:

const HtmlWebpackPlugin = require('html-webpack-plugin')
Enter fullscreen mode Exit fullscreen mode

Add Plugin:

plugins:[
    new HtmlWebpackPlugin({
        template: path.resolve(__dirname, '../src/index.html'),
        minify: true
    }),
],
Enter fullscreen mode Exit fullscreen mode

yarn add html-loader
Append this rule in the rules array

//HTMl:
{
    test: /\.(html)$/,
    use: ['html-loader']
},
Enter fullscreen mode Exit fullscreen mode

Doing this will now output an html file as well in the dist folder.

  • JS support in webpack:

yarn add @babel/core
yarn add @babel/preset-env
yarn add babel-loader

(Currently we are adding them as user dependencies but we can change it to dev dependencies)

//JS
{
    test: /\.js$/,
    exclude: /node_modules/,
    use:[
        'babel-loader'
    ]
},
Enter fullscreen mode Exit fullscreen mode
  • CSS support in webpack:

yarn add mini-css-extract-plugin

yarn add css-loader

  • Require the dependency at the top of the page.
const MiniCSSExtractPlugin = require('mini-css-extract-plugin')
Enter fullscreen mode Exit fullscreen mode
  • Declaring the plugin
plugins:
[
    new MiniCSSExtractPlugin()
],
Enter fullscreen mode Exit fullscreen mode
  • Add the following rule:
// CSS
{
    test: /\.css$/,
    use:
    [
        MiniCSSExtractPlugin.loader,
        'css-loader'
    ]
},
Enter fullscreen mode Exit fullscreen mode

More Information: Learn Webpack Pt. 8: Html-loader, File-loader, & Clean-webpack

Step 5 - Adding file-loader other loaders for handeling fonts and images

  • yarn add file-loader
  • Add the following rules for working with images and fonts being used in the app.
// Images
{
    test: /\.(jpg|png|gif|svg)$/,
    use:
    [
        {
            loader: 'file-loader',
            options: {
                outputPath: 'assets/images/'
            }
        }
    ]
},

// Fonts
{
    test: /\.(ttf|eot|woff|woff2)$/,
    use:
    [
        {
            loader: 'file-loader',
            options: {
                outputPath: 'assets/fonts/'
            }
        }
    ]
},
Enter fullscreen mode Exit fullscreen mode

In options we are specifying that after building the app, put images, fonts in the assets folder.

When we run a npm run build to create a production ready distribution folder, webpack will browse through your code and as soon as it finds something like image or fonts, it will automatically create an asset folder inside which there will be an image folder to store that imported image and there will be an font folder in assets created to store its corresponding font.

Step 6 - Adding copy-webpack-plugin:**

Copies individual files or entire directories, which already exist, to the build directory.

The idea here is you will have a static folder in dev where you will store all your fonts, images, etc, and in prod build you want this to be all copied in the build folder.

Without this plugin, in the final production folder that is created, only those images will be bundles that are imported in the javascript.

Also make sure from now on you have atleast one image inside the static folder, else it will trown an error.

The structure inside the static folder will be replicated in the dist (production build) folder.

Make sure you create a static folder first.

  • yarn add copy-webpack-plugin
  • Require/import it:
const CopyWebpackPlugin = require('copy-webpack-plugin')
Enter fullscreen mode Exit fullscreen mode
  • Declare it:
plugins:[
    new CopyWebpackPlugin({
        patterns: [
            { from: path.resolve(__dirname, '../static') }
        ]
    }),
    ....
],
Enter fullscreen mode Exit fullscreen mode

Here, From now on make sure that there is a test image in your static folder else building it (npm run test) will result in an error.

Step 7 - Creating Dev Configuration: to start a live server.

We will use webpack.common.js a commn configuration that will be used by for the dev and prod configurations:

In Dev server, files get build in memory and are destroyed as soon as the server id destroyed.

  1. Create a webpack.dev.js file in bundler folder
  2. Importing commonConfiguration from webpack.common.js To import we will need webpack-merge module
  3. yarn add webpack-merge
  4. Adding basic things to webpack.dev.js
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
module.exports = merge(
    commonConfiguration,
    {
        mode: 'development'
    }
)
Enter fullscreen mode Exit fullscreen mode
  1. Adding dev script in package.json
"scripts": {
  "test": "webpack --config ./bundler/webpack.common.js",
  "dev": "webpack serve --config ./bundler/webpack.dev.js",
},
Enter fullscreen mode Exit fullscreen mode

here the serve flag will make it live reload, but before running it, there are still a few things we need to add to serve this app.

  1. Adding Server Dependencies.
  2. yarn add portfinder-sync > Find an open port synchronously.
  3. yarn add D webpack-dev-server

    Dev Server for web applications, ideal for buildless es module workflows. Optionally supports simple code transformations.
    This is added so now the webpack 'serve' command is recognised

  4. Updating wepack dev:

  5. Importing required modules:

const ip = require('internal-ip')
const portFinderSync = require('portfinder-sync')
Enter fullscreen mode Exit fullscreen mode
  • Function that prints local domain (where server is running) names distinctly:
const infoColor = (_message) => {
    return `\u001b[1m\u001b[34m${_message}\u001b[39m\u001b[22m`
}
Enter fullscreen mode Exit fullscreen mode
  • Adding devServer key to module exports:
devServer: {
  host: '0.0.0.0',
  port: portFinderSync.getPort(8080),
  contentBase: './dist',
  watchContentBase: true,
  open: true,
  https: false,
  useLocalIp: true,
  disableHostCheck: true,
  overlay: true,
  noInfo: true,
  after: function(app, server, compiler)
  {
      const port = server.options.port
      const https = server.options.https ? 's' : ''
      const localIp = ip.v4.sync()
      const domain1 = `http${https}://${localIp}:${port}`
      const domain2 = `http${https}://localhost:${port}`

      console.log(`Project running at:\n  - ${infoColor(domain1)}\n  - ${infoColor(domain2)}`)
  }
}
Enter fullscreen mode Exit fullscreen mode

Try running npm run dev and you should see a live server being sprung up! and this is now live updating with all the changes you make!

Step 8 - Creating build Configuration: to creat a production ready dist folder.

  1. Creating a production configuration file in bundlers folder: bundlers → webpack.prod.js
  2. Adding basic configurations to webpack.prod.js:
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')

module.exports = merge(
    commonConfiguration,
    {
        mode: 'production',
    }
)
Enter fullscreen mode Exit fullscreen mode

It will use the same merge and commonConfiguration as dev configuration. We will just change the mode.

  1. Adding Plugin:
  2. yarn add clean-webpack-plugin > A webpack plugin to remove/clean your build folder(s). It makes sure that there is no dist folder.
  • Importing and Declating Plugin in the webpack production configuration:
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = merge(
    commonConfiguration,
    {
        mode: 'production',
        plugins:
        [
            new CleanWebpackPlugin()
        ]
    }
)
Enter fullscreen mode Exit fullscreen mode
  1. Add scripts to package.json for a build command.
"scripts": {
  "test": "webpack --config ./bundler/webpack.common.js",
  "dev": "webpack serve --config ./bundler/webpack.dev.js",
  "build": "webpack --config ./bundler/webpack.prod.js"
},
Enter fullscreen mode Exit fullscreen mode

And that should be it, try running npm run build and check the dist folder that would have been created.

Step 9 - Adding raw-loader for loading shaders:

  • yarn add raw-loader
  • Webapack rules:
// Shaders
{
    test: /\.(glsl|vs|fs|vert|frag)$/,
    exclude: /node_modules/,
    use: [
        'raw-loader'
    ]
}
Enter fullscreen mode Exit fullscreen mode

Resources:
Webpack - A Detailed Introduction - Smashing Magazine
Notion version of this blogpost
A lot of the webpack configurations were inspired by Bruno Simons template, which he uses for his fabulous course - Three.js Journey

Discussion (2)

Collapse
jonrandy profile image
Jon Randy

F**k me - something went badly wrong with web development at some point. The amount of 'convenient' tooling required for seemingly every 'normal' project these days is truly ludicrous

Collapse
jonrandy profile image
Jon Randy

(This is in no way a criticism of your post BTW)