How to use yeoman, grunt, usemin 2 and requirejs? - requirejs

I'm trying to wrap my head around using Grunt, usemin 2, requirejs, and uglify. What I'm observing in my builds is that requirejs is not properly including my dependencies into my single concatenated build file. When I run index.html out of /dist, I see errors when looking for 'jquery', 'app', and some third party js files or sometimes "define is not defined".
I read the following issues on grunt-usemin and removing the require blocks, but some questions still remain in those threads.
Recommended way to handle RequireJS, concat, uglify
How to handle requirejs in v2.0
I followed up my search and came across this post How to integrate Yeoman and Requirejs, which sort of got me there in that I saw the Requirejs optimizer running when I changed from using grunt-contrib-requirejs to grunt-requirejs. Unfortunately, I still see these errors:
Uncaught ReferenceError: define is not defined.
I have the following in my index.html:
<!-- build:js js/main.js -->
<script src="bower_components/requirejs/require.js"></script>
<script src="js/main.js"></script>
<!-- endbuild -->
Here is my Grunt file: http://jsbin.com/futocusu/3/edit?js
There was talk in issue #112 about creating an article on using Yeoman on this topic, but I don't think it's been written yet.
Has anyone figured out the best way to use usemin v2 with grunt and requirejs to output to a single concat+uglify file on build? I'm also not sure what the difference is in using grunt-contrib-requirejs and grunt-requirejs and when to use which one.

It looks as though you are trying to do too much with main.js.
I have the following build tasks in Gruntfile.js
grunt.registerTask('build', [
'copy', // copies the src directory to dist (htdocs)
'requirejs', // do an r.js build to concat modular dependencies
'concat:head', // concats js in the head
'uglify:head', // compresses js in the head
'uglify:foot', // compresses js in the foot
'cssmin', // minifies and concats css
'usemin', // runs through html and inputs minified js and css
'clean:afterBuild' // deletes files that are not required for build
]);
Here are each of the relevant Grunt tasks (for me these are stored in separate files because I use load-grunt-config). If you would like to use these in your gruntfile then all you need to do is grab everything that is in the returned object and stick that in your task value in your gruntfile:
copy
module.exports = function (grunt, options) {
return {
main: {
cwd: 'src/',
src: '**',
dest: 'dist/',
expand: true,
flatten: false
},
};
};
requirejs
module.exports = function(grunt, options) {
return {
compile: {
options: {
appDir: "src/to/require/app",
baseUrl: "./",
mainConfigFile: "src/to/require/app/common",
dir: "dist/to/require/app",
// build a common layer
modules: [
{
"name": "common"
}
]
}
}
};
};
concat
module.exports = function (grunt, options) {
return {
head: {
/* other stuff */
},
foot: {
src: [
'dist/to/require/app/some_other_js.js',
'dist/to/require/app/external/require.js',
'dist/to/require/app/external/require.text.js',
'dist/to/require/app/common.js'
],
dest: 'src/to/require/app/compiled_footer_js.js',
}
};
};
uglify
module.exports = function (grunt, options) {
return {
head: {
/* other stuff *
},
foot: {
files: {
'src/to/require/app/compiled_footer_js.min.js': ['src/to/require/app/compiled_footer_js.js']
}
}
};
};
usemin
module.exports = function (grunt, options) {
return {
html: [
'src/path/to/index.html'
]
};
};

Related

Grunt CSS min - Issue with minifying different CSS files

I'm having issue with minifying multiple CSS files using grunt cssmin I'm not looking to minify all files into single file. I would like to have the files having same name with min.css extension.
My gruntfile.js is as follows.
module.exports = function (grunt) {
grunt.initConfig({
cssmin: {
target: {
files: [{
src: ['assets/css/*.css', '!assets/css/*.min.css'], // source files mask
dest: 'assets/css/', // destination folder
expand: true, // allow dynamic building
flatten: true, // remove all unnecessary nesting
ext: '.min.css' // replace .css to .min.css
}],
/* BELOW IS ONLY A TRICK USED TO MINIFY THE FILES SKIPPED BY THE ABOVE */
/*files: [{
src: ['assets/css/home.css', 'assets/css/institutions.css', 'assets/css/form-elements.css'], // source files mask
dest: 'assets/css/', // destination folder
expand: true, // allow dynamic building
flatten: true, // remove all unnecessary nesting
ext: '.min.css' // replace .css to .min.css
}],*/
}
}
});
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', [ 'cssmin' ]);
};
As per the code (except the commented out part), it is suppose to minify all the css files. But for some reason it skips few css files from the directory. To minify those, I have to comment out the first files[{}] and uncomment the later.
It doesn't work when both files[{}] are uncommented. I'm clueless on why its happening.
My Nodejs version is: v0.10.25
Thanks in advance.
Note: I used to get warnings & task aborts on grunt cssmin due to node version issue and got it fixed by downgrading nvm version to nvm v0.10.39. Thanks to blindMoe for pointing the solution

