How do I include the filename in an uglify banner in grunt? - gruntjs

I am using grunt to minify some JS files. It would be nice to include the filename in the banner. I have found several examples how to include the package name in the banner, but I haven't yet managed to get the filename in there. So: What do I have to put in the gruntfile (see below) instead of pkg.name to get the source filename (or the dest filename) in there?
Thanks in advance
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: {
files: [{
expand: true,
src: '**/*.js',
dest: 'build',
cwd: 'src',
ext: '.min.js'
}]
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};

I tried to do the same thing, and couldn't find any reference to the current filename that was exposed to the template. Here's the workaround I finally figured out; it's a custom task that dynamically creates a new target for each file:
grunt.registerMultiTask('minify', function () {
this.files.forEach(function (file) {
var path = file.src[0],
target = path.match(/src\/(.*)\.js/)[1];
// store some information about this file in config
grunt.config('ugtargets.' + target, {
path: path,
filename: path.split('/').pop()
});
// create and run an uglify target for this file
grunt.config('uglify.' + target + '.files', [{
src: [path],
dest: path.replace(/^src\/(.*)\.js$/, 'build/$1.min.js')
}]);
grunt.task.run('uglify:' + target);
});
});
And my uglify config:
uglify: {
options: {
banner: 'Filename: <% ugtargets[grunt.task.current.target].filename %>\n'
}
}
For each file in your source directory, we create a target name out of the filename. The exact process will depend on how your files are named; you'll want to strip out any dots or slashes. Here I used a regex; in my app I actually used fs to read some JSDoc data from the files themselves.
Then we store that filename in an object in the Grunt configuration, indexed by the target name. Here I used an object with a couple of properties; you could add more stuff here or just use a plain string if you wanted.
Finally we add a target configuration to the uglify config. This is just the src and dest for the current file; we have to do our own filename processing but it's not much work. Then we run the uglify task with the new target.
In the banner template, you can now use grunt.task.current.target to grab the data we stored in the config earlier. Presto!

In case you have more scripts to be uglify is convenient to separate it to particular subtasks with custom property as name identifier.
uglify: {
options: {
banner:
'<% var subtask = uglify[grunt.task.current.target]; %>' +
'/* <%= subtask.name %> <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
' */\n'
},
main: {
name: 'main.min.js',
files: [{
src: 'build/files/js/main.min.js',
dest: 'build/files/js/main.min.js'
}]
},
vendor: {
name: 'vendor.min.js',
files: [{
src: 'build/files/js/vendor.min.js',
dest: 'build/files/js/vendor.min.js'
}]
}
}

Try the following:
banner: grunt.file.read('./folder/file.js'),
https://stackoverflow.com/questions/38854998/dynamic-mapping-and-concat-with-grunt-uglify/

