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:

  1. The name of the website
  2. 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.

Comments

blog comments powered by Disqus

About ParkJi

Me!

My name is Ben Parker, I'm a 26 year old PHP web developer with a passion for web design, standards & new & innovative technologies.