Grunt: Watch file changes and compile parent directory

I'm working on a project using grunt, I haven't worked with grunt before and currently this is setup as to watch files and when a file has been changed recompile all the files (multiple subdirectories containing hundreds of files) using handlebars into html which is quite slow. I want to improve this to a faster process by only compiling what is needed.
Watching the files with grunt newer doesn't really work because there are dependencies within the directory and thus only recompiling the changed files will not result in a valid page.
I would basically need to recompile the whole parent directory of the file that has changed, but I'm not quite sure on how I would configure something like that.
Any hints where I should look at?
The assemble itself is configured like this:
var _ = require('lodash');
var path = require('path');
// expand the data files and loop over each filepath
var pages = _.flatten(_.map(grunt.file.expand('./src/**/*.json'), function(filepath) {
// read in the data file
var data = grunt.file.readJSON(filepath);
var dest=path.dirname(filepath)+ '/' +path.basename(filepath, path.extname(filepath));
dest=dest.replace("src/","");
var hbs;
if (data.hbs){
hbs=grunt.file.read(path.dirname(filepath)+ '/' + data.hbs)
}
// create a 'page' object to add to the 'pages' collection
return {
// the filename will determine how the page is named later
filename: dest,
// the data from the json file
data: data,
// add the recipe template as the page content
content:hbs
};
}));
return {
options: {
/*postprocess: require('pretty'),*/
marked: {sanitize: false},
data: '<%= options.src %>/**/*.json',
helpers: '<%= options.src %>/helpers/helper-*.js',
layoutdir: '<%= options.src %>/templates',
partials: ['<%= options.src %>/components/**/*.hbs']
},
build: {
options: {
layout: 'base.hbs',
assets: '<%= options.build %>',
pages: pages
},
files: [
{
cwd: '<%= options.src %>',
dest: '<%= options.build %>',
src: '!*'
}
]
},
}
So every time this loads all the pages get scanned down like /src/sites/abc/xyz/foo.json and get compiled, but I only want to have changed files. Watch does detect changed files, but all the files get compiled again and I'm not sure how I could get the changed files that watch has recognized in the config to only process part of the files.
I think what you need is already there in watch.
Check the Using the watch event in grunt doc.
Copying down the content here to satisfy the SO MODS/GODS.
This task will emit a watch event when watched files are modified. This is useful if you would like a simple notification when files are edited or if you're using this task in tandem with another task. Here is a simple example using the watch event:
grunt.initConfig({
watch: {
scripts: {
files: ['lib/*.js'],
},
},
});
grunt.event.on('watch', function(action, filepath, target) {
grunt.log.writeln(target + ': ' + filepath + ' has ' + action);
});
The watch event is not intended for replacing the standard Grunt API for configuring and running tasks. If you're trying to run tasks from within the watch event you're more than likely doing it wrong. Please read configuring tasks.
Compiling Files As Needed
A very common request is to only compile files as needed. Here is an example that will only lint changed files with the jshint task:
grunt.initConfig({
watch: {
scripts: {
files: ['lib/*.js'],
tasks: ['jshint'],
options: {
spawn: false,
},
},
},
jshint: {
all: {
src: ['lib/*.js'],
},
},
});
// on watch events configure jshint:all to only run on changed file
grunt.event.on('watch', function(action, filepath) {
grunt.config('jshint.all.src', filepath);
});
If you need to dynamically modify your config, the spawn option must be disabled to keep the watch running under the same context.
If you save multiple files simultaneously you may opt for a more robust method:
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
grunt.config('jshint.all.src', Object.keys(changedFiles));
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});

Using Gulp to build requireJS project - gulp-requirejs

