Writing a custom Yeoman generator

Source: Internet
Author: User
Tags class generator
Reprinted from JSCON-Jian Shikong: "Custom Yeoman Generator" 1. Getting Started 1.1, setting Node module
Yeoman provides generator-generator to quickly write your own generator.

Install: npm install -g generator-generator Run: yo generator
Create a folder named generator-name (name is the name of your generator); [important]
Create the package.json file, which is the "infographic" of the NodeJS module, which can be generated manually or with the command npm init
{
  "name": "generator-name",
  "version": "0.1.0",
  "description": "",
  "keywords": ["yeoman-generator"],
  "dependencies": {
    "yeoman-generator": "^0.17.3"
  }
}
 The name attribute must have generator- prefix; the keywords attribute must contain yeoman-generator, make sure it is the latest, you can run the command npm install --save yeoman-generator to complete the update/installation
Add other package.json related properties
  1.2, file tree structure
When the yo name command is called, the app generator is called by default, and the logic is placed in the app/ folder
When calling the yo name:subcommand command, there must be a corresponding subcommand/folder
If the file structure is as follows, the generator exposes two commands yo name and yo name: router

├───package.json
├───app/
│ └───index.js
└───router/
    └───index.js
If you don’t want to put all the code in the root directory, Yeoman provides another way: you can put it in the generators/ directory

├───package.json
└───generators/
    ├───app/
    │ └───index.js
    └───router/
        └───index.js
 

1.3, inherit generator
After the structure is written, you need to start writing the actual logic code. Yeoman provides basic generators for you to inherit. These basic generators provide many convenient methods for you to call. Basic writing:

var generators = require(‘yeoman-generator’);
module.exports = generators.Base.extend();
If your generator requires a name parameter (such as foo in yo name:router foo), you want to assign it to this.name:

var generators = require(‘yeoman-generator’);
module.exports = generators.NamedBase.extend();
 The above two methods can be used to create app generators or sub-generators. Base is mostly used for app generators, and NamedBase is mostly used for sub-generators that need to specify a file name.
  1.4, rewrite the constructor
Some methods can only be called in the constructor method and are often used for state control; you can pass in the constructor to override the default constructor:

module.exports = generators.Base.extend({
  // The name `constructor` is important here
  constructor: function () {
    // Calling the super constructor is important so our generator is correctly set up
    generators.Base.apply(this, arguments);

    // Next, add your custom code
    this.option(‘coffee’); // This method adds support for a `--coffee` flag
  }
});
 

1.5, adding method
Generally, the methods added to the prototype are executed in order, but we will see later that some special methods trigger different execution orders:

module.exports = generators.Base.extend({
  method1: function () {
    console.log(‘method 1 just ran’);
  },
  method2: function () {
    console.log(‘method 2 just ran’);
  }
});
 

1.6, run the generator
At this point, you already have a running generator. The next step is to verify that the generator runs according to its own logic. Since the generator is developed locally, it does not exist in the global npm module and needs to be linked manually. Go to the generator-name/ folder and run:

npm link
This will automatically install the project dependency package and link the local files into the global module; after running, you can call the yo name and see the console.log information defined before; at this point, congratulations on completing the simple generator!

1.7, find the project root directory
When running a generator, Yeoman will calculate the current file directory information. The most important thing is that Yeoman uses the directory where .yo-rc.json is located as the root directory of the project, and then Yeoman jumps the current file directory to the root directory to run the requested generator. This .yo-rc.json file is created by the Storage module, and it will be created by calling this.config.save() method inside the generator. So, if you find that your generator is not running in your current working directory, please make sure. yo-rc.json does not exist in other levels of your directory

 
  2. Running context 2.1. Static methods are all Actions
If a function is directly used as an attribute of the generator's prototype (prototype), it will be automatically (in order) executed as an action. How to declare auxiliary functions and private functions that will not be executed automatically? There are three ways

Add a prefix to the method (for example: _method)
Use instance function declaration (this.mehtod)
generators.Base.extend({
  init: function () {
    this.helperMethod = function () {
      console.log(‘won\‘t be called automatically’);
    };
  }
});
Inherited from the parent class generator
var MyBase = generators.Base.extend({
  helper: function () {
    console.log(‘won\‘t be called automatically’);
  }
});

module.exports = MyBase.extend({
  exec: function () {
    this.helper();
  }
});
 

