Generate dynamic filenames with grunt.js - javascript

Is it possible to generate dynamic filenames outside the built-in grunt tasks (e.g. concat or min)? I tried to use something like <config:concat.dist.dest> or <%= dirs.dest %> as it is described in the docs. But this never gets interpreted / compiled, it just writes out the string.
Update:
That's what I tried based on jakub.g's answer. My grunt.js looks like this:
// ... grunt file contents
jquery: {
exclude: [],
version: '1.8.3',
dest: '../dist/js/jquery-' + grunt.task.directive('<config:jquery.version>') + '.js',
minify: false
}, // ... even more grunt file contents
grunt.task.directive('<config:jquery.version>') returns null. So the filename was named jquery-null.js.
I then tried grunt.template.process('<%= grunt.jquery.version %>') and grunt.config.process('<%= grunt.jquery.version %>'), but none of them worked.

This is hidden under the hood of Grunt magic in the built-in tasks and in fact not documented clear enough.
You need to use sth like grunt.task.directive(dest) to evaluate things like <config:..>. in a custom task.
For <%= foo %>, have a look at Grunt templates.
Furthermore, wildcards like * and ** and also not expanded by default, if you want to use them in custom tasks, you may use grunt.file.expandFiles().

Related

Concatenate javascript and css in order

I have several task to concatenate css and javascript together, here is an example:
gulp.task('javascript', function() {
return gulp.src([
libPath + 'jquery-2.1.4.min.js',
libPath + '*.js',
jsPath + 'app.js'
])
.pipe(concat('app.min.js'))
.pipe(uglify())
.pipe(gulp.dest(jsPath))
});
Ideally (in this example) I would like following order jQuery -> libraries -> app.js and so far, using code above it seems to work. However I conducted a little research and people seem to be using plugins like gulp-order or require streamqueue .. method instead of require gulp.src, thus I wanted to make sure that these are needed, or does gulp take care of that now?
For what I saw in the links, those plugins are used to order the stream of multiple files, like: gulp.src(["js/*.js"]), but you are explicitly declaring the order, so it works.

Add "active" classes to menu items using Grunt

I am using Grunt to build a set of static pages that operate together as a site/application. In the interest of sticking to DRY practices, I am using a package called grunt-processhtml to do "includes".
However, my "included" navigation does not have the appropriate classes to indicate the current page in the navigation. I can pull it off with JavaScript if I need to (set the active class after the menu is created, based on either a variable or the URL). However, I also stumbled across this:
grunt-autonav
Which post-processes my assembled static files and adds the appropriate class. However, I can't for the life of me figure out how to configure it for "process all of the .html files and add the appropriate classes to each of them."
My last failed attempt looks like this:
autonav: {
options: {
parent: '.nav'
},
dev: {
src: '<%= dirs.purgatory %>/html/**/*.html',
dest: '<%= dirs.dev %>'
}
}
However, the plugin doesn't seem to want to use this kind of input for the source:
Warning: Unable to read
"purgatory/html/download.html,purgatory/html/upload.html" file (error
code: ENOENT). Use --force to continue.
It sees my two HTML files but doesn't know how to take it from there. I can't tell if I have a configuration error or if the plugin just doesn't work this way. The sample given in their documentation seems to require specifying every single page that needs its nav customized. But it might be a reading comprehension issue.
Does anybody know how to accomplish my goal in Grunt (not in JS) using the above or any other tools? I don't mind adding a new tool, but I've come up short.
To use dynamic file lists in grunt and all its plugins, you need to wrap your src/dest in a files object and set expand to true:
autonav: {
options: {
parent: '.nav'
},
dev: {
files: {
expand: true,
src: '<%= dirs.purgatory %>/html/**/*.html',
dest: '<%= dirs.dev %>'
}
}
}
Xavier Priour set me on the right path, but I had to scratch my head a bit before I arrived at the final solution:
autonav: {
options: {
parent: '.nav'
},
dev: {
files: [
{
expand: true,
cwd: '<%= dirs.purgatory %>/html',
src: '**/*.html',
dest: '<%= dirs.dev %>'
}
]
}
}
First, I had to look into expand, which provided samples that used the files property Xavier mentioned. However, the examples show that files is an array of objects. Not sure if it "has" to be, but I followed that pattern and wrapped it up as an array.
Next was realizing it wasn't the input string that was wrong, it was that I was configuring the copy incorrectly. When expanded, the destination is a recreation of the source path. To get around this, you issue a CWD. This means that your input path is essentially "null"-ish and it doesn't create a "purgatory" directory in the destination.
The final task now works as expected!

How do I parse '<%= =>' in grunt?