I am trying to use gulp-requirejs to build a demo project. I expect result to be a single file with all js dependencies and template included. Here is my gulpfile.js
var gulp = require('gulp');
var rjs = require('gulp-requirejs');
var paths = {
scripts: ['app/**/*.js'],
images: 'app/img/**/*'
};
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
// The default task (called when you run `gulp` from cli)
gulp.task('default', ['requirejsBuild']);
The above build file works with no error, but the result.js only contains the content of main.js and config.js. All the view files, jquery, underscore, backbone is not included.
How can I configure gulp-requirejs to put every js template into one js file?
If it is not the right way to go, can you please suggest other method?
Edit
config.js
require.config({
paths: {
"almond": "/bower_components/almond/almond",
"underscore": "/bower_components/lodash/dist/lodash.underscore",
"jquery": "/bower_components/jquery/dist/jquery",
"backbone": "/bower_components/backbone/backbone",
"text":"/bower_components/requirejs-text/text",
"book": "./model-book"
}
});
main.js
// Break out the application running from the configuration definition to
// assist with testing.
require(["config"], function() {
// Kick off the application.
require(["app", "router"], function(app, Router) {
// Define your master router on the application namespace and trigger all
// navigation from this instance.
app.router = new Router();
// Trigger the initial route and enable HTML5 History API support, set the
// root folder to '/' by default. Change in app.js.
Backbone.history.start({ pushState: false, root: '/' });
});
});
The output is just a combination this two files, which is not what I expected.
gulp-requirejs has been blacklisted by the gulp folks. They see the RequireJS optimizer as its own build system, incompatible with gulp. I don't know much about that, but I did find an alternative in amd-optimize that worked for me.
npm install amd-optimize --save-dev
Then in your gulpfile:
var amdOptimize = require('amd-optimize');
var concat = require('gulp-concat');
gulp.task('bundle', function ()
{
return gulp.src('**/*.js')
.pipe(amdOptimize('main'))
.pipe(concat('main-bundle.js'))
.pipe(gulp.dest('dist'));
});
The output of amdOptimize is a stream which contains the dependencies of the primary module (main in the above example) in an order that resolves correctly when loaded. These files are then concatenated together via concat into a single file main-bundle.js before being written into the dist folder.
You could also minify this file and perform other transformations as needed.
As an aside, in my case I was compiling TypeScript into AMD modules for bundling. Thinking this through further I realized that when bundling everything I don't need the asynchronous loading provided by AMD/RequireJS. I am going to experiment with having TypeScript compile CommonJS modules instead, then bundling them using webpack or browserify, both of which seem to have good support within gulp.
UPDATE
My previous answer always reported taskReady even if requirejs reported an error. I reconsidered this approach and added error logging. Also I try to fail the build completely as described here gulp-jshint: How to fail the build? because a silent fail really eats your time.
See updated code below.
Drew's comment about blacklist was very helpfull and gulp folks suggest using requirejs directly. So I post my direct requirejs solution:
var DIST = './dist';
var requirejs = require('requirejs');
var requirejsConfig = require('./requireConfig.js').RJSConfig;
gulp.task('requirejs', function (taskReady) {
requirejsConfig.name = 'index';
requirejsConfig.out = DIST + 'app.js';
requirejsConfig.optimize = 'uglify';
requirejs.optimize(requirejsConfig, function () {
taskReady();
}, function (error) {
console.error('requirejs task failed', JSON.stringify(error))
process.exit(1);
});
});
The file at ./dist/app.js is built and uglified. And this way gulp will know when require has finished building. So the task can be used as a dependency.
My solution works like this:
./client/js/main.js:
require.config({
paths: {
jquery: "../vendor/jquery/dist/jquery",
...
},
shim: {
...
}
});
define(["jquery"], function($) {
console.log($);
});
./gulpfile.js:
var gulp = require('gulp'),
....
amdOptimize = require("amd-optimize"),
concat = require('gulp-concat'),
...
gulp.task('scripts', function(cb) {
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
configFile: "./client/js/main.js",
baseUrl: './client/js'
}
))
.pipe(concat('main.js'));
.pipe(gulp.dest(path.destScripts));
}
...
This part was important:
configFile: "./client/js/main.js",
baseUrl: './client/js'
This allowed me to keep my configuration in one place. Otherwise I was having to duplicate my paths and shims into gulpfile.js.
This works for me. I seems that one ought to add in uglification etc via gulp if desired. .pipe(uglify()) ...
Currently I have to duplicate the config in main.js to run asynchronously.
....
var amdOptimize = require("amd-optimize");
...
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
paths: {
jquery: "client/vendor/jquery/dist/jquery",
jqueryColor: "client/vendor/jquery-color/jquery.color",
bootstrap: "client/vendor/bootstrap/dist/js/bootstrap",
underscore: "client/vendor/underscore-amd/underscore"
},
shim: {
jqueryColor : {
deps: ["jquery"]
},
bootstrap: {
deps: ["jquery"]
},
app: {
deps: ["bootstrap", "jqueryColor", "jquery"]
}
}
}
))
.pipe(concat('main.js'));
Try this code in your gulpfile:
// Node modules
var
fs = require('fs'),
vm = require('vm'),
merge = require('deeply');
// Gulp and plugins
var
gulp = require('gulp'),
gulprjs= require('gulp-requirejs-bundler');
// Config
var
requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('app/config.js') + '; require;'),
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
name: 'main',
baseUrl: './app',
out: 'result.js',
paths: {
requireLib: 'bower_modules/requirejs/require'
},
insertRequire: ['main'],
// aliases from config.js - libs will be included to result.js
include: [
'requireLib',
"almond",
"underscore",
"jquery",
"backbone",
"text",
"book"
]
});
gulp.task('requirejsBuild', ['component-scripts', 'external-scripts'], function (cb) {
return gulprjs(requireJsOptimizerConfig)
.pipe(gulp.dest('app/dist'));
});
Sorry for my english. This solution works for me. (I used gulp-requirejs at my job)
I think you've forgotten to set mainConfigFile in your gulpfile.js. So, this code will be work
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
mainConfigFile: 'path_to_config/config.js',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
In addition, I think when you run that task in gulp, require can not find its config file and
This is not gulp-requirejs fault.
The reason why only main.js and config.js is in the output is because you're not requiring/defining any other files. Without doing so, the require optimizer wont understand which files to add, the paths in your config-file isn't a way to require them!
For example you could load a main.js file from your config file and in main define all your files (not optimal but just a an example).
In the bottom of your config-file:
// Load the main app module to start the app
requirejs(["main"]);
The main.js-file: (just adding jquery to show the technique.
define(["jquery"], function($) {});
I might also recommend gulp-requirejs-optimize instead, mainly because it adds the minification/obfuscation functions gulp-requirejs lacks: https://github.com/jlouns/gulp-requirejs-optimize
How to implement it:
var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('requirejsoptimize', function () {
return gulp.src('src/js/require.config.js')
.pipe(requirejsOptimize(function(file) {
return {
baseUrl: "src/js",
mainConfigFile: 'src/js/require.config.js',
paths: {
requireLib: "vendor/require/require"
},
include: "requireLib",
name: "require.config",
out: "dist/js/bundle2.js"
};
})).pipe(gulp.dest(''));
});

How to get a single javascript page using r.js

I am doing my first try using requireJS and it works great !
I now would like to use the optimizer and i meet some issues when running my code in the browser.
I have these JS files:
/public/javascripts/build.js
/public/javascripts/main.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/c.js
a.js, b.js and c.js are modules i define for my application using requireJS.
main.js:
require.config({
paths: {
'jQuery': 'lib/jquery.min.js'
},
shim: {
'jQuery': {
exports: '$'
}
}
});
require(['a.js'], function(A){
var Entity = new A();
});
build.js
({
baseUrl: ".",
paths: {
requireLib: "lib/require.min",
jquery: "lib/jquery.min"
},
name: "main",
out: "main-built.js",
include: ["requireLib"]
})
Also i am wondering why do we have to specify the paths of the libraries into the build.js and not the other javascript files.
When i do not use the optimizer and only load the file
<script src="/javascripts/lib/require.min.js" data-main="/javascripts/main"></script>
it works great, but when i run r.js -o ./public/javascripts/build.js and only load
<script src="/javascripts/main-built.js"></script> i get the error Uncaught TypeError: undefined is not a function in the minified code.
How to explain that ?
Here are the logs i get when running r.js
Tracing dependencies for: main
Uglifying file: /public/javascripts/main-built.js
/public/javascripts/main-built.js
----------------
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/c.js
/public/javascripts/main.js
This is definitely wrong:
require(['a.js'], function(A){
var Entity = new A();
});
You should not use extensions in the list of dependencies you give to require or define. Modules should be named without extension. So here 'a', not 'a.js'. Using 'a.js' will cause RequireJS to fail loading what you really want once the optimizer has run. Let's say you have a file named a.js which has:
define(function () {
return function () {};
});
The optimizer will include it into your main-built.js file like this:
define("a", function () {
return function () {};
});
Note how the first parameter to define is now "a". This has been added by r.js. This is the name of the module. When you load main-built.js, a module named "a" is defined. When you use require with "a.js", you are telling RequireJS you want something in a file named a.js so RequireJS will go looking for that and ignore what is in main-built.js.
Also, jQuery 1.8 or over does not need a shim.
I just have added
shim: {
'jQuery': {
exports: '$'
}
}
into the build.js file, and it works perfectly !
Thanks !

Require JS optimization

I have around 50 JS files and I have to optimize it using r.js and node...
I dont want to specify all the JS files, instead specify the top level folder and somehow let r.js to get all the required js files....
Is there a way to achieve this? Currently I am specifying all the 50 js files in a common js files and referring it in my build.js...I have more files in the coming weeks, and so maintaining a common js file will be a pain.
Please suggest some steps.
here is my build file
({
baseUrl: ".",
mainConfigFile: "../App/main.js",
//modules: [
// //{ name: "../App/Crosspoint/Address/AddressList" }
// { name: "../App/Crosspoint/Office/OfficeDetails" }
//],
//paths: {
// app: '../App',
// jquery: 'jquery'
//},
name: "../App/Crosspoint/Office/OfficeDetails",
deps: ["../App"],
out: "main-built123.js",
rawText: {
'some/id': 'define(["another/id"], function () {});'
},
// dir: "app",
})
Optimizer should include all the dependencies required by main config file. Also if you have nested dependencies then specify:
findNestedDependencies: true

Resources