How to use polyfills with babel 7.4 and grunt? - gruntjs

I run into a few issues with IE11 when using babel and grunt. I need to polyfill some code using new ECMA features written by a former colleague, who updated babel to 7.4 (but does not change babelrc to use corejs) and code was not testet in IE11. So now code doesn't work in Internet Explorer, and I had to polyfill, without deeper knowledge with babel and grunt. I change babelrc preset-env to use builtIns, but can't get it to run.
I change babelrc to use preset-env with builtIns. So I set useBuiltIns to usage, so polyfills only include when needed. But then babel generates "require" statements which of course could not be interpreted by browsers. Even when I set useBuiltIns to "entry" (and I have no idea what is the difference to "usage"), I get "SCRIPT5009: "Symbol" is undefined" as error in js console in my IE11.
package.json:
{
"name": "xyproject",
"version": "1.0.0",
"dependencies": {
"bootstrap3-dialog": "^1.35.4",
"core-js": "^3.1.4",
"datatables.net-bs": "^1.10.19",
"datatables.net-buttons-bs": "^1.5.6",
"datatables.net-colreorder-bs": "^1.5.1",
"datatables.net-responsive": "^2.2.3",
"datatables.net-responsive-bs": "^2.2.3",
"normalize-scss": "^7.0.1"
},
"devDependencies": {
"#babel/cli": "^7.5.5",
"#babel/core": "^7.5.5",
"#babel/plugin-proposal-class-properties": "^7.5.5",
"#babel/plugin-transform-object-assign": "^7.2.0",
"#babel/preset-env": "^7.5.5",
"babel-preset-minify": "^0.5.0",
"grunt": "^1.0.4",
"grunt-babel": "^8.0.0",
"grunt-concurrent": "^2.3.1",
"grunt-contrib-clean": "^2.0.0",
"grunt-contrib-compass": "^1.1.1",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-sass": "^1.0.0",
"grunt-contrib-uglify": "^3.4.0",
"grunt-contrib-uglify-es": "^3.3.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-newer": "^1.3.0",
"grunt-notify": "^0.4.5",
"jit-grunt": "^0.10.0",
"time-grunt": "^1.4.0",
"uglify-es": "github:mishoo/UglifyJS2#harmony"
}
}
.babelrc:
{
"sourceType": "unambiguous",
"sourceMap": true,
"plugins": [
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-template-literals",
"#babel/plugin-transform-object-assign"
],
"presets": [
[
"#babel/preset-env", {
"debug": false,
"useBuiltIns": "usage",
"corejs": 3,
"targets": {
"ie": "9"
}
}
],
[
"minify", {
"mangle": true,
"deadcode": true,
"simplify": true,
"builtIns": false
}
]
]
}
gruntfile.js:
'use strict';
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('../../package.json'),
config: {
sass: 'assets/scss',
css: 'assets/css',
js: 'assets/js',
templates: 'assets/templates',
build: 'assets/build',
node_modules: 'node_modules'
},
// notify if process is done
notify: {
watch_js: {
options: {
title: 'Task Complete',
message: 'JS Tasks are completed'
}
},
watch_css: {
options: {
title: 'Task Complete',
message: 'SCSS/CSS Tasks are completed'
}
}
},
// Bablify our js files - we're now able
// to write JS code in newest style :)
// Babel will transpile the code for elder browsers
babel: {
options: {
// babelrc: false,
// sourceType: 'unambiguous',
// sourceMap: true,
// presets: [
// [
// '#babel/preset-env', {
// targets: {
// ie: '9'
// }
// },
// ],
// [
// 'minify', {
// mangle: true,
// deadcode: true,
// simplify: true,
// // evaluate: true
// }
// ]
// ],
},
dist: {
files: {
'<%= config.build %>/backend.min.js': '<%= config.build %>/backend.min.js',
'<%= config.build %>/backend_tables.min.js': '<%= config.build %>/backend_tables.min.js',
'<%= config.build %>/backend_pages.min.js': '<%= config.build %>/backend_pages.min.js',
'<%= config.build %>/wysiwyg.min.js': '<%= config.build %>/wysiwyg.min.js',
'<%= config.build %>/dataview.min.js': '<%= config.build %>/dataview.min.js',
'<%= config.build %>/reporting.min.js': '<%= config.build %>/reporting.min.js',
// '<%= config.build %>/search.min.js': '<%= config.build %>/search.min.js',
}
}
},
build: {
clean: {
tasks: ['clean']
},
css: {
tasks: ['sass']
},
js: {
tasks: [
'concat',
'babel',
'uglify',
'remove_unnecessary_files'
]
}
},
// removes all files from folder
clean: ['<%= config.build %>/'],
// compiles scss to css
sass: {
dist: {
options: {
style: 'compressed',
compass: false,
loadPath: [
'node_modules/normalize-scss/sass'
]
},
files: {
'<%= config.build %>/main.css': [
'<%= config.sass %>/main.scss',
'<%= config.node_modules %>/bootstrap3-dialog/src/css/bootstrap-dialog.css'
]
}
}
},
concat: {
options: {
sourceMap: true
},
libs: {
src: [
'<%= config.node_modules %>/#babel/polyfill/dist/polyfill.min.js',
'<%= config.js %>/libs/jquery.min.js',
'<%= config.js %>/libs/jquery-ui.min.js',
'<%= config.js %>/libs/jquery.validate.js',
// bootstrap-4.1.3-dist/js/bootstrap.bundle.js
// '<%= config.js %>/bootstrap-4.1.3-dist/js/bootstrap.js',
// '<%= config.js %>/bootstrap-4.1.3-dist/js/bootstrap.bundle.js',
'<%= config.js %>/libs/bootstrap.min.js',
'<%= config.js %>/libs/bootstrap-select.js',
'<%= config.js %>/libs/bootstrap-datepicker.js',
'<%= config.js %>/libs/bootstrap-tagsinput.js',
'<%= config.js %>/libs/jquery.mCustomScrollbar.min.js',
'<%= config.js %>/libs/icheck.min.js',
// '<%= config.js %>/libs/jquery.tagsinput.min.js',
'<%= config.js %>/libs/jquery.noty.js',
'<%= config.js %>/libs/noty-layouts/topRight.js',
'<%= config.js %>/libs/noty-themes/default.js',
'<%= config.js %>/libs/jquery.nestable.js',
'<%= config.js %>/libs/fileinput.min.js',
'<%= config.js %>/libs/moment.min.js',
'<%= config.js %>/libs/plugins.js',
'<%= config.js %>/libs/actions.js',
'<%= config.js %>/libs/jquery.cookie.js',
'<%= config.js %>/libs/spin.min.js',
'<%= config.js %>/libs/select2.js',
'<%= config.js %>/libs/icheck_custom.js',
'<%= config.js %>/libs/bootstrap-typeahead.min.js',
'<%= config.js %>/libs/daterangepicker.js',
// if you want to add Datatatables plugins, see the following lines on how to do so:
// '<%= config.js %>/libs/datatables.js',
'<%= config.node_modules %>/datatables.net/js/jquery.dataTables.js',
'<%= config.node_modules %>/datatables.net-bs/js/dataTables.bootstrap.js',
'<%= config.node_modules %>/datatables.net-buttons/js/dataTables.buttons.js',
'<%= config.node_modules %>/datatables.net-buttons/js/buttons.colVis.js',
'<%= config.node_modules %>/datatables.net-buttons-bs/js/buttons.bootstrap.js',
'<%= config.node_modules %>/datatables.net-colreorder/js/dataTables.colReorder.js',
'<%= config.node_modules %>/datatables.net-responsive/js/dataTables.responsive.js',
'<%= config.node_modules %>/datatables.net-responsive-bs/js/responsive.bootstrap.js',
'<%= config.node_modules %>/bootstrap3-dialog/src/js/bootstrap-dialog.js',
'<%= config.js %>/libs/lodash.min.js',
'<%= config.js %>/libs/filesaver.js',
'<%= config.js %>/libs/queue.js',
],
dest: '<%= config.build %>/libs.min.js'
},
backend: {
src: [
'<%= config.js %>/globals.js',
'<%= config.js %>/backend.js',
'<%= config.js %>/datatable.js',
'<%= config.js %>/action.js',
'<%= config.js %>/menu.js',
'<%= config.js %>/validate.js',
'<%= config.js %>/ajax.js',
'<%= config.js %>/adv_search.js',
'<%= config.js %>/massupdate.js',
'<%= config.js %>/storage.js',
'<%= config.js %>/widgets.js',
'<%= config.js %>/sessionTimer.js',
'<%= config.js %>/widgets.js',
'<%= config.js %>/impersonate.js',
'<%= config.js %>/helper/*.js'
],
dest: '<%= config.build %>/backend.min.js'
},
pages: {
src: [
'<%= config.js %>/page_*.js',
],
dest: '<%= config.build %>/backend_pages.min.js'
},
dataview: {
src: [
'<%= config.js %>/dataview/*.js',
],
dest: '<%= config.build %>/dataview.min.js'
},
reporting: {
src: [
'<%= config.js %>/reporting/*.js',
],
dest: '<%= config.build %>/reporting.min.js'
},
tables: {
src: [
'<%= config.js %>/table_*.js',
'<%= config.js %>/table_search/type_base.js',
'<%= config.js %>/table_search/*.js',
],
dest: '<%= config.build %>/backend_tables.min.js'
},
/*search: {
src: [
// '<%= config.js %>/table_*.js',
'<%= config.js %>/table_search/*.js',
],
dest: '<%= config.build %>/search.min.js'
},*/
wysiwyg: {
src: [
'<%= config.js %>/wysiwyg.js',
],
dest: '<%= config.build %>/wysiwyg.min.js'
}
},
watch: {
options: {
spawn: false // add spawn option in watch task
},
css: {
files: [
'<%= config.sass %>/*.scss',
'<%= config.sass %>/*/**'
],
tasks: [
'sass',
'notify:watch_css'
]
},
devIe: {
files: [
'<%= config.js %>/*.js',
'<%= config.js %>/*/**'
],
tasks: [
'concat',
'babel',
'notify:watch_js'
]
},
js_dev: {
files: [
'<%= config.js %>/*.js',
'<%= config.js %>/*/**'
],
tasks: [
'concat',
// 'babel',
// 'rename_js_files',
'notify:watch_js'
]
},
livereload: {
options: {
livereload: true
},
files: ['<%= config.build %>**/*.js', '<%= config.build %>/main.css']
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %> */',
sourceMap: {
includeSources: true
},
report: 'gzip',
output: {
comments: false
},
sourceMapIn: function(path) {
return path + '.map';
}
},
dist: {
files: {
'<%= config.build %>/libs.min.js': ['<%= config.build %>/libs.min.js'],
'<%= config.build %>/backend.min.js': ['<%= config.build %>/backend.min.js'],
'<%= config.build %>/backend_tables.min.js': ['<%= config.build %>/backend_tables.min.js'],
'<%= config.build %>/backend_pages.min.js': ['<%= config.build %>/backend_pages.min.js'],
'<%= config.build %>/wysiwyg.min.js': '<%= config.build %>/wysiwyg.min.js',
'<%= config.build %>/dataview.min.js': ['<%= config.build %>/dataview.min.js'],
'<%= config.build %>/reporting.min.js': ['<%= config.build %>/reporting.min.js'],
'<%= config.build %>/search.min.js': ['<%= config.build %>/search.min.js'],
}
}
},
concurrent: {
options: {
logConcurrentOutput: true
},
dev: {
tasks: ['watch:js_dev', 'watch:css', 'watch:livereload']
// tasks: ['watch:js_dev', 'watch:css']
},
devIe: {
tasks: ['watch:devIe', 'watch:css']
// tasks: ['watch:js_dev', 'watch:css']
}
}
});
// grunt.loadNpmTasks('grunt-contrib-sass');
// grunt.loadNpmTasks('grunt-contrib-watch');
// grunt.loadNpmTasks('grunt-contrib-concat');
// grunt.loadNpmTasks('grunt-contrib-uglify');
// grunt.loadNpmTasks('grunt-contrib-handlebars');
// grunt.loadNpmTasks('grunt-notify');
// grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-contrib-uglify-es');
grunt.registerMultiTask('build', 'build the given task', function() {
grunt.task.run( this.data.tasks );
});
grunt.registerTask('remove_unnecessary_files', 'Remove *.js to *.min.js', () => {
let fs,
fileNames,
fileName;
fs = require('fs');
fileNames = [
'backend_tables.js',
'backend_tables.js.map',
'backend_tables.min.js.map',
'backend.js',
'backend.js.map',
'backend.min.js.map',
'backend_pages.js',
'backend_pages.js.map',
'backend_pages.min.js.map',
'libs.js',
'libs.js.map',
'libs.min.js.map',
'main.css.map'
];
for (fileName in fileNames) {
if (fs.existsSync(grunt.config.data.config.build + '/' + fileNames[fileName])) {
fs.unlink(grunt.config.data.config.build + '/' + fileNames[fileName], (err) => {
if (err) {
grunt.log.error(`Remove ${grunt.config.data.config.build + '/' + fileNames[fileName]} failed: ${err}`);
} else {
grunt.log.ok(`Remove ${grunt.config.data.config.build + '/' + fileNames[fileName]} done`);
}
});
}
}
});
grunt.registerTask('rename_js_files', 'Rename *.js to *.min.js', function() {
var fs,
libsFileName,
libsMinFileName,
backendFileName,
backendMinFileName;
fs = require('fs');
libsFileName = grunt.config.data.config.build + '/libs.js';
libsMinFileName = grunt.config.data.config.build + '/libs.min.js';
backendFileName = grunt.config.data.config.build + '/backend.js';
backendMinFileName = grunt.config.data.config.build + '/backend.min.js';
fs.rename(libsFileName, libsMinFileName, function(err) {
if (err) {
grunt.log.error('ERROR: ' + err);
}
});
fs.rename(backendFileName, backendMinFileName, function(err) {
if (err) {
grunt.log.error('ERROR: ' + err);
}
});
});
// grunt.registerTask('default', ['build', 'watch:css', 'watch:js']);
grunt.registerTask('watch:dev', ['build', 'concurrent:dev']);
// grunt.registerTask('watch:dev-ie', ['build', 'concurrent:devIe']);
grunt.registerTask('watch:dev-ie', ['watch:devIe', 'watch:css', 'watch:livereload']);
require('jit-grunt')(grunt);
};
I am running the app with "./node_modules/.bin/grunt watch:dev-ie", then change some javascript code to trigger livereload, so "devIe" tasks are executed and babel will transpile code. If I use "entry" as value for useBuiltIn option in babelrc code works, but I get "symbol is undefined". When I use "usage" as value, then babel adds require statements to my minified js files and I get "require is undefined" in the js console.
I test the code with IE11. I do not want to use Browserify or webpack as this will make things more complex. Can anyone tell me what I am missing in my configs to make things work? I would have expected that babel does not create require statements which could not be interpreted by modern browsers and needs to be translated by webpack or something to make things work. I just want to extend my babel / grunt config to polyfill the javascript code to work in IE11 also. Maybe I am missing a package in package.json also?

