How Webpack 4 works with Phoenix 1.4

Hello all, I want to understand better how Webpack works with Phoenix 1.4.

I’ve seen conflicting implementations on Github, so would be great to clarify how it interacts with Phoenix.

Here’s the default Webpack.config.js

    const path = require('path');
    const glob = require('glob');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
    const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    const CopyWebpackPlugin = require('copy-webpack-plugin');

    module.exports = (env, options) => ({
      optimization: {
        minimizer: [
          new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
          new OptimizeCSSAssetsPlugin({})
        ]
      },
      entry: {
          './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
      },
      output: {
        filename: 'app.js',
        path: path.resolve(__dirname, '../priv/static/js')
      },
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader'
            }
          },
          {
            test: /\.css$/,
            use: [MiniCssExtractPlugin.loader, 'css-loader']
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({ filename: '../css/app.css' }),
        new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
      ]
    });

One thing big thing I noticed is that this config does not specify the mode (ie. development or production). Therefore it will default to production according to the Webpack docs.

Is this on purpose? If so, why?

More specifically, how does this impact what packages should be added as DevDependencies vs general dependencies in Webpack?

In setting up Bootstrap 4, here’s how I set up my dependencies in Webpack:

    {
      "repository": {},
      "license": "MIT",
      "scripts": {
        "deploy": "webpack --mode production",
        "watch": "webpack --mode development --watch"
      },
      "dependencies": {
        "autoprefixer": "^9.3.1",
        "bootstrap": "^4.1.3",
        "coffeescript": "^2.3.2",
        "jquery": "^3.3.1",
        "node-sass": "^4.10.0",
        "phoenix": "file:../deps/phoenix",
        "phoenix_html": "file:../deps/phoenix_html",
        "popper.js": "^1.14.5",
        "postcss-loader": "^3.0.0",
        "sass-loader": "^7.1.0"
      },
      "devDependencies": {
        "@babel/core": "^7.0.0",
        "@babel/preset-env": "^7.0.0",
        "babel-loader": "^8.0.0",
        "copy-webpack-plugin": "^4.5.0",
        "css-loader": "^0.28.10",
        "mini-css-extract-plugin": "^0.4.0",
        "optimize-css-assets-webpack-plugin": "^4.0.0",
        "uglifyjs-webpack-plugin": "^1.2.4",
        "webpack": "4.4.0",
        "webpack-cli": "^2.0.10"
      }
    }

Basically, I added everything as dependencies and nothing as DevDependencies. Is this right? My hunch is no. My hunch is that everything should be a DevDependency, as Webpack will compile the assets and Phoenix will then compile the app.

My hunch is that the only two dependencies should be Phoenix and Phoenix_html. Even Jquery should be a DevDependency (or should it just be installed in node_modules and then just imported in Phoenix’s App.js without listing it as a dependency?).

So many potential conflicts here.

Would be great to get more insight into this!

2 Likes

The mode is specified in the arguments for the watcher in config/dev.exs

No. You ideally want anything related to building in your devDependencies. I.e. your node-sass, autoprefixer, etc. should ideally be in devdeps. JQuery is a runtime dependent on your web app (I.e it’s not compiled away) so it should be a regular dep imho.

Erm. No.

You want running npm install to get all your deps. With them not in your package.json that’s not gonna work when you install on a new dev machine or VM, and they won’t be present in your node_modules

I think you should read the getting started of webpack and nodejs package management. It may help.

3 Likes

Thanks for the reply!

Tell me if this looks right:

{
  "repository": {},
  "license": "MIT",
  "scripts": {
    "deploy": "webpack --mode production",
    "watch": "webpack --mode development --watch"
  },
  "dependencies": {
    "bootstrap": "^4.1.3",
    "jquery": "^3.3.1",
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html",
    "popper.js": "^1.14.5",
  },
  "devDependencies": {
    "autoprefixer": "^9.3.1",
    "coffeescript": "^2.3.2",
    "@babel/core": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "babel-loader": "^8.0.0",
    "copy-webpack-plugin": "^4.5.0",
    "css-loader": "^0.28.10",
    "mini-css-extract-plugin": "^0.4.0",
    "node-sass": "^4.10.0",
    "optimize-css-assets-webpack-plugin": "^4.0.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "uglifyjs-webpack-plugin": "^1.2.4",
    "webpack": "4.4.0",
    "webpack-cli": "^2.0.10"
  }
}

I added all the loaders and coffeescript to the DevDependencies.

I’m still wondering why we need to have Bootstrap, Jquery, and Popper.js, in the dependencies list if they are imported manually inside app.sass and app.js. If I am understanding this right, Webpack or Nodejs is the mechanism by which they are imported into the app.sass and app.js? So if they are not listed in dependencies then they won’t be found?

Thanks again.

1 Like

These are 2 different things…

The package.json describes where to get and how to install jquery, bootstrap, etc. from npm. If you’ve got the files in your assets directory outside of node_modules then it’s not required in your package.json.

The import in your app.js and app.scss brings files into your project from either node modules (npm, etc.), or other files in your assets.

2 Likes