What is Yeoman?
~45 minute read
Yeoman is a simple, structured, and powerful code generation tool used to scaffold code of any kind (from a single file to an entire project).
A rich ecosystem of open-source, extensible Yeoman generators (built by the community) is available on the Yeoman site: Yeoman generators.
Find a generator you’re interested in and bootstrap an application quickly—or read on to learn how to build one yourself.
How do I use it?
Prerequisites:
First: what is a generator? A generator is just a script that runs with the intent of creating source code files. It can generate one file or an entire project. By providing boilerplate, you can encode best practices and give new projects a solid foundation.
Yeoman isn’t only for the beginning of a project. Because it runs on Node.js, you can use the full ecosystem to generate code from data: remote APIs, internal services, files on disk, etc.
Run a generator
There are thousands of different Yeoman generators. Once you find one you’re interested in, getting a complete application up and running is often as easy as 1–2–3:
- Install a generator with npm (for example):
npm install -g generator-angular3 - Run it:
yo angular - Follow the prompts
Sub-generators are just as easy, and passing arguments is a breeze. For example:
yo angular:controller MainControllerCreate a generator
Yeoman generators are configuration-based, and writing one is pretty approachable once you learn a few core concepts:
- How the generator “run loop” works (lifecycle steps)
- How templates get copied and interpolated
- How to persist config so reruns are safe
- How to compose generators and create sub-generators
- How to test generators
The generator
Start by creating a directory for your generator project. We’ll call it generator-hello-yeoman.
mkdir generator-hello-yeoman
cd generator-hello-yeoman
npm init -y
npm install --save yeoman-generatorBy convention, Yeoman uses the app/ directory to contain the base generator. Create your entrypoint at app/index.js.
Here’s a simple “Hello World” generator:
'use strict';
const Generator = require('yeoman-generator');
module.exports = class extends Generator {
writing() {
this.log('Hello World');
}
};To make the generator runnable locally, link it:
npm linkNow you can run it (the command uses the name after generator-):
yo hello-yeomanOlder Yeoman code often used
generator.Base.extend(...). Modern Yeoman commonly uses ES6 classes as shown above—the concepts are the same.
Config conventions (the run loop)
Yeoman has a well-defined execution order. The most common lifecycle steps are:
initializingpromptingconfiguringdefaultwritingconflictsinstallend
Each step can be a function or an object of functions. The main point: Yeoman gives you a structured place to do each kind of work.
Code templates
Templates usually live in:
app/templates/
Yeoman has two useful “contexts”:
- Template context: where templates are read from (
this.templatePath(...)) - Destination context: where files are written (
this.destinationPath(...))
A common convention is to prefix template filenames with _ (and then write them without it), and to handle dotfiles specially (e.g. _gitignore → .gitignore).
Copying files
Copy a file:
this.fs.copy(this.templatePath('index.html'), this.destinationPath('src/index.html'));Copy a directory:
this.fs.copy(this.templatePath('src'), this.destinationPath('src'));Templating syntax
Use <%= variableName %> inside template files to interpolate values.
To copy a template file and inject variables:
this.fs.copyTpl(
this.templatePath('index.html'),
this.destinationPath('src/index.html'),
{ variableName: 'Foo' },
);Create JSON files programmatically
this.fs.writeJSON(this.destinationPath('config.json'), { hello: 'world' });Install dependencies automatically
Inside the install step, you can run package managers. For example:
install() {
this.npmInstall();
}Because installs can be expensive, Yeoman supports --skip-install to skip that step.
Arguments
Arguments allow you to pass values to the generator (often for naming):
this.argument('name', { type: String, required: true });Use it like this.options.name (or assign it to a property if you prefer).
Options (command-line switches)
Options are similar to arguments, but are often booleans:
this.option('includeUtils', {
desc: 'Include additional utility helpers',
type: Boolean,
default: false,
});Prompts
Prompts let you ask questions and generate customized code. Under the hood, Yeoman uses inquirer.
Example prompt (modern async style):
async prompting() {
const answers = await this.prompt([
{
type: 'input',
name: 'appName',
message: 'What is the application name?',
default: 'app',
},
]);
this.log(answers);
}Persist config
If you add store: true to prompts, Yeoman will remember the last answers. You can also use the storage API directly:
this.config.set('appName', 'helloWorld');
const appName = this.config.get('appName');Generator composition
Generator composition is a great feature: you can reuse generators as building blocks.
Docs: Yeoman composability
Conceptually, you call this.composeWith(...) to run another generator as part of yours.
Sub-generators
Sub-generators are just generators living in a different folder than app/. You run them by appending :name:
yo angular:controller fooHow to test a Yeoman generator
Mocha is a good fit for generator tests. A straightforward setup today is:
npm install --save-dev mocha yeoman-test yeoman-assertThen create a test/ directory and add a test file (example: test/app.test.js):
'use strict';
const path = require('path');
const helpers = require('yeoman-test');
const assert = require('yeoman-assert');
describe('generator-hello-yeoman', function () {
it('creates expected files', function () {
return helpers
.run(path.join(__dirname, '../app'))
.withOptions({ skipInstall: true })
.then(() => {
assert.file(['package.json']);
});
});
});Run tests:
npx mochaAppendix
Credits
This post was heavily inspired by Steve Michelotti’s Yeoman course on Pluralsight. If anything here feels fuzzy, that course is a great walkthrough.
Footnotes
-
To use npm effectively, it helps to set things up so global installs don’t require
sudo. One guide: How to use npm global without sudo on OSX. Also see Stack Overflow and sindresorhus guides. ↩ -
Install Yeoman globally with
npm install -g yo. ↩ -
Yeoman generators are node packages whose name starts with
generator-. For example, installinggenerator-angulargives youyo angular. ↩