Related

How to load css file from bower component

I am using grunt in my angularjs one project. I have created the project using yeoman. I have used emojionearea emojis in my project the link is here.
According to docs css and js file should me included in project. I have installed the package and include it in my bower.json file like here
"dependencies": {
"angular": "^1.5.0",
"bootstrap": "^3.2.0",
"angular-animate": "^1.5.0",
"emojionearea": "3.0.0"
},
when I try to run the project using grunt serve command it only include the js file in my index.html which is
<script src="components/emojionearea/dist/emojionearea.js"></script>
I also want to include the following css file
<link rel="stylesheet" href="components/emojionearea/dist/emojionearea.min.css">
How can I do that?
Here is my grunt file content
// Generated using generator-angular 0.15.1
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Automatically load required Grunt tasks
require('jit-grunt')(grunt, {
useminPrepare: 'grunt-usemin',
ngtemplates: 'grunt-angular-templates',
cdnify: 'grunt-google-cdn'
});
// Configurable paths for the application
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: appConfig,
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
tasks: ['newer:jshint:all', 'newer:jscs:all'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
jsTest: {
files: ['test/spec/{,*/}*.js'],
tasks: ['newer:jshint:test', 'newer:jscs:test', 'karma']
},
styles: {
files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
tasks: ['newer:copy:styles', 'postcss']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
// The actual grunt server settings
connect: {
options: {
port: 29060,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/app/components',
connect.static('./components')
),
connect().use(
'/app/styles',
connect.static('./app/styles')
),
connect.static(appConfig.app)
];
}
}
},
test: {
options: {
port: 9001,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect.static('test'),
connect().use(
'/app/components',
connect.static('./components')
),
connect.static(appConfig.app)
];
}
}
},
dist: {
options: {
open: true,
base: '<%= yeoman.dist %>'
}
}
},
// Make sure there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: {
src: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
]
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,*/}*.js']
}
},
// Make sure code styles are up to par
jscs: {
options: {
config: '.jscsrc',
verbose: true
},
all: {
src: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
]
},
test: {
src: ['test/spec/{,*/}*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/{,*/}*',
'!<%= yeoman.dist %>/.git{,*/}*'
]
}]
},
server: '.tmp'
},
// Add vendor prefixed styles
postcss: {
options: {
processors: [
require('autoprefixer-core')({browsers: ['last 1 version']})
]
},
server: {
options: {
map: true
},
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: /\.\.\//
},
test: {
devDependencies: true,
src: '<%= karma.unit.configFile %>',
ignorePath: /\.\.\//,
fileTypes:{
js: {
block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'{{filePath}}\','
}
}
}
}
},
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
// Performs rewrites based on filerev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'],
options: {
assetsDirs: [
'<%= yeoman.dist %>',
'<%= yeoman.dist %>/images',
'<%= yeoman.dist %>/styles'
],
patterns: {
js: [[/(images\/[^''""]*\.(png|jpg|jpeg|gif|webp|svg))/g, 'Replacing references to images']]
}
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
ngtemplates: {
dist: {
options: {
module: 'AIMirLMSApp',
htmlmin: '<%= htmlmin.dist.options %>',
usemin: 'scripts/scripts.js'
},
cwd: '<%= yeoman.app %>',
src: 'views/{,*/}*.html',
dest: '.tmp/templateCache.js'
}
},
// ng-annotate tries to make the code safe for minification automatically
// by using the Angular long form for dependency injection.
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
},
// Replace Google CDN references
cdnify: {
dist: {
html: ['<%= yeoman.dist %>/*.html']
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'*.html',
'images/{,*/}*.{webp}',
'styles/fonts/{,*/}*.*'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= yeoman.dist %>/images',
src: ['generated/*']
}, {
expand: true,
cwd: 'app/components/bootstrap/dist',
src: 'fonts/*',
dest: '<%= yeoman.dist %>'
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'copy:styles'
],
test: [
'copy:styles'
],
dist: [
'copy:styles',
'imagemin',
'svgmin'
]
},
// Test settings
karma: {
unit: {
configFile: 'test/karma.conf.js',
singleRun: true
}
}
});
grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'wiredep',
'concurrent:server',
'postcss:server',
'connect:livereload',
'watch'
]);
});
grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve:' + target]);
});
grunt.registerTask('test', [
'clean:server',
'wiredep',
'concurrent:test',
'postcss',
'connect:test',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'wiredep',
'useminPrepare',
'concurrent:dist',
'postcss',
'ngtemplates',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', [
'newer:jshint',
'newer:jscs',
'test',
'build'
]);
};
Add placeholders for your bower css dependencies in head of index.html
<!-- bower:css -->
<!-- endbower -->
Note that by default this includes style tags for all distributed css files in bower components. You can make a fine grain selection by passing ignorePath or exclude options to wiredep grunt task.
Reference
https://github.com/taptapship/wiredep#getting-started

