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!
Related
copy: {
build: {
cwd: 'app',
src: ['**', '!**/vendors/**', '!**src/js/*.js',],
dest: 'dist',
expand: true
}
}
I am using grunt build scripts to build a distribution folder for the completed product. However, its not 100% automatic and dynamic. For example, I have a folder of xml content files. Yet, I don't use them all. Right now, the whole folder is copied over to the build version. Manually I have to go in and delete the xml files I don't want in the build version then run it. Or I could go into the grunt file and and tell it to ignore those files.
The problem is that I don't want to do that every time. A theoretical idea I had would be to have an xml file where I define elements to represent certain other files.
<bootstrap>true</bootstrap>
<extraContent>false</extraContent>
This would say that the file correlated to bootstrap and extraContent should or shouldn't be ignored in the build. I am trying to figure out if you could do this in grunt.
something like the following is how I see the logic playing out...
var bootstrap = $(xml).find("bootstrap").text()
if(bootstrap == "false"){
var url = src/bootstrap.css
//Here add the correlated filepath defined above to be ignored
}
The problem is not only writing this so grunt knows what it is, but also combining that logic with the actual "copy:{}" script I showed above
If you want to include/exclude files based on their contents you can use filter function for this. Examples can be found in the official documentation: https://gruntjs.com/configuring-tasks#custom-filter-function.
The filter property can help you target files with a greater level of detail.
In your case this could be something like this:
copy: {
build: {
cwd: 'app',
src: ['**', '!**/vendors/**', '!**src/js/*.js',],
dest: 'dist',
expand: true,
// this filter function will copy xml files only when `bootstrap` is set to 'true'
filter: filepath => {
if (require('path').extname(filepath) !== 'xml')
return true;
const xml = require('fs').readFileSync(filepath, 'utf8');
const json = require('xml2json').toJson(xml);
return json.bootstrap === 'true';
}
}
}
You can then use the process function to copy only certain contents from specific files: https://github.com/gruntjs/grunt-contrib-copy#process
This option is passed to grunt.file.copy as an advanced way to control the file contents that are copied.
Well met!
For the past hours I have been trying to get the mainFiles option to work, but it seems to ignore every file that I include in there. I have tried multiple plugins in the list- but none of them get through. I am rather new with Grunt, admittedly, but I have been going through the grunt-bower-concat documentation but, nothing there. And have been adding a number of console logs inside the actual plugin code, shows no files are being passed (reverted the file after, of course).
This is the GruntFile I'm working with:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
bower_concat: {
main: {
dest: 'Assets/Build/Scripts/plugins.js',
cssDest: 'Assets/Build/Styles/plugins.css',
dependencies: {
'amplify': 'jquery'
},
mainFiles: {
bootstrap: ['bower_components/bootstrap/dist/css/bootstrap.css']
},
exclude: [
'leaflet-illustrate'
]
}
}
});
grunt.loadNpmTasks('grunt-bower-concat');
grunt.registerTask('buildbower', ['bower_concat']);
};
Leaflet-Illustrate has been excluded (for now) because it wreaks havoc on the actual task, and without the mainFiles option, I can't include it correctly.
Is there anyone who can direct me in the right way, or correct me?
Thanks in advance!
As it turns out, I entered a full path which was not needed. Meaning, this part works:
main: {
mainFiles: {
'bootstrap': ['dist/css/bootstrap.css']
}
As a part of my Gruntfile.js:
...
concat: {
...
js: {
src: [
'src/**/*.js'
],
dest: 'build/js/main.js',
nonull: true
}
},
...
How can I prevent concat from generating a blank main.js if my src directory contains no scripts? Must I really create a separate task if I know I won't need to build any scripts?
Not exactly a proper solution, but I managed to find a package called grunt-cleanempty which I set to run after concat to delete any generated empty 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.
I use grunt to convert all my less files into css files,using this:
less: {
development: {
files: {
"css/*.css": "less/*.less"
}
}
}
This worked on version 0.3.0, but now that I have upgraded to v0.4.0 it doesn't work anymore.
The following code (not using * in the destination) works on both versions, so the problem is with the star on the destination file.
less: {
development: {
files: {
"css/test.css": "less/*.less"
}
}
}
Any idea ?
This isn't a bug. Grunt no longer supports globbing in dest using that configuration. However, you can use the "files array" format, like this:
files: [
{
expand: true,
cwd: 'src',
src: ['*.less'],
dest: 'assets/css/',
ext: '.css'
}
]
Also, if you use a library like Bootstrap and you want to build each LESS file (component) into a separate, individual CSS file, it's not very easy to accomplish "out of the box". The reason is that each LESS file would need to have its own #import statements for variables.less and mixins.less (and a couple of others like forms.less and navbar.less, since they are referenced in other files).
To make this really easy, try the Grunt plugin, assemble-less (disclaimer: I'm one of the maintainers of the project, and I'm also on the core team for less.js). assemble-less is a fork of grunt-contrib-less by Tyler Kellen, but it adds some experimental features that will accomplish what you need (if you want stability, please stick with grunt-contrib-less). For example:
// Project configuration.
grunt.initConfig({
less: {
// Compile all targeted LESS files individually
components: {
options: {
imports: {
// Use the new "reference" directive, e.g.
// #import (reference) "variables.less";
reference: [
"bootstrap/mixins.less",
"bootstrap/variables.less"
]
}
},
files: [
{
expand: true,
cwd: 'bootstrap/less',
// Compile each LESS component excluding "bootstrap.less",
// "mixins.less" and "variables.less"
src: ['*.less', '!{boot,var,mix}*.less'],
dest: 'assets/css/',
ext: '.css'
}
]
}
}
...
}
The imports feature essentially prepends the specified #import statements onto the source files. The reference option allows you to "reference" other less files while only outputting styles that are specifically referenced via mixins or :extend. You might need to reference a few more files than shown here, since Bootstrap cross-references styles from other components, like forms.less, buttons.less, etc. (See the Gruntfile in assemble-less for examples.)
So after running the assemble-less task with the configuration in the example above, the assets/css folder would have:
alerts.css
badges.css
breadcrumbs.css
button-groups.css
buttons.css
carousel.css
close.css
code.css
component-animations.css
dropdowns.css
forms.css
glyphicons.css
grid.css
input-groups.css
jumbotron.css
labels.css
list-group.css
media.css
modals.css
navbar.css
navs.css
normalize.css
pager.css
pagination.css
panels.css
popovers.css
print.css
progress-bars.css
responsive-utilities.css
scaffolding.css
tables.css
theme.css
thumbnails.css
tooltip.css
type.css
utilities.css
wells.css
There are other features that should help you with this, but the imports feature is super powerful since it allows you to add directives directly to the Gruntfile.