33

I'm having trouble using webpack instead of Codekit v1.9.3. I started working to move from CodeKit to Grunt and Gulp, and then learned about webpack which sounds very cool. I just can't seem to get it working correctly.

"Like Codekit" means I can:

  • Write javascript with the coffeescript syntax
  • Have all script source files and libraries minified / uglified and combined into one file
  • Selectively include components of the bootstrap-sass (scss) framework as needed
  • Maintain a small file with bootstrap customizations via sass variables, like $brand-primary
  • Use webpack --watch to compile both scripts and styles automatically when they are changed
  • End up with one css file and one script file that can be included with a stylesheet and script tag.

Codekit Project Setup

Bower resources:

I'm currently storing these globally, outside of the project:

~/bower_components/twbs-bootstrap-sass/vendor/assets/stylesheets

Because CodeKit supports compass, I've got this in my config.rb file:

add_import_path "~/bower_components/twbs-bootstrap-sass/vendor/assets/stylesheets"

Project Structure

js/fancybox.js
js/main.js               <-- currently the compiled js 'output' file
js/main.coffee

css/styles.css           <-- currently the compiled css 'output' file

scss/styles.scss
scss/modules/_bootstrap-customizations.scss
scss/modules/_typography.scss
scss/partials/_header.scss
scss/partials/_footer.scss

Contents of styles.scss

@import "modules/bootstrap-customizations";  # local customizations
@import "bootstrap/variables";
@import "bootstrap/mixins";
...                                          # load bootstrap files as required
@import "bootstrap/wells";

System Setup:

  • system: OS X 10.9
  • node - v0.10.32
  • npm - v2.1.7
  • zsh - zsh 5.0.7 (x86_64-apple-darwin13.4.0)

node was installed with homebrew's brew install node and seems to be working fine otherwise.


What I've Tried

I've read over these pages:

I've attempted to create a webpack.config.js file several times, my latest attempt was several versions of this:

module.exports = {
    entry: [
    "./node_modules/bootstrap-sass-webpack!./bootstrap-sass.config.js",
    "./js/main.coffee"
    ],
    output: {
        path: __dirname,
        filename: "main.js"
    },
    module: {
        loaders: [
        { test: /\.css$/, loader: "style!css" }
        ]
    }
};

Webpack Error

When I run webpack I get this:

ERROR in ./~/bootstrap-sass-webpack/~/css-loader!/Users/cwd/~/sass-loader!./~/bootstrap-sass-webpack/bootstrap-sass-styles.loader.js!./bootstrap-sass.config.js
stdin:1: file to import not found or unreadable: "~bootstrap-sass/assets/stylesheets/bootstrap/variables

NPM Error

I get an error when attempting to npm install bootstrap-sass, and not had any luck when searching for a solution. I'm not even sure I need this module.

npm ERR! Darwin 13.4.0
npm ERR! argv "node" "/usr/local/bin/npm" "install" "bootstrap-sass"
npm ERR! node v0.10.32
npm ERR! npm  v2.1.7
npm ERR! code EPEERINVALID

npm ERR! peerinvalid The package bootstrap-sass does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer bootstrap-sass-webpack@0.0.3 wants bootstrap-sass@~3.2.0

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/cwd/webpack-test/npm-debug.log

Sources of Confusion

The most confusing parts of webpack for me are:

  1. Where should things like require("bootstrap-sass-webpack") be added - is it in the webpack.config.js file, or in the js/main.js file?
  2. Should modules like this available to webpack as soon as they are installed with npm install ?
  3. I thought that I should do npm install webpack -g so that webpack was installed globally, and use npm install without the -g for the other modules. However, I don't see any node_modules folder being created in my project. Shouldn't there be one?
  4. How are the search paths determined / specified for things like require("bootstrap-sass-webpack") ?

What node modules should I install to be able to do this? And what should my webpack.config.js look like?

cwd
  • 53,018
  • 53
  • 161
  • 198
  • I have a very similar configuration, I'm using exlir which is based on top of gulp, but it calls the webpack loading mechanism. I cannot get bootstrap-sass-webpack to write any asset files, beyond show they are called "file.css", "file.wof" when they are bundled together. Nothing writes to disk. – krob Oct 10 '15 at 08:20

1 Answers1

67

Introduction

