How to use YAML front matter in the destination directory path? - javascript

I want the path of my blog posts to use the fancy date format e.g. /blog/2013/09/17 so the links from my old octopress blog are not broken.
In the YAML front matter on each markdown page I have added the date.
---
date: 2013-09-13
---
So in the Gruntfile.js I want to do something like this:
dest: './blog/<%= date.getFullYear() %>/<%= date.getMonth() %>/<%= date.getDate() %>'
Though can't find a way to get hold of that date property from the YFM. Is this possible, and it is the right way to achieve what I want?

EDIT: Since this answer was initially posted, a permalinks plugin was published that makes this super easy: https://github.com/assemble/assemble-contrib-permalinks
I think a better approach would be to create a feature request at http://github.com/assemble/assemble/issues, however, if you prefer to find a way to use custom logic in lodash templates inside the gruntfile, then my suggestion would be to create mixins that can then be used in your templates.
To add mixins, just do something like this outside the initConfig object (there are other ways, but this is easiest):
module.exports = function (grunt) {
grunt.util._.mixin({
year: function(foo) {
return date.getFullYear(foo);
},
month: function(foo) {
return date.getMonth(foo);
},
day: function(foo) {
return date.getDay(foo);
}
});
grunt.initConfig({
foo: {
src: 'path/to/my/files/**`
// Now we can use the mixins like this:
dest: <%= _.year() %>/<%= _.month() %>/<%= _.day() %>'
});
grunt.registerTask(...);
};
The challenge with this approach is getting the context from the YAML front matter of the src files and then returning the dates to be used in the dest paths. Alternatively, this should't be difficult to implement as a native feature in assemble, and I think others would benefit from it as well.

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!

Most efficient way to Grunt concat directories files?

I apologize for the very awkward question title, if anyone can think of a better way to phrase this question I'll change it immediately.
I'm building an app with Angular and RequireJS and to try to optimize performance, dependencies and lazy-loading I'm looking to build a file structure like this:
/app
----/regisitration
--------_registration.module.js
--------registration.ctrl.js
--------registration.svc.js
--------registration.directive.js
----/courses
--------_courses.module.js
--------courses.directive.js
--------courses.controller.js
--------courses.service.js
--------/course
------------course.controller.js
----/admin
--------_admin.module.js
--------admin.controller.js
Where I set up my routing, I want to be able to have a user who goes to any /registration/ view load the entirety of _registration.module.js which would be the concatenation of all the other .js files within the /registraion directory (and any sub directories) so that my team isn't bogged down by needing to include several and possibly duplicate dependencies as well as serve the entirety of the site "section" to the user in one shot. Hopefully the sample above shows why I wouldn't want to just front-load all the files, because most users will never hit the admin section of the site. I'm trying to figure out the most efficient way to achieve this with grunt, but so far I'm working very manually with code like this:
grunt.initConfig({
concat: {
app: {
files: [
{
src: ['..app/registration/*.js', '!..app/registraion/*.module.js'],
dest: '..app/registration/_registration.module.js'
},
{
src: ['..app/courses/*.js', '!..app/courses/*.module.js'],
dest: '..app/courses/_courses.module.js'
},
{
src: ['..app/admin/*.js', '!..app/admin/*.module.js'],
dest: '..app/admin/_admin.module.js'
}
],
}
},
});
I think there must be a more efficient and less manual way to do what I'm trying to achieve. Does anyone have any suggestions?
Remember that you can still execute JavaScript within your Gruntfile.
grunt.initConfig({
concat: {
app: {
files: grunt.file.expand({ cwd: 'app', filter: 'isDirectory' }, '*')
.map(function(ngModule) {
return {
src: ['app/' + ngModule + '/*.js', '!app/' + ngModule + '/*.module.js'],
dest: 'app/' + ngModule + '/_' + ngModule + '.module.js'
};
})
}
},
});
With this, you should be able to create new modules without needing to remember to update a config entry for them.

Grunt - Dynamically Set Config Before Running a Task

So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.

Generate dynamic filenames with grunt.js

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().

Categories