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
I have a Node/Angular app, developed using the WebStorm IDE. I can run the application fine via WebStorm (Shift + F10). However, any time I try to run in debug mode, I am receiving the EADDRINUSE error:
Running "concurrent:default" (concurrent) task
Verifying property concurrent.default exists in config...OK
Files: [no src] -> default
Options: limit=10, logConcurrentOutput
Error: listen EADDRINUSE :::52387
Here is my gruntfile.js - like I said, WebStorm builds and runs it just fine, until I try to run it in debug mode, where it throws the error:
'use strict';
var fs = require('fs');
module.exports = function(grunt) {
// Unified Watch Object
var watchFiles = {
serverViews: ['app/views/**/*.*'],
serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js', '!app/tests/'],
clientViews: ['public/modules/**/views/**/*.html'],
sass: ['public/css/*.scss'],
clientJS: ['public/js/*.js', 'public/modules/**/*.js'],
clientCSS: ['public/modules/**/*.css'],
mochaTests: ['app/tests/**/*.js']
};
// Project Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
serverViews: {
files: watchFiles.serverViews,
options: {
livereload: true
}
},
serverJS: {
files: watchFiles.serverJS,
tasks: ['jshint'],
options: {
livereload: true
}
},
clientViews: {
files: watchFiles.clientViews,
options: {
livereload: true
}
},
clientJS: {
files: watchFiles.clientJS,
tasks: ['jshint'],
options: {
livereload: true
}
},
clientCSS: {
files: watchFiles.clientCSS,
tasks: ['csslint'],
options: {
livereload: true
}
},
sass: {
files: watchFiles.sass,
tasks: ['sass:dev'],
options: {
livereload: true
}
},
mochaTests: {
files: watchFiles.mochaTests,
tasks: ['test:server'],
}
},
jshint: {
all: {
src: watchFiles.clientJS.concat(watchFiles.serverJS),
options: {
jshintrc: true
}
}
},
csslint: {
options: {
csslintrc: '.csslintrc'
},
all: {
src: watchFiles.clientCSS
}
},
uglify: {
production: {
options: {
mangle: false
},
files: {
'public/dist/application.min.js': 'public/dist/application.js'
}
}
},
cssmin: {
combine: {
files: {
'public/dist/application.min.css': '<%= applicationCSSFiles %>'
}
}
},
nodemon: {
dev: {
script: 'server.js',
options: {
nodeArgs: ['--debug'],
ext: 'js,html',
watch: watchFiles.serverViews.concat(watchFiles.serverJS)
}
}
},
'node-inspector': {
custom: {
options: {
'web-port': 1337,
'web-host': 'localhost',
'debug-port': 5858,
'save-live-edit': true,
'no-preload': true,
'stack-trace-limit': 50,
'hidden': []
}
}
},
concurrent: {
default: ['nodemon', 'watch'],
debug: ['nodemon', 'watch', 'node-inspector'],
options: {
logConcurrentOutput: true,
limit: 10
}
},
env: {
test: {
NODE_ENV: 'test'
},
secure: {
NODE_ENV: 'secure'
},
development: {
NODE_ENV: 'development'
}
},
mochaTest: {
src: watchFiles.mochaTests,
options: {
reporter: 'spec',
require: 'server.js'
}
},
karma: {
unit: {
configFile: 'karma.conf.js'
}
},
sass: {
dev: {
files: {
'public/css/style.css': 'public/css/*.scss'
}
}
},
copy: {
localConfig: {
src: 'config/env/local.example.js',
dest: 'config/env/local.js',
filter: function() {
return !fs.existsSync('config/env/local.js');
}
}
}
});
// Load NPM tasks
require('load-grunt-tasks')(grunt);
// Making grunt default to force in order not to break the project.
grunt.option('force', true);
// A Task for loading the configuration object
grunt.task.registerTask('loadConfig', 'Task that loads the config into a grunt option.', function() {
var init = require('./config/init')();
var config = require('./config/config');
grunt.config.set('applicationJavaScriptFiles', config.assets.js);
grunt.config.set('applicationCSSFiles', config.assets.css);
});
//Sass task
grunt.registerTask('default', ['lint', 'sass:dev', 'concurrent:default']);
// Debug task.
grunt.registerTask('debug', ['lint', 'copy:localConfig', 'concurrent:debug']);
// Secure task(s).
grunt.registerTask('secure', ['env:secure', 'lint', 'copy:localConfig', 'concurrent:default']);
// Lint task(s).
grunt.registerTask('lint', ['jshint', 'csslint']);
// Build task(s).
grunt.registerTask('build', ['lint', 'loadConfig', 'ngAnnotate', 'uglify', 'cssmin']);
// Test task.
grunt.registerTask('test', ['copy:localConfig', 'test:server', 'test:client']);
grunt.registerTask('test:server', ['env:test', 'mochaTest']);
grunt.registerTask('test:client', ['env:test', 'karma:unit']);
};
The problem is caused by the way Grunt spawns child tasks. By default, grunt.util.spawn() inherits main process arguments and starts child process with same options as the main process (--debug-brk=52387), so child process is started on the same debug port as the main process, causing EADDRINUSE. See https://github.com/gruntjs/grunt/issues/1420.
As a workaround, try adding process.execArgv = []; to the top of Gruntfile.js - it should help
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"
}
I'm trying to build a website with Bootstrap, using Grunt to run tasks. I have been trying to figure out why my gruntfile does not seem to be running any tasks except for 'watch', but I can't seem to find the answer. No errors show up either. In terminal, time-grunt will show the execution time (although no tasks are executed) and then it would say 'Waiting....' which means watch is running. Could someone shed some light on what I've done wrong in my gruntfile? Any help would be greatly appreciated :)
module.exports = function(grunt) {
//time
require('time-grunt')(grunt);
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
options: {
sourceMap: true,
style: 'compressed',
},
files: {
'assets/css/style.css' : 'assets/scss/style.scss',
}
}
},
copy: {
scripts: {
cwd: 'bower_components/jquery/dist/',
expand: true,
src: [
'jquery.min.js',
],
dest: 'assets/js/',
filter: 'isFile',
}
},
concat: {
options: {
separator: ';',
},
js: {
src: [
'bower_components/bootstrap-sass/assets/javascripts/bootstrap.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/button.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/collapse.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
'bower_components/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
],
dest: 'assets/js/bootstrap.js',
},
sass: {
src: [
'bower_components/bootstrap-sass/assets/stylesheets/_bootstrap.scss',
'bower_components/bootstrap-sass/assets/stylesheets/bootstrap/**/*.scss'
],
dest: 'assets/scss/style.scss'
}
},
uglify: {
options: {
mangle: false,
},
my_target: {
files: {
'assets/css/style.min.css' : ['assets/css/style.css'],
'assets/js/bootstrap.min.js' : ['assets/js/bootstrap.js']
}
}
},
watch: {
gruntfile: {
files: 'Gruntfile.js',
tasks: ['jshint:gruntfile'],
options: {
livereload: true
}
},
src: {
files: ['assets/js/*.js', 'assets/scss/*.scss'],
tasks: ['default'],
options: {
livereload: true
}
}
},
jshint: {
all: ['Gruntfile.js', 'assets/**/*.js']
},
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('build', ['sass', 'copy', 'concat', 'uglify']);
grunt.registerTask('default', ['watch', 'jshint']);
};
Your tasks names and orders were a bit mixed up, here are 3 points to fully fix it - and provide StackOverflow with an answer to the question:
watch should always be the last task to run, as it does not end. So:
grunt.registerTask('default', ['jshint', 'watch']);
if you want to run a task (like build by just calling grunt, you have to put it in your default task, so:
grunt.registerTask('default', ['jshint', 'build', 'watch']);
your watch task must not launch a watch task itself, so watch.src should not call default (as default includes watch):
watch: {
gruntfile: {
files: 'Gruntfile.js',
tasks: ['jshint:gruntfile'],
options: {
livereload: true
}
},
src: {
files: ['assets/js/*.js', 'assets/scss/*.scss'],
tasks: ['jshint', 'build'],
options: {
livereload: true
}
}
},