Grunt CLI crashing trying to compile bootstrap.less - node.js

I have been working on my mac for some time on a web app, compiling using Grunt on my mac - never a problem compiling.
I just set the project up on a new ultrabook PC (Acer Spin 5, i5, 8gig RAM, small but capable), but if I don't remove almost all entries in bootstrap.less the compile fails.
I start my compile using:
grunt dev -env=dev -f --verbose
The error I'm getting is:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
I've read about setting "max-old-space-size" setting for node, but I am not entirely sure how to set it up in my gruntfile.js or package.json (I'm not so well versed in Grunt/Node on the whole).
If I remove all but the first couple of imports in bootstrap.less the compile finishes as expected.
My gruntfile is a bit of a monster too, too long to post in fact ;( I have removed irrelevant parts of it below:
module.exports = function (grunt) {
'use strict';
// Force use of Unix newlines
grunt.util.linefeed = '\n';
RegExp.quote = function (string) {
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
var fs = require('fs');
var path = require('path');
var npmShrinkwrap = require('npm-shrinkwrap');
var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
var getLessVarsData = function () {
var filePath = path.join(__dirname, 'less/variables.less');
var fileContent = fs.readFileSync(filePath, {
encoding: 'utf8'
});
var parser = new BsLessdocParser(fileContent);
return {
sections: parser.parseFile()
};
};
var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', {
encoding: 'utf8'
});
Object.keys(configBridge.paths).forEach(function (key) {
configBridge.paths[key].forEach(function (val, i, arr) {
arr[i] = path.join('./docs/assets', val);
});
});
grunt.loadNpmTasks('grunt-include-replace-more');
grunt.loadNpmTasks('grunt-dev-prod-switch');
grunt.loadNpmTasks('grunt-minify-html');
grunt.loadNpmTasks('grunt-symlink');
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
' * Plenty.rdthree.com v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
' * Copyright 2015-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' +
' */\n',
jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
// Task configuration.
clean: {
dist: 'dist',
docs: 'docs/dist',
pageSpecificJS: ['dist/js/pageScripts/*.js', '!dist/js/pageScripts/*.min.js'],
pageSpecificCSS: ['dist/css/*.css', '!dist/css/*.min.css']
},
jshint: {
options: {
jshintrc: 'js/.jshintrc'
},
grunt: {
options: {
jshintrc: 'grunt/.jshintrc'
},
src: ['Gruntfile.js', 'grunt/*.js']
},
core: {
src: 'js/*.js'
},
test: {
options: {
jshintrc: 'js/tests/unit/.jshintrc'
},
src: 'js/tests/unit/*.js'
},
assets: {
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
}
},
jscs: {
options: {
config: 'js/.jscsrc'
},
grunt: {
src: '<%= jshint.grunt.src %>'
},
core: {
src: '<%= jshint.core.src %>'
},
test: {
src: '<%= jshint.test.src %>'
},
assets: {
options: {
requireCamelCaseOrUpperCaseIdentifiers: null
},
src: '<%= jshint.assets.src %>'
}
},
symlink: {
prod: {
dest: 'dist/prod',
relativeSrc: '../dist',
options: {
type: 'dir'
} // 'file' by default
},
perf: {
dest: 'dist/perf',
relativeSrc: '../dist',
options: {
type: 'dir'
} // 'file' by default
},
},
concat: {
bootstrap: {
options: {
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
stripBanners: false
},
src: [
'js/vendor/jquery-migrate-3.0.0.js',
'js/bootstrap/transition.js',
'js/bootstrap/alert.js',
'js/bootstrap/button.js',
'js/bootstrap/carousel.js',
'js/bootstrap/collapse.js',
'js/bootstrap/dropdown.js',
'js/bootstrap/modal.js',
'js/bootstrap/tooltip.js',
'js/bootstrap/popover.js',
'js/bootstrap/tab.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
minifyHtml: {
options: {
cdata: true
},
dist: {
files: [{
expand: true,
cwd: 'dist',
src: ['*.html'],
dest: 'dist'
}]
}
},
dev_prod_switch: {
options: {
environment: grunt.option('env') || 'dev',
env_char: '#',
env_block_dev: 'env:dev',
env_block_prod: 'env:prod'
},
dynamic_mappings: {
files: [{
expand: true,
cwd: 'dist',
src: ['*.html', 'mobile/*.html', 'js/includes.js', 'mobile/js/mobile.js', 'js/includes.min.js', 'js/pageScripts/setup*.js', 'js/touch/touch-support*.js'],
dest: 'dist'
}]
}
},
uglify: {
options: {
preserveComments: 'false'
},
core: {
src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
},
includes: {
src: 'dist/js/includes.js',
dest: 'dist/js/includes.min.js'
},
mobile: {
src: 'dist/mobile/js/mobile.js',
dest: 'dist/mobile/js/mobile.min.js'
},
customize: {
src: configBridge.paths.customizerJs,
dest: 'docs/assets/js/customize.min.js'
},
docsJs: {
src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js'
},
dashboard: {
src: 'dist/js/pageScripts/dashboard.js',
dest: 'dist/js/pageScripts/dashboard.min.js'
},
setup: {
src: 'dist/js/pageScripts/setup.js',
dest: 'dist/js/pageScripts/setup.min.js'
},
billing: {
src: 'dist/js/pageScripts/billing.js',
dest: 'dist/js/pageScripts/billing.min.js'
},
schedule: {
src: 'dist/js/pageScripts/schedule.js',
dest: 'dist/js/pageScripts/schedule.min.js'
},
},
qunit: {
options: {
inject: 'js/tests/unit/phantom.js'
},
files: 'js/tests/index.html'
},
less: {
compileCore: {
options: {
strictMath: false,
sourceMap: false,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'less/bootstrap.less',
dest: 'dist/css/<%= pkg.name %>.css'
},
},
autoprefixer: {
options: {
browsers: configBridge.config.autoprefixerBrowsers
},
core: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>.css'
},
theme: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>-theme.css'
},
plenty: {
options: {
map: true
},
src: 'dist/css/plenty.css'
},
docs: {
src: ['docs/assets/css/anchor.css', 'docs/assets/css/src/docs.css']
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: ['**/*.css'],
dest: 'docs/examples/'
}
},
csslint: {
options: {
csslintrc: 'less/.csslintrc'
},
dist: [
'dist/css/bootstrap.css',
'dist/css/bootstrap-theme.css'
],
examples: [
'docs/examples/**/*.css'
],
docs: {
options: {
ids: false,
'overqualified-elements': false
},
src: 'docs/assets/css/src/docs.css'
}
},
cssmin: {
options: {
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
compatibility: 'ie8',
keepSpecialComments: '*',
advanced: false
},
minifyCore: {
src: 'dist/css/<%= pkg.name %>.css',
dest: 'dist/css/<%= pkg.name %>.min.css'
},
minifyTheme: {
src: 'dist/css/<%= pkg.name %>-theme.css',
dest: 'dist/css/<%= pkg.name %>-theme.min.css'
},
minifyPlenty: {
src: 'dist/css/plenty.css',
dest: 'dist/css/plenty.min.css'
},
minifySetup: {
src: 'dist/css/setup.css',
dest: 'dist/css/setup.min.css'
},
minifyDashboard: {
src: 'dist/css/dashboard.css',
dest: 'dist/css/dashboard.min.css'
},
},
usebanner: {
options: {
position: 'top',
banner: '<%= banner %>'
},
files: {
src: 'dist/css/*.css'
}
},
csscomb: {
options: {
config: 'less/.csscomb.json'
},
dist: {
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/'
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: '**/*.css',
dest: 'docs/examples/'
},
docs: {
src: 'docs/assets/css/src/docs.css',
dest: 'docs/assets/css/src/docs.css'
}
},
copy: {
fonts: {
expand: true,
src: 'fonts/*',
dest: 'dist/'
},
docs: {
expand: true,
cwd: 'dist/',
src: [
'**/*'
],
dest: 'docs/dist/'
},
img: {
cwd: 'img', // set working folder / root to copy
src: '**/*', // copy all files and subfolders
dest: 'dist/img', // destination folder
expand: true // required when using cwd
},
ajax: {
cwd: 'ajax', // set working folder / root to copy
src: '**/*', // copy all files and subfolders
dest: 'dist/ajax', // destination folder
expand: true // required when using cwd
},
},
includereplacemore: {
your_target: {
options: {
// Task-specific options go here.
},
// Files to perform replacements and includes with
src: ['*.html', 'ajax/*.html', "mobile/*.html"],
// Destination directory to copy files to
dest: 'dist/'
}
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
jekyll: {
options: {
config: '_config.yml'
},
docs: {},
github: {
options: {
raw: 'github: true'
}
}
},
jade: {
options: {
pretty: true,
data: getLessVarsData
},
customizerVars: {
src: 'docs/_jade/customizer-variables.jade',
dest: 'docs/_includes/customizer-variables.html'
},
customizerNav: {
src: 'docs/_jade/customizer-nav.jade',
dest: 'docs/_includes/nav/customize.html'
}
},
htmllint: {
options: {
ignore: [
'Attribute "autocomplete" not allowed on element "button" at this point.',
'Attribute "autocomplete" not allowed on element "input" at this point.',
'Element "img" is missing required attribute "src".'
]
},
src: '_gh_pages/**/*.html'
},
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:src', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: 'less'
}
},
sed: {
versionNumber: {
pattern: (function () {
var old = grunt.option('oldver');
return old ? RegExp.quote(old) : old;
})(),
replacement: grunt.option('newver'),
recursive: true
}
},
'saucelabs-qunit': {
all: {
options: {
build: process.env.TRAVIS_JOB_ID,
throttled: 10,
maxRetries: 3,
maxPollRetries: 4,
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
}
}
},
exec: {
npmUpdate: {
command: 'npm update'
}
},
compress: {
main: {
options: {
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
mode: 'zip',
level: 9,
pretty: true
},
files: [{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: 'bootstrap-<%= pkg.version %>-dist'
}]
}
}
});
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt, {
scope: 'devDependencies'
});
require('time-grunt')(grunt);
// Docs HTML validation task
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
var runSubset = function (subset) {
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
};
var isUndefOrNonZero = function (val) {
return val === undefined || val !== '0';
};
// Test task.
var testSubtasks = [];
// Skip core tests if running a different subset of the test suite
if (runSubset('core') &&
// Skip core tests if this is a Savage build
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
}
// Skip HTML validation if running a different subset of the test suite
if (runSubset('validate-html') &&
// Skip HTML5 validator on Travis when [skip validator] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
testSubtasks.push('validate-html');
}
// Only run Sauce Labs tests if there's a Sauce access key
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
// Skip Sauce if running a different subset of the test suite
runSubset('sauce-js-unit') &&
// Skip Sauce on Travis when [skip sauce] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
testSubtasks.push('connect');
testSubtasks.push('saucelabs-qunit');
}
grunt.registerTask('test', testSubtasks);
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs', 'includereplacemore']);
// JS minify page specific scripts
grunt.registerTask('uglify-pageScripts', ['uglify:dashboard', 'uglify:setup', 'uglify:billing', 'uglify:schedule', 'uglify:inventory', 'uglify:grainMarketing', 'uglify:map', 'uglify:organization', 'uglify:plans', 'uglify:schedule', 'uglify:services', 'uglify:servicessetup', 'uglify:timeentry', 'uglify:signin', 'uglify:characterPatterns', "uglify:mobile"]);
grunt.registerTask('uglify-touchScripts', ['uglify:touch_dashboard', 'uglify:touch_setup', 'uglify:touch_billing', 'uglify:touch_schedule', 'uglify:touch_inventory', 'uglify:touch_grainMarketing', 'uglify:touch_map', 'uglify:touch_organization', 'uglify:touch_plans', 'uglify:touch_schedule', 'uglify:touch_services', 'uglify:touch_servicessetup', 'uglify:touch_timeentry', 'uglify:touch_support']);
grunt.registerTask('uglify-mobile-pageScripts', ['uglify:mobile_activityList', 'uglify:mobile_addActivity', 'uglify:mobile_addPhotos', 'uglify:mobile_contactList', 'uglify:mobile_getPrepared', 'uglify:mobile_notes', 'uglify:mobile_schedule', 'uglify:mobile_taskDetail', 'uglify:mobile_taskInProgress', 'uglify:mobile_requirements', 'uglify:mobile_collect', 'uglify:mobile_viewPhoto', 'uglify:mobile_returnProducts', 'uglify:mobile_returnProduct']);
// Compile Page CSS
grunt.registerTask('compile-pageCSS', ['less:compilePlenty', 'less:compileSetup', 'less:compileDashboard', 'less:compileField', 'less:compileSignin', 'less:compileMobile']);
// Mimify CSS
grunt.registerTask('minify-pageCSS', ['cssmin:minifyCore', 'cssmin:minifyTheme', 'cssmin:minifyPlenty', 'cssmin:minifyDashboard', 'cssmin:minifySetup', 'cssmin:minifyField', 'cssmin:minifySignin', 'cssmin:minifyMobile']);
// JS Default Task.
grunt.registerTask('default-js', ['concat', 'uglify:core', 'uglify:includes', 'uglify:mobile', 'uglify-pageScripts', 'uglify-touchScripts', 'uglify-mobile-pageScripts', 'commonjs', 'includereplacemore']);
// CSS distribution task.
grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme', 'compile-pageCSS', 'minify-pageCSS']);
grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'autoprefixer:plenty', 'usebanner', 'csscomb:dist']);
// create symlinks
grunt.registerTask("symlinks", ['symlink:prod', 'symlink:perf']);
// Full distribution task.
grunt.registerTask('dev', ['clean:dist', 'dist-css', 'copy:fonts', 'copy:fontAwesome', 'symlinks', 'copy:weatherIcons', 'copy:intlTelInput', 'copy:phoneLib', 'copy:jquery311', 'copy:charPatterns', 'copy:pageScripts', 'copy:outdatedbrowser', 'copy:flatPickr', 'copy:hammertime', 'copy:hammerjs', 'copy:initialize', 'copy:touchsupport', 'copy:dropzone', 'copy:mobile', 'copy:xeditable', 'copy:manifest', 'copy:htaccess', 'copy:img', 'copy:ajax', 'dist-js', 'dev_prod_switch', 'minifyHtml']);
// Default task.
grunt.registerTask('prod', ['clean:dist', 'dist-css', 'copy:fonts', 'copy:fontAwesome', 'symlinks', 'copy:weatherIcons', 'copy:intlTelInput', 'copy:phoneLib', 'copy:jquery311', 'copy:charPatterns', 'copy:pageScripts', 'copy:outdatedbrowser', 'copy:flatPickr', 'copy:hammertime', 'copy:hammerjs', 'copy:initialize', 'copy:touchsupport', 'copy:dropzone', 'copy:mobile', 'copy:xeditable', 'copy:manifest', 'copy:htaccess', 'copy:img', 'copy:ajax', 'default-js', 'clean:pageSpecificJS', 'clean:pageSpecificCSS', 'dev_prod_switch', 'minifyHtml']);
// Version numbering task.
// grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
// This can be overzealous, so its changes should always be manually reviewed!
grunt.registerTask('change-version-number', 'sed');
grunt.registerTask('build-glyphicons-data', function () {
generateGlyphiconsData.call(this, grunt);
});
// task for building customizer
grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
grunt.registerTask('build-customizer-html', 'jade');
grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
var banner = grunt.template.process('<%= banner %>');
generateRawFiles(grunt, banner);
});
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
var srcFiles = grunt.config.get('concat.bootstrap.src');
var destFilepath = 'dist/js/npm.js';
generateCommonJSModule(grunt, srcFiles, destFilepath);
});
// Docs task.
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);
grunt.registerTask('prep-release', ['jekyll:github', 'compress']);
// Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
// This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
grunt.registerTask('_update-shrinkwrap', function () {
var done = this.async();
npmShrinkwrap({
dev: true,
dirname: __dirname
}, function (err) {
if (err) {
grunt.fail.warn(err);
}
var dest = 'test-infra/npm-shrinkwrap.json';
fs.renameSync('npm-shrinkwrap.json', dest);
grunt.log.writeln('File ' + dest.cyan + ' updated.');
done();
});
});
};
Any assistance appreciated - thanks

