I'm using load-grunt-config and grunt-prompt, and I'm developing an init task, which copies some php templates between two folders.
Right now the template filenames are hardcoded, but I'd rather have grunt scan the right folder and provide the filenames dynamically.
I've tried using grunt.file.expand, but I'm unable to get it to work. Is it possible to scan a folder and return an array (or object, not sure what you'd call it) of filenames in the format that grunt-prompt expects?
// -------------------------------------
// Grunt prompt
// -------------------------------------
module.exports = {
// ----- Initialization prompt ----- //
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: [{
name: '404.php',
checked: false
}, {
name: 'archive.php',
checked: false
}, {
name: 'ments.php',
checked: false
}]
}]
}
}
};
By the way, I have found this answer: , which relates to the problem. But it does not go into detail about how one would specifically address this problem. Also, I need more specific syntax than just an array of filenames:
[{
name: '404.php'
}, {
name: 'archive.php'
}]
I'm using load-grunt-config and grunt-prompt, and I'm developing an init task, which copies some php templates between two folders.
Right now the template filenames are hardcoded, but I'd rather have grunt scan the right folder and provide the filenames dynamically.
I've tried using grunt.file.expand, but I'm unable to get it to work. Is it possible to scan a folder and return an array (or object, not sure what you'd call it) of filenames in the format that grunt-prompt expects?
// -------------------------------------
// Grunt prompt
// -------------------------------------
module.exports = {
// ----- Initialization prompt ----- //
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: [{
name: '404.php',
checked: false
}, {
name: 'archive.php',
checked: false
}, {
name: 'ments.php',
checked: false
}]
}]
}
}
};
By the way, I have found this answer: https://stackoverflow./a/22270703/1694077, which relates to the problem. But it does not go into detail about how one would specifically address this problem. Also, I need more specific syntax than just an array of filenames:
[{
name: '404.php'
}, {
name: 'archive.php'
}]
Share
Improve this question
edited May 23, 2017 at 12:22
CommunityBot
11 silver badge
asked Apr 25, 2014 at 13:40
user1694077user1694077
7
- Why won't you do it yourself? – Vinz243 Commented Apr 25, 2014 at 20:07
- @Vinz243; you mean manually supply the list of files? Well I'm already doing that (as you can see in the code), but it is rather fragile since someone might accidentally delete a file or add one. Which would cause unexpected behaviour in the prompt and any subsequent grunt tasks. – user1694077 Commented Apr 25, 2014 at 21:02
- @Vinz243; What do you mean? This is already a grunt task. – user1694077 Commented Apr 25, 2014 at 21:07
- for your scan. or perhaps I didn't really understand your problem. – Vinz243 Commented Apr 25, 2014 at 21:09
- @Vinz243; Oh right. Well as far as I know there isn't a grunt task that does this. There is a grunt built in function that does this (grunt.file.expand), I think. But I don't know how to use it in this case.. – user1694077 Commented Apr 25, 2014 at 21:11
3 Answers
Reset to default 12 +100Basic Principle
Here's a way to do it that uses Grunt's file matching capabilities to get a list of files. The following code will seek templates in a subdirectory named templates
. You just need to put your php
files there and the script will find it. Note I've omitted the use of load-grunt-config
since it is not a factor in the specific problem of getting a list of files.
The key is to use grunt.file.expand
to get the files.
module.exports = function (grunt) {
// List all files in the templates directory.
var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
["*"]);
// Make actual choices out of them that grunt-prompt can use.
var choices = templates.map(function (t) {
return { name: t, checked: false};
});
grunt.initConfig({
prompt: {
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: choices
}]
}
}
}
});
grunt.task.loadNpmTasks("grunt-prompt");
grunt.registerTask("default", ["prompt"]);
};
You could use something more sophisticated than "*"
as a pattern. For instance, if you are going to have other types of files there that you don't want to list "*.php"
would be indicated. I also use isFile
for the filter
option to avoid listing directories. And I use cwd
to change the working directory to templates
before listing the files, which means the file names returned do not include templates/
in their name. It would also be possible to do this instead:
var templates = grunt.file.expand({filter: "isFile"}, ["templates/*"]);
and get a list of files that include the templates/
directory in their name.
With load-grunt-config
By default, load-grunt-config
wants a package.json
file (because it calls load-grunt-tasks
). This is what I've used:
{
"dependencies": {
"load-grunt-config": "^0.8.0",
"grunt-prompt": "^1.1.0",
"grunt": "^0.4.4"
}
}
The Gruntfile.js
bees:
module.exports = function (grunt) {
grunt.registerTask("default", ["prompt"]);
require('load-grunt-config')(grunt);
};
And then in grunt/prompt.js
you need this:
module.exports = function(grunt) {
// List all files in the templates directory.
var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
["*"]);
// Make actual choices out of them that grunt-prompt can use.
var choices = templates.map(function (t) {
return { name: t, checked: false};
});
return {
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: choices
}]
}
}
};
};
Here is a short code to list the files in a dir:
var fs = require("fs")
var files = [];
var list = function (path) {
fs.readdirSync(path).forEach(function (file) {
if(fs.lstatSync(path + '/' +file).isDirectory())
list(path + '/' +file);
else
files.push({name: file});
});
}
list(YOUR_PATH)
console.log(files)
In your example :
var fs = require("fs")
var files = [];
var list = function (path) {
fs.readdirSync(path).forEach(function (file) {
if(fs.lstatSync(path + '/' +file).isDirectory())
list(path + '/' +file);
else
files.push({name: file});
});
}
list(YOUR_PATH)
module.exports = {
// ----- Initialization prompt ----- //
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: files
}]
}
}
};
There is another way to do this that uses a different Grunt utility method than what's listed in the accepted answer. Its worth mentioning because it exposes the options object for the "glob" npm package that Grunt uses internally.
See README.md for the version of glob used by your version of Grunt for more info (check its package.json file). I first looked at the most current glob and was puzzled by the fact that Grunt had no grunt.file.globSync() method. I eventually realized Grunt was using an earlier version of glob, after which {sync: true} was removed from the options object and replaced by the globSync() function I'd been looking for.
NOTE: grunt.file.glob() does not take a src array as grunt.file.expand() does, but you can use braces to effectively encode an array:
var options = { ... };
var results =
grunt.file.glob(
'{first/path/**/*,second/path/**/*}',
options
);
Here's an example block showing how I use this in jade processing, which I seem to recall does not handle expand blocks natively for some reason. The options:
- allow wildcards to match dot prefixed files
- disable unnecessary stat calls for non-wildcard glob pattern path nodes
- disables sorting since I don't care about input order
- disables de-duping because I know my pattern matches each file exactly once
requests an empty array rather than an array if no files match
var filesObj = { }; var configObj = { build: { options: { pretty: true, data: { titleStr: 'This is a title from data' } }, files: filesObj } };
// Get source and build output directory roots from config var srcPrefix = new RegExp('^' + grunt.config.get('sourceAssets')); var buildPrefix = grunt.config.get('buildAssets');
// Call once per glob match to populate files object. function addJadeFile(srcPath) { // Handle glob output on Windows by denormlalizing path. srcPath = srcPath.replace(/\/g, '/'); // Extract mon path suffix and change file extension var relPath = srcPath.replace(srcPrefix, '..').replace(/.jade$/, '.html'); // Prepend destination path prefix and a property // to files sub-object of config object. filesObj[buildPath + relPath] = srcPath; }
grunt.file.glob( appConfig.source.client + '/**/*.jade', { sync: true, stat: false strict: true, dot: false, nonull: false, nodir: true, nosort: true, nounique: true } ).forEach(addJadeFile);
return configObj;
P.S. Both grunt.file.glob() and grunt.file.expand() are generally preferable over traversal methods from the fs package because glob keeps a traversal cache.
It's also good to be aware of glob's traversal cache if you are looking for files that may have been created by a previous build step, but not before other tasks first traversed their creation location and populated that cache. I've not run into this yet, but be aware of it in case you need to find out how to purge or disregard the cache in such a corner case.