Using Yeoman to scaffold out new websites
Recently I’ve been playing around with Yeoman, a web scaffolding tool. If you’ve never heard of it before, or are unfamiliar with it, stop right now & go check it out!
From first impressions Yeoman seemed geared towards scaffolding JavaScript based single page applications, however the basic functionality of a generator is to copy files, make directories etc., so I decided to make one I could use when starting a new project.
I followed the documentation to generate my generator, giving it the name parkji-vanilla
(the generator-
is automatically added by generator-generator
).
One of the great things about yeoman generators is that they can run bower install
& npm install
after they’ve finished scaffolding, thereby saving you the effort (granted, this is minimal effort).
Prompts
I wanted my generator to ask for two things when it ran:
- The name of the website
- The type of website (static, Perch etc.)
Asking for the name was really easy, I just added the following object to the prompts
array:
{
name: 'siteName',
message: 'What is the name of this website?'
}
Prompting for the type required a bit of digging around because I wanted to list out the different types & then choose from these, rather than have to type it in & validate it.
Yeoman uses Inquirer.js for prompts so I took a look at the docs & realised that I needed to use the list
type, like so:
{
type: 'list',
name: 'framework',
message: 'What sort of site are you building?',
choices: [
{name: "Static", value: "static"},
{name: "Perch", value: "perch"}
],
default: 'static'
}
Generator prototype methods
By default generators created using generator-generator
will have an app
method, I cleared out the example commands & added the code below:
ParkjiVanillaGenerator.prototype.app = function app() {
this.template('package-config/_bower.json', 'bower.json');
this.template('package-config/_package.json', 'package.json');
this.copy('Gruntfile.js', 'Gruntfile.js');
this.directory('stylesheets', 'stylesheets');
this.copy('robots.txt', 'web/robots.txt');
this.template('humans.txt', 'web/humans.txt');
this.mkdir('web');
};
Most of the calls in this are self-explanatory, but it’s worth noting that template
is special because it will inject the arguments into the file, if required. For example, the _bower.json
template file I created has the line
"name": "<%= _.slugify(siteName) %>",
template()
will ensure that this becomes
"name": "the-name-entered-at-prompt-in-slug-format".
There are other string commands that you can use, the docs for which can be seen here.
I decided to have different methods for each different website type with each one checking the chosen type before creating files if necessary. This seems to fit in with the yeoman way of doing things & it makes the code easier to follow since the code for each type is separate from the others.
ParkjiVanillaGenerator.prototype.installStatic = function installStatic() {
if (this.framework == 'static') {
this.copy(this.framework + '/gitignore', '.gitignore')
this.template(this.framework + '/index.html', 'web/index.html');
this.template(this.framework + '/vhost.conf', 'vhost.conf');
}
};
ParkjiVanillaGenerator.prototype.installPerch = function installPerch() {
if (this.framework == 'perch') {
this.copy(this.framework + '/gitignore', '.gitignore')
this.template(this.framework + '/index.php', 'web/index.php');
this.template(this.framework + '/vhost.conf', 'vhost.conf');
}
};
Running the generator
First up, it’s important to note that if the generator isn’t in the global node_modules
directory, yo
won’t be able to find it, this can be fixed by running
npm link
in the generator directory. It’ll create a symlink to your generator in the global node_modules
directory for you.
Now the generator can be run with:
yo parkji-vanilla
Here’s a showterm of this in action.
Tests
By default the generator-generator
will create a test
directory for you & add some example test files too. These tests use the mocha test framework. I found that I didn’t really have to alter the example tests too much since my generator was very simple anyway. I did however want to check that the templated files had the correct siteName
value inserted, this can be done by using an array with two values the first being the file name, the second being a regular expression that you expect be found within that file. E.g. my list of expected files for the static type looks like this:
var expected = [
['web/index.html', /<title>Static Test<\/title>/],
['vhost.conf', /ServerName StaticTest\.localhost/],
'.gitignore'
];
This array is then passed to the helpers.assertFiles()
method.
Here’s the full test for the generic files that my generator should create:
it('creates expected files', function (done) {
var expected = [
['bower.json', /"name": "testing"/],
['package.json', /"name": "testing"/],
'Gruntfile.js',
'web',
'stylesheets',
'web/robots.txt',
['web/humans.txt', /Testing/]
];
helpers.mockPrompt(this.app, {
'siteName': 'Testing'
});
this.app.options['skip-install'] = true;
this.app.run({}, function () {
helpers.assertFiles(expected);
done();
});
});
The tests can be run by executing
npm test
Also, the generator-generator
will create a travis config file for you, which means that you can hook your repo up to it & have continuous integration.
Repository
My generator can be found on github - https://github.com/parkji/generator-parkji-vanilla.