Webpack 2 - compile scss to css and miniffy, together with sourcemaps - source-maps

I'm completely new to webpack (been using gulp since... forever).
However, I've just decided to use webpack. Decided to go with webpack 2 (2.1.0-beta.20 currently).
Been looking all over, still couldn't do a simple task as "give webpack my bootstrap.scss file (which imports all other bootstrap partial scss files needed) and have returned bootstrap.custom.min.css and bootstrap.custom.min.css.map".
I have my own bootstrap.scss file which only imports what I need from bootstrap (not using all of it), but after a custom custom-variables.scss file imported at the top, to overwrite some default bootstrap variables - like colors, grid columns etc. Anyway, I'm sure this is not relevant... The issue is compiling scss to css with custom output file name and sourcemap.
Not that it would make any difference, but to start with, here's my custom bootstrap.scss:
#import "custom-variables"; // to overwrite default bootstrap variables
/**
* Twitter Bootstrap
* This is actually copy/paste from the original bootstrap file, just changed paths
*/
// Core variables and mixins
#import "../../../../node_modules/bootstrap/scss/variables";
#import "../../../../node_modules/bootstrap/scss/mixins";
// and so on... only what I need. I don't need tables, forms and a few other.
In addition to this, I also have my own style.scss for which I need to do the same (to have returned style.min.css and style.min.css.map).
As for my webpack.config.js file, this is all I have:
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const sassLoaders = [
'css-loader',
'postcss-loader',
'sass-loader?indentedSyntax=sass&includePaths[]=' + path.resolve(__dirname, './dev')
];
const config = {
entry: {
'bootstrap.custom.min': ['./wp-bootstrap'], // this file only contains 1 line: require('./dev/css/overwrite/bootstrap/bootstrap.scss');
'style.min': ['./wp-style'], // this file also contains 1 line: require('./dev/css/style.scss');
},
module: {
loaders: [
{
test: /\.scss$/,
loader: 'file',
// or, other examples I've found said to use:
// loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loaders: 'css!sass' }),
// but if I try like that, I get: "Cannot read property 'query' of undefined"
query: {
name: '[name].css'
}
}
]
},
plugins: [
new ExtractTextPlugin('[name].css')
],
postcss: [
autoprefixer({
browsers: ['last 2 versions']
})
],
resolve: {
modules: [
path.resolve('./'),
'node_modules'
]
}
};
module.exports = config;
These are all the related packages I have installed:
"devDependencies": {
"autoprefixer": "^6.4.0",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^2.0.0-beta.3",
"node-sass": "^3.8.0",
"postcss-loader": "^0.9.1",
"sass-loader": "^4.0.0",
"style-loader": "^0.13.1",
"webpack": "^2.1.0-beta.20"
}
If I use a version of extract-text-webpack-plugin which is <2.x, then I get other errors, it's not compatible with webpack 2.
So, baby steps in the code above... Simply tried to at least obtain my bootstrap.scss and style.scss transformed into 2 separate css files: bootstrap.custom.min.css and style.min.css (don't know what to do about sourcemaps yet).
This is all I could come up with after searching google and trying to follow some examples. No solid tutorial out there that could make me understand how to use webpack for what I need to accomplish. I'm only guessing here, blind-folded.
But when I type webpack in the console and hit Enter, I don't get any css file, instead I get the following 3 files:
bootstrap.css - with the exact same content as the source
bootstrap.scss, like it just copies the file content over, instead of compiling scss to css;
bootstrap.custom.min.js which has a bunch of javascript code in
it;
style.min.js - which also has a bunch of javascript code in it.
I've been stuck here for days, didn't even get to all the rest I need (sourcemaps and a destination folder of my choosing for the css files and css.map files).

Related

Minify a CSS and HTML file to the same location using Laravel Mix

I have two files, one CSS and one HTML in these locations:
resources/js/example/file.css
resources/js/example/file.html
What I would like to do using Laravel Mix (webpack) is
Minify the CSS file into file.min.css into the same directory
Minify the HTML file into file.min.html into the same directory
I have tried:
mix.postCss('resources/js/example/file.css', 'resources/js/example/file.min.css', [
require('cssnano')
]);
But this creates the CSS file in public/js/example/file.min.css.
I'm not sure why you don't want compiled files to be in the public directory. Regardless, you can use mix.setPublicPath() to change the output directory. This will tells Mix what you want the basic directory where all of your assets should be compiled to.
webpack.mix.js
const mix = require('laravel-mix');
mix.setPublicPath('');
mix.postCss('resources/js/example/file.css', 'resources/js/example/file.min.css', []);
If you want to Mix and minify HTML files you can install this plugin.
npm install minify-html-webpack-plugin --save-dev
Then add to your webpack...
const MinifyHtmlWebpackPlugin = require('minify-html-webpack-plugin');
const mix = require('laravel-mix');
mix.setPublicPath('');
mix.postCss('resources/js/example/file.css', 'resources/js/example/file.min.css', []);
mix.webpackConfig({
plugins: [
new MinifyHtmlWebpackPlugin({
afterBuild: true,
src: './resources/js/example',
dest: './resources/js/example',
ignoreFileNameRegex: /\.(gitignore|php)$/,
ignoreFileContentsRegex: /(<\?xml version)|(mail::message)/,
rules: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
minifyJS: true,
}
})
]
});
And make a dev or production build. Keep in mind you will have to play around with the src and dest to get the output exactly the way you want it.

