Compiling handlebars templates using Grunt - javascript

I'm trying to compile my handlebars template using Grunt as described below. It's okay but it compiles all the hbs files into one file which is not so good. I would like to have a separate file for Menu1 and Menu2 directories. I'd be really great if I could somehow make a separate task for each directory.
But as I'm still learning this stuff I couldn't find any better way to make it work..
Or perhaps if I could somehow make the Handlebars compile each file separately.
Project tructure:
/js/:
->/app/
app_init.js
->/templates/
-> /Menu1/
template1.hbs
template2.hbs
-> /Menu2/
template1.hbs
template2.hbs
Gruntfile.js
module.exports = function(grunt) {
var TEMPLATES_LOCATION = "./js/templates/",
TEMPLATES_EXTENSION = ".hbs",
TEMPLATES_OUTPUT_LOCATION = TEMPLATES_LOCATION,
TEMPLATES_OUTPUT_FILENAME = "compiled_templates.js";
grunt.initConfig({
watch: {
handlebars: {
files: [TEMPLATES_LOCATION + '**/*' + TEMPLATES_EXTENSION],
tasks: ['handlebars:compile']
}
},
handlebars: {
compile: {
src: TEMPLATES_LOCATION + '**/*' + TEMPLATES_EXTENSION,
dest: TEMPLATES_OUTPUT_LOCATION + TEMPLATES_OUTPUT_FILENAME,
options: {
amd: true,
namespace: "templates"
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-handlebars');
grunt.loadNpmTasks('grunt-contrib-watch');
}
Can anyone please advise?

I just came up with a slight workaround. Not sure if this is the best way to do this but it works..
handlebars: {
compile_Menu1: {
src: TEMPLATES_LOCATION + '/Menu1/*' + TEMPLATES_EXTENSION,
dest: TEMPLATES_OUTPUT_LOCATION+"Menu1"+TEMPLATES_OUTPUT_EXTENSION,
options: {
amd: true,
namespace: "M1_templates"
}
},
compile_Menu2: {
src: TEMPLATES_LOCATION + '/Menu2/*' + TEMPLATES_EXTENSION,
dest: TEMPLATES_OUTPUT_LOCATION+"Menu2"+TEMPLATES_OUTPUT_EXTENSION,
options: {
amd: true,
namespace: "M2_templates"
}
}
}

Related

grunt sass: compile only changed files

I have the following folder structure:
+ web
+ component
- component.js
- component.scss
- component.css
+ another
- another.js
- another.scss
- another.css
As you can see I compile every css into his component folder.
This is my configuration:
grunt.initConfig({
sass: {
dist: {
files: [{
expand: true,
cwd: './',
src: ['web/**/*.scss'],
dest: './',
ext: '.css'
}]
}
},
watch: {
files: ["web/**/*.scss"],
tasks: ['sass'],
options : { spawn: false }
}
});
Everything works fine, but the problem is that every time a scss file is changed, it compiles ALL the scss in my web folder.
I want to compile only the changed scss.
I also tried installing grunt-newer and changing the watch configuration in:
watch: {
files: ["web/**/*.scss"],
tasks: ['newer:sass'],
options : { spawn: false }
}
But it doesn't compile anything now.
ps: I would like to avoid mapping every single file in the sass configuration (scss -> css) since the rule is the same for all the files (same name, same folder), and because I have tons of them.
ok, i was close.. i'll post the answer as well, it may help someone.
i had to remove options : { spawn: false }, so my config file now is like this:
grunt.initConfig({
sass: {
dist: {
files: [{
expand: true,
cwd: './',
src: ['web/**/*.scss'],
dest: './',
ext: '.css'
}]
}
},
watch: {
files: ["web/**/*.scss"],
tasks: ['newer:sass']
}
});

Can you pass a Grunt variable to a JavaScript function within a Grunt task?

Here is an example of what I'm looking for:
module.exports = function(grunt) {
grunt.initConfig({
config: grunt.file.readYAML('_config.yml'),
// example variable: <%= config.scripts %>
copy: {
scripts: (function() {
if (config.scripts === true) { // I want to target <%= config.scripts %>
return {
expand: true,
cwd: '<%= input %>/_assets/js/',
src: '**/*.js',
dest: '<%= output %>/assets/js/'
};
} else {
return {
// do nothing
};
}
})()
}
});
};
I know Grunt can read the data from within a file using 'grunt.file.readJSON', and then have that data available with the following type of variable, '<%= pkg.value %>'.
What I'm wanting to do is to create a task with if/else options based on the variables from within the JSON file. What I'm unclear on is how to pass a Grunt variable '<%= pkg.value %>' into the JavaScript if statement in a way that it understands. I've tried passing it in the same Grunt format with '<%= %>', as well as stripping that part away and passing 'pkg.value', but neither seems to work.
If someone can shed some light on whether or not this can be done and how, I would greatly appreciate it. Thanks!
Instead of directly assign the grunt config in config attribute, store it in a variable (gruntConfig). Now you will be able to access it in the following code.
module.exports = function(grunt) {
// store your grunt config
var gruntConfig = grunt.file.readYAML('_config.yml');
// init `script` with an empty object
var script = {};
if (gruntConfig.script) {
script = {
expand: true,
cwd: '<%= input %>/_assets/js/',
src: '**/*.js',
dest: '<%= output %>/assets/js/'
};
}
// Init Grunt configuration
grunt.initConfig({
config: gruntConfig,
copy: {
scripts: script
}
});
};
When I have more time, I'll probably look into some additional ideas, but based on the ideas offered here, this is what I ended up going with for now.
module.exports = function(grunt) {
var config = grunt.file.readYAML('_config.yml');
grunt.initConfig({
copy: {
scripts: (function() {
if (config.scripts) {
return {
expand: true,
cwd: 'input/_assets/js/',
src: '**/*.js',
dest: 'output/assets/js/'
};
} else {
return {
// do nothing
};
}
})()
}
});
};
Thanks, everyone, for your help!
Grunt has an API to read files, which you can use something like this:
test.json
{
"fruit": "apple"
}
Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
...
})
grunt.registerTask('sample-task', function() {
var test = grunt.file.readJSON('./test.json');
if (test.fruit === 'apple') {
// do this one thing
} else {
// do something else
}
});
grunt.registerTask('default', ['sample-task']);
};

message: 'Unexpected token: punc (.)', while using uglify in grunt

Goal
My goal is to concatenate all my css,js files and minify all of them.
I can minify my concat.js, but I'm struggling trying to minify my concat.css.
Gruntfile.js
module.exports = function(grunt) {
"use strict";
grunt.initConfig({
concat: {
js: {
src: [
'js/bootstrap.min.js',
'js/jquery-1.10.2.min.js',
'js/jquery.easypiechart.min.js',
'js/jquery.isotope.min.js',
'js/jquery.magnific-popup.min.js',
'js/waypoints.min.js',
'js/respond.min.js',
'js/jquery.vegas.min.js',
'js/modernizr-2.6.2.min.js',
'js/jquery.nav.js',
'js/html5shiv.js',
'js/jquery.scrollTo.js',
'js/jquery.sticky.js',
'js/jquery.validate.js',
'js/main.js',
],
dest: 'dist/concat.js'
},
css: {
src: [
'css/magnific-popup.css',
'css/main.css',
'css/xl.css',
'css/lg.css',
'css/md.css',
'css/sm.css',
'css/xs.css',
'css/print.css',
'css/bootstrap.min.css',
'css/font-awesome.min.css',
],
dest: 'dist/concat.css'
}
},
watch: {
js: {
files: ['js/*.js'],
task: ['concat:js']
},
css: {
files: ['css/*.css'],
task: ['concat:css']
}
},
uglify: {
js: {
files: {
'dist/minified.js': ['dist/concat.js']
}
},
css: {
files: {
'dist/minified.css': ['dist/concat.css']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['concat', 'uglify']);
};
Result
I concatenate all my css and js files succesfully, and they're generated at :
dist/concat.js
dist/concat.css
Then, I can also minify my concat.js with no problem, but I'm struggling trying to minify my concat.css.
I kept getting this error in the bottom of my Terminal :
Running "uglify:css" (uglify) task
{ message: 'Unexpected token: punc (.)',
filename: 'concat.css',
line: 4,
and line4 is just the beginning of my class : .mfp-bg {
Can someone please give me a little push here ?
Also, should I perform minify after concatenation or the other way around ?
Is there a better way to do this ?
uglify is for minimising JavaScript only, not CSS.
If you want to minimise CSS you can use the cssmin task for Grunt instead.

Register Grunt tasks without grunt.initConfig()

I want my gruntfile.js to be naturally structured so that micro-tasks follow each other one by one.
Suppose I have the following structure:
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
clean: {
movedTyping: 'options...'
},
copy: {
typing: 'options...',
lessVariables: 'options...',
html: 'options...'
},
less: {
compile: 'options...'
},
typescript: {
compile: 'options...'
}
});
grunt.registerTask('build', [
// TYPESCRIPT
'typescript:compileSingle',
'copy:typing',
'clean:movedTyping',
// LESS
'less:compile',
'copy:lessVariables',
// HTML
'copy:html'
]);
But I would like to achieve the other structure:
grunt.registerTask('build', function () {
// TYPESCRIPT
grunt.task.run('typescript', 'options...');
grunt.task.run('copy', 'options...');
grunt.task.run('clean', 'options...');
// LESS
grunt.task.run('less', 'options...');
grunt.task.run('copy', 'options...');
// HTML
grunt.task.run('copy', 'options...');
});
How?
You can set properties of the object by using . (dot) notation. So nested structure data can also be set. I haven't come across a more cleaner approach and will be happy to see a better approach.
grunt.registerTask('build', function () {
// TYPESCRIPT
grunt.config.set('typescript.compile','<options>');
grunt.task.run('typescript');
......................
});
In order to achieve this I created NPM module create-grunt-tasks.
Now my grunt file looks like this:
// Gruntfile.js
module.exports = function (grunt) {
require('create-grunt-tasks')(grunt, function (create) {
create.task('build')
// Compile TypeScript and move typing
.sub('typescript', {
src: 'src/index.ts',
dest: 'build/index.js',
options: { module: 'amd', target: 'es5', declaration: true }
})
.sub('copy', {
expand: true, flatten: true,
src: 'build/index.d.ts',
dest: 'build/typing/index.d.ts'
})
.sub('clean', ['build/index.d.ts'])
// Copy HTML
.sub('copy', {
expand: true, flatten: true,
src: 'src/index.html',
dest: 'build/index.html'
});
});
}

Dynamic Grunt Watch configuration

My site file structure is something like this:
app
less
themes
default
modern
etc
public
themes
default
css
modern
css
etc
So /app/less/themes/{{ themeName }}/*.less will compile into /public/themes/{{ themeName }}/css/*.css.
I have my Grunt Less task set up dynamically and working fine.
Example: grunt less:compileTheme:modern
less: {
compileTheme: {
options: {
compress: false,
yuicompress: false,
optimization: 2,
sourceMap: true,
sourceMapFilename: "public/themes/<%= grunt.task.current.args[0] %>/css/styles.css.map",
sourceMapURL: "/themes/<%= grunt.task.current.args[0] %>/css/styles.css.map"
},
files: {
"public/themes/<%= grunt.task.current.args[0] %>/css/styles.css": "app/less/themes/<%= grunt.task.current.args[0] %>/styles.less"
}
}
}
But I'm not sure how to get the Grunt Watch task working. Ideally, it'll see the theme folder name and run the grunt task as grunt less:compileTheme:{{ themeName }}:
watch: {
themeCss: {
files: "app/less/themes/{{ themeName }}/**/*.less",
tasks: ["less:compileTheme:{{ themeName }}"],
options: {
spawn: false
}
}
}
Is this possible? And/or is there a better way I should be doing this?
edit your watch task to run the less:compileTheme-task without themeName, and watch all less files in your themes folder:
watch: {
themeCss: {
files: "app/less/themes/**/*.less",
tasks: ["less:compileTheme"],
options: {
spawn: false
}
}
}
now you can add a watch event-handler where you get the changed file. extract the themeName, and configure your less-task:
grunt.event.on('watch', function(action, filepath) {
var themeName = filepath.split('/')[3];
grunt.config('less.compileTheme.files', {
"public/themes/" + themeName + "/css/styles.css": "app/less/themes/" + themeName + "/styles.less"
});
});
of course the detection of your themeName should be more reliable, but i think you get the idea on how to do dynamic watching!

Categories