In my web project I have the following directory structure
|- target
|- foo
|- bar
|- baz
I'm trying to write a Grunt task that will copy all JSON files into the target directory from the directory whose name matches a parameter provided to the build
grunt.registerTask('flavor', function(srcDir) {
var from = './' + srcDir + '/*.json';
var dest = './target/';
grunt.file.expand(from).forEach(function(src) {
grunt.file.copy(src, dest);
});
});
But when I call this with
grunt flavor:foo
I get an error
Warning: Unable to write "./target/" file (Error code: EISDIR). Use --force to continue.
As #DanielApt mentioned, you should just use grunt-contrib-copy. To build on his answer regarding on your comment about build-parameters, you can get them to the task via grunt-option.
Way one: running different target
grunt.initConfig({
copy: {
foo: {
files: [
{
'expand': 'true',
'cwd': 'foo/',
'src': [**],
'dest': 'dist/en'
}
]
},
bar: {
files: [/**/]
},
baz: {
files: [/**/]
}
}
});
var target = grunt.option("target") || "foo";
grunt.registerTask("default", ["copy:" + target]);
// run with grunt --target=foo
Way 2: Arbituary folder with templating:
var target = grunt.option("target") || "foo";
grunt.initConfig({
target: target,
copy: {
default_target: {
files: [
{
'expand': 'true',
'cwd': '<%= target %>/',
'src': [**],
'dest': 'dist/en'
}
]
},
}
});
grunt.registerTask("default", ["copy"]);
// run with grunt --target=anyfolderhere
Do you need to write your own task? If you don't I achieved this with grunt-contrib-copy.
copy: {
default: {
files: [
{
expand: true,
src: ['foo/**', 'bar/**'],
dest: 'target/'
}
]
}
}
Related
I am trying to minify an angularjs application using grunt and terser. I first used uglifiy-es but then read that it has some issues. So I tried terser. But the output does not give me minified files.
The gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//grunt task configuration will go here
ngAnnotate: {
options: {
singleQuotes: true
},
app: {
files: {
'./public/min-safe/js/_config_min.js': ['./controllers/_config.js'],
'./public/min-safe/js/ctrl_accountingdashboard.js': ['./controllers/ctrl_accountingdashboard.js'],
}
}
},
concat: {
js: { //target
src: ['./public/min/app.js', './public/min-safe/js/*.js'],
dest: './public/min/app.js'
}
},
terser: {
options: {},
files: {
'./public/min/app.js': ['./public/min/app.js'],
},
}
});
//load grunt tasks
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-terser');
grunt.loadNpmTasks('grunt-ng-annotate');
//register grunt default task
grunt.registerTask('default', ['ngAnnotate', 'concat', 'terser']);
}
I had the same problem. According to the documentation, this should work but it didn't for me. Wrapping the "files" setting in a custom target works for me:
terser: {
options: {},
main: {
files: {
'./public/min/app.js': ['./public/min/app.js'],
}
}
}
To add to #Tim's great answer:
Here is an example that allows to run grunt-terser with path / file wildcard patterns (globbing) – which it does not support out of the box.
Please note the helper properties _src and _dest in the terser config which are not read by grunt-terser itself but by the task terser_all. This task expands the globbing pattern(s) in _src and builds the real config in the files property. When done it runs terser with that updated config.
module.exports = function (grunt) {
grunt.initConfig({
terser: {
dist: {
options: {
compress: {
drop_console: true // remove console.log, console.info, ...
}
},
files: {
// FILLED through terser_all task below!
// Examples config:
// "dist/example.js": [ "path/to/files/example.js" ]
// "dist/example_joined.js": [ "path/to/files/*.js" ]
},
// -----
// HELPER PROPERTIES to build the files prop (see above) in the terser_all task below.
_src: [
"path/to/files/*.js"
],
_dest: "dist/"
}
}
});
grunt.registerTask('terser_all', function () {
// work on this target in terser config
var terser_target_name = "dist";
// read the terser config
var terser_config = grunt.config.get('terser') || {};
var terser_target_config = terser_config[terser_target_name] || {};
// get the destination dir
var destDir = terser_target_config._dest;
// loop through all source files and create an entry in the terser config for each of it
var files = grunt.file.expand(terser_target_config._src);
for (const [i, file] of files.entries()) {
grunt.log.writeln(file);
// add this file to the terser config as: dest_file: src_file
terser_target_config.files[destDir + file] = file;
}
// show new config on CLI
grunt.log.writeflags(terser_target_config);
// write back config and run task
grunt.config.set('terser', terser_config);
grunt.task.run('terser');
});
grunt.loadNpmTasks('grunt-terser');
grunt.registerTask('build', ['terser_all']);
grunt.registerTask('default', ['build']);
};
Just a note:
If you try to "disable" some options by renaming this disables the whole process. At least this was my result with grunt-terser. I was left with the original js file.
{
mangleX: {
reserved: [/* ... */]
}
}
I use this code in grunt config:
const libDir = 'public/lib'
const cssDir = 'public/css'
// Project configuration.
grunt.initConfig({
watch: {
scripts: {
files: '**/*.js',
tasks: ['default'],
},
},
clean: [libDir],
bower_concat: {
all: {
dest: {
'js': libDir + '/vendor.js',
'css': libDir + '/vendor.css'
},
}
},
sass: {
options: {
sourceMap: true
},
dist: {
files: {
'output.css': 'input.scss'
}
}
}
});
How do I generate output.css dynamically by concatenating cssDir and some string ?
I tried using templating but it ignores cssDir.
Templates are not expanded for all config properties and especially not for config keys.
Since Grunt files are basically Javascript programs, you can construst the object programmatically:
const cssDir = 'public/css';
var sassFilesMap = {};
// build sass output file mapping programmatically
sassFilesMap[cssDir + "/output.scss"] = "input.scss";
// Project configuration.
grunt.initConfig({
// ...
sass: {
options: {
sourceMap: true
},
dist: {
files: sassFilesMap
}
}
});
If there are multiple .scss files to be converted (to be configured) you might with something like this:
const sassMappings = [["edit.scss", "edit.css"], ["public.scss", "public.css"]];
sassMappings.forEach(function(pair) {
// pair[0] is the .scss filename, pair[1] is the .css filename
sassFilesMap[cssDir + "/" + pair[0]] = pair[1];
});
I found out that ES6 supports dynamic property keys so basically this solves the problem as I can now compute both the key and value as dirVar + 'outputFile': inputDir + 'input.file'
I'm new to Grunt and I'm lost in all it's option.
I'm trying to do two things :
Minify all my js files into one each time I change one of them
Process a specific scss file when one of my scss file is changed
Here is the current Gruntfile.js I got :
module.exports = function (grunt) {
grunt.initConfig({
// define source files and their destinations
uglify: {
files: {
src: 'js/*.js', // source files mask
dest: 'jsm/', // destination folder
expand: true, // allow dynamic building
flatten: true, // remove all unnecessary nesting
ext: '.min.js' // replace .js to .min.js
}
},
watch: {
js: { files: 'js/*.js', tasks: [ 'uglify' ] },
}
});
// load plugins
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
// register at least this one task
grunt.registerTask('default', [ 'uglify' ]);
};
How could I achieve this?
Use the grunt-contrib-concat
grunt.initConfig({
concat: {
dist: {
src: ['files/**.min.js'],
dest: 'project.js',
},
}
});
You should wrap all you files in an anonymous function and define variables using var so you do not get variable conflicts between files.
(function(){
// foo has function scope
var foo = 3;
console.log(foo);
// FOO is a global variable and
// can be accessed between files
window.FOO = 3;
console.log(FOO);
}).call()
I'm trying to template all of the files in a directory and put the result in bin/admin directory such that the destination file has the same name as the source file. However, it seems like the dest in data is the name of file and cannot be specified as a destination directory.
module.exports = function(grunt) {
grunt.initConfig({
'template': {
'process-html-template': {
'options': {
'data': {
'api_url': 'My blog post'
}
},
'files': {
'bin/admin/': ['src/admin/*'] // <-- Here
}
}
}
});
grunt.loadNpmTasks('grunt-template');
grunt.registerTask('default', ['template']);
}
What can I do to template all of the files in src/ and put them in a destination folder with the same name as the source? I tried to use bin/admin/* as the destination, but that just create a file with the filename * in bin/admin. I want to avoid listing all of the files in the source directory manually.
I figured it out. It's an object with a src and dest attribute.
module.exports = function(grunt) {
grunt.initConfig({
'template': {
'process-html-template': {
'options': {
'data': {
'api_url': 'My blog post'
}
},
'files': [
{
expand:true,
src: 'src/admin/*',
dest: 'bin/admin/'
}
]
}
}
});
grunt.loadNpmTasks('grunt-template');
grunt.registerTask('default', ['template']);
}
I have a directory with a bunch of jade templates, and a grunt task that compiles all of them to individual html files.
I'd like to have a watch task that recompiles a template when it changes, but right now my task recompiles every template when any of them change.
Here is a demo in a gist.
Is there a succinct way to write a task that recompiles a template when it changes, but not all of the other templates?
the solution is to add a filter function to the files list:
var fs = require('fs');
var join = require('path').join;
module.exports = function(grunt) {
grunt.initConfig({
jade: {
files: {
src: ['*.jade'],
dest: './',
expand: true,
ext: '.html',
filter: function(destDir, src) {
var dest = join(destDir, src.replace(/jade$/, 'html'));
var destMod;
try {
destMod = +fs.lstatSync(dest).mtime;
} catch (e) {
if (e.code === 'ENOENT') {
// there's no html file, so ensure that the
// jade file is compiled by returning true
return true;
}
throw e;
}
return fs.lstatSync(src).mtime > destMod;
}
}
},
watch: {
files: ['*.jade'],
tasks: ['jade']
}
});
grunt.loadNpmTasks('grunt-contrib-jade');
grunt.loadNpmTasks('grunt-contrib-watch');
};