How to get webpack to inline css into the generated html file

I'm trying to get webpack to inline some scss into the generated html file to use as my app-shell css.
The idea is to get webpack bundle any scss file ending in '-file.scss' into a css file and to inline any scss in files ending '-shell.scss'.
This way I would get my app-shell to style before react or anything else loads.
By inline I mean to put it in STYLE tags in the generated html file.
The css part of my webpack is:
{
test: /\.css$/,
include: [paths.appSrc, paths.appNodeModules],
// Disable autoprefixer in css-loader itself:
// https://github.com/webpack/css-loader/issues/281
// We already have it thanks to postcss.
loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss')
},
{
test: /-shell\.scss$/,
include: paths.appSrc,
loaders: ['style', 'css', 'sass'],
},
{
test: /-file\.scss$/,
include: paths.appSrc,
loader: ExtractTextPlugin.extract('style', 'css!sass'),
},
I had thought that removing the ExtractTextPugin loader from "test: /-shell.scss$/" would do it but it doesn't put those styles into STYLE tags in the generated html build file.
I've adapted the Create React App setup from https://github.com/facebookincubator/create-react-app

How to combine `css-modules` with normal `sass`, preferably in `webpack`

TLDR: How to combine css-modules with normal sass, preferably in webpack.
The Setup:
I am working on the styling build process for an e-commerce website. The site's styles are currently done in sass along with the js through a gulp browserify build process.
I have recently added a single page app that is built using react with webpack and babel. Inside of that application I am taking advantage of css-modules provided by webpack to scope the class names to each react component.
The problem:
I would like to incorporate the styles from the webpack css-modules build in with the main styling bundle for the site. To do this, I was considering building a webpack configuration to build the styles for the whole site. The problem I have is how to get the styles which are currently built by the single page webpack configuration and inject just the style chunk into a global webpack configuration that handles styles for the whole site. I should mention that I would like to keep the two configurations as separate as possible
The Questions:
Is there a proper way of having decoupled webpack builds where one is still able to use chunks from the other?
If so, how do I do it so that the css-module setup stays in the single page configuration, and the extract-text-webpack part along with a boring sass build goes into a global configuarion?
If not, how should I go about having one section of sass go through the css-modules workflow, and still combine it with the bundle from the rest of the site.
Thanks in advance.
EDIT
based on #Alexandr Subbotin's answer, I have updated my webpack to look something like the code below. I did have to change names and paths because of the code belongs to my employer, so there may be slight errors.
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const JSDIR = './build/js/';
const STYLES = './build/css/bundle.css';
module.exports = {
entry : {
'styles' : './src/styles.scss',
'app' : './src/index.js',
// a javascript file that includes the root of the single page app.
'single-page' : './src/single-page/styles-entry.js',
},
output : {
path : JSDIR,
filename : '[name].js', // normally compiles my
publicPath: 'http://localhost:8080/',
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader : 'babel-loader',
query : {
presets: [
'react','es2015','stage-0',
]
},
},
{
test : /\.scss$/,
loader: ExtractTextPlugin.extract('style?sourceMap', 'css?-url&sourceMap!sass?sourceMap'),
exclude : /\/single-page\//,
},
{
test : /\.scss$/,
loader: ExtractTextPlugin.extract(
'style?sourceMap',
'css?-url&modules&importLoaders=1&localIdentName=SinglePage__[name]__[local]!sass?sourceMap'
),
include : /\/single-page\//,
}
]
},
plugins : [
new ExtractTextPlugin(STYLES, {
allChunks : true,
}),
],
resolve : {
alias: {
"eventEmitter/EventEmitter": "wolfy87-eventemitter",
},
extensions: ['', '.js','.jsx'],
},
}
If I understood your question you want to apply css-modules only to one part of your application and leave simple sass building process in other parts.
To do this you can use exclude and include options in loaders. I.e. if you have your single page application inside single-page directory your webpack config can be:
module: {
entry: {
// it is your global sass styles
application_css: './css/application.scss',
// it is main file of your SPA bundle. Somewhere inside you will use require('./styles.scss') that should be processed by css-modules
spa_index: './single-page/index.js'
},
loaders: [
...,
{
// This loader will build all your sass that are not in `single-page` directory
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style', 'css!sass'),
exclude: /\/single-page\//
},
{
// This loader will handle all your css module in `single-page` directory
test: /\.scss$/,
loader: 'style!css?modules!sass',
include: /\/single-page\//
},
],
...
}
So, in this case all css from single-page/ will use css modules, and the rest won't.
EDIT:
If you take a look in API section of ExtractTextPlugin documentation you find
The ExtractTextPlugin generates an output file per entry, so you must use [name], [id] or [contenthash] when using multiple entries.
In your example you have have two chunks with css (styles and single-page), but only one output ./build/css/bundle.css. If you change your output to ./build/css/[name].css your will have two css files: styles.css with your global css and single-page.css with SPA styles.

