Getting the features of webpack to work with phoenix (webpack-dev-server, sass and

Hi there,

I am getting crazy with webpack, because no configuration actually does exactly what I need. I thought that I had it, but now my <%= static_path(@conn, '/images/phoenix.png') %> does not work within sass and I don’t see any way to fix that, so the only option I see is to get some help.

Here is my webpack.config.js:

const path = require('path');
module.exports = function (env, argv) {
  return {
    devtool: env.production ? 'source-maps' : 'eval',
    entry: {
      app: './js/app.js',
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
          },
        },
        {
          test: /\.scss$/,
          use: env.production ? [
            {
              loader: 'file-loader',
              options: {
                name: '[name].css',
                outputPath: '../css',
                publicPath: 'http://localhost:8080/'
              }
            },
            {
              loader: "sass-loader", options: {sourceMap: env.development} // compiles Sass to CSS
            }
          ] : [
            "style-loader",
            "css-loader",
            "sass-loader",
          ]
        }
      ],
    },
    resolve: {
      modules: ['node_modules', path.resolve(__dirname, 'js')],
      extensions: ['.js'],
    },
    devServer: {
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    },

    output: env.production ? {
      path: path.resolve(__dirname, '../priv/static/js'),
      filename: '[name].js',
      publicPath: '/',
    } : {
      path: path.resolve(__dirname, 'public'),
      filename: '[name].js',
      publicPath: 'http://localhost:8080/',
    },
  };
};

And the relevant parts of my package.json:

  "scripts": {
    "deploy": "webpack --mode production --env.production",
    "build": "webpack --mode production --env.production",
    "start": "yarn run watch",
    "watch": "webpack-dev-server --mode development --env development --hot --watch-stdin"
  },

assets/js.app.js has the line require("../css/app.scss") inside, which does work well.

Within templates/layout/app.html.eex, I did the following to get the js in:

  <%= if (Mix.env == :dev) do %>
    <script src='http://localhost:8080/app.js'></script>
  <% else %>
    <script src="<%= static_path(@conn, "/js/app.js") %>"></script>
  <% end %>

(And something similar without the else clause for the production app.css file)

I understand that with the webpack-dev-server, phoenix does not handle the files and can therefore not extrapolate <%= static_path(@conn, '/images/phoenix.png') %> from my .sass files.

Now how can I add sass support to phoenix / webpack with automatic reloading and static_path working? Please give me some hint. I checked: https://github.com/phoenixframework/phoenix, but they don’t seem to tackle that problem at all with their webpack setup.

1 Like

With webpack-dev-server webpack does not save compiled files to your filesystem at all, but just makes them available under the configured domain, so any of your static assets (the ones with static_path) would need to be called like your javascript file. You might want to look into https://github.com/gajus/write-file-webpack-plugin or https://github.com/webpack/webpack-dev-middleware#writetodisk for having css and static assets being written to the filesystem with the dev server.

3 Likes

That’s what I was searching for. I can save the css to disk and use it from there while js is still served from :8080. I think I won’t really need static_path from js. Awesome, ty. Will reply here when it worked.

EDIT: It worked. Here is my final webpack.config.js:

const WriteFilePlugin  = require('write-file-webpack-plugin');
const path = require('path');

module.exports = function (env, argv) {
  return {
    devtool: env.production ? '' : 'inline-source-map',
    entry: {
      app: './js/app.js',
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
          },
        },
        {
          test: /\.scss$/,
          use:[
            {
              loader: 'file-loader',
              options: {
                name: '[name].css',
                outputPath: env.production ? '../css' : '../../priv/static/css/',
                publicPath: 'http://localhost:8080/'
              }
            },
            {
              loader: "sass-loader", options: {sourceMap: env.development} // compiles Sass to CSS
            }
          ]
        }
      ],
    },
    resolve: {
      modules: ['node_modules', path.resolve(__dirname, 'js')],
      extensions: ['.js'],
    },
    devServer: {
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    },
    plugins: [
      new WriteFilePlugin({
        // Write only files that have ".css" extension.
        test: /\.css$/
      })
    ],

    output: env.production ? {
      path: path.resolve(__dirname, '../priv/static/js'),
      filename: '[name].js',
      publicPath: '/',
    } : {
      path: path.resolve(__dirname, 'public'),
      filename: '[name].js',
      publicPath: 'http://localhost:8080/',
    },
  };
};

Upon second thought, I think my problem was a different one. I used the <%= static_path ... inside a css file. Apparently that is not only unnecessary, but doesn’t work at all.

The css and js files should be written as usual. They will be rewritten for production with mix phx.digest and don’t get a different name in development. One can use the webpack-dev-server in development without any issues.
I feel pretty stupid for my previous thoughts, but that’s part of learning…

Well it does if the css is an exs file. ^.^

1 Like