A lot of grunt plugins allow for this syntax when telling it to include files:
['<%= src_dir %>/common/**/*.js', '<%= src_dir %>/app/**/*.js']
or
['<%= test_files.js %>']
Is there any way I can call into some library that will parse these and give me an array of the actual output? Or is this built directly into grunt? I am not sure what terms to google to even make this show up.
Thanks
You are either looking for grunt.config.get, grunt.config.process or grunt.template.process, depending on where you are getting the values from and how you want to process them.
grunt.config.get
Get a value from the project's Grunt configuration. If prop is specified, that property's value is returned, or null if that property is not defined. If prop isn't specified, a copy of the entire config object is returned. Templates strings will be recursively processed using the grunt.config.process method.
grunt.config.get([prop])
grunt.config.process
Process a value, recursively expanding <% %> templates (via the grunt.template.process method) in the context of the Grunt config, as they are encountered. this method is called automatically by grunt.config.get but not by grunt.config.getRaw.
grunt.config.process(value)
[...]
grunt.template.process
Process a Lo-Dash template string. The template argument will be processed recursively until there are no more templates to process.
The default data object is the entire config object, but if options.data is set, that object will be used instead. The default template delimiters are <% %> but if options.delimiters is set to a custom delimiter name (set with grunt.template.addDelimiters), those template delimiters will be used instead.
grunt.template.process(template [, options])
Inside templates, the grunt object is exposed so that you can do things like <%= grunt.template.today('yyyy') %>. Note that if the data object already has a grunt property, the grunt API will not be accessible in templates.
In this example, the baz property is processed recursively until there are no more <% %> templates to process.
var obj = {
foo: 'c',
bar: 'b<%= foo %>d',
baz: 'a<%= bar %>e'
};
grunt.template.process('<%= baz %>', {data: obj}) // 'abcde'

r.js optimizer - don't mangle function names

I am using r.js with uglify to minify and concatenate my scripts. I am getting some errors on a production site where the stack trace returned is unintelligible. I would like to temporarily turn off the mangling of function names (variable names are fine) and am having trouble working out how to do this as r.js wraps the configuration options that are passed to uglify.js
The uglify config section in my r,js build config looks like this
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
}
I would like to add the
-nmf or --no-mangle-functions – in case you want to mangle variable names, but not touch function names. (from here)
If i add the line
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
'--no-mangle-functions': true
}
It does nothing, as does 'no-mangle-functions': true.
How do I pass this option to uglify?
Trying to make uglified/mangled code readable kind of defeats it's purpose in the first place.
Probably, what you are after are source maps.
To generate source maps in Uglify just add these options:
uglify: {
options: {
sourceMap: 'build.min.map',
sourceMappingURL: 'build.min.map'
}
}
Map filename must mirror the final javascript filename:
uglified.js <=> uglified.map
From what i can see in the source code of r.js there is no direct differentiation between functions and variable names. But there is an option called no_functions which is actually passed to the uglify section where the default value is false
Passing of options:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L25067
Defaulting no_functionsto false:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L11492
I cannot test it right now, so i am only guessing. Maybe you can try this option

Alternate JavaScript compression approach in r.js build

I'm using r.js to build a production script for my Require JS application and I'm looking for a way to either use an alternate compression library or change the defaults used. I want whitespace to be removed but for variable names to remain the same.
I have a particular requirement to retain variable names as they are and not have them altered. The need for constant variable names introduces a little 'code smell' but it makes the application's configuration file more robust against non-expert editors - so please try to avoid suggesting a design change here.
I currently have r.js configured to not optimise the JavaScript at all, which means not only are variable names retained but also whitespace. The relevant piece from gruntfile.js is provided below.
Can anyone suggest a way to compress whitespace but not change variable names in an r.js build?
english: {
options: {
baseUrl: "js",
mainConfigFile: "js/app-en.js",
name: "app-en",
out: "js/dist/<%= pkg.name %>-en.js",
optimize: "none"
}
}
The r.js optimizer has settings you can use to control how the minifier operates. The default minifier used is UglifyJS. The uglifyjs option tells r.js how to invoke it. Given the settings you've shown, removing optimize: "none" and adding uglify: { no_mangle: true } is what is needed:
english: {
options: {
baseUrl: "js",
mainConfigFile: "js/app-en.js",
name: "app-en",
out: "js/dist/<%= pkg.name %>-en.js",
uglify: {
no_mangle: true
},
}
}
The whole set of settings that UglifyJS takes is documented here. If you ever need or want to switch to UglifyJS2 or Closure, r.js has uglify2 and closure settings that you can use to set their options.
For Uglify2, the setting to prevent mangling would be:
uglify2: {
mangle: false
}
With Closure, I believe you'd want:
closure: {
CompilationLevel: 'WHITESPACE_ONLY',
},

Categories