Webpack SASS-loader include statement breaks SASS #import statemetns

So for background originally I was excluding the node_modules directory in my Webpack config, which was working fine for my sass #import statements, but made it very difficult to include things from the node_modules directory. So I switched the SASS loader to the following
{
test: /\.scss$/,
include: [path.resolve(__dirname, "/src/client"), path.resolve(__dirname, "/node_modules/angular-material/")],
loader: 'style-loader!css-loader!autoprefixer-loader!sass-loader'
},
Please also note I tried with and without the trailing slash on client.
All my src files including the sass files are in ./src/client directory including sub-directories. The problem now is when I run Webpack all my import statements are no longer functional. and I end up with the following error whenever I try to import one of my own sass files:
ERROR in ./src/client/app/app.scss
Module parse failed: /Users/mikedklein/development/vncuxf/src/client/app/app.scss Line 1: Unexpected token ILLEGAL
You may need an appropriate loader to handle this file type.
| #import 'common/common';
| #import 'general';
|
# ./src/client/app/app.module.es6 85:0-21
If I comment out the include statement all is well, but I know this is not a good approach. For reference I have also included my resolve statement from my config:
// Resolves so require statements don't need extentions
resolve: {
extensions: ['', '.js', '.es6', '.scss', '.css', '.html', '.json'],
alias: {
angular_material_scss: __dirname + "/node_modules/angular-material/angular-material.scss"
}
}
So I think I solved this
module: {
loaders: [
{
test: /\.(es6|js)$/,
exclude: /node_module/,
loader: 'babel-loader'
},
{
test: /\.css$/,
exclude: /node_modules/,
loader: 'style-loader!css-loader!autoprefixer-loader'
},
{
test: /\.scss$/,
loader: 'style-loader!css-loader!autoprefixer-loader!sass loader'
}
]
},
// This is the key need to have include paths in the sass loader option.
sassLoader: {
includePaths: [path.resolve(__dirname, './src/app'), path.resolve(__dirname, '/node_modules/angular-material')]
}
One thing I am still curious about is whether this is the most efficient approach, when sass files are stored in multiple directories.

Using Grunt.js to dynamically watch, and subsequently compile, a directory of SASS files into one CSS file

I'm brand new to Grunt.js, but I'm starting to get the hang of it. The main thing I'd like to do with it however, I can't seem to nail down.
My goal here, is to point grunt at a directory, and have it watch all of the matching files, and upon changes, compile them into a new single CSS file.
Here's my current gruntfile:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// CONFIG =========================/
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
files: {
'assets/css/style.css' : 'assets/css/sass/*.scss'
}
}
},
watch: {
css: {
files: 'assets/css/sass/*.scss',
tasks: ['sass']
}
}
});
// DEPENDENT PLUGINS =========================/
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-sass');
// TASKS =====================================/
grunt.registerTask('default', ['watch']);
};
Thus far I've been using grunt-contrib-watch, and grunt-contrib-sass. I've tried compass, as well as directory import but I couldn't get either of them to do what I'm trying to do either.
At the end of the day, I'm really just trying to avoid writing an import file, both because source order isn't going to matter for the way I'm writing my SASS, and becuase I'd really like to know how to make this happen.
I'm not sure of a way to do exactly what you want to achieve by just using Sass and Grunt-Contrib-Sass but you can achieve something similar by using Sass-Globbing, a SASS plug-in that lets you import entire directories. To use the plug-in, you'd use the require option in Grunt-Contrib-Sass and you'd have it target a main styles.scss file that may look something like:
#import "vendor/*";
#import "modules/*";
#import "partials/*";
And then your grunt file would have something like:
sass: {
dist: {
options: {
require: 'sass-globbing'
},
files: {
'assets/css/style.css' : 'assets/css/sass/style.scss'
}
}
}

Resources