https://github.com/mattstyles/grunt-banner/issues/5#issuecomment-33445038
process: function (src, filepath) {} did the job.
For me, I wanna add "//# sourceUrl=xxx.min.js" at the bottom of each uglified .min.js so that I can debug these dynamically loaded .min.js. The following simple Gruntfile.js works for me:
module.exports = function (grunt) {
var cwd = "/Branding/Layouts/JS/";
var src = [
"file1.js",
"file2.js",
"file3.js"
];
var minified_src = [];
for (i=0; i< src.length; i++)
minified_src.push(src[i].replace(/\.js$/g, ".min.js"));
var config = grunt.initConfig({
"uglify": {
options: {
sourceMap: true
},
target: {
files: [
{
expand: true,
cwd: cwd,
src: src,
dest: cwd,
ext: ".min.js",
extDot: "last"
}
]
}
},
concat: {
options: {
process: function (src, filepath) {
return src + "\n//# sourceURL=" + filepath.split("/").slice(-1);
}
},
target: {
files: [
{
expand: true,
cwd: cwd,
src: minified_src,
dest: cwd
}
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['uglify', 'concat']);
};
Note: uglify can't preserve a comment that is not inside a code block (like //# sourceMap=xxx.js), I have to use concat to append the comment after uglify is done.
Wow, this is my first stackoverflow post.

Documentation is indeed scarse, but <%= pkg.name %> in the banner string implies you can also do <% for ( var s in grunt) { %> \ngrunt.<%=s%><% } %> or even <% for ( var s in this) { %> \nthis.<%=s%><% } %>.
So (after some searching) to get the filename you can do this:
var bannerTemplate = '<%'
+' var subtask = uglify[grunt.task.current.target];'
+' var file = subtask?subtask.dest:\'\';'
+' var filename = file.split(\'/\').pop();'
+'%>'
+'/*! <%= filename %>'
+'\n * version: <%= pkg.version %>'
+'\n * author: <%= pkg.author %>'
+'\n */\n';

Related

How to use array variable properly in gruntfile.js

Trying to use a predefined array inside of a grunt file, thought using this.js_paths would work, but doesn't seem to work as I'm getting the error, "Cannot read property IndexOf of undefined" when it comes to trying to uglify the scripts. How can I link the js_paths variable to the files src property properly instead of copying the array into the files. Would like to define it separately at the top. Is this possible?
module.exports = function(grunt) {
// loadNpmTasks from package.json file for all devDependencies that start with grunt-
require("matchdep").filterDev("grunt-*", './package.json').forEach(grunt.loadNpmTasks);
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
js_paths: [
'inc/header1/js/*.js',
'!inc/header1/js/*.min.js',
'inc/header2/js/*.js',
'inc/header2/js/*.js',
'!inc/header2/js/*.min.js',
'js/*.js',
'!js/*.min.js'
],
uglify: {
options: {
mangle: true
},
build: {
files: [{
expand: true,
src: this.js_paths,
rename: function(dst, src) {
return src.replace('.js', '.min.js');
}
}]
}
},
watch: {
scripts: {
files: ['inc/header1/js/*.js', 'inc/header2/js/*.js', 'js/*.js'],
tasks: ['uglify'],
options: {
spawn: false,
}
}
}
});
grunt.registerTask('default', ['uglify', 'watch']);
};
Preferrably would like to use the same array js_paths in the watch files (since it's required there), if that makes sense? Still kinda new to using gruntfile.js
Utilize the Templates syntax. It's described in the docs as following:
Templates
Templates specified using <% %> delimiters will be automatically expanded when tasks read them from the config. Templates are expanded recursively until no more remain.
Essentially, change this.js_paths to '<%= js_paths %>' in your uglify task.
For instance:
// ...
uglify: {
options: {
mangle: true
},
build: {
files: [{
expand: true,
src: '<%= js_paths %>', // <-----
rename: function(dst, src) {
return src.replace('.js', '.min.js');
}
}]
}
},
// ...
Likewise for your watch task too.
For instance:
watch: {
scripts: {
files: '<%= js_paths %>', // <-----
tasks: ['uglify'],
options: {
spawn: false,
}
}
}

grunt-depend-concat is not replace #depend tags with file contents

I'm trying to use grunt-depend-concat to create a site.uncompressed.js file with the contents of all the dependencies using #depend tags in my site.js file. The destination file is being created, but the #depend comments are still at the top of the file. Am I using this grunt package correctly? Is there a problem with my code? I'm open to alternative Grunt packages if I can still use #depend tags, but the grunt package documentation is rather sparse and I can figure out how else it might be possible to do this.
site.js (partial):
/**
* #depend vendor/jquery-1.11.1.min.js
* #depend vendor/jquery.smooth-scroll-1.4.13.min.js
[…]
*/
[…]
Gruntfile.js:
(function () {
'use strict';
}());
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
filepaths: {
assetDir: 'httpdocs/files/assets/',
jsDir: '<%= filepaths.assetDir %>' + 'js/',
jsSrc: '<%= filepaths.jsDir %>' + 'site.js',
jsComb: '<%= filepaths.jsDir %>' + 'site.uncompressed.js',
jsMin: '<%= filepaths.jsDir %>' + 'site.min.js';
},
'depend-concat': {
depend_doctag: {
options: {
method: {
type: 'doctag',
tag: 'depend'
}
},
src: ['<%= filepaths.jsSrc %>'],
dest: '<%= filepaths.jsComb %>'
},
},
uglify: {
options: {
preserveComments: false,
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */',
sourceMap: true
},
dist: {
files: {
'<%= filepaths.jsMin %>': ['<%= filepaths.jsComb %>']
}
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['jshint', 'depend-concat', 'uglify']);
};
Grunt Output:
Running "jshint:files" (jshint) task
>> 2 files lint free.
Running "depend-concat:depend_doctag" (depend-concat) task File
"httpdocs/files/assets/js/site.uncompressed.js"
created.
Running "uglify:dist" (uglify) task
>> 1 sourcemap created.
>> 1 file created.
Done, without errors.
site.uncompressed.js (partial):
/**
* #depend vendor/jquery-1.11.1.min.js
* #depend vendor/jquery.smooth-scroll-1.4.13.min.js
[…]
*/
Unfortunately, I have not been able to get this Grunt plugin to work properly, so I have given up on using grunt-depend-concat with #depend rules in the site.js file.
I added a section to filepaths in the Gruntfile, then used the grunt-contrib-concat plugin instead.

Update variable used by Grunt Template

I am using Grunt Templates, but wondering how I can update the variable which is used. Please check out the example below.
(function () {
"use strict";
module.exports = function(grunt) {
grunt.initConfig({
directory: 'deploy', // I want to be able to update this
concat: {
options: {
separator: ';\n\n',
banner: '/*! Last edited: <%= grunt.template.today("yyyy-mm-dd") %> */\n /* DO NOT EDIT THIS FILE DIRECTLY */\n\n'
},
js_site: {
src: '<%= directory %>/assets/js/src/*.js',
dest: '<%= directory %>/assets/js/site.js',
},
js_vendor: {
src: '<%= directory %>/assets/js/vendor/*.js',
dest: '<%= directory %>/assets/js/vendor.js',
}
},
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('watch', function() {
var args = process.argv;
var dir = args[args.indexOf('--dir') + 1];
// Now update the directory variable above
});
};
})();
This should work :
(function () {
"use strict";
module.exports = function(grunt) {
grunt.initConfig({
directory: 'deploy', // I want to be able to update this
concat: {
options: {
separator: ';\n\n',
banner: '/*! Last edited: <%= grunt.template.today("yyyy-mm-dd") %> */\n /* DO NOT EDIT THIS FILE DIRECTLY */\n\n'
},
js_site: {
src: '<%= directory %>/assets/js/src/*.js',
dest: '<%= directory %>/assets/js/site.js',
},
js_vendor: {
src: '<%= directory %>/assets/js/vendor/*.js',
dest: '<%= directory %>/assets/js/vendor.js',
}
},
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('watch', function() {
var args = process.argv;
var dir = args[args.indexOf('--dir') + 1];
// Now update the directory variable above
grunt.config.set('directory', dir);
});
};
})();

Grunt: sending array of sourceFiles to grunt task uglify

I can't seem to find the answer in the documentation. I want to send an array of sourceFiles from my package.json to the grunt task.
Here is my config.json
{
"sourceFiles": ["src/js/d3js/d3.js", "src/js/testingGrunt.js"]
}
and here is my gruntfile.js
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
config: grunt.file.readJSON('config.json')
});
// Load the plugin that provides the concat tasks.
grunt.loadNpmTasks('grunt-contrib-concat');
// defining my concat task
grunt.config('concat', {
dist: {
//src: ['src/js/testingGrunt.js', 'src/js/d3js/d3.js'],
src: '<%config.sourceFiles%>',
dest: 'build/<%= pkg.name %>-build.js'
}
});
// Load the plugin that provides the uglify tasks.
grunt.loadNpmTasks('grunt-contrib-uglify');
// defining my uglify task
grunt.config('uglify', {
options: {
banner: '/*\n* ©<%= pkg.author%>\n* <%= pkg.name %>\n* <%= grunt.template.today("yyyy-mm-dd HH:mm:ss") %> \n*/\n'
},
build: {
src: 'build/<%= pkg.name %>-build.js',
dest: 'build/<%= pkg.name %>-build.min.js'
}
});
// Default task(s).
var defaultTasks;
defaultTasks = ['concat', 'uglify']
grunt.registerTask('default', defaultTasks);
};
The commented out line inside the grunt.config('concat',... works just fine. However I want to set up my gruntfile to read the files from the config file.
Eventually I'm going to be doing other things in this grunt task and I want to set it up so that I never need to edit the grunt file.
Looks like the template syntax is off try replacing:
src: '<%config.sourceFiles%>',
with the following:
src: '<%= config.sourceFiles %>',

Using Grunt to set environment based variables

I'm looking at grunt-string-replace to accomplish changing variables in my files to an environment specific variable. However, when I try to run the grunt script below I get this warning: "Task min:dev not found". But in this code I have that defined:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
min: {
dev: {
"string-replace": {
dist: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [
{
pattern: /$APPNAME/ig,
replacement: "services"
},
{
pattern: /$ENV/ig,
replacement: "nonprod"
}
]
}
}
}
},
prod: {
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.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['min:dev']);
grunt.registerTask('prod', ['min:prod']);
};
Am I missing something?
Have you read the manual? The getting started guide states that the tasks expect their configuration to be specified in a property of the same name. You have no 'min' task defined, thus it's erroring.
The top-level keys of the config object need to match the task names, and then you define 'targets' (eg, 'dev' or 'prod') within the task config.
So, here's your Gruntfile re-worked. I have not tested this so I can't promise it will work, but it will certainly lead you in the right direction:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
"string-replace": {
dev: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [{
pattern: /$APPNAME/ig,
replacement: "services"
}, {
pattern: /$ENV/ig,
replacement: "nonprod"
}]
}
}
},
uglify: {
prod: {
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.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['string-replace:dev']);
grunt.registerTask('prod', ['uglify:prod']);
};
think your trying to do this, this fragment will put the environment variable SOME_ENV, into the grunt config, the replace is simply giving you a easy way to do ${SOME_ENV} placeholders in files for replacement
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
SOME_ENV: process.env.SOME_ENV,
copy: {
files: [
{expand: true, cwd: "src/", src: ["*.*", "**/*.*"], dest: "./build",}
],
options: {
// Replace ${MEH} in files with grunt.config.get("MEH"),
// eg ${SOME_ENV}
// == grunt.config.get("SOME_ENV")
// == process.env.SOME_ENV == systems SOME_ENV environment var
process: function (content, srcpath) {
return content.replace(
/\$\{([a-zA-Z.]+)\}/g,
function replacer(match, p1, offset, string){
var value = grunt.config.get(p1);
grunt.log.write(" in file '" + srcpath + "'\n replacing '" + match + "'\n with '" + value + "'\n");
return value;
});
}
},
}
});
// Production Build Tools
grunt.loadNpmTasks('grunt-contrib-copy');
// Default Production Build task(s).
grunt.registerTask('default', ['copy']);
};

Resources