2.2. Running sequence
Yeoman executes the defined methods in order of priority. When the function name you define is the priority function name defined by Yeoman, the function will be automatically listed in the priority queue, otherwise it will be listed in the default priority queue. The names of the methods executed in sequence are:

initializing-your initialization method (check current directory status, get configuration, etc.)
prompting-show the user an option prompt (call this.prompt())
configuring-save user configuration items and configure the project at the same time (create .editorconfig file or other metadata files)
default
writing-used to generate files related to generators (such as routes, controllers, etc.)
conflicts-used to handle conflict exceptions (internal use)
install-used to install related libraries (npm, bower)
end-the last call, often used for cleanup, goodbye, etc.
 
  3. UI
Yeoman runs on the terminal by default, but it is not limited to the terminal. So remember, do not use console.log() or process.stdout.write() to feedback information to the user, you should use the generator.log method.

  3.1, prompt box
The most important UI interaction in Yeoman is the prompt box, which is provided by the Inquirer.js component. Use the following method to call:

module.exports = generators.Base.extend({
  prompting: function () {
    var done = this.async();
    this.prompt({
      type: ‘input’,
      name: ‘name’,
      message: ‘Your project name’,
      default: this.appname // Default to current folder name
    }, function (answers) {
      this.log(answers.name);
      done();
    }.bind(this));
  }
})
 Here we use the priority level of promotion. Since consulting the user is an asynchronous process and will block the operation of the command logic, we need to call the asynchronous method of yo: var cb = this.async();.
  3.2. Remember user preferences
When users run your generator, many times they will enter the same answer; Yeoman extends the API of Inquirer.js and adds the store attribute to indicate that users can use the answers previously filled in as the subsequent default answers:

this.prompt({
  type: ‘input’,
  name: ‘username’,
  message: ‘What\’s your Github username’,
  store: true
}, callback);
 When a default answer is provided, the program will force the user to enter
  3.3, command line parameters
You can pass parameters directly like in the command:

yo webapp my-project
Here, my-project is the first parameter. In order to prompt the system that we expect the user to pass in parameters, we need to call the generator.argument() method, which accepts name as a parameter and additional restrictions.

The argument method must be called in the constructor. These conditions are (key/value type): desc: Description for the argumentrequired: Boolean whether it is required optional: Boolean whether it is optionaltype: String, Number, Array, or Objectdefaults: Default value for this argumentbanner: String to show on usage notes (this one is provided by default)
Sample code:

module.exports = generators.Base.extend({
  // note: arguments and options should be defined in the constructor.
  constructor: function () {
    generators.Base.apply(this, arguments);

    // This makes `appname` a required argument.

    this.argument(‘appname’, {type: String, required: true });

// And you can then access it later on this way; e.g. CamelCased
    this.appname = this._.camelize(this.appname);
  }
});
 

3.4, options
The option looks like a parameter, but there are two short dashes (flags) in front of it:

yo webapp --coffee
Use the generator.option() method to get the option value. This method also has optional restriction attributes (key/value type):

desc: Description for the optiontype: Either Boolean, String or Number defaults: Default valuehide: Boolean whether to hide from help
For example:

module.exports = generators.Base.extend({
  // note: arguments and options should be defined in the constructor.
  constructor: function () {
    generators.Base.apply(this, arguments);

    // This method adds support for a `--coffee` flag
    this.option(‘coffee’);
    // And you can then access it later on this way; e.g.
    this.scriptSuffix = (this.options.coffee? ".coffee": ".js");
  }
});
 

 

4. Deal with dependencies
When running the generator, it is often accompanied by npm and bower commands to install dependent files. Yeoman has extracted these functions for users to use.

4.1, npm
Use generator.npmInstall() to run the npm install command. No matter how many times you call it, Yeoman will ensure that the command is executed only once.

generators.Base.extend({
  installingLodash: function() {
    var done = this.async();
    this.npmInstall([‘lodash‘], {‘saveDev‘: true }, done);
  }
}):
The above code is equivalent to the command line:

npm install lodash --save-dev
 

4.2, bower
Use generator.bowerInstall() to run the bower installation command. No matter how many times you call it, Yeoman will ensure that the command is executed only once.

Using generator.installDependencies() will run the above two installation commands at the same time. No matter how many times you call them, Yeoman will ensure that the command is executed only once.
  4.3, use other tools
