Phoenix 1.3 to 1.4 - Migrating from Brunch to Webpack

Do you recommend any fast guide to migrate brunch (Phoenix 1.3) to webpack (Phoenix 1.4)? Including globals, css, the whole nine yards.
At least to me, this is a huge burden in migration right now.
Can we still use brunch in Phoenix 1.4?

You don’t have to migrate from Brunch to webpack. There were issues with rc version, but as far as I understand these been sorted out, so you can update the app without migrating to webpack, and then migrate to webpack optionally whenever you feel like it.

I agree with @hubertlepicki you can stay on Brunch and just focus on one upgrade at a time.

If you want to jump to webpack with SCSS support, then here’s where I landed in my webpack config below. This isn’t a fast guide, but you can use below as a point of comparison.

const path = require('path')
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')
const devMode = process.env.NODE_ENV !== 'production'

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: devMode }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    app: ['./js/app.js', './scss/app.scss']
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, '../priv/static/js')
  },
  devtool: devMode ? 'source-map' : undefined,
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.(scss|sass|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: devMode
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: devMode
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: devMode
            }
          },
          {
            loader: 'import-glob-loader'
          }
        ]
      },
      {
        test: /\.svg$/,
        oneOf: [
          {
            use: {
              loader: 'svg-inline-loader',
              options: {
                removeSVGTagAttrs: false
              }
            },
            issuer: [{ not: [{ test: /\.css$/i }] }]
          },
          {
            use: {
              loader: 'file-loader'
            },
            issuer: [{ test: /\.css$/i }]
          }
        ]
      },
      {
        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: '../fonts'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/[name].css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
  ]
})

This is in the ./assets/.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "targets": {
          "browsers": "Firefox ESR, Chrome >= 49, Safari >= 10, iOS >= 10, last 1 version"
        },
        "useBuiltIns": "entry"
      }
    ]
  ],
  "plugins": []
}

This is in the package.json:

  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "babel-eslint": "^9.0.0",
    "babel-loader": "^8.0.0",
    "copy-webpack-plugin": "^4.5.0",
    "css-loader": "^0.28.10",
    "eslint": "^5.4.0",
    "eslint-plugin-jsx-a11y": "^6.1.1",
    "eslint-plugin-prettier": "^2.6.2",
    "eslint-plugin-react": "^7.11.1",
    "file-loader": "^2.0.0",
    "import-glob-loader": "^1.1.0",
    "mini-css-extract-plugin": "^0.4.0",
    "node-sass": "^4.9.3",
    "optimize-css-assets-webpack-plugin": "^4.0.0",
    "postcss-import": "^12.0.0",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.0.10",
    "prettier": "^1.14.2",
    "sass-loader": "^7.1.0",
    "svg-inline-loader": "^0.8.0",
    "uglifyjs-webpack-plugin": "^1.2.4",
    "webpack": "4.4.0",
    "webpack-cli": "^2.0.10"
  },

then in app.html.eex

<!-- within <head> -->
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>

<!-- at the bottom of <body> -->
<script async type="text/javascript" src="<%= Routes.static_path(@conn, "/js/vendor.js") %>"></script>

This supports SVGs, fonts, SCSS/SASS, and modern JavaScript.

This config above includes PostCSS and Node SASS, but the way most people actually use SCSS should be mostly compatible with PostCSS. In my project, I can actually get away with dropping node-sass and the sass-loader, renaming my *.scss files to *.css, and compile faster.

I don’t have any globals, so I can’t provide a comparison there, but it looks like you’ll want the https://webpack.js.org/plugins/provide-plugin/

Hopefully that helps.

1 Like

I will try to use Brunch with Phoenix 1.4 and will post here.

The ideia is something like this. (Copy files…)

BRUNCH

  plugins: {
    copycat: {
      "fonts": [
        "node_modules/notosans-fontface/fonts",
        "node_modules/grapesjs/dist/fonts"
      ],
      "flags": [
        "node_modules/flag-icon-css/flags"
      ]
    }
  },

WEBPACK

const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = (env, options) =>; ({
      plugins: [
        new CopyWebpackPlugin([
          { from: 'static/', to: '../' },
          { from: 'node_modules/notosans-fontface/fonts', to: '../fonts' },
          { from: 'node_modules/flag-icon-css/flags', to: '../flags' }
        ]),
1 Like

I just confirmed that we can still use the same BRUNCH from previous (<= 1.3) inside Phoenix 1.4.

Other than correct “Routes.my_path” shortcut mentioned in README, we need also to comment in dev.exs watcher:

config :myapp, MyAppWeb.Endpoint,
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
    watchers: [
      # node: [
      #   "node_modules/webpack/bin/webpack.js",
      #   "--mode",
      #   "development",
      #   "--watch-stdin",
      #   cd: Path.expand("../assets", __DIR__)
      # ]
      node: [
        "node_modules/brunch/bin/brunch", "watch", "--stdin",
        cd: Path.expand("../assets", __DIR__)
      ]
    ]
3 Likes