My "older" web application uses Grunt and Webpack, but I am wondering if I can remove Grunt completely and still have the same functionality with just Webpack? Or should I replace Grunt with another technology? I've noticed grunt development slow to a crawl, and as new security warnings arise, I am having difficulty upgrading packages.
Below is my grunt file which performs a few basic tasks and allows me to run some very handy commands like "grunt dev" (for packaging dev bundle), "grunt dev-watch" (for packaging and watching for changed files), and "grunt prod" (for production bundle). Additionally, I use maven to build my web application, so I can define these commands from within the pom.xml depending on the build profile (development vs production)
Below: Grunt file, Webpack (common), and Webpack (dev)
gruntfile.js
/**
* Grunt Settings
*
* Contains settings for Grunt tasks that allow for development and production environments
*
* Usage (in bash shell, /webapp directory):
* grunt dev
* grunt dev-watch
* grunt prod
*/
// Define webpack configuration files
const webpackConfig_dev = require('./webpack.dev.js');
const webpackConfig_prod = require('./webpack.prod.js');
module.exports = function(grunt) {
let baseDir = ".";
// Set fileVersion from command line arguments
let fileVersion = grunt.option('fileVersion');
if (typeof fileVersion === 'undefined') {
fileVersion = 'dev';
}
fileVersion = fileVersion.trim();
grunt.initConfig({
// Identify which packages to load
pkg: grunt.file.readJSON('package.json'),
// Remove old files
clean: {
build: {
src: [
// js files
baseDir + '/js/dist',
// css files
baseDir + '/styles/*.css',
baseDir + '/styles/*.map',
]
}
},
// Crunch style files (eg *.scss)
sass: {
options:{
implementation: require('node-sass'),
sourceMap: true,
outputStyle: 'expanded'
},
dist: {
files: [{
src: baseDir + '/styles/main.scss',
dest: baseDir + '/styles/main.' + fileVersion + '.css',
}]
}
},
// Modifications after main.css is created
postcss: {
options: {
map: true,
processors: [
require('autoprefixer')
]
},
dist: {
src: baseDir + '/styles/main.' + fileVersion + '.css'
}
},
// Copy jquery external library to the dist/ folder
copy: {
main: {
files: [
{
expand: true,
cwd: 'node_modules/jquery/dist',
src: 'jquery.min.js',
dest: baseDir + '/js/dist'
},
],
},
},
// Run webpack (depending on development versus production)
webpack: {
dev: webpackConfig_dev(fileVersion),
prod: webpackConfig_prod(fileVersion),
},
// Enable live reloading of js/scss files
watch: {
css: {
files: [baseDir + '/styles/**/*.scss'],
tasks: ['sass']
},
js: {
files: [baseDir + '/js/src/**/*.js', '!' + baseDir + '/js/src/**/*.min.js'],
tasks: ['webpack:dev']
}
}
});
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-webpack');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
// Available Grunt tasks
// development
grunt.registerTask('default', ['dev']);
grunt.registerTask('dev', ['clean', 'sass', 'postcss', 'copy', 'webpack:dev']);
// development (and watches for changes of .js and .css files)
grunt.registerTask('dev-watch', ['sass', 'postcss', 'webpack:dev', 'watch']);
// production ready
grunt.registerTask('prod', ['clean', 'sass', 'postcss', 'copy', 'webpack:prod']);
};
webpack.common.js
/**
* Webpack settings file :: Common (development & production)
*/
const ESLintPlugin = require('eslint-webpack-plugin');
const baseDir_js = "./js";
module.exports = {
// Define the "source" entry point for the application (or sub apps)
entry: {
main: baseDir_js + '/src/index.js',
admin: baseDir_js + '/src/admin.js',
sysAdmin: baseDir_js + '/src/sysAdmin.js',
},
// Let grunt know we have external libraries available
externals: {
jquery: 'jQuery',
google: 'google',
Stripe: 'Stripe',
},
module: {
rules: [
// process css files
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ],
},
// process images/fonts -> base64
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
type: 'asset/inline',
}
],
},
plugins: [new ESLintPlugin()],
};
webpack.dev.js
/**
* Webpack settings file :: Development
*/
// Import common settings
const {merge} = require('webpack-merge');
const common = require('./webpack.common.js');
const baseDir_js = "./js";
const path = require('path');
// Merge settings with "common"
module.exports = (fileVersion) => merge(common, {
mode: 'development',
// Use full source map
devtool: 'inline-source-map',
// Output entire code base to one file
output: {
filename: '[name].' + fileVersion + '.js',
// store in "dist/" directory
path: path.resolve(baseDir_js, 'dist'),
},
});
Related
This is my first time build a webpack.production.config.js file. Here is my directory structure.
My webpack dev config entry and output is as follows:
const path = require('path');
const webpack = require('webpack');
let config = {
entry: path.resolve(__dirname, 'app/index.js'),
output: {
path: path.resolve(__dirname, '/public'),
filename: 'bundle.js',
},
....
My webpack production file is this:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'app/index.js'),
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js',
},
module: {
loaders: [
{ test: /\.json$/, loader: 'json' },
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
{
test: /\.css$/,
use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }),
},
{ test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'] },
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({ title: 'My App', filename: 'admin.html' }),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin('styles.css'),
new webpack.optimize.CommonsChunkPlugin('common.js'),
],
};
Npm start works fine. Npm run build compiles and creates my build folder. In the folder are my assets, a main.js, common.js.js, and admin.html generated from the HtmlWebpackPlugin.
When built, the admin.html is reading the commmon.js.js and main.js sources.
As I try to deploy to heroku, How to get my output paths in production correct so the new generated html is in the root and reads the proper js files? Currently in my package.json, the entry point is "app/index.js".
What is your workplace?
Hello, I would like to ask what is your approach to developing the frontend and backend simultaneously?
I am into Webpack, but, what to do when I want to edit framework?
Do I need to run webpack in or out of the box?
Is it possible to reference webpack --watch or some other module to server as proxy? And if so how to set for example *.php files on change to force refresh page.
So far I have worked separately on the framework and particularly on frontend. Now I do not really know how to combine together, especially when many modules of webpack2 is obsolete.
Windows X, Docker(laradock), Webpack, SASS, JS, PHP
Thank you for the future suggestions.
You should use webpack-dev-server as proxy to support multiple hosts.
Then you will be able to load 'backend' from server and developing in another enviroment using all benefits from webpack MHR inlining.
Here is my webpack.config.js
var path = require('path');
var htmlWebPack = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack')
var ProvidePlugin = require('webpack/lib/ProvidePlugin');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');
var buildPath = path.join(__dirname); // Local /public/
var serverURL = 'http://localhost:8080/' // Webpack-dev-server
var proxyURL = 'http://LaraDock.dev:85/' // External server (laradock)
var proxy = {
'*': proxyURL
};
module.exports = {
devtool: 'cheap-eval-source-map',
context: path.join(__dirname, 'resources'), // ABSOLUTE ROUTE ./
entry: {
app: [
'webpack-dev-server/client?' + serverURL,
'webpack/hot/only-dev-server',
path.join(__dirname, 'resources/assets/js/app.js')
]
},
output: {
publicPath: serverURL + buildPath,
path: path.resolve(__dirname, buildPath),
filename: "js/[name].bundle.js"
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
},
},
module: {
loaders: [
// JS
{
test: /\.js$/, // ON WHAT TYPE USE THIS LOADER
loader: 'babel-loader',
include: path.join(__dirname, 'resources', 'assets', 'js'),
},
// VUE
{
test: /\.vue$/,
loader: 'vue-loader',
include: path.join(__dirname, 'resources', 'assets', 'js'),
},
// STYLE
{
test: /\.(sass|scss|css)$/,
loader: ['style-loader', 'css-loader', 'sass-loader'],
include: path.join(__dirname, 'resources', 'assets', 'scss'),
},
// FILES
{
test: /\.(jpe?g|png|gif|svg|ico)$/i,
loader: "file-loader?name=[path][name].[ext]&context=./resources/assets"
},
// FONTS
{
test: /\.(otf|eot|svg|ttf|woff)$/,
loader: 'url-loader?limit=10000'
},
{
test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "url-loader?limit=10000"
},
{
test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/,
loader: 'file-loader'
},
// BOOTSTRAP
{
test: /bootstrap\/public\/js\/umd\//,
loader: 'imports?jQuery=jquery'
},
],
},
devServer: {
contentBase: serverURL + buildPath,
proxy: proxy,
historyApiFallback: true,
hot: true,
noInfo: true,
stats: {
errors: true,
colors: true,
errorDetails: true,
reasons: false,
hash: false,
version: false,
timings: false,
assets: false,
chunks: false,
modules: false,
children: false,
source: false,
warnings: false,
publicPath: false
}
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new BrowserSyncPlugin(
// BrowserSync options
{
// Webpack-dev-server endpoint
host: 'http://localhost',
port: 80,
// proxy the Webpack Dev Server endpoint
// (which should be serving on http://localhost:80/) through BrowserSync
proxy: serverURL,
// Files
files: ['public/*', 'app/**/*.php', 'config/**/*.php', 'resources/views/**/*.php'],
},
// plugin options
{
// prevent BrowserSync from reloading the page
// and let Webpack Dev Server take care of this
reload: false
}),
new ExtractTextPlugin({
filename: './css/[name].style.css',
disable: false,
allChunks: true
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"Tether": 'tether' // Bootstrap v4 problem
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
],
};
I'm trying to follow some tutorials and documentation and have the webpack build for me sass files into separates css files.
It all kind of works, as long as I'm proving full relative path in require:
require("../sass/ss.scss")
But it I want to use:
require("./ss.scss")
and I turn comment out the 'sassLoader' in the config, I get error:
[1] "sassLoader" is not allowed
As you can see I have been trying to use inline settings too:
sourceMap&includePaths[]=' + (PATHS.sass)
but they are ignored.
What am I doing wrong?
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const validate = require('webpack-validator');
const merge = require('webpack-merge');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const PATHS = {
app: path.join(__dirname, 'app'),
js: path.join(__dirname, 'app', 'js'),
sass: path.join(__dirname, 'app', 'sass'),
build: path.join(__dirname, 'build')
};
const common = {
// Entry accepts a path or an object of entries.
// We'll be using the latter form given it's
// convenient with more complex configurations.
entry: {
app: path.join(PATHS.js, 'index.js')
},
output: {
path: PATHS.build,
filename: '[name].js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack demo'
}),
new ExtractTextPlugin(
'[name].css', {
allChunks: true
}
),
],
devtool: "source-map",
module: {
loaders: [
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract(
'style!css?sourceMap!sass?sourceMap&includePaths[]=' + (PATHS.sass)
)
}
]
}
// sassLoader: {
// includePaths: [PATHS.sass]
// }
};
var config;
// Detect how npm is run and branch based on that
switch(process.env.npm_lifecycle_event) {
case 'build':
config = merge(common,
{}
);
break;
default:
config = merge(common,
{}
);
}
module.exports = validate(config);
The error comes from webpack-validator because it doesn't recognize the sassLoader property. You need to configure a schema extension:
const Joi = require('webpack-validator').Joi
const schemaExtension = Joi.object({
sassLoader: Joi.any(),
})
module.exports = validate(config, {schemaExtension: schemaExtension})
Requiring CSS modules is handled by the css-loader, only #import is handled by sass-loader. (There are major differences in their behaviour other than that, make sure you are using what you need).
In order to require from the root directory, you need to configure resolve.root in the webpack config file. See answer here: Resolving require paths with webpack
I am receiving the error "file to import not found or unreadable: "compass"" when I am attempting to run my "grunt" task.
"Running "sass:app" (sass) task"
Warning: build/global/css/base:1: file to import not found or unreadable: "compass"
Here is my gruntfile:
module.exports = function(grunt) {
// COMMANDS
// comple css and js - $ grunt
// optim - run $ grunt imgoptim
// FILE STRUCTURE
// css/app.css, css/app.min.css
// build/global/css/*.scss
// build/global/css/**/*.scss
// build/components/componentname/css/*.scss
// css/singlefile.css, css/singlefile.min.css
// build/global/solo/css/*.scss
// js/app.js, js/app.min.css
// build/global/js/*.js
// build/global/js/**/*.js
// build/components/componentname/js/*.js
// singlefile.js, singlefile.min.js
// build/global/solo/js/*.js
var watch = {};
var uglify = {};
var concat = {};
var sass = {};
var cssmin = {};
var js_files = [
'build/global/js/vendor/respond.js',
'build/global/js/vendor/AccessifyHTML5.js',
'build/global/js/vendor/jquery.equalHeights.js',
'build/global/js/vendor/jquery.infinitescroll.js',
'build/global/js/vendor/jquery.verticalcentering.js',
'build/global/js/global.js',
'build/components/**/js/vendor/*.js',
'build/components/**/js/*.js'
];
// WATCH =========================================/
watch.solocss = {
options: { livereload: true }, // currently not working correctly, should be able to remove this line
livereload: {
options: { livereload: true },
files: ['css/*.css', '!css/*.min.css']
},
files: 'build/solo/css/*.scss',
tasks: [
'newer:sass:solo'
]
};
watch.appcss = {
options: { livereload: true }, // currently not working correctly, should be able to remove this line
livereload: {
options: { livereload: true },
files: ['css/*.css', '!css/*.min.css']
},
files: [
'build/global/css/**/*.scss',
'build/global/css/*.scss',
'build/components/**/*.scss'
],
tasks: [
'sass:app',
'concat:appcss',
'sass:solo' // run solo here too incase there are changes to sass vars etc
]
};
watch.cssmin = {
files: ['css/*.css', '!css/*.min.css'],
tasks: [
'newer:cssmin:compress',
]
};
watch.solojs = {
options: { livereload: true }, // currently not working correctly, should be able to remove this line
livereload: {
options: { livereload: true },
files: ['js/*.js', '!js/*.min.js']
},
files: 'build/solo/js/*.js',
tasks: [
'newer:uglify:solojsdev',
'newer:uglify:solojsprod'
]
};
watch.globaljs = {
options: { livereload: true }, // currently not working correctly, should be able to remove this line
livereload: {
options: { livereload: true },
files: ['js/*.js', '!js/*.min.js']
},
files: ['build/global/js/*.js', 'build/global/js/**/*.js', 'build/components/**/*.js'],
tasks: [
'concat:appjs',
'uglify:appjs'
]
};
// CSS COMPLILATION ====================================/
//solo files, compiled to /css/file.css
sass.solo = {
options:{
includePaths: require('node-bourbon').with('build'),
sourceComments: true,
style: 'expanded', // compile in expanded format with comments for dev,
compass: true
},
files: [{
expand: true,
src: watch.solocss.files, // where it's coming from
dest: 'css', // where it's going
flatten: true, // removes folder tree
ext: '.css', // what file extension to give it - standard
extDot: 'last'
}]
};
// app scss files, compiled individually to /css/build/...
sass.app = {
options:{
includePaths: require('node-bourbon').with('build'),
style: 'expanded', // compile in expanded format with comments for dev
sourceComments: 'normal',
},
files: [{
//cwd: 'build',
expand: true,
src: watch.appcss.files, // where it's coming from
dest: 'css', // where it's going
ext: '.css' // what file extension to give it - standard
}]
};
// concat app.css files
concat.appcss = {
src: [
'css/build/global/css/**/*.css',
'css/build/global/css/*.css',
'css/build/components/**/*.css'
],
dest: 'css/app.css',
seperator: '\n'
};
// create compressed version of all css files for prod
cssmin.compress = {
files: [{
expand: true,
cwd: 'css/',
src: ['*.css', '!*.min.css', '!editor.css'],
dest: 'css/',
ext: '.min.css',
extDot: 'last'
}]
}
// JAVASCRIPT COMPLILATION ============================/
// concat app.js files
concat.appjs = {
src: js_files,
dest: 'js/app.js',
separator: ';'
};
// minify app.js
uglify.appjs = {
files: {
'js/app.min.js': [
'js/app.js'
]
}
};
// uglify solo files for dev
uglify.solojsdev = {
options: {
compress: false,
mangle: false,
beautify: true
},
files: [{
expand: true,
flatten: true, // removes folder tree
src: watch.solojs.files,
dest: 'js/'
}]
};
// uglify + minify solo files for prod
uglify.solojsprod = {
options: {
compress: true
},
files: [{
expand: true,
flatten: true, // removes folder tree
src: watch.solojs.files,
dest: 'js/',
ext: '.min.js',
extDot: 'last',
}]
};
// IMAGE OPTIMISATION ==========================/
imageoptim = {
optimise: {
src: ['images']
}
}
// GRUNT ======================================/
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: watch,
sass: sass,
concat: concat,
cssmin: cssmin,
uglify: uglify,
imageoptim: imageoptim
});
// DEPENDENT PLUGINS =========================/
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-imageoptim');
grunt.loadNpmTasks('grunt-newer');
// TASKS =====================================/
grunt.registerTask('default', [
'sass',
'concat',
'newer:cssmin',
'newer:uglify',
'watch'
]);
grunt.registerTask('imgoptim', [
'imageoptim'
]);
}
My directory structure looks like the following relative from the gruntfile.js:
/build/global/components/FOLDERS/*.scss files
/build/global/css/FOLDERS & *.scss files
/dist/*.css files
Here is my config.rb file in the same directory as the gruntfile:
http_path = "/"
css_dir = "css"
sass_dir = "build"
images_dir = "images"
javascripts_dir = "javascript"
I already have compass installed and all the node_modules.
Beginning with Grunt, which I love but cannot make it work as I get continously an error with my sass implementation. I get the following alerts:
Danis-MacBook-Pro:020 DanielRamirez$ grunt
Running "concat:dist" (concat) task
File "js/build/production.js" created.
Running "uglify:build" (uglify) task
File js/build/production.min.js created.
>> No "sass" targets found.
Warning: Task "sass" failed. Use --force to continue.
Aborted due to warnings.
Danis-MacBook-Pro:020 DanielRamirez$
If I --force I get the following:
Danis-MacBook-Pro:020 DanielRamirez$ grunt
Running "concat:dist" (concat) task
File "js/build/production.js" created.
Running "uglify:build" (uglify) task
File js/build/production.min.js created.
>> No "sass" targets found.
Warning: Task "sass" failed. Use --force to continue.
Aborted due to warnings.
Danis-MacBook-Pro:020 DanielRamirez$
This is my Gruntfile.js:
module.exports = function(grunt) {
// 1. All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// 2. Configuration for concatinating files goes here.
// Concatonate various files into one
concat: {
dist: {
src: [
'js/vendor/*.js', // All JS in the libs folder
'js/plugin.js', // All JS in the libs folder
'js/global.js' // This specific file
],
dest: 'js/build/production.js',
}
},
// Creates a minified version of the javascript files of the project
uglify: {
build: {
src: ['js/vendor/*.js', 'js/plugin.js', 'js/global.js'],
dest: 'js/build/production.min.js'
}
},
// Minifies automatically the images of the project
// imagemin: {
// dynamic: {
// files: [{
// expand: true,
// cwd: 'images/',
// src: ['**/*.{png,jpg,gif}'],
// dest: 'images/build/'
// }]
// }
// },
// Watches for changes done on the project and builds the commands if neccesary
watch: {
options: {
livereload: true,
},
scripts: {
files: ['js/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
},
sass: {
dist: {
options: {
style: 'compressed'
},
files: {
'css/build/style.css': 'sass/style.scss'
}
}
},
css: {
files: ['css/*.scss'],
tasks: ['sass'],
options: {
spawn: false,
}
}
}
});
// 3. Where we tell Grunt we plan to use this plug-in.
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
// grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
// 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat', 'uglify', 'sass', 'compass', 'watch']);
};
Any ideas?
Yes, what you've done is put the entire Sass configuration inside the watch config. Grunt does not work this way. Instead, change your config to something like this:
module.exports = function(grunt) {
// 1. All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// 2. Configuration for concatinating files goes here.
// Concatonate various files into one
concat: {
dist: {
src: [
'js/vendor/*.js', // All JS in the libs folder
'js/plugin.js', // All JS in the libs folder
'js/global.js' // This specific file
],
dest: 'js/build/production.js',
}
},
// Creates a minified version of the javascript files of the project
uglify: {
build: {
src: ['js/vendor/*.js', 'js/plugin.js', 'js/global.js'],
dest: 'js/build/production.min.js'
}
},
// Compile sass to css
sass: {
dist: {
options: {
style: 'compressed'
},
files: {
'css/build/style.css': 'sass/style.scss'
}
}
},
// Minifies automatically the images of the project
// imagemin: {
// dynamic: {
// files: [{
// expand: true,
// cwd: 'images/',
// src: ['**/*.{png,jpg,gif}'],
// dest: 'images/build/'
// }]
// }
// },
// Watches for changes done on the project and builds the commands if neccesary
watch: {
options: {
livereload: true,
},
scripts: {
files: ['js/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
},
css: {
files: ['css/*.scss'],
tasks: ['sass'],
options: {
spawn: false,
}
}
}
});
// 3. Where we tell Grunt we plan to use this plug-in.
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
// grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
// 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat', 'uglify', 'sass', 'compass', 'watch']);
};