I split up the bootstrap.less into two files which solved the issue

Related

Grunt app deployed on Heroku ... and what's next ?... my app doesn't start

I have a basic node app with gruntfile and I had some pains to make the deploy. So I did a new grunt task, did some changes.
The deploy then is fine.
But now when I go to my app. I have a blank page.
It doesn't load the appConfig.app dir.
I probably miss something big I couldn't figure out. I thought grunt connect would do the wire but it seems server.js is waiting something.
NB : I think I don't need Express here.
Below are different files :
gruntfile.js
server.js
package.js
Gruntfile.js
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
var modRewrite = require('connect-modrewrite');
// Configurable paths for the application
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
//Environment vars
var envConfig = {
dev: {
//baseUrl: 'http://localhost:3000',
loginUrl: 'http://demo.lvh.me:9000',
uploadUrl: 'anuploadurl/upload'
},
prod: {
baseUrl: 'http://aprodurl',
loginUrl: 'an herokuapp url'
}
};
// Define the configuration for all the tasks
grunt.initConfig({
appConf: appConfig,
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= appConf.dist %>/{,*/}*',
'!<%= appConf.dist %>/.git*'
]
}],
options: {
force: true //since dist directory is outside, we must use force flag.
}
},
server: '.tmp'
},
//server settings
ngconstant: {
// Options for all targets
options: {
space: ' ',
wrap: '\'use strict\';\n\n {%= __ngModule %}',
name: 'config'
},
// Environment targets
development: {
options: {
dest: 'app/config.js'
},
constants: envConfig.dev
},
production: {
options: {
dest: '<%= appConf.app %>/config.js'
},
constants: envConfig.prod
}
},
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= appConf.app %>/index.html'],
ignorePath: /\.\.\//,
'options': {
'overrides': {
'semantic-ui': {
'main': ['dist/semantic.css', 'dist/semantic.js']
}
}
}
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= appConf.app %>/*',
dest: '<%= appConf.dist %>',
src: [
'*.{ico,png,txt}',
'{,*/}*.html',
'images/{,*/}*.{webp}',
'fonts/*'
]
}, {
expand: true,
cwd: '<%= appConf.app %>/images',
dest: '<%= appConf.dist %>/images',
src: ['generated/*']
}, { // not in use
expand: true,
cwd: 'bower_components/font-awesome',
src: 'fonts/*',
dest: '<%= appConf.dist %>'
}]
},
styles: {
files: [
{
expand: true,
cwd: '<%= appConf.app %>/*',
dest: '<%= appConf.dist %>/styles',
src: '{,*/}*.css'
}
]
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'copy:styles'
],
test: [
'copy:styles'
],
dist: [
'copy:styles',
'svgmin'
]
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '<%= appConf.app %>/*',
src: '{,*/}*.css',
dest: '<%= appConf.dist %>/styles/'
}]
}
},
// 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 [
modRewrite(['^[^\\.]*$ /index.html [L]']),
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app)
];
}
}
},
test: {
options: {
port: 9001,
middleware: function (connect) {
return [
rewriteRulesSnippet,
connect.static('.tmp'),
connect.static('test'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app)
];
}
}
},
dist: {
options: {
port:process.env.PORT,
open: true,
base: '<%= appConf.app %>'
}
}
},
// 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',
'server.js',
'<%= appConf.app %>/{,*/}*.js'
]
}
},
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js: {
files: ['<%= appConf.app %>/{,**/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
css: {
files: ['<%= appConf.app %>/styles/{,**/}*.scss'],
tasks: ['sass'],
options: {
livereload: true,
},
},
styles: {
files: [
'<%= appConf.app %>/styles/{,**/}*.css',
'<%= appConf.app %>/../templating/css/style.css'
],
tasks: ['newer:copy:styles', 'autoprefixer']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= appConf.app %>/{,**/}*.html',
'.tmp/styles/{,**/}*.css',
'<%= appConf.app %>/images/{,**/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
sass: {
dev: {
options: {
style: 'expanded'
},
files: [
{
expand: true,
cwd: '<%= appConf.app %>/styles',
src: ['*.scss'],
ext: '.css',
dest: '<%= appConf.dist %>/styles'
}
]
}
}
});
grunt.loadNpmTasks('grunt-sass');
// Build the development application
grunt.registerTask('serve', 'Compile then start a connect web server', function () {
grunt.task.run([
'clean:server',
'ngconstant:development',
'wiredep',
'sass',
'concurrent:server',
'autoprefixer',
'connect:livereload',
'watch'
]);
});
// Build the production application
grunt.registerTask('build', 'Compile app on production settings', function () {
grunt.task.run([
'clean:server',
'ngconstant:production',
'wiredep',
'sass',
'concurrent:server',
'autoprefixer',
'connect:dist'
]);
});
};
server.js
var http = require('http');
var port = process.env.PORT || 80;
http.createServer(function (req, res) {
res.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin' : '*'
});
//res.end('app/index.html');
}).listen(port);
console.log('Server running at port '+port);
package.json
{
"private": true,
"engines": {
"node": "4.2.2"
},
"scripts": {
"postinstall": "bower install && grunt build",
"start": "node server.js"
},
"dependencies": { all the dependancies .... }
}
It was a huge and harsh debugging but I finally found out.
It was coming from multiple mistakes and bugs I had :
the node server.js is useless
I invstigated on the grunt connect method and created a production setup
I splitted the grunt serve into a grunt build task and a webconnect task
Then updated package.json file
Very important for heroku : in Gruntfile.js on the connect object :
dist: {
options: {
port:process.env.PORT, #<=== need to have a dynamic port env for Heroku deployment
open: true,
base: '<%= appConf.dist %>'
}
}
here are the grunt tasks added :
// Build the production application
grunt.registerTask('build', 'Compile on dist folder', function () {
grunt.task.run([
'clean:dist',
'ngconstant:production',
'wiredep',
'sass',
'concurrent:dist',
'autoprefixer:dist'
]);
});
// Build the production application
grunt.registerTask('webconnect', 'connect web server', function () {
grunt.task.run([
'connect:dist'
]);
});
then on package.json :
"scripts": {
"postinstall": "bower install && grunt build",
"start": "grunt webconnect"
}

