I have a Javascript project with multiple subdirectories, each its own individual project. I could just use one massive Gruntfile with different tasks for each project, but I'd rather have a Gruntfile in each subfolder. A typical file structure would be this
main_folder/
project_1
src
js/
dist/
doc/
Gruntfile.js
package.json
package.json
node_modules/
And then repeat the file structure for each project
Here is my Gruntfile inside project_1
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['uglify']);
};
It has fewer tasks than I will use, but the errors are still there. Whenever I try to run grunt, I get the error message Local Npm module "grunt-contrib-uglify" not found. Is it installed?, even though I have it installed in node_modules.
How can I specify the location of node_modules, or do I have to reinstall node_modules inside each folder?
I think you can still keep everything in the same place if you tell Grunt more explicitly where to find your plugins. For example, here's a bit from one of my Gruntfiles:
grunt.loadTasks tasks for tasks in grunt.file.expand '../node_modules/grunt-*/tasks'
As a side note: I faced exactly the same choice you did, but made the opposite choice: a single Gruntfile for the whole project and created a symlink to it from each project sub-directory. It's turned out to be a very easy way to keep things together in one place, and side-steps a lot of confusing issues like the one you're facing.
Another good way to solve this might be the use of subgrunt. It let's you configure various targets for your subprojects and has the option npmInstall, which "Determines wether npm install will be ran for the sub-project (thus installing dev dependencies)."
Related
I used yeoman yeogurt generator to kickstart my project.
The grunt file generated uses (to the best of my knowledge) include-all and grunt-jit to load the modules.
What I would like to do, is add the building of jekyll to this file. To do this I installed grunt-jekyll. Afterwards I created a simple config file in grunt/config/compile/ and added the following:
// Configuration for jekyll task(s)
// Compiles the blog
'use strict';
var taskConfig = function(grunt) {
grunt.config.set('jekyll', {
options: { // Universal options
bundleExec: true,
src : '<%= jekyll %>'
},
dist: { // Target
options: { // Target options
dest: '<%= dist %>/blog',
config: '_config.yml,_config.build.yml'
}
}
});
};
module.exports = taskConfig;
After creating this file, I tested if grunt was still working by running "grunt build" and as it turns out, it didn't. I get the following error:
Running "useminPrepare:html" (useminPrepare) task
Going through dist/index.html to update the config
Looking for build script HTML comment blocks
Fatal error: Maximum call stack size exceeded
I have no idea why I get this error, as I did not tell grunt to do anything with jekyll (except load it), so I would not expect anything to have changed. Removing the file makes my grunt setup function without error.
Adding jekyll:dist to grunt/tasks/build.js results in a similar error but in the correct module:
Running "jekyll:dist" (jekyll) task
Warning: Maximum call stack size exceeded Use --force to continue.
I don't know if it helps, but here is some extra info about my setup:
node v0.12.2
grunt: v0.4.5
grunt-cli: v0.1.13
using: jade, sass, dashboard
gruntfile: https://gist.github.com/Boelensman1/95bb3ec7298377c8c4c9
package.json: https://gist.github.com/Boelensman1/1f0c97a7d7720f9e2be4
.yo-rc.json: https://gist.github.com/Boelensman1/cef88cb2cb925faa355d
build.js: https://gist.github.com/Boelensman1/5f55c251648b881e3f2d
I think I made an error in my jekyll config file, but I don't see where. If someone could help me it would be greatly appreciated.
I don't know why this results in a stack overflow, but the problem is that <%= dist %> and <%= jekyll %> are wrong. It should be <%= yeogurt.jekyll %> and <%= yeogurt.dist %>. This is because yeogurt is the configuration object, see line #20 of your gruntfile.
I have the following simple structure for my website:
src
js
core.js
main.js
lib
jquery-1.8.2.js
require-2.1.1.js
require-text.js
templates
1.html
2.html
index.html
build
I want all js+lib files to be compiled into one build/js/main.js file and other files just to be copied to build folder. How to write grunt.js config for this task? It seems I should use grunt-contrib-require..
The second question is how to compile 1.html and 2.html (I use require text! plugin) into one line for each and include theese lines to build/js/main.js? This case there should be only two files into build folder - index.html and main.js.
Take a look at grunt-contrib-requirejs and see if it is helpful to you.
The Grunt Website offers a very good tutorial to get you started, this is what you will need:
grunt-contrib-concat - To put files together in one
grunt-contrib-copy - To copy files to your "build" folder
grunt-usemin - To use the compiled js file in your html
I am not sure how to put those html files together though, feels weird to do that but maybe you can find a plugin for it.
Your Gruntfile.js should reside at the root of the directory i.e ls should show src/ build/ Gruntfile.js
Contents of `Gruntfile.js specific for your requirements:
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
js: {
src: [
'src/js/*', 'src/lib/*'
],
dest: 'build/js/combined.js'
}
},
uglify: {
js: {
files: {
'build/js/main.js': ['build/js/combined.js']
}
}
},
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['concat:js', 'uglify:js']);
};
I don't think require-js to be used here. Require-js is helpful when you need to load your js scripts in a specific order. If that being the case add the below code in your Gruntfile.js just below pkg: grunt.file.readJSON('package.json'), line
requirejs: {
compile: {
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js",
name: "path/to/almond", // assumes a production build using almond
out: "path/to/optimized.js"
}
}
}
You might consider adding grunt-require to the list that luschn put together. It uses r.js, has lots of options, and is pretty darn nice.
Is there an easy way to reference all js files in an HTML file rather than referencing it one by one?
Instead of this -
<script type="text/javascript" src="js/controllers/mainCtrl.js"></script>
<script type="text/javascript" src="js/controllers/browseCtrl.js"></script>
...
I'm looking for something like this -
<script type="text/javascript" src="js/controllers/*.js"></script>
Or is there a tool out there that copies the contents of these files into one file and reference that one file instead? This will be minimize the HTTP calls.
Is there an easy way to reference all js files in an HTML file rather than referencing it one by one?
For some value of "easy". There is nothing built in to browsers that will do it though.
Or is there a tool out there that copies the contents of these files into one file and reference that one file instead?
There are many. cat is the simplest one.
Call it from your usual build tool.
You can use something like require.js to combine them at runtime during development, and call r.js via Node from your build tool for packaging for your staging and live environments.
You can give Require.js a go. Require.js is the only JavaScript-file that is loaded through the script-tag. When you go out of development you can use Require.js's r.js to minify and concat everything into one file.
I use this tool all the time to minify my JS files:
Online Javascript Compression Tool
You can upload multiple files and it will concatenate them into one for you. It also produces smaller filesizes than YUI compressor, and Google's JS compiler most of the time too.
I am not sure why this hasn't been mentioned yet, but I do suppose this thread is a bit dated. Since I stumbled on this during my search to solve this very problem, I thought I would put a quick write-up about GruntJS here for other newbie JS guys to find.
Essentially, a properly configured Gruntfile.js will be able to perform a variety of tasks around JS including, but not limited to: concatenating files, minifying files, code linting, and much much more.
You can install grunt on Ubuntu with:
$ sudo apt-get install nodejs
$ sudo npm -g install grunt-cli
$ cd /path/to/my/project
--- Assumming you have a package.json file already in place ---
$ npm install grunt --save-dev
--- Install grunt plugins you wish to use ---
$ npm install grunt-contrib-concat --save-dev
$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-jshint --save-dev
$ npm install grunt-contrib-watch --save-dev
On the GruntJS site, there is a pretty good write-up for how to use GruntJS, but here is an example Gruntfile.js that will:
Lint all the JS files (app.js in the current directory and all .js files in the ngmodules directory).
Concatenate and save the files to dist/package-name.js.
Minify the concatenated file and save it to dist/package-name.min.js.
Gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['app.js', 'ngmodules/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'app.js', 'ngmodules/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
There is also Gulp (http://gulpjs.com/), which can be used with various plugins. Here's an example that concatenates *.js files in one single file (main.js), then renames the resulting file and finally minifies it:
var gulp = require('gulp'),
rename = require('gulp-rename'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat');
gulp.task('scripts', function(){
return gulp.src('./src/js/*.js')
.pipe(concat('main.js'))
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest('./src/js/*.js'));
You can try and combine your javascript files or plugins into one:
<script type="text/javascript" src="js/controllers/plugins.js"></script>
You'll have to do it manually though.
Another option would be to write a server-side script to combine and minify all your javascript files.
I have a node app that includes multiple unpublished modules. My app's package.json includes a few git dependencies:
"module-a": "git+ssh://git#github.com:me/module-a.git",
"module-b": "git+ssh://git#github.com:me/module-b.git"
and each of those have their own grunt config. Eg in node_modules/module-a/grunt.js:
module.exports = function(grunt) {
grunt.initConfig({
lint: {
files: ['server/**/*.js', 'test/**/*.js']
},
jshint: {
options: require('./lint-ci')
}
});
grunt.registerTask('default', 'lint');
};
(they also run tests, etc, but I'm keeping it simple here)
Is there a built-in way to do this with grunt? Note that I want to keep the dependent grunt.js files for convenience when I've only changed something within that dependency.
The only solutions I have found are
build up my main grunt.js programmatically (eg, iterating over my dependencies in package.json to build the lint and test config)
call grunt multiple times using --config node_modules/module-a/grunt.js
Neither seems ideal. Is there a better way?
Just a thought but have you looked at grunt-hub?
https://github.com/shama/grunt-hub
I have some projects that use RequireJS to load individual JavaScript modules in the browser, but I haven't optimized them yet. In both development and production, the app makes a separate request for each JavaScript file, and now I would like to fix that using Grunt.
I have tried to put together a simple project structure to no avail, so I'm wondering if someone can provide a working example for me. My goals are the following:
In development mode, everything works in the browser by issuing a separate request for each required module. No grunt tasks or concatenation are required in development mode.
When I'm ready, I can run a grunt task to optimize (combine) all of the JavaScript files using r.js and test that out locally. Once I'm convinced the optimized application runs correctly, I can deploy it.
Here's a sample structure for the sake of this conversation:
grunt-requirejs-example/
grunt.js
main.js (application entry point)
index.html (references main.js)
lib/ (stuff that main.js depends on)
a.js
b.js
requirejs/
require.js
text.js
build/ (optimized app goes here)
node_modules/ (necessary grunt tasks live here)
Specifically, I'm looking for a working project structure that I can start from. My main questions are:
If this project structure is flawed, what do you recommend?
What exactly needs to be in my grunt.js file, especially to get the r.js optimizer working?
If all of this isn't worth the work and there's a way to use the grunt watch task to automatically build everything in development mode every time I save a file, then I'm all ears. I want to avoid anything that slows down the loop from making a change to seeing it in the browser.
I use the grunt-contrib-requirejs task to build project based on require.js. Install it inside your project directory with:
npm install grunt-contrib-requirejs --save-dev
BTW: --save-dev will add the package to your development dependencies in your package.json. If you're not using a package.json in your project, ignore it.
Load the task in your grunt file with:
grunt.loadNpmTasks('grunt-contrib-requirejs');
And add the configuration to your grunt.initConfig
requirejs: {
production: {
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js",
out: "path/to/optimized.js"
}
}
}
Now you're able to build your require.js stuff into a single file that will be minimized with uglifyjs by running grunt requirejs
You can bundle a set of different tasks into some sort of main task, by adding this to your grunt file
grunt.registerTask('default', ['lint', 'requirejs']);
With this, you can simply type grunt and grunt will automatically run the default task with the two 'subtasks': lint and requirejs.
If you need a special production task: define it like the above
grunt.registerTask('production', ['lint', 'requirejs', 'less', 'copy']);
and run it with
grunt production
If you need different behaviors for 'production' and 'development' inside i.e. the requirejs task, you can use so called targets. In the configuration example above it's already defined as production. You can add another target if you need (BTW, you can define a global config for all targets by adding a options object on the same level)
requirejs: {
// global config
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js"
},
production: {
// overwrites the default config above
options: {
out: "path/to/production.js"
}
},
development: {
// overwrites the default config above
options: {
out: "path/to/development.js",
optimize: none // no minification
}
}
}
Now you can run them both at the same time with grunt requirejs or individually with grunt requirejs:production, or you define them in the different tasks with:
grunt.registerTask('production', ['lint', 'requirejs:production']);
grunt.registerTask('development', ['lint', 'requirejs:development']);
Now to answer your questions:
I would definitely use a subfolder in your project. In my case I use a 'src' folder for development that is build into a 'htdocs' folder for production. The project layout I prefere is:
project/
src/
js/
libs/
jquery.js
...
appname/
a.js
b.js
...
main.js // require.js starter
index.html
...
build/
... //some tmp folder for the build process
htdocs/
... // production build
node_modules/
...
.gitignore
grunt.js
package.json
see above
You can do so, but I wouldn't recommend to add requirejs to the watch task, it's a resource hungry task and it will slow down your machine noticeable.
Last but not least: Be very cautious when playing around with r.js. Especially when you want to optimize the whole project with r.js by adding a modules directive to your config. R.js will delete the output directory without asking. If it happens that it is accidentally configured to be your system root, r.js will erase your HDD. Be warned, I erased my whole htdocs folder permanently some time ago while setting up my grunt task... Always add keepBuildDir:true to your options when playing around with the r.js config.