error while configuring grunt-forever

I am on CentOS and working on a node.js - angular.js project and I have setup node to keep running in background even when logged out from the terminal, using forever module.
I want to do the same for the frontend so it keeps running in the background. I am using Yoeman in the frontend and so far I have been launching the frontend using the command grunt serve.
I installed module grunt-forever but I am having issue configuring it.
After referring to the docs, I added:
grunt.loadNpmTasks('grunt-forever');
at the beginning of the module.exports block and the following config at the bottom of the module.exports code block:
forever: {
lorem-server: {
options: {
index: 'app/app.js',
logDir: 'logs'
}
}
}
When I did grunt test, I got:
root#mail [417 03:12:25 /projects/lorem/client]# grunt test
/projects/lorem/client/Gruntfile.js:481
lorem-server: {
^
Loading "Gruntfile.js" tasks...ERROR
>> SyntaxError: Unexpected token :
Warning: Task "test" not found. Use --force to continue.
Aborted due to warnings.
I still went ahead and did grunt forever:lorem-server:start, but got the same error.
Here is my complete Gruntfile.js:
// Generated on 2015-05-04 using generator-angular 0.11.1
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-forever');
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Configurable paths for the application
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: appConfig,
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
jsTest: {
files: ['test/spec/{,*/}*.js'],
tasks: ['newer:jshint:test', 'karma']
},
compass: {
files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
tasks: ['compass:server', 'autoprefixer']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect().use(
'/app/styles',
connect.static('./app/styles')
),
connect.static(appConfig.app)
];
}
}
},
test: {
options: {
port: 9001,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect.static('test'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app)
];
}
}
},
dist: {
options: {
open: true,
base: '<%= yeoman.dist %>'
}
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: {
src: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
]
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,*/}*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/{,*/}*',
'!<%= yeoman.dist %>/.git{,*/}*'
]
}]
},
server: '.tmp'
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
server: {
options: {
map: true,
},
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: /\.\.\//
},
test: {
devDependencies: true,
src: '<%= karma.unit.configFile %>',
ignorePath: /\.\.\//,
fileTypes:{
js: {
block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'{{filePath}}\','
}
}
}
},
sass: {
src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
ignorePath: /(\.\.\/){1,2}bower_components\//
}
},
// Compiles Sass to CSS and generates necessary files if requested
compass: {
options: {
sassDir: '<%= yeoman.app %>/styles',
cssDir: '.tmp/styles',
generatedImagesDir: '.tmp/images/generated',
imagesDir: '<%= yeoman.app %>/images',
javascriptsDir: '<%= yeoman.app %>/scripts',
fontsDir: '<%= yeoman.app %>/styles/fonts',
importPath: './bower_components',
httpImagesPath: '/images',
httpGeneratedImagesPath: '/images/generated',
httpFontsPath: '/styles/fonts',
relativeAssets: false,
assetCacheBuster: false,
raw: 'Sass::Script::Number.precision = 10\n'
},
dist: {
options: {
generatedImagesDir: '<%= yeoman.dist %>/images/generated'
}
},
server: {
options: {
sourcemap: true
}
}
},
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
// Performs rewrites based on filerev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
options: {
assetsDirs: [
'<%= yeoman.dist %>',
'<%= yeoman.dist %>/images',
'<%= yeoman.dist %>/styles'
]
}
},
// The following *-min tasks will produce minified files in the dist folder
// By default, your `index.html`'s <!-- Usemin block --> will take care of
// minification. These next options are pre-configured if you do not wish
// to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= yeoman.dist %>/styles/main.css': [
// '.tmp/styles/{,*/}*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= yeoman.dist %>/scripts/scripts.js': [
// '<%= yeoman.dist %>/scripts/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true,
removeOptionalTags: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'views/{,*/}*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// ng-annotate tries to make the code safe for minification automatically
// by using the Angular long form for dependency injection.
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
},
// Replace Google CDN references
cdnify: {
dist: {
html: ['<%= yeoman.dist %>/*.html']
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'.htaccess',
'*.html',
'views/{,*/}*.html',
'images/{,*/}*.{webp}',
'styles/fonts/{,*/}*.*'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= yeoman.dist %>/images',
src: ['generated/*']
}, {
expand: true,
cwd: '.',
src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
dest: '<%= yeoman.dist %>'
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'compass:server'
],
test: [
'compass'
],
dist: [
'compass:dist',
'imagemin',
'svgmin'
]
},
// Test settings
karma: {
unit: {
configFile: 'test/karma.conf.js',
singleRun: true
}
}
});
grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'wiredep',
'concurrent:server',
'autoprefixer:server',
'connect:livereload',
'watch'
]);
});
grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve:' + target]);
});
grunt.registerTask('test', [
'clean:server',
'wiredep',
'concurrent:test',
'autoprefixer',
'connect:test',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'wiredep',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', [
'newer:jshint',
'test',
'build'
]);
forever: {
loremserver: {
options: {
index: 'app/app.js',
logDir: 'logs'
}
}
}
};
What am I doing wrong here? Can someone please point me in the right direction?
Your declaration is invalid, dash symbol interrupts the property name; try quotes to explicitly say the property name
"lorem-server":
Also you must put your code inside of initconfig, e.g.
grunt.initConfig({
forever: {
loremserver: {
options: {
index: 'app/app.js',
logDir: 'logs'
}
}
},
// Project settings
yeoman: appConfig,

How to disable uglify file renaming for a specific file in production build?

In my production build uglify breaks the linkage to a referenced file in my css, due to file renaming. It seems there is a simple fix for this, but in my google search, I apparently missed it. Any help would be greatly appreciated!
The css file:
.priority_stars {
background: url:('assets/images/star.gif') no repeat;
....
}
And as expected, uglify renames the file as expected. This breaks the linkage. Is there a simple way to get grunt to automatically rename this file. This clearly is already taking place most every other type of file linkage in my build, just not in my css files. How to tell the Gruntfile.js to do this?
Here is the Gruntfile.js (from the yo angular-fullstack framework):
// Generated on 2015-02-27 using generator-angular-fullstack 2.0.13
'use strict';
module.exports = function (grunt) {
var localConfig;
try {
localConfig = require('./server/config/local.env');
} catch(e) {
localConfig = {};
}
// Load grunt tasks automatically, when needed
require('jit-grunt')(grunt, {
express: 'grunt-express-server',
useminPrepare: 'grunt-usemin',
ngtemplates: 'grunt-angular-templates',
cdnify: 'grunt-google-cdn',
protractor: 'grunt-protractor-runner',
injector: 'grunt-asset-injector',
buildcontrol: 'grunt-build-control'
});
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
pkg: grunt.file.readJSON('package.json'),
yeoman: {
// configurable paths
client: require('./bower.json').appPath || 'client',
dist: 'dist'
},
express: {
options: {
port: process.env.PORT || 9000
},
dev: {
options: {
script: 'server/app.js',
debug: true
}
},
prod: {
options: {
script: 'dist/server/app.js'
}
}
},
open: {
server: {
url: 'http://localhost:<%= express.options.port %>'
}
},
watch: {
injectJS: {
files: [
'<%= yeoman.client %>/{app,components}/**/*.js',
'!<%= yeoman.client %>/{app,components}/**/*.spec.js',
'!<%= yeoman.client %>/{app,components}/**/*.mock.js',
'!<%= yeoman.client %>/app/app.js'],
tasks: ['injector:scripts']
},
injectCss: {
files: [
'<%= yeoman.client %>/{app,components}/**/*.css'
],
tasks: ['injector:css']
},
mochaTest: {
files: ['server/**/*.spec.js'],
tasks: ['env:test', 'mochaTest']
},
jsTest: {
files: [
'<%= yeoman.client %>/{app,components}/**/*.spec.js',
'<%= yeoman.client %>/{app,components}/**/*.mock.js'
],
tasks: ['newer:jshint:all', 'karma']
},
injectSass: {
files: [
'<%= yeoman.client %>/{app,components}/**/*.{scss,sass}'],
tasks: ['injector:sass']
},
sass: {
files: [
'<%= yeoman.client %>/{app,components}/**/*.{scss,sass}'],
tasks: ['sass', 'autoprefixer']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
files: [
'{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css',
'{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html',
'{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js',
'!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js',
'!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js',
'<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}'
],
options: {
livereload: true
}
},
express: {
files: [
'server/**/*.{js,json}'
],
tasks: ['express:dev', 'wait'],
options: {
livereload: true,
nospawn: true //Without this option specified express won't be reloaded
}
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '<%= yeoman.client %>/.jshintrc',
reporter: require('jshint-stylish'),
"jasmine": true
},
server: {
options: {
jshintrc: 'server/.jshintrc',
"jasmine": true
},
src: [
'server/**/*.js',
'!server/**/*.spec.js'
]
},
serverTest: {
options: {
jshintrc: 'server/.jshintrc-spec',
"jasmine": true
},
src: ['server/**/*.spec.js']
},
all: [
'<%= yeoman.client %>/{app,components}/**/*.js',
'!<%= yeoman.client %>/{app,components}/**/*.spec.js',
'!<%= yeoman.client %>/{app,components}/**/*.mock.js'
],
test: {
src: [
'<%= yeoman.client %>/{app,components}/**/*.spec.js',
'<%= yeoman.client %>/{app,components}/**/*.mock.js'
]
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*',
'!<%= yeoman.dist %>/.openshift',
'!<%= yeoman.dist %>/Procfile'
]
}]
},
server: '.tmp'
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '.tmp/',
src: '{,*/}*.css',
dest: '.tmp/'
}]
}
},
// Debugging with node inspector
'node-inspector': {
custom: {
options: {
'web-host': 'localhost'
}
}
},
// Use nodemon to run server in debug mode with an initial breakpoint
nodemon: {
debug: {
script: 'server/app.js',
options: {
nodeArgs: ['--debug-brk'],
env: {
PORT: process.env.PORT || 9000
},
callback: function (nodemon) {
nodemon.on('log', function (event) {
console.log(event.colour);
});
// opens browser on initial server start
nodemon.on('config:update', function () {
setTimeout(function () {
require('open')('http://localhost:8080/debug?port=5858');
}, 500);
});
}
}
}
},
// Automatically inject Bower components into the app
wiredep: {
target: {
src: '<%= yeoman.client %>/index.html',
ignorePath: '<%= yeoman.client %>/',
exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/', /bootstrap.css/, /font-awesome.css/ ]
}
},
// Renames files for browser caching purposes
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/public/{,*/}*.js',
'<%= yeoman.dist %>/public/{,*/}*.css',
'<%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/public/assets/fonts/*'
]
}
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: ['<%= yeoman.client %>/index.html'],
options: {
dest: '<%= yeoman.dist %>/public'
}
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/public/{,*/}*.html'],
css: ['<%= yeoman.dist %>/public/{,*/}*.css'],
js: ['<%= yeoman.dist %>/public/{,*/}*.js'],
options: {
assetsDirs: [
'<%= yeoman.dist %>/public',
'<%= yeoman.dist %>/public/assets/images'
],
// This is so we update image references in our ng-templates
patterns: {
js: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
}
}
},
// The following *-min tasks produce minified files in the dist folder
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.client %>/assets/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/public/assets/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.client %>/assets/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/public/assets/images'
}]
}
},
// Allow the use of non-minsafe AngularJS files. Automatically makes it
// minsafe compatible so Uglify does not destroy the ng references
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat',
src: '*/**.js',
dest: '.tmp/concat'
}]
}
},
uglify: {
options: {
mangle: false
},
},
cssmin: {
},
// Package all the html partials into a single javascript payload
ngtemplates: {
options: {
// This should be the name of your apps angular module
module: 'tracker2App',
htmlmin: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
},
usemin: 'app/app.js'
},
main: {
cwd: '<%= yeoman.client %>',
src: ['{app,components}/**/*.html'],
dest: '.tmp/templates.js'
},
tmp: {
cwd: '.tmp',
src: ['{app,components}/**/*.html'],
dest: '.tmp/tmp-templates.js'
}
},
// Replace Google CDN references
cdnify: {
dist: {
html: ['<%= yeoman.dist %>/public/*.html']
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.client %>',
dest: '<%= yeoman.dist %>/public',
src: [
'*.{ico,png,txt}',
'.htaccess',
'bower_components/**/*',
'assets/images/{,*/}*.{webp}',
'assets/fonts/**/*',
'index.html'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= yeoman.dist %>/public/assets/images',
src: ['generated/*']
}, {
expand: true,
dest: '<%= yeoman.dist %>',
src: [
'package.json',
'server/**/*'
]
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.client %>',
dest: '.tmp/',
src: ['{app,components}/**/*.css']
}
},
buildcontrol: {
options: {
dir: 'dist',
commit: true,
push: true,
connectCommits: false,
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
},
heroku: {
options: {
remote: 'heroku',
branch: 'master'
}
},
openshift: {
options: {
remote: 'openshift',
branch: 'master'
}
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'sass',
],
test: [
'sass',
],
debug: {
tasks: [
'nodemon',
'node-inspector'
],
options: {
logConcurrentOutput: true
}
},
dist: [
'sass',
'imagemin',
'svgmin'
]
},
// Test settings
karma: {
unit: {
configFile: 'karma.conf.js',
singleRun: true
}
},
mochaTest: {
options: {
reporter: 'spec'
},
src: ['server/**/*.spec.js']
},
protractor: {
options: {
configFile: 'protractor.conf.js'
},
chrome: {
options: {
args: {
browser: 'chrome'
}
}
}
},
env: {
test: {
NODE_ENV: 'test'
},
prod: {
NODE_ENV: 'production'
},
all: localConfig
},
// Compiles Sass to CSS
sass: {
server: {
options: {
loadPath: [
'<%= yeoman.client %>/bower_components',
'<%= yeoman.client %>/app',
'<%= yeoman.client %>/components'
],
compass: false
},
files: {
'.tmp/app/app.css' : '<%= yeoman.client %>/app/app.scss'
}
}
},
injector: {
options: {
},
// Inject application script files into index.html (doesn't include bower)
scripts: {
options: {
transform: function(filePath) {
filePath = filePath.replace('/client/', '');
filePath = filePath.replace('/.tmp/', '');
return '<script src="' + filePath + '"></script>';
},
starttag: '<!-- injector:js -->',
endtag: '<!-- endinjector -->'
},
files: {
'<%= yeoman.client %>/index.html': [
['{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js',
'!{.tmp,<%= yeoman.client %>}/app/app.js',
'!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js',
'!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js']
]
}
},
// Inject component scss into app.scss
sass: {
options: {
transform: function(filePath) {
filePath = filePath.replace('/client/app/', '');
filePath = filePath.replace('/client/components/', '');
return '#import \'' + filePath + '\';';
},
starttag: '// injector',
endtag: '// endinjector'
},
files: {
'<%= yeoman.client %>/app/app.scss': [
'<%= yeoman.client %>/{app,components}/**/*.{scss,sass}',
'!<%= yeoman.client %>/app/app.{scss,sass}'
]
}
},
// Inject component css into index.html
css: {
options: {
transform: function(filePath) {
filePath = filePath.replace('/client/', '');
filePath = filePath.replace('/.tmp/', '');
return '<link rel="stylesheet" href="' + filePath + '">';
},
starttag: '<!-- injector:css -->',
endtag: '<!-- endinjector -->'
},
files: {
'<%= yeoman.client %>/index.html': [
'<%= yeoman.client %>/{app,components}/**/*.css'
]
}
}
},
});
// Used for delaying livereload until after server has restarted
grunt.registerTask('wait', function () {
grunt.log.ok('Waiting for server reload...');
var done = this.async();
setTimeout(function () {
grunt.log.writeln('Done waiting!');
done();
}, 1500);
});
grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
this.async();
});
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']);
}
if (target === 'debug') {
return grunt.task.run([
'clean:server',
'env:all',
'injector:sass',
'concurrent:server',
'injector',
'wiredep',
'autoprefixer',
'concurrent:debug'
]);
}
grunt.task.run([
'clean:server',
'env:all',
'injector:sass',
'concurrent:server',
'injector',
'wiredep',
'autoprefixer',
'express:dev',
'wait',
'open',
'watch'
]);
});
grunt.registerTask('server', function () {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve']);
});
grunt.registerTask('test', function(target) {
if (target === 'server') {
return grunt.task.run([
'env:all',
'env:test',
'mochaTest'
]);
}
else if (target === 'client') {
return grunt.task.run([
'clean:server',
'env:all',
'injector:sass',
'concurrent:test',
'injector',
'autoprefixer',
'karma'
]);
}
else if (target === 'e2e') {
return grunt.task.run([
'clean:server',
'env:all',
'env:test',
'injector:sass',
'concurrent:test',
'injector',
'wiredep',
'autoprefixer',
'express:dev',
'protractor'
]);
}
else grunt.task.run([
'test:server',
'test:client'
]);
});
grunt.registerTask('build', [
'clean:dist',
'injector:sass',
'concurrent:dist',
'injector',
'wiredep',
'useminPrepare',
'autoprefixer',
'ngtemplates',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'rev',
'usemin'
]);
grunt.registerTask('default', [
'newer:jshint',
'test',
'build'
]);
};
Thanks to the post by Yauheni Leichanok I discovered that the issue was in the rev plugin and I was able to fix my issue by simply adding a pattern to usemin settings in the Gruntfile.js to also handle *.css files. Here is the fix/adjustment to the Gruntfile.js:
css: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
Or showing it in context for clarity:
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/public/{,*/}*.html'],
css: ['<%= yeoman.dist %>/public/{,*/}*.css'],
js: ['<%= yeoman.dist %>/public/{,*/}*.js'],
options: {
assetsDirs: [
'<%= yeoman.dist %>/public',
'<%= yeoman.dist %>/public/assets/images'
],
// This is so we update image references in our ng-templates
patterns: {
js: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
],
css: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
}
}
},

