Phoenix 1.4, Webpack 4 and Bulma, Bootstrap 4 SASS

Bulma

{
“repository”: {},
“license”: “MIT”,
“scripts”: {
“deploy”: “webpack --mode production --progress”,
“watch”: “webpack --watch-stdin --progress --color”
},
“dependencies”: {
“animate.css”: “^3.5.2”,
“bulma”: “^0.6.2”,
“bulma-extensions”: “^1.0.0”,
“jquery”: “^3.3.1”,
“phoenix”: “file:…/…/…/deps/phoenix”,
“phoenix_html”: “file:…/…/…/deps/phoenix_html”
},
“devDependencies”: {
“babel-core”: “^6.26.0”,
“babel-loader”: “^7.1.3”,
“babel-preset-env”: “^1.6.1”,
“copy-webpack-plugin”: “^4.5.0”,
“css-loader”: “^0.28.10”,
“exports-loader”: “^0.7.0”,
“file-loader”: “^1.1.11”,
“mini-css-extract-plugin”: “^0.4.0”,
“node-sass”: “^4.9.0”,
“optimize-css-assets-webpack-plugin”: “^4.0.0”,
“postcss-loader”: “^2.1.5”,
“sass-loader”: “^7.0.1”,
“style-loader”: “^0.21.0”,
“uglifyjs-webpack-plugin”: “^1.2.4”,
“webpack”: “4.4.0”,
“webpack-cli”: “^2.0.10”
}
}

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 webpack = require(“webpack”);

const staticDir = path.join(__dirname, ‘.’);
const destDir = path.join(__dirname, ‘…/priv/static’);
const publicPath = ‘/’;

module.exports = (env, options) => ({
entry: {
app: [
staticDir + “/css/app.scss”,
staticDir + “/js/app.js”
]
},
output: {
path: destDir,
filename: “js/[name].js”,
publicPath
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: ‘babel-loader’
}
},
// Adds font implications
{
test: /.(css|scss|sass)/,
use: [ ‘style-loader’, MiniCssExtractPlugin.loader, ‘css-loader’, ‘sass-loader’]
},
{
test: /.(eot|svg|ttf|woff|woff2)(?\S*)?$/,
loader: “file-loader”
},
{
test: /.(png|jpe?g|gif|svg)(?\S*)?$/,
loader: “file-loader”,
query: {
name: “[name].[ext]?[hash]”
}
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: ‘./css/app.css’ }),
new CopyWebpackPlugin([{ from: “./static” }]),
new webpack.ProvidePlugin({
$: “jquery”,
jQuery: “jquery”,
“window.jQuery”: “jquery”
}),

]
});

if (process.env.NODE_ENV === “production”) {
module.exports.devtool = “#cheap-module-eval-source-map”;

module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
“process.env”: {
NODE_ENV: “‘production’”
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new OptimizeCssAssetsPlugin()
]);
}

Bootstrap 4

{
“repository”: {},
“license”: “MIT”,
“scripts”: {
“deploy”: “webpack --mode production --progress”,
“watch”: “webpack --watch-stdin --progress --color”
},
“dependencies”: {
“animate.css”: “^3.5.2”,
“bootstrap”: “4.0.0-alpha.6”,
“bootswatch”: “https://github.com:/thomaspark/bootswatch.git#v4.0.0-beta.2”,
“phoenix”: “file:…/…/…/deps/phoenix”,
“phoenix_html”: “file:…/…/…/deps/phoenix_html”
},
“devDependencies”: {
“babel-core”: “^6.26.0”,
“babel-loader”: “^7.1.3”,
“babel-preset-env”: “^1.6.1”,
“copy-webpack-plugin”: “^4.5.0”,
“css-loader”: “^0.28.10”,
“exports-loader”: “^0.7.0”,
“file-loader”: “^1.1.11”,
“mini-css-extract-plugin”: “^0.4.0”,
“node-sass”: “^4.9.0”,
“optimize-css-assets-webpack-plugin”: “^4.0.0”,
“postcss-loader”: “^2.1.5”,
“sass-loader”: “^7.0.1”,
“style-loader”: “^0.21.0”,
“uglifyjs-webpack-plugin”: “^1.2.4”,
“webpack”: “4.4.0”,
“webpack-cli”: “^2.0.10”
}
}

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 webpack = require(“webpack”);