why this error come 'can not read property summary of undefined' when running command grunt filerev_apply?

why I am getting can not read property summary of undefined error while I run grunt filerev_apply or grunt filerev_replace . And when I run grunt.filerev.summary
It shows grunt.filerev.summary is not recognized as internal or external command.
Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
filerev: {
options: {
algorithm: 'md5',
length: 8
},
images: {
src: ['views/common/abc.html'],
}
},
filerev_replace: {
options: {
assets_root: 'views/common/abc.html'
},
compiled_assets: {
src: 'views/common/abc.html'
},
views: {
options: {
views_root: 'views/'
},
src: 'views/**/*.html'
}
},
useminPrepare: {
html: 'index.html',
options: {
dest: 'indexfolder'
}
},
usemin: {
html: 'indexfolder/index.html',
options: {
dirs: ['views','views1'],
assetsDirs: ['views','views1'],
revmap: ''
}
},
filerev_apply: {
files: [
{
//expand: true,
//cwd: 'views',
src: ['views/**/*.html'],
//dest: 'views'
}
]
},
reload: {
//port: 8000,
proxy: {
host: 'localhost',
port: 80
}
},
watch: {
sass: {
files: ['views/**/*.html'],
tasks: ['reload'],
options: {
livereload: true,
}
}
}
});
grunt.loadNpmTasks('grunt-usemin');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-reload');
grunt.loadNpmTasks('grunt-filerev');
grunt.loadNpmTasks('grunt-filerev-apply');
grunt.loadNpmTasks('grunt-filerev-replace');
grunt.registerTask('default', ['usemin', 'useminPrepare', 'watch','connect', 'reload', 'filerev', 'filerev_apply', 'filerev_replace']);
};