Webpack is mainly a JavaScript-bundler. Its "native" language is JavaScript and every other source requires a loader which transforms it to JavaScript. If you require() an html-file for example...

var template = require("./some-template.html");

...you'll need the html-loader. It turns...

<div>
    <img src="./assets/img.png">
</div>

...into...

module.exports = "<div>\n    <img src=\"" + require("./assets/img.png") + "\">\n</div>";

If a loader doesn't return JavaScript, it needs to be "piped" to another loader.


How to load SASS-files

Configure loaders

In order to use SASS you'll need at least the sass-loader and the css-loader. The css-loader returns a JavaScript string. If you want to import the returned JavaScript string as StyleSheet, you'll also need the style-loader.

Run npm i sass-loader css-loader style-loader --save

Now you need to apply these loaders on all files that match /\.scss$/:

// webpack.config.js
...
module: {
    loaders: [
        // the loaders will be applied from right to left
        { test: /\.scss$/, loader: "style!css!sass" }
    ]
}
...

You can also pass options to node-sass as query parameters:

{
    test: /\.scss$/, loader: "style!css!sass?includePaths[]=" + 
        path.resolve(__dirname, "./bower_components/bootstrap-sass/assets/stylesheets/"
}

Since bootstrap references icons via the url() statement, the css-loader will try to include these assets into the bundle and will throw an exception otherwise. That's why you'll also need the file-loader:

// webpack.config.js
...
module: {
    loaders: [
        { test: /\.scss$/, loader: "style!css!sass" },
        { test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/, loader: "file" },
    ]
}
...

Configure entry

To include bootstrap into your bundle there are several ways. One is via the multi-entry option as you've already tried. I recommend to use a single entry where you require() your main sass-file:

// main.js
require("./main.scss");

Given that your includePaths are configured then you can do:

// main.scss
// Set the font path so that url() points to the actual file
$icon-font-path: "../../../fonts/bootstrap";

@import "bootstrap";

Please note that import statements inside scss-files are not touched by webpack because libsass has no api (yet) to provide custom resolvers.

To prevent code duplication it's also important to have a single main sass-file, because webpack compiles every sass-file individually.

With the coffee-loader installed via npm your final webpack.config.js should look like:

module.exports = {
    entry: "./js/main.coffee",
    output: {
        path: __dirname,
        filename: "main.js"
    },
    module: {
        loaders: [
            { test: /\.scss$/, loader: "style!css!sass" },
            { test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/, loader: "file" },
            { test: /\.coffee$/, loader: "coffee" }
        ]
    }
};

Webpack globally?

It's best not to install webpack globally, because it's a dependency of your project and thus should be controlled via npm. You can use the scripts-section of your package.json:

{
    ...
    "scripts": {
        "start": "webpack --config path/to/webpack.config.js & node server.js"
    }
}

Then you just need to run npm start

Johannes Ewald
  • 17,665
  • 5
  • 44
  • 39
  • really appreciate you taking the time to answer this one. – cwd Nov 12 '14 at 02:03
  • 3
    Based on the premises that webpack is "mainly a JavaScript-bundle", I am wondering if we are asking webpack to do too much on the CSS side of thing that it feel awkward. Would you think using Webpack for purely JavaScript ES6 workflow and using Gulp for autoprefixer+SASS workflow a better combination or do you think we should absolutely try to gun for one build-tool approach? – mech Mar 31 '15 at 15:16
  • 1
    I understand your concerns, but one benefit I see is, that webpack is also handling url() statements in CSS files. It makes totally sense to me to treat CSS just as another module. I've written "mainly a JavaScript-bundler" because JS is the universal language that can host HTML, CSS, Markdown, whatever as strings. – Johannes Ewald Apr 01 '15 at 16:29
  • 1
    So, there is no way of building css without `require`ing some file somewhere? Should I stick with gulp/grunt/whatever for building css? – tutuca Jul 03 '15 at 23:11
  • 1
    how to add hot reloading with sass ? – Sahan Aug 23 '15 at 18:24
  • @Sahan You should ask a separate question. But if you do some searches, you'll find articles that refer to including the Sass/SCSS directly in the JavaScript files, via `require()` – Nathan Lutterman Aug 25 '15 at 11:42
  • Thank you for this great answer. One comment I'd like to add is that I had to add the trailing slash when setting the `$icon-font-path` variable. – djheru Feb 20 '16 at 17:11