const staticDir = path.join(__dirname, ‘.’);
const destDir = path.join(__dirname, ‘…/priv/static’);
const publicPath = ‘/’;

module.exports = (env, options) => ({
entry: {
app: [
staticDir + “/css/animate.css”,
staticDir + “/js/app.js”,
staticDir + “/js/bootstrap/alert.js”,
staticDir + “/js/bootstrap/button.js”,
staticDir + “/js/bootstrap/carousel.js”,
staticDir + “/js/bootstrap/collapse.js”,
staticDir + “/js/bootstrap/dropdown.js”,
staticDir + “/js/bootstrap/modal.js”,
staticDir + “/js/bootstrap/popover.js”,
staticDir + “/js/bootstrap/scrollspy.js”,
staticDir + “/js/bootstrap/tab.js”,
staticDir + “/js/bootstrap/tooltip.js”,
staticDir + “/js/bootstrap/util.js”]
},
output: {
path: destDir,
filename: “js/[name].js”,
publicPath
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: ‘babel-loader’
}
},
// Adds font implications
{
test: /.(css|scss|sass)/,
use: [ ‘style-loader’, MiniCssExtractPlugin.loader, ‘css-loader’, ‘sass-loader’]
},
{
test: /.(eot|svg|ttf|woff|woff2)(?\S*)?$/,
loader: “file-loader”
},
{
test: /.(png|jpe?g|gif|svg)(?\S*)?$/,
loader: “file-loader”,
query: {
name: “[name].[ext]?[hash]”
}
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: ‘./css/app.css’ }),
new CopyWebpackPlugin([{ from: “./static” }]),
new webpack.ProvidePlugin({
$: “jquery”,
jQuery: “jquery”,
“window.jQuery”: “jquery”,
Tether: “tether”,
“window.Tether”: “tether”,
Alert: “exports-loader?Alert!bootstrap/js/dist/alert”,
Button: “exports-loader?Button!bootstrap/js/dist/button”,
Carousel: “exports-loader?Carousel!bootstrap/js/dist/carousel”,
Collapse: “exports-loader?Collapse!bootstrap/js/dist/collapse”,
Dropdown: “exports-loader?Dropdown!bootstrap/js/dist/dropdown”,
Modal: “exports-loader?Modal!bootstrap/js/dist/modal”,
Popover: “exports-loader?Popover!bootstrap/js/dist/popover”,
Scrollspy: “exports-loader?Scrollspy!bootstrap/js/dist/scrollspy”,
Tab: “exports-loader?Tab!bootstrap/js/dist/tab”,
Tooltip: “exports-loader?Tooltip!bootstrap/js/dist/tooltip”,
Util: “exports-loader?Util!bootstrap/js/dist/util”,
}),

]
});

if (process.env.NODE_ENV === “production”) {
module.exports.devtool = “#cheap-module-eval-source-map”;

module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
“process.env”: {
NODE_ENV: “‘production’”
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new OptimizeCssAssetsPlugin()
]);
}

2 Likes

Were you able to get live reloading working for the css/scss? I’ve hit a wall and just reverted back to my webpack3 setup that works flawlessly.

I dont think its working actually

I see the 1.4 book is out in beta and the paper edition is due for September, so I guess things are going to be settling down next few months

I actually think a few things are broken actually, not suprising

to be clear, I am having problems with live reloading and I think sockets, bit hard to unpack my bugs, core code changes, and core code bugs

I will probably pull myself away from this for a few weeks and see what happens with 1.4 etc

Adding sass/scss to a Phoenix 1.4 project involves the following:

  1. Adding node-sass and sass-loader to assets/package.json
  2. Renaming assets/css/app.css to assets/css/app.scss
  3. Adding the sass-loader to the css file section in webpack.config.js and changing the extension to .scss
  4. changing import css from "../css/app.css" to import css from "../css/app.scss" in assets/js/app.js