Grunt Serve - express:dev task gets stuck

I'm having issues with my grunt serve task. It seems to hang when it gets to the express:dev task;
Running "express:dev" (express) task
Starting background Express server
Debugger listening on port 5858
This an existing project where grunt serve used to work fine. This issue started yesterday when my Mac ran out of power mid process; in my head its either that or I have inadvertently changed something and broken it just before the mac went down and the power thing is just coincidence.
Has anyone got any ideas?
'use strict';
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
var deploySettings = grunt.file.readJSON('config.json'),
deploy = deploySettings.production;
var useminSettings = []
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist' //require('./bower.json').distPath ||
};
grunt.initConfig({
mainApp: appConfig,
express: {
options: {
port: process.env.PORT || 9000
},
dev: {
options: {
script: 'app.js',
debug: true,
node_env: 'development',
livereload:true,
serverreload: true
}
},
prod: {
options: {
script: 'dist/app.js',
node_env: 'production'
}
}
},
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js : {
files: ['<%= mainApp.app %>/js/**/*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: true //'<%= connect.options.livereload %>'
}
},
gruntfile: {
files: ['Gruntfile.js']
},
less: {
files: ['<%= mainApp.app %>/css/**/*.less'],
tasks: ['less:development']
},
livereload: {
options: {
livereload : true //'<%= connect.options.livereload %>'
},
files: [
'<%= mainApp.app %>/**/*.html',
'<%= mainApp.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
},
express: {
files: [
'server/**/*.{js,json}'
],
tasks: ['less:development','express:dev', 'wait'],
options: {
livereload: true,
nospawn: true //Without this option specified express won't be reloaded
}
}
},
// 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)
];
}
}
}
},
*/
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= mainApp.app %>/index.html']
//ignorePath: /\.\.\//
}
},
// 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',
'<%= mainApp.app %>/js/{,*/}*.js'
]
}
},
//compiles our less down
less : {
development:{
options:{
path: '<%= mainApp.app %>/css/main.less',
rootPath: '/css/',
relativeUrls: false
},
files: {
'<%= mainApp.app %>/css/main.css' : '<%= mainApp.app %>/css/main.less'
}
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src : [
'.tmp',
'<%= mainApp.dist %>/{,*/}*',
'<%= mainApp.dist %>/.git*'
]
}]
},
server: '.tmp'
},
// 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: '<%= mainApp.app %>/index.html',
options: {
dest: '<%= mainApp.dist %>/public',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '<%= mainApp.dist %>/public/styles/'
}]
}
},
// 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'
}]
}
},
//copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= mainApp.app %>',
dest: '<%= mainApp.dist %>/public',
src:[
'*.html',
'views/partials/{,*/}*.html',
'static/img/{,*/}*.{png,jpg,jpeg,gif,ico,svg}',
'css/fonts/{,*/}*.{ttf,woff,eot,otf}'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= mainApp.dist %>/public/images',
src: ['generated/*']
}, {
expand: true,
cwd: 'public/bower_components/bootstrap/dist',
src: 'fonts/{,*/}*.{ttf,woff,eot,otf}',
dest: '<%= mainApp.dist %>/public'
}, {
expand: true,
dest: '<%= mainApp.dist %>',
src: [
'app.js',
'package.json',
'server/**/*'
]
}]
},
debug: {
files: [{
expand: true,
dot: true,
cwd: '<%= mainApp.app %>',
dest: '<%= mainApp.dist %>',
src:[
'*.html',
'views/partials/{,*/}*.html',
'js/{,*/}*.js',
'css/{,*/}*.css',
'static/img/{,*/}*.{png,jpg,jpeg,gif}',
'css/fonts/{,*/}*.{ttf,woff,eot,otf}'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= mainApp.dist %>/images',
src: ['generated/*']
}, {
expand: true,
cwd: 'public/bower_components/bootstrap/dist',
src: 'fonts/{,*/}*.{ttf,woff,eot,otf}',
dest: '<%= mainApp.dist %>'
}]
},
styles : {
expand: true,
cwd: '<%= mainApp.app %>/styles',
dest: '.tmp/styles',
src: '{,*/}*.css'
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
dist:[
'copy:styles'
]
},
// Replace Google CDN references
cdnify: {
dist: {
html: ['<%= mainApp.dist %>/public/*.html']
}
},
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= mainApp.dist %>/public/scripts/{,*/}*.js',
'<%= mainApp.dist %>/public/styles/{,*/}*.css'
// '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
// '<%= yeoman.dist %>/styles/fonts/*'
]
}
},
// Performs rewrites based on filerev and the useminPrepare configuration
usemin: {
html: ['<%= mainApp.dist %>/public/{,*/}*.html'],
css: ['<%= mainApp.dist %>/public/styles/{,*/}*.css'],
options: {
assetsDirs: ['<%= mainApp.dist %>/public']
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true,
removeOptionalTags: true
},
files: [{
expand: true,
cwd: '<%= mainApp.dist %>',
src: ['*.html', 'views/partials{,*/}*.html'],
dest: '<%= mainApp.dist %>/public'
}]
}
},
imagemin : {
dynamic: { // Another target
files: [{
expand: true, // Enable dynamic expansion
cwd: '<%= mainApp.dist %>/public/static', // Src matches are relative to this path
src: ['**/*.{png,jpg,gif}'], // Actual patterns to match
dest: '<%= mainApp.dist %>/public/static' // Destination path prefix
}]
}
},
});
// 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', 'Compile then start a connect web server', function(){
grunt.task.run([
'clean:server',
'less:development',
'wiredep',
// 'concurrent:server',
//'connect:livereload',
'express:dev',
//'express-keepalive',
'wait',
'watch'
]);
});
grunt.registerTask('build', function(){
grunt.task.run([
'clean:dist',
'less:development',
'wiredep',
'useminPrepare',
'concurrent:dist',
'copy:styles',
'autoprefixer',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin',
'express:dev'
]);
});
grunt.registerTask('debug', [
'clean:dist',
'less:development',
'wiredep',
'copy:debug'
]);
grunt.registerTask('default', [
'serve'
]);
};
I found the solution to this by reverting to a previous git and manually adding changed.
Somehow it was an issue with node-scheduler. I was scheduling a task using a recurrence rule for dayOfWeek = 7. This was producing the error in grunt serve, nothing to do with the grunt file as it happens.
I changed this to dayOfWeek = 0 and it was fine.