Uglify with sourceMap option enabled don't match the filerev js file it should reference

Hello,
I just enable sourceMap option of the grunt-contrib-uglify module to generate a map file for my concat-jshint-minified-filerev javascript files.
In the dist directory I can see :
dist/
vendor.6ff9fbf4.js
vendor.6ff9fbf4.js.map
But if I edit vendor.6ff9fbf4.js, I can see at the end of file that the referenced sourceMap is wrong
/* much, much, much minified code here */
//# sourceMappingURL=vendor.js.map
And if I open vendor.6ff9fbf4.js.map I can see at the first line that the reference source file is wrong too.
{"version":3,
"file":"vendor.js",
"sources":["../../.tmp/concat/scripts/vendor.js"]
/* much, much, much more things here */
}
Obviously, the files are not renamed after the grunt-filerev task.
Any suggestions on how to do that?
Config: Gruntfile.js
// Generated on 2014-06-19 using generator-angular 0.9.0-1
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-ng-annotate');
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Configurable paths for the application
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: appConfig,
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js: {
files: ['<%= yeoman.app %>/scripts/{,*/**/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
jsTest: {
files: ['test/spec/{,*/**/}*.js'],
tasks: ['newer:jshint:test', 'karma']
},
styles: {
files: ['<%= yeoman.app %>/styles/{,*/**/}*.css'],
tasks: ['newer:copy:styles', 'autoprefixer']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/{,*/**/}*.html',
'.tmp/styles/{,*/**/}*.css',
'<%= yeoman.app %>/images/{,*/**/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: '0.0.0.0',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app)
];
}
}
},
test: {
options: {
port: 9001,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect.static('test'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app)
];
}
}
},
dist: {
options: {
open: true,
base: '<%= yeoman.dist %>'
}
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: {
src: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/**/}*.js'
]
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,*/**/}*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/{,*/**/}*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/**/}*.css',
dest: '.tmp/styles/'
}]
}
},
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: new RegExp('^<%= yeoman.app %>/|../')
}
},
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/**/}*.js',
'<%= yeoman.dist %>/styles/{,*/**/}*.css',
'<%= yeoman.dist %>/images/{,*/**/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
// Performs rewrites based on filerev and the useminPrepare configuration
//html: ['<%= yeoman.dist %>/{,*/**/}*.html'],
usemin: {
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/**/}*.css'],
options: {
assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
}
},
// The following *-min tasks will produce minified files in the dist folder
// By default, your `index.html`'s <!-- Usemin block --> will take care of
// minification. These next options are pre-configured if you do not wish
// to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= yeoman.dist %>/styles/main.css': [
// '.tmp/styles/{,*/}*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= yeoman.dist %>/scripts/scripts.js': [
// '<%= yeoman.dist %>/scripts/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true,
removeOptionalTags: true
},
// src: ['*.html', 'views/{,*/**/}*.html'],
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'views/**/*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// ngAnnotate tries to make the code safe for minification automatically by
// using the Angular long form for dependency injection. It doesn't work on
// things like resolve or inject so those have to be done manually.
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
},
// Replace Google CDN references
cdnify: {
dist: {
html: ['<%= yeoman.dist %>/*.html']
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
//'views/{,*/}*.html',
src: [
'*.{ico,png,txt}',
'.htaccess',
'*.html',
'views/**/*.html',
'images/{,*/}*.{webp}',
'fonts/*'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= yeoman.dist %>/images',
src: ['generated/*']
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'copy:styles'
],
test: [
'copy:styles'
],
dist: [
'copy:styles',
'imagemin',
'svgmin'
]
},
// Test settings
karma: {
unit: {
configFile: 'test/karma.conf.js',
singleRun: true
}
} ,
uglify: {
options : {
sourceMap : true,
compress : true,
mangle : true
}
}
});
grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'wiredep',
'concurrent:server',
'autoprefixer',
'connect:livereload',
'watch'
]);
});
grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve:' + target]);
});
grunt.registerTask('test', [
'clean:server',
'concurrent:test',
'autoprefixer',
'connect:test',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'wiredep',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', [
'newer:jshint',
'test',
'build'
]);
};
Recently faced this problem (it's actually a set of problems). Here is how I solved it.
First of all had to update the grunt plugin dependencies for:
"grunt-filerev": "^2.3.*"
"grunt-contrib-uglify": "^0.9.*",
The first one because the latest filerev also changes the source map names to the reved ones (matching the .js), the second because the version I was using didn't even had the sourceMap boolean option.
But this was not enough. You have to make the "uglification" on the dist folder. With your setup
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
you are making it inside the concat folder and the uglify task is resolving the relative path to "sources":["../../.tmp/concat/scripts/vendor.js"].
So remove the 'uglifyjs' from the js steps array.
steps: {
js: ['concat'],
css: ['cssmin']
},
We now need to indicate where the uglification will be made so:
uglify: {
options : {
sourceMap : true,
compress : true,
mangle : true
},
all: {
files: [{
expand: true,
cwd: '<%= yeoman.dist %>/scripts',
src: [
'*.js',
'!*<%= yeoman.beforeUglifyMarker %>.js'
],
dest: '<%= yeoman.dist %>/scripts',
ext: '.js'
}]
}
}
You will notice the <%= yeoman.beforeUglifyMarker %>. You should create a property beforeUglifyMarker in the appConfig object, that we will use to exclude the non uglified original files from the uglification, keeping them available in the same path as the source map.
So before the uglify task runs we will duplicate the files that will be uglified, renaming them with the value of yeoman.beforeUglifyMarker (so we can keep the originals to match with the source map).
In your copy task create 2 targets:
savePrettyVersion: {
expand: true,
cwd: '<%= yeoman.dist %>/scripts',
dest: '<%= yeoman.dist %>/scripts',
src: '*.js',
rename: function(dest, src){
var name = src.split('.')[0];
return dest + '/' + name + appConfig.beforeUglifyMarker + '.js';
}
},
restorePrettyVersion: {
expand: true,
cwd: '<%= yeoman.dist %>/scripts',
dest: '<%= yeoman.dist %>/scripts',
src: '*<%= yeoman.beforeUglifyMarker %>.js',
rename: function(dest, src){
var originalParts = src.split(appConfig.beforeUglifyMarker);
return dest + '/' + originalParts.join('');
}
},
Use them in your build task like this:
'copy:savePrettyVersion',
'uglify',
'filerev',
'usemin',
'htmlmin',
'copy:restorePrettyVersion',
'clean:nonUglyTempFiles',
The last on is optional, just cleans the temp files.
Then make sure you exclude the temp files (marked with beforeUglifyMarker) from the filerev uglify and usemin tasks with a negated pattern or a regex.
usemin: {
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/**/}*.css'],
js: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'!<%= yeoman.dist %>/scripts/*<%= yeoman.beforeUglifyMarker %>.js',
],
options: {
assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
}
},
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/**/}*.js',
'!<%= yeoman.dist %>/scripts/*<%= yeoman.beforeUglifyMarker %>.js',
'<%= yeoman.dist %>/styles/{,*/**/}*.css',
'<%= yeoman.dist %>/images/{,*/**/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
},
This is a hacky workaround, that works but should be obviated in the future, look/watch this issue on usemin. Meanwhile I hope its helpfull.
Finally, I updated all my packages and all goes smoothly.
Here after my uglify task :
uglify: {
options : {
sourceMap : true,
compress : {},
mangle : true
}
}
Here after an extract of my package.json
{
"grunt": "^0.4.5",
"grunt-autoprefixer": "^2.2.0",
"grunt-concurrent": "^1.0.0",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-connect": "^0.9.0",
"grunt-contrib-copy": "^0.7.0",
"grunt-contrib-cssmin": "^0.11.0",
"grunt-contrib-htmlmin": "^0.3.0",
"grunt-contrib-imagemin": "^0.9.4",
"grunt-contrib-jshint": "^0.11.0",
"grunt-contrib-uglify": "^0.7.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-filerev": "^2.1.2",
"grunt-google-cdn": "^0.4.3",
"grunt-karma": "~0.10.1",
"grunt-newer": "^1.1.0",
"grunt-ng-annotate": "~0.9.2",
"grunt-svgmin": "^2.0.0",
"grunt-usemin": "^3.0.0",
"grunt-wiredep": "^2.0.0",
"jshint-stylish": "^1.0.0",
"karma": "~0.12.31",
"karma-chrome-launcher": "~0.1.7",
"karma-jasmine": "~0.3.5",
"karma-ng-html2js-preprocessor": "~0.1.2",
"karma-phantomjs-launcher": "~0.1.4",
"load-grunt-tasks": "^3.0.0",
"time-grunt": "^1.0.0"
}

Grunt watch warning: EPERM, operation not permitted

I have a very annoying problem with gruntjs and grunt-contrib-watch plugin. After some time, I can not define, it may be half an hour or two hours of work (it's random) the console spits out this error:
Running "watch" task
Waiting...Warning: EPERM, operation not permitted 'C:\dev\project\app\index.html~RF97bf99.TMP'
it repeats ~50 times, and next this:
Warning: An error occurred while processing a template (An error occurred while processing a templat
e (Maximum call stack size exceeded).).
Warning: An error occurred while processing a template (An error occurred while processing a templat
e (An error occurred while processing a template (Maximum call stack size exceeded).).).
Warning: An error occurred while processing a template (An error occurred while processing a templat
e (An error occurred while processing a template (An error occurred while processing a template (Max
imum call stack size exceeded).).).).
Warning: An error occurred while processing a template (An error occurred while processing a templat
e (An error occurred while processing a template (An error occurred while processing a template (An
error occurred while processing a template (Maximum call stack size exceeded).).).).).
My Grunfile.js:
'use strict';
module.exports = function (grunt) {
// Load all grunt tasks
require('load-grunt-tasks')(grunt);
// Show elapsed time at the end
require('time-grunt')(grunt);
grunt.initConfig({
// Project settings
project: {
app: 'app',
dist: 'dist'
},
bootstrap: {
src: 'bower_components/sass-bootstrap',
pkg: grunt.file.readJSON('bower_components/sass-bootstrap/package.json'),
banner: '/*!\n' +
' * Bootstrap v<%= bootstrap.pkg.version %> by #fat and #mdo\n' +
' * Copyright <%= grunt.template.today("yyyy") %> <%= bootstrap.pkg.author %>\n' +
' * Licensed under <%= _.pluck(bootstrap.pkg.licenses, "url").join(", ") %>\n' +
' *\n' +
' * Designed and built with all the love in the world by #mdo and #fat.\n' +
' */\n\n',
jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n'
},
watch: {
sass: {
files: ['<%= project.app %>/sass/{,*/}*.scss'],
tasks: ['sass:dev', 'autoprefixer'],
options: {
spawn: false,
livereload: true
},
},
js: {
files: [
'<%= project.app %>/js/{,*/}*.js',
'!<%= project.app %>/js/vendor/{,*/}*.js',
'!<%= project.app %>/js/plugins/{,*/}*.js'
],
options: {
livereload: true
}
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= project.app %>/{,*/}*.html',
'<%= project.app %>/css/{,*/}*.css',
'<%= project.app %>/images/{,*/}*.{gif,jpeg,jpg,png,svg,webp}'
]
}
},
connect: {
options: {
port: 9000,
livereload: 35729,
// Change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
livereload: {
options: {
open: true,
base: '<%= project.app %>'
}
},
dist: {
options: {
open: true,
base: '<%= project.dist %>',
livereload: false
}
}
},
sass: {
options: {
includePaths: ['<%= bootstrap.src %>/lib/']
},
dev: {
files: {
'<%= project.app %>/css/app.css': '<%= project.app %>/sass/app.scss'
}
},
dist: {
files: {
'<%= project.app %>/css/app.css': '<%= project.app %>/sass/app.scss'
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 version']
},
dist: {
files: [{
expand: true,
cwd: '<%= project.app %>/css/',
src: '{,*/}*.css',
dest: '<%= project.app %>/css/'
}]
}
},
concat: {
options: {
nonull: true,
separator: '\n\n'
},
bootstrap: {
options: {
banner: '<%= bootstrap.banner %><%= bootstrap.jqueryCheck %>'
},
src: [
'<%= bootstrap.src %>/js/transition.js',
// '<%= bootstrap.src %>/js/alert.js',
// '<%= bootstrap.src %>/js/button.js',
// '<%= bootstrap.src %>/js/carousel.js',
'<%= bootstrap.src %>/js/collapse.js',
'<%= bootstrap.src %>/js/dropdown.js',
// '<%= bootstrap.src %>/js/modal.js',
// '<%= bootstrap.src %>/js/tooltip.js',
// '<%= bootstrap.src %>/js/popover.js',
// '<%= bootstrap.src %>/js/scrollspy.js',
// '<%= bootstrap.src %>/js/tab.js',
// '<%= bootstrap.src %>/js/affix.js'
],
dest: '<%= project.app %>/js/plugins/bootstrap.js'
},
plugins: {
files: {
'<%= project.app %>/js/plugins.js': ['<%= project.app %>/js/plugins/bootstrap.min.js', '<%= project.app %>/js/plugins/*.min.js']
}
}
},
uglify: {
bootstrap: {
options: {
banner: '<%= bootstrap.banner %>'
},
files: {
'<%= project.app %>/js/plugins/bootstrap.min.js': '<%= concat.bootstrap.dest %>'
}
},
all: {
options: {
preserveComments: 'some',
report: 'min'
},
files: [{
expand: true,
cwd: '<%= project.app %>/js/',
src: ['*.js', '!plugins.js'],
ext: '.min.js',
dest: '<%= project.app %>/js/'
}]
}
},
cssmin: {
options: {
report: 'min'
},
dist: {
expand: true,
cwd: '<%= project.app %>/css/',
src: ['*.css', '!*.min.css'],
dest: '<%= cssmin.dist.cwd %>',
ext: '.min.css'
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= project.app %>/img',
src: ['{,*/}*.{gif,jpeg,jpg,png}'],
dest: '<%= project.dist %>/img'
}]
}
}
});
// Register tasks
// ==================================================
// Run local server
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'connect:livereload',
'watch'
]);
});
// Build JS
grunt.registerTask('build-js', [
'concat:bootstrap',
'uglify',
'concat:plugins'
]);
// Build CSS
grunt.registerTask('build-css', [
'sass:dist',
'autoprefixer',
'cssmin'
]);
// Minify images
grunt.registerTask('test', ['imagemin']);
// grunt.registerTask('default', ['']);
};
package.json (all dependencies are up to date):
{
"name": "project",
"version": "0.0.0",
"dependencies": {},
"devDependencies": {
"load-grunt-tasks": "~0.2.1",
"time-grunt": "~0.2.6",
"grunt": "~0.4.2",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-connect": "~0.6.0",
"grunt-autoprefixer": "~0.6.3",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-cssmin": "~0.7.0",
"grunt-sass": "~0.9.0",
"jpegtran-bin": "~0.2.0",
"grunt-contrib-imagemin": "~0.4.0"
}
}
I'm on Windows 7 32-bit, gruntjs v.0.4.2, nodejs v0.10.22. Any ideas what may causes this problem? And how would I fix it? Thanks a lot.
I had a similar problem "EPERM, operation not permitted..." and it was because i didn't opened the CMD in administrator mode.
So right click on CMD and run as administrator.
This solved my error.
I'm not familiar with this problem, but I think it might have something to do with the way you're using wildcards on your Gruntfile.js. A co-worker once complained that his watch task was taking an incredible amount of time to run, up to 3-4 minutes a time. He was using the syntax:
/css/{,*/}*.css
I didn't have that problem, so while making his Gruntfile.js more like mine, we found out that using:
/css/**/*.css
...solved the whole problem! All his tasks ran at less than 10 seconds.
I have made changes to your Gruntfile.js, you might want to use a diff tool to compare my version to yours and see what was changed.
I would also strongly recommend using jshint to lint the JavaScript in your project, specially the Gruntfile.js and any other configuration file important to Grunt you might have detached from your Gruntfile.js. An example here (look around the same project for more detail).
'use strict';
module.exports = function (grunt) {
// Load all grunt tasks
require('load-grunt-tasks')(grunt);
// Show elapsed time at the end
require('time-grunt')(grunt);
grunt.initConfig({
// Project settings
project: {
app: 'app',
dist: 'dist'
},
bootstrap: {
src: 'bower_components/sass-bootstrap',
pkg: grunt.file.readJSON('bower_components/sass-bootstrap/package.json'),
banner: '/*!\n' +
' * Bootstrap v<%= bootstrap.pkg.version %> by #fat and #mdo\n' +
' * Copyright <%= grunt.template.today("yyyy") %> <%= bootstrap.pkg.author %>\n' +
' * Licensed under <%= _.pluck(bootstrap.pkg.licenses, "url").join(", ") %>\n' +
' *\n' +
' * Designed and built with all the love in the world by #mdo and #fat.\n' +
' */\n\n',
jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n'
},
watch: {
sass: {
files: ['<%= project.app %>/sass/**/*.scss'],
tasks: ['sass:dev', 'autoprefixer'],
options: {
spawn: false,
livereload: true
},
},
js: {
files: [
'<%= project.app %>/js/**/*.js',
'!<%= project.app %>/js/vendor/**/*.js',
'!<%= project.app %>/js/plugins/**/*.js'
],
options: {
livereload: true
}
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= project.app %>/**/*.html',
'<%= project.app %>/css/**/*.css',
'<%= project.app %>/images/**/*.{gif,jpeg,jpg,png,svg,webp}'
]
}
},
connect: {
options: {
port: 9000,
livereload: 35729,
// Change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
livereload: {
options: {
open: true,
base: '<%= project.app %>'
}
},
dist: {
options: {
open: true,
base: '<%= project.dist %>',
livereload: false
}
}
},
sass: {
options: {
includePaths: ['<%= bootstrap.src %>/lib/']
},
dev: {
files: {
'<%= project.app %>/css/app.css': '<%= project.app %>/sass/app.scss'
}
},
dist: {
files: {
'<%= project.app %>/css/app.css': '<%= project.app %>/sass/app.scss'
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 version']
},
dist: {
files: [{
expand: true,
cwd: '<%= project.app %>/css',
src: '/**/*.css',
dest: '<%= project.app %>/css'
}]
}
},
concat: {
options: {
nonull: true,
separator: '\n\n'
},
bootstrap: {
options: {
banner: '<%= bootstrap.banner %><%= bootstrap.jqueryCheck %>'
},
src: [
'<%= bootstrap.src %>/js/transition.js',
// '<%= bootstrap.src %>/js/alert.js',
// '<%= bootstrap.src %>/js/button.js',
// '<%= bootstrap.src %>/js/carousel.js',
'<%= bootstrap.src %>/js/collapse.js',
'<%= bootstrap.src %>/js/dropdown.js'
// '<%= bootstrap.src %>/js/modal.js',
// '<%= bootstrap.src %>/js/tooltip.js',
// '<%= bootstrap.src %>/js/popover.js',
// '<%= bootstrap.src %>/js/scrollspy.js',
// '<%= bootstrap.src %>/js/tab.js',
// '<%= bootstrap.src %>/js/affix.js'
],
dest: '<%= project.app %>/js/plugins/bootstrap.js'
},
plugins: {
files: {
'<%= project.app %>/js/plugins.js': ['<%= project.app %>/js/plugins/bootstrap.min.js', '<%= project.app %>/js/plugins/*.min.js']
}
}
},
uglify: {
bootstrap: {
options: {
banner: '<%= bootstrap.banner %>'
},
files: {
'<%= project.app %>/js/plugins/bootstrap.min.js': '<%= concat.bootstrap.dest %>'
}
},
all: {
options: {
preserveComments: 'some',
report: 'min'
},
files: [{
expand: true,
cwd: '<%= project.app %>/js/',
src: ['*.js', '!plugins.js'],
ext: '.min.js',
dest: '<%= project.app %>/js/'
}]
}
},
cssmin: {
options: {
report: 'min'
},
dist: {
expand: true,
cwd: '<%= project.app %>/css/',
src: ['*.css', '!*.min.css'],
dest: '<%= cssmin.dist.cwd %>',
ext: '.min.css'
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= project.app %>/img',
src: ['/**/*.{gif,jpeg,jpg,png}'],
dest: '<%= project.dist %>/img'
}]
}
}
});
// Register tasks
// ==================================================
// Run local server
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'connect:livereload',
'watch'
]);
});
// Build JS
grunt.registerTask('build-js', [
'concat:bootstrap',
'uglify',
'concat:plugins'
]);
// Build CSS
grunt.registerTask('build-css', [
'sass:dist',
'autoprefixer',
'cssmin'
]);
// Minify images
grunt.registerTask('test', ['imagemin']);
// grunt.registerTask('default', ['']);
};
On linux, change directory permissions
$ sudo chmod -R 777 name_directory

Resources