Here’s a diff of the altered files:

package.json:

     "copy-webpack-plugin": "^4.5.0",
     "css-loader": "^0.28.10",
     "mini-css-extract-plugin": "^0.4.0",
+    "node-sass": "^4.9.0",
     "optimize-css-assets-webpack-plugin": "^4.0.0",
+    "sass-loader": "^7.0.1",
     "uglifyjs-webpack-plugin": "^1.2.4",
     "webpack": "4.4.0",
     "webpack-cli": "^2.0.10"

webpack.config.js:

       {
-        test: /\.css$/,
-        use: [MiniCssExtractPlugin.loader, 'css-loader']
+        test: /\.scss$/,
+        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
       }
     ]
   },

assets/js/app.js

 // We need to import the CSS so that webpack will load it.
 // The MiniCssExtractPlugin is used to separate it out into
 // its own CSS file.
-import css from "../css/app.css"
+import css from "../css/app.scss"

Live reloading works for this setup.

27 Likes

Thanks, I will give this a try - but what are you using in the package.json scripts to start the dev watcher? I was able to have webpack extract the CSS, but then the changes in files weren’t triggering the reload (I was using phoenix 1.3 though no sure it would matter?)

Thanks i upgraded webpack3 to webpack4 without any problem.

When using Phoenix 1.4 (currently with the master branch) generating a new application will use:

  watchers: [node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin",
                    cd: Path.expand("../assets", __DIR__)]]
4 Likes

Thank you @gazler, I will give this a try

thanks very much. works for me on Brunch SASS etc

:grin:

it works! the problem was I was using yarn to launch the script here it seems - although it looks like it should work (I probably misconfigured something somewhere else…) thx

correction, webpack works with Bulma AND webpack (mistyped Brunch)

Don’t use yarn: https://mixmax.com/blog/to-yarn-and-back-again-npm

1 Like

There should be a js.flavour-of-the-month.com website were we would check what was the right tool in a certain month to use - I won’t probably run into the issues mentioned there and I was quite happy now that I found out I could create offline mirrors for the yarn.lock (which means able to do fully reproducible build without downloading packages, nor having to commit the whole node_modules to the repo).

Perhaps I need to take yours and perry’s advice and just build my own pipeline but it’s such a grunt…

1 Like

I’ve never used yarn publish, but in 2 years of using yarn, I’ve never encountered any of the problems the author mentioned, but I’ve been burned by npm several times.

1 Like

Do you know how to configure the app to export multiple css files? For example, if I wanted a vendor.css and app.css?

Just add a reference to your CSS file.

module.exports = {
  entry: {
    app: ['./assets/App.js', './assets/App.scss'],
    vendor: ['react', 'react-dom', './assets/Vendor.scss']
  },
  // ...
}

However, I don’t see any reason to split your CSS.

2 Likes

Hi everyone, I am also trying to install Bulma for a 1.4-dev project.

Here are the steps I followed (thanks to @niccolox ox and @Gazler) :

I added to my dependencies in package.json (left Jquery in devDependencies):

  • “animate.css”: “^3.5.2”,
  • “bulma”: “^0.6.2”,
  • “bulma-extensions”: “^1.0.0”,

I added to my devDependencies in packages.json:

  • “node-sass”: “^4.9.0”,

  • “sass-loader”: “^7.0.1”,

  • Added node-sass and sass-loader to assets/package.json

  • Renamed assets/css/app.css to assets/css/app.scss

  • Added the sass-loader to the css file section in webpack.config.js and changing the extension to .scss

  • changed import css from "../css/app.css" to import css from "../css/app.scss" in assets/js/app.js

  • ran npm i in /myproject/assets (got no error)

but I Bulma isn’t working.

Did I forget something ?

Did you ran npm run build (or whatever you named it) to build your assets with Webpack? got any errors?

It’d helpful if you can share your webpack.config.js with us. Also if you’re not referencing “css” inside your JavaScript, keep it simple:

import '../css/app.scss';

1 Like