How to bind server and client at grunt.js?

I'm new at mean stack develop.
I have a project in which it is necessary to add a user interface which compile through grunt.
My server starting at command node server and gruntfile compile all my client side. But I need my grunt start server and compile all views.
I added grunt-express, added it to tasks but when I make grunt serve there is no server side on it. It stayed on port 3000.
How can I implement two sides on grunt compile?
Gruntfile.js
"use strict";
var LIVERELOAD_PORT, lrSnippet, mountFolder;
LIVERELOAD_PORT = 35728;
lrSnippet = require("connect-livereload")({
port: LIVERELOAD_PORT
});
mountFolder = function(connect, dir) {
return connect["static"](require("path").resolve(dir));
};
module.exports = function(grunt) {
var yeomanConfig;
require("load-grunt-tasks")(grunt);
require("time-grunt")(grunt);
yeomanConfig = {
app: "client",
dist: "dist",
docs: "documentation"
};
try {
yeomanConfig.app = require("./bower.json").appPath || yeomanConfig.app;
} catch (_error) {}
grunt.initConfig({
yeoman: yeomanConfig,
serverJS: ["Gruntfile.js", "server.js", "config/**/*.js", "app/**/*.js"],
watch: {
express: {
files: [
"app/**/*.js",
"config/{,*/}*.js",
"server.js",
"Gruntfile.js"
],
tasks: "express:dev",
options: {
nospawn: true
}
},
compass: {
files: ["<%= yeoman.app %>/styles/**/*.{scss,sass}"],
tasks: ["compass:server"]
},
less: {
files: ["<%= yeoman.app %>/styles-less/**/*.less"],
tasks: ["less:server"]
},
jade: {
files: ["<%= yeoman.docs %>/jade/*.jade"],
tasks: ["jade:docs"]
},
livereload: {
options: {
livereload: LIVERELOAD_PORT
},
files: [
"<%= yeoman.app %>/index.html",
"<%= yeoman.app %>/app/**/*.html",
"<%= yeoman.app %>/app/**/*.js",
"<%= yeoman.app %>/styles/**/*.scss",
"<%= yeoman.app %>/styles-less/**/*.less",
".tmp/styles/**/*.css",
"<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}",
"<%= yeoman.docs %>/jade/*.jade"
]
}
},
express: {
server: {
options : {
port: 3000,
script: 'server.js'
}
}
},
connect: {
options: {
port: 5000,
hostname: "localhost"
},
livereload: {
options: {
middleware: function(connect) {
return [lrSnippet, mountFolder(connect, ".tmp"), mountFolder(connect, yeomanConfig.app)];
}
}
},
docs: {
options: {
middleware: function(connect) {
return [lrSnippet, mountFolder(connect, yeomanConfig.docs)];
}
}
},
test: {
options: {
middleware: function(connect) {
return [mountFolder(connect, ".tmp"), mountFolder(connect, "test")];
}
}
},
dist: {
options: {
middleware: function(connect) {
return [mountFolder(connect, yeomanConfig.dist)];
}
}
}
},
open: {
server: {
url: "http://localhost:<%= connect.options.port %>"
}
},
clean: {
dist: {
files: [
{
dot: true,
src: [".tmp", "<%= yeoman.dist %>/*", "!<%= yeoman.dist %>/.git*"]
}
]
},
all: [".tmp", ".sass-cache", "readme.md", "client/bower_components", "documentation/jade", "documentation/config.codekit", "landing/jade", "landing/config.codekit", "node_modules", ".git"],
server: ".tmp"
},
jshint: {
options: {
jshintrc: ".jshintrc"
},
all: ["Gruntfile.js", "<%= yeoman.app %>/scripts/**/*.js"]
},
injector: {
options: {
relative: true
},
local_dependencies: {
files: {
"<%= yeoman.app %>/index.html": [
"<%= yeoman.app %>/app/**/*.module.js",
"<%= yeoman.app %>/app/**/*.js",
"!" + "<%= yeoman.app %>/**/*.spec.js"
]
}
},
},
jade: {
docs: {
options: {
pretty: true
},
files: {
"<%= yeoman.docs %>/index.html": ["<%= yeoman.docs %>/jade/index.jade"]
}
}
},
compass: {
options: {
sassDir: "<%= yeoman.app %>/styles",
cssDir: ".tmp/styles",
generatedImagesDir: ".tmp/styles/ui/images/",
imagesDir: "<%= yeoman.app %>/styles/ui/images/",
javascriptsDir: "<%= yeoman.app %>/scripts",
fontsDir: "<%= yeoman.app %>/fonts",
importPath: "<%= yeoman.app %>/bower_components",
httpImagesPath: "styles/ui/images/",
httpGeneratedImagesPath: "styles/ui/images/",
httpFontsPath: "fonts",
relativeAssets: true
},
dist: {
options: {
outputStyle: 'compressed',
debugInfo: false,
noLineComments: true
}
},
server: {
options: {
debugInfo: true
}
},
forvalidation: {
options: {
debugInfo: false,
noLineComments: false
}
}
},
less: {
server: {
options: {
strictMath: true,
dumpLineNumbers: true,
sourceMap: true,
sourceMapRootpath: "",
outputSourceFiles: true
},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>/styles-less",
src: "main.less",
dest: ".tmp/styles",
ext: ".css"
}
]
},
dist: {
options: {
cleancss: true,
report: 'min'
},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>/styles-less",
src: "main.less",
dest: ".tmp/styles",
ext: ".css"
}
]
}
},
useminPrepare: {
html: "<%= yeoman.app %>/index.html",
options: {
dest: "<%= yeoman.dist %>",
flow: {
steps: {
js: ["concat"],
css: ["cssmin"]
},
post: []
}
}
},
usemin: {
html: ["<%= yeoman.dist %>/**/*.html", "!<%= yeoman.dist %>/bower_components/**"],
css: ["<%= yeoman.dist %>/styles/**/*.css"],
options: {
dirs: ["<%= yeoman.dist %>"]
}
},
htmlmin: {
dist: {
options: {},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>",
src: ["*.html", "views/*.html"],
dest: "<%= yeoman.dist %>"
}
]
}
},
copy: {
dist: {
files: [
{
expand: true,
dot: true,
cwd: "<%= yeoman.app %>",
dest: "<%= yeoman.dist %>",
src: [
"favicon.ico",
"bower_components/font-awesome/css/*",
"bower_components/font-awesome/fonts/*",
"bower_components/weather-icons/css/*",
"bower_components/weather-icons/fonts/*",
"bower_components/weather-icons/font/*",
"fonts/**/*",
"i18n/**/*",
"images/**/*",
"styles/fonts/**/*",
"styles/img/**/*",
"styles/ui/images/*",
"app/**/*.html"
]
}, {
expand: true,
cwd: ".tmp",
dest: "<%= yeoman.dist %>",
src: ["styles/**", "assets/**"]
}, {
expand: true,
cwd: ".tmp/images",
dest: "<%= yeoman.dist %>/images",
src: ["generated/*"]
}
]
},
styles: {
expand: true,
cwd: "<%= yeoman.app %>/styles",
dest: ".tmp/styles/",
src: "**/*.css"
}
},
concurrent: {
server: ["compass:server", "copy:styles"],
dist: ["compass:dist", "copy:styles", "htmlmin"],
lessServer: ["less:server", "copy:styles"],
lessDist: ["less:dist", "copy:styles", "htmlmin"]
},
cssmin: {
options: {
keepSpecialComments: '0'
},
dist: {}
},
concat: {
options: {
separator: grunt.util.linefeed + ';' + grunt.util.linefeed
},
dist: {}
},
uglify: {
options: {
mangle: false,
compress: {
drop_console: true
}
},
dist: {
files: {
"<%= yeoman.dist %>/scripts/app.js": [".tmp/**/*.js", "<%= yeoman.app %>/scripts/**/*.js", "!<%= yeoman.app %>/scripts/vendors/**"]
}
}
}
});
grunt.registerTask("docs", function() {
return grunt.task.run(["jade:docs", "connect:docs", "open", "watch"]);
});
grunt.registerTask("server", function(target) {
if (target === "dist") {
return grunt.task.run(["serve:dist"]);
}
return grunt.task.run(["serve"]);
});
grunt.registerTask("serve", function(target) {
if (target === "dist") {
return grunt.task.run(["build", "open", "connect:dist:keepalive"]);
}
return grunt.task.run(["clean:server", "concurrent:server", "express:server", "connect:livereload", "open", "watch"]);
});
grunt.registerTask("build", ["clean:dist", "useminPrepare", "concurrent:dist", "copy:dist", "cssmin", "concat", "uglify", "usemin"]);
return grunt.registerTask("default", ["serve"]);
};
server.js
var express = require('express'),
fs = require('fs'),
passport = require('passport'),
mongoose = require('mongoose'),
http = require('http'),
app = express(),
server = require('http').createServer(app),
config = require('./config/environment'),
auth = require('./config/middlewares/authorization'),
models_path = __dirname + '/app/models',
routes_path = __dirname + '/app/routes';
fs.readdirSync(models_path).forEach(function (file) {
require(models_path + '/' + file);
});
app.set('port', process.env.OPENSHIFT_NODEJS_PORT || 3000);
app.set('ipaddr', process.env.OPENSHIFT_NODEJS_IP || 'localhost');
require('./config/express')(app, config, passport);
require('./config/passport')(passport, config);
fs.readdirSync(routes_path).forEach(function (file) {
require(routes_path + '/' + file)(app, passport, auth);
});
mongoose.connect(config.db);
server.listen(app.get('port'), app.get('ipaddr'), function(){
console.log('Express server listening on IP/hostname: "' + app.get('ipaddr') + '" and port: "' + app.get('port') + '"');
});
Ok. I solved it with grunt-connect-proxy. I added to connect task proxy and it finaly works. Here is all code of gruntfile:
"use strict";
var LIVERELOAD_PORT, lrSnippet, mountFolder, proxySnippet;
LIVERELOAD_PORT = 35728;
lrSnippet = require("connect-livereload")({
port: LIVERELOAD_PORT
});
mountFolder = function(connect, dir) {
return connect["static"](require("path").resolve(dir));
};
module.exports = function(grunt) {
var yeomanConfig;
proxySnippet = require("grunt-connect-proxy/lib/utils").proxyRequest;
require("load-grunt-tasks")(grunt);
require("time-grunt")(grunt);
yeomanConfig = {
app: "client",
dist: "dist",
docs: "documentation"
};
try {
yeomanConfig.app = require("./bower.json").appPath || yeomanConfig.app;
} catch (_error) {}
grunt.initConfig({
yeoman: yeomanConfig,
appFiles: {
serverJS: ["Gruntfile.js", "server.js", "config/{,*/}*.js", "app/**/*.js"]
},
watch: {
express: {
files: "<%= appFiles.serverJS %>",
tasks: "express:dev",
options: {
nospawn: true
}
},
compass: {
files: ["<%= yeoman.app %>/styles/**/*.{scss,sass}"],
tasks: ["compass:server"]
},
less: {
files: ["<%= yeoman.app %>/styles-less/**/*.less"],
tasks: ["less:server"]
},
jade: {
files: ["<%= yeoman.docs %>/jade/*.jade"],
tasks: ["jade:docs"]
},
livereload: {
options: {
livereload: LIVERELOAD_PORT
},
files: [
"<%= yeoman.app %>/index.html",
"<%= yeoman.app %>/app/**/*.html",
"<%= yeoman.app %>/app/**/*.js",
"<%= yeoman.app %>/styles/**/*.scss",
"<%= yeoman.app %>/styles-less/**/*.less",
".tmp/styles/**/*.css",
"<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}",
"<%= yeoman.docs %>/jade/*.jade"
]
}
},
express: {
server: {
options : {
port: 3000,
script: 'server.js'
}
}
},
connect: {
options: {
port: 5000,
hostname: "localhost"
},
proxies: [
{
context: "/",
host: "localhost",
port: 3000 + "",
https: false,
changeOrigin: false
}
],
livereload: {
options: {
middleware: function(connect) {
return [lrSnippet, mountFolder(connect, ".tmp"), mountFolder(connect, yeomanConfig.app), proxySnippet];
}
}
},
docs: {
options: {
middleware: function(connect) {
return [lrSnippet, mountFolder(connect, yeomanConfig.docs)];
}
}
},
test: {
options: {
middleware: function(connect) {
return [mountFolder(connect, ".tmp"), mountFolder(connect, "test")];
}
}
},
dist: {
options: {
middleware: function(connect) {
return [mountFolder(connect, yeomanConfig.dist)];
}
}
}
},
open: {
server: {
url: "http://localhost:<%= connect.options.port %>"
}
},
clean: {
dist: {
files: [
{
dot: true,
src: [".tmp", "<%= yeoman.dist %>/*", "!<%= yeoman.dist %>/.git*"]
}
]
},
all: [".tmp", ".sass-cache", "readme.md", "client/bower_components", "documentation/jade", "documentation/config.codekit", "landing/jade", "landing/config.codekit", "node_modules", ".git"],
server: ".tmp"
},
jshint: {
options: {
jshintrc: ".jshintrc"
},
all: ["Gruntfile.js", "<%= yeoman.app %>/scripts/**/*.js"]
},
injector: {
options: {
relative: true
},
local_dependencies: {
files: {
"<%= yeoman.app %>/index.html": [
"<%= yeoman.app %>/app/**/*.module.js",
"<%= yeoman.app %>/app/**/*.js",
"!" + "<%= yeoman.app %>/**/*.spec.js"
]
}
},
},
jade: {
docs: {
options: {
pretty: true
},
files: {
"<%= yeoman.docs %>/index.html": ["<%= yeoman.docs %>/jade/index.jade"]
}
}
},
compass: {
options: {
sassDir: "<%= yeoman.app %>/styles",
cssDir: ".tmp/styles",
generatedImagesDir: ".tmp/styles/ui/images/",
imagesDir: "<%= yeoman.app %>/styles/ui/images/",
javascriptsDir: "<%= yeoman.app %>/scripts",
fontsDir: "<%= yeoman.app %>/fonts",
importPath: "<%= yeoman.app %>/bower_components",
httpImagesPath: "styles/ui/images/",
httpGeneratedImagesPath: "styles/ui/images/",
httpFontsPath: "fonts",
relativeAssets: true
},
dist: {
options: {
outputStyle: 'compressed',
debugInfo: false,
noLineComments: true
}
},
server: {
options: {
debugInfo: true
}
},
forvalidation: {
options: {
debugInfo: false,
noLineComments: false
}
}
},
less: {
server: {
options: {
strictMath: true,
dumpLineNumbers: true,
sourceMap: true,
sourceMapRootpath: "",
outputSourceFiles: true
},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>/styles-less",
src: "main.less",
dest: ".tmp/styles",
ext: ".css"
}
]
},
dist: {
options: {
cleancss: true,
report: 'min'
},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>/styles-less",
src: "main.less",
dest: ".tmp/styles",
ext: ".css"
}
]
}
},
useminPrepare: {
html: "<%= yeoman.app %>/index.html",
options: {
dest: "<%= yeoman.dist %>",
flow: {
steps: {
js: ["concat"],
css: ["cssmin"]
},
post: []
}
}
},
usemin: {
html: ["<%= yeoman.dist %>/**/*.html", "!<%= yeoman.dist %>/bower_components/**"],
css: ["<%= yeoman.dist %>/styles/**/*.css"],
options: {
dirs: ["<%= yeoman.dist %>"]
}
},
htmlmin: {
dist: {
options: {},
files: [
{
expand: true,
cwd: "<%= yeoman.app %>",
src: ["*.html", "views/*.html"],
dest: "<%= yeoman.dist %>"
}
]
}
},
copy: {
dist: {
files: [
{
expand: true,
dot: true,
cwd: "<%= yeoman.app %>",
dest: "<%= yeoman.dist %>",
src: [
"favicon.ico",
"bower_components/font-awesome/css/*",
"bower_components/font-awesome/fonts/*",
"bower_components/weather-icons/css/*",
"bower_components/weather-icons/fonts/*",
"bower_components/weather-icons/font/*",
"fonts/**/*",
"i18n/**/*",
"images/**/*",
"styles/fonts/**/*",
"styles/img/**/*",
"styles/ui/images/*",
"app/**/*.html"
]
}, {
expand: true,
cwd: ".tmp",
dest: "<%= yeoman.dist %>",
src: ["styles/**", "assets/**"]
}, {
expand: true,
cwd: ".tmp/images",
dest: "<%= yeoman.dist %>/images",
src: ["generated/*"]
}
]
},
styles: {
expand: true,
cwd: "<%= yeoman.app %>/styles",
dest: ".tmp/styles/",
src: "**/*.css"
}
},
concurrent: {
server: ["compass:server", "copy:styles"],
dist: ["compass:dist", "copy:styles", "htmlmin"],
lessServer: ["less:server", "copy:styles"],
lessDist: ["less:dist", "copy:styles", "htmlmin"]
},
cssmin: {
options: {
keepSpecialComments: '0'
},
dist: {}
},
concat: {
options: {
separator: grunt.util.linefeed + ';' + grunt.util.linefeed
},
dist: {}
},
uglify: {
options: {
mangle: false,
compress: {
drop_console: true
}
},
dist: {
files: {
"<%= yeoman.dist %>/scripts/app.js": [".tmp/**/*.js", "<%= yeoman.app %>/scripts/**/*.js", "!<%= yeoman.app %>/scripts/vendors/**"]
}
}
}
});
grunt.registerTask("docs", function() {
return grunt.task.run(["jade:docs", "connect:docs", "open", "watch"]);
});
grunt.registerTask("server", function(target) {
if (target === "dist") {
return grunt.task.run(["serve:dist"]);
}
return grunt.task.run(["serve"]);
});
grunt.registerTask("serve", function(target) {
if (target === "dist") {
return grunt.task.run(["build", "open", "connect:dist:keepalive"]);
}
return grunt.task.run(["clean:server", "concurrent:server", "configureProxies", "express:server", "connect:livereload", "open", "watch"]);
});
grunt.registerTask("build", ["clean:dist", "useminPrepare", "concurrent:dist", "copy:dist", "cssmin", "concat", "uglify", "usemin"]);
return grunt.registerTask("default", ["serve"]);
};