In order to facilitate calling other program commands, Yeoman pulls out the spawn command cross-platform; for example, you want to call the composer command of PHP:

generators.Base.extend({
  end: function () {
    this.spawnCommand(‘composer’, [‘install’]);
  }
});
Remember to call the spawnCommand command in the end queue, otherwise the user will not have the patience to wait that long.
 
  

5. File system
To facilitate the input and output of file streams, Yeoman uses two location environments.

5.1, target location context
The first is the destination context, where the "target" refers to the location where you want to structure the application. This location is either the current folder or the parent folder location where the file .yo-rc.json is located;

The .yo-rc.json file ensures that all end users use the same method as the sub-file (folder) where the generator is located
Use generator.destinationRoot() to get the target location context; you can also manually pass parameters to reset, of course no one wants to do that; use generator.destinationPath(‘sub/path’) to splice the required path strings; example:

// Given destination root is ~/projects
generators.Base.extend({
  paths: function () {
    this.destinationRoot();
    // returns ‘~/projects’

    this.destinationPath(‘index.js’);
    // returns ‘~/projects/index.js’
  }
});
 

5.2, template location context
Template context is the location of the folder where your template files are located. This folder is basically where you read and copy files. The default template context is ./templates/, you can use generator.sourceRoot('new/template/path') to specify the location of the new template folder; similar to the above, you can use generator.sourceRoot() to get the template location, use generator .templatePath('app/index.js') splicing path; example:

generators.Base.extend({
  paths: function () {
    this.sourceRoot();
    // returns ‘./templates’

    this.templatePath(‘index.js’);
    // returns ‘~/templates/index.js’
  }
});
 

5.3, file operation API
Yeoman puts all file methods in this.fs, which is an example object of mem-fs-editor, you can check the API interface by yourself.

Example: Copy template files
if. , The content of templates/index.html file is:

<html>
  <head>
    <title><%= title %></title>
  </head>
</html>
We use the copyTpl method to copy the template: (see Lodash template syntax for more)

generators.Base.extend({
  writing: function () {
    this.fs.copyTpl(
      this.templatePath(‘index.html‘),
      this.destinationPath(‘public/index.html‘),
      {title: ‘Templating with Yeoman’}
    );
  }
});
Once the generator has finished running, we will get public/index.html:

<span class="hiddenSpellError" pre="" data-mce-bogus="1">Templating</span> with Yeoman
  
 Yeoman still retains the old file API, please refer to the API documentation. The old file API always assumes that the file comes from the template context, and the file is always written in the destination context, so they do not require you to pass in the file path information, and the program will automatically handle the suggestion: use the new fsAPI as much as possible, and it is more convenient to use Clear
 
  

6. Store user settings
It is often necessary to store the user's settings and use them in the sub-generator, such as what programming language the user uses (such as using CoffeeScript?). These configuration items are stored in .yo-rc.json (using Yeoman Storage API.). Get the API method through the generator.config object.

  6.1. Common methods
generator.config.save() saves the configuration items to the file .yo-rc.json (if the file does not exist, it will be created automatically). Since this file determines the root directory of the project, a best practice is: even if there is nothing The save method should also be called.

The save method is automatically called every time the configuration item is set, so you can call it without displaying
generator.config.set(key,val)

Name Description
key the key used for storage
val any JSON type value (String, Number, Array, Object)
generator.config.get() obtains configuration items according to the key generator.config.getAll() obtains all available configuration information; the main return value is not returned by reference, so you need to call the set method to change the configuration items inside. generator.config.delete() deletes a key value (and its value) generator.config.defaults() takes the object as the default configuration information, and adopts the principle of non-overwrite.

  6.2. .yo-rc.json file structure
This file can store the information of multiple generators, and each generator divides the namespace according to the name to prevent conflicts; this also means that the configuration items of each generator can only be read by the sub-generators, and the configuration information between different generators Cannot be accessed through Yeoman Storage API. (Use command line parameters or options to pass parameters between different constructors)

File sample:
{
  "generator-backbone": {
    "requirejs": true,
    "coffee": true
  },
  "generator-gruntfile": {
    "compass": false
  }
}
 

  

references
WRITING YOUR OWN YEOMAN GENERATOR Learning Bower-Front-end Development Kit Management Tool Bower: Client Library Management Tool Custom Project Engineering Generator-Yeoman Getting Started Guide (2)

Write a custom Yeoman generator

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.