Grunt watch task is not starting until the first output to console

My task configuration works fine but I have annoying issue where the watch task does not start to monitor unless I refresh a page or add a line for console output. So normally I am supposed to get:
Running "watch" task
Waiting...
But I don't get it until some output goes to the console.
Is it a bug?
My config is:
module.exports = function(grunt) {
grunt.initConfig({
concat: {
options: {
separator: '\n\n'
},
dev: {
src: ['ng_app/app.js', 'ng_app/**/*.js'],
dest: 'public/js/app.js'
}
},
less: {
dev: {
files: [
{
expand: true,
cwd: 'styles',
src: ['*.less', '!mixins.less', '!var.less'],
dest: 'public/css/',
ext: '.css'
},
{
expand: true,
cwd: 'styles/views',
src: ['*.less'],
dest: 'public/css/views',
ext: '.css'
}
]
}
},
express: {
dev: {
options: {
script: 'bin/www'
}
}
},
watch: {
options: {
livereload: true
},
express: {
files: ['**/*.js'],
tasks: ['express:dev'],
options: {
spawn: false
}
},
less: {
files: ['styles/**/*.less'],
tasks: ['less'],
options: {
livereload: false
}
},
concat: {
files: ['ng_app/**/*.js'],
tasks: ['concat'],
options: {
livereload: false
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-express-server');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['concat', 'less']);
grunt.registerTask('server', [ 'express:dev', 'watch' ]);
};

Resources