If you like, please follow my blog or subscribe to RSS feed.

The introduction

The following is a typical example of using scaffolding to initialize a project.

As the concept of front-end engineering continues to grow, more and more people are choosing to use scaffolding to build their projects from scratch. Create-react-app and VUE-CLI help you initialize the configuration, generate the project structure, automatically install dependencies, and finally run the project with a single command to start development or build the project.

These scaffolds provide best practices in a general sense, but AS I develop, I find that as the business evolves, there will inevitably be adjustments that need to be made to suit the realities of business development. Such as:

  • Optimize the performance of Webpack packaging by adjusting the plug-in and configuration
  • Remove some functionality built out of scaffolding
  • Project structure adjustment
  • Fusion company development tools

In short, as our business evolves, we tend to develop a more “personalized” business solution. The most straightforward thing to do at this point is to develop a scaffolding for the solution so that we can reuse the best practices and solutions in the future.

1. How does scaffolding work?

Functional rich degree of scaffolding, the complexity of nature is not quite the same. But in general, scaffolding work generally includes several steps:

  • Initialization, usually at this point the environment is initialized, do some pre-checking
  • User input, such as vue-CLI, will “ask” you for configuration options
  • Generating a Configuration File
  • Generate the project structure, which may use a project template
  • Install dependencies
  • Cleaning, calibration and other final work

In addition, you need to deal with command line behavior and so on. Often we just want to create scaffolding for a particular scene lightweight and quickly (not as complete as vue-CLI). To create a scaffold quickly, you don’t have to start from scratch. Yeoman is a tool that helps us create scaffolding quickly.

Many of you may not know much about Yeoman, so let’s briefly introduce what Yeoman is and how he helped us simplify scaffolding.

First, Yeoman would be simple to understand as a scaffolding operation framework, it defines a scaffold in the process of running go through stages (such as said above, we may first read the user input, then generated project file, the final install dependencies), all we need is in the corresponding stages of life cycle, Fill in the corresponding operation code. The place where we populate the code is called a Generator in Yeoman, and as the name suggests, Yeoman calls a generator to generate the corresponding project.

If you’re not sure how they relate, here’s a quick example:

Using the analogy of scaffolding development to front-end component development, Yeoman’s role is like React, which is a framework, specifically defining component lifecycle functions; The Generator is similar to a React business component that you write and fills in the code in each lifecycle according to the React rules.

Yeoman’s built-in “lifecycle” methods are executed in the following order:

  1. initializing
  2. prompting
  3. default
  4. writing
  5. conflicts
  6. install
  7. end

The default stage will execute your custom methods.

Yeoman also integrates various tools commonly used in scaffolding development, such as file manipulation, template filling, user interaction on terminals, command lines, and more, packaged in an easy-to-use method.

With these two things, Yeoman can help us greatly standardize and simplify scaffolding development.

2. Develop your own scaffolding

With some knowledge of how scaffolding works and Yeoman’s basic concepts, we can now create our own scaffold. As an example, the scaffolding is simple in that it creates a minimal version of a WebPack-based front-end project for us. The final effect of scaffolding is as follows:

2.1. Prepare a project template

Scaffolding helps us to quickly generate a set of established project architectures, files, and configurations. The most common way to do this is to write a set of project framework templates, and when scaffolding is ready to generate the project, copy this template to the target directory. There are actually two small points to focus on here.

The first is the padding of variables within the template.

Some file content in the template may need to be dynamically replaced at generation time, such as the name value in package.json that is dynamically populated based on what the user enters in the terminal. Yeoman has a built-in EJS template engine that can be used directly.

The second is where the template is placed.

Each installation is a local copy, which is very fast. However, it is difficult to update the project template itself, so users need to be prompted to upgrade the generator.

The other option is to put the template file on a server and dynamically download it from a certain address every time you use scaffolding initialization. It’s easy to update and upgrade the template, usually hosted on Github.

Placed on the second template is good choose locally, or distal good, actually or based on your personal business scenario, the demand of the limitations of the different scenarios, before I wrote both template on local scaffold (i.e., by NPM with scaffolding installation), also wrote a hosting this way on the git repository.

Back to our goal of “creating a minimal webPack-based front-end project,” I prepared a project template that I would then use as the scaffolding generated project content.

2.2. Creating a Generator (Yeoman-generator)

Creating a Generator for Yeoman requires following its rules.

The first is the generator naming convention. Start with generator and connect horizontally. For example, if you want to create a generator named webpack-kickoff, the package name should be generator-webpack-kickoff.

This way, when you pass

npm i -g yo
Copy the code

Once you have installed Yeoman’s CLI, you can start scaffolding using the Generator with the yo command:

yo webpack-kickoff
Copy the code

In this case, webpack-kickoff is what comes after the package name generator-, and Yeoman will use this rule to search globally for matching packages.

Secondly, according to Yeoman’s specification, by default you will need to create index.js in the generators/app/ directory of your project (i.e. generator), where you will write your scaffolding workflow. Of course, you can extend or change this rule by modifying the configuration.

In addition, the generator class you create needs to inherit from Yeoman-generator. / / generators/app/index.js: generators/app/index.js

const Generator = require('yeoman-generator');
class WebpackKickoffGenerator extends Generator {
    constructor(params, opts) {
        super(params, opts); }}module.exports = WebpackKickoffGenerator;
Copy the code

Remember the “lifecycle” approach mentioned earlier? This includes initializing, Only chosen by insurgents, default, Writing, Conflicts, Install, and end. All but default represent a method of the same name in Generator, and you need to override the corresponding method in subclasses. The default phase executes user-defined class methods.

For example, if you want to print version information during initialization, you can do this:

const Generator = require('yeoman-generator');
class WebpackKickoffGenerator extends Generator {
    constructor(params, opts) {
        super(params, opts);
    }
    
    initializing() {
        const version = require('.. /.. /package.json').version;
        this.log(version); }}module.exports = WebpackKickoffGenerator;
Copy the code

As you can see, all that remains is to fill in the implementation details of the various methods in the WebpackKickoffGenerator class.

2.3. Handle user interaction

Scaffolding typically has some user-defined content, such as the name of the project directory to create, or whether a configuration is enabled. These interactions are typically implemented through interactive terminals, such as the following feature.

This can be done using inquier.js. Yeoman has already integrated this for us, just call this.prompt from the generator.

The requirements in the user interaction section are also relatively simple, just ask the user for the name of the project directory they want to create, which is then used as the project name. According to Yeoman’s process specification, we wrote this part of the code in the Heavily-guarded method:

class WebpackKickoffGenerator extends Generator {
    / /...
    prompting() {
        const done = this.async();

        const opts = [{
            type: 'input'.name: 'dirName'.message: 'Please enter the directory name for your project:.default: 'webpack-app'.validate: dirName= > {
                if (dirName.length < 1) {
                    return '⚠️  directory name must not be null!';
                }
                return true; }}];return this.prompt(opts).then(({dirName}) = > {
            this.dirName = dirName;
            done();
        });
    }
    / /...
}
Copy the code

Note that since the user interaction is an “asynchronous” behavior, in order for subsequent lifecycle methods to continue executing after the “asynchronous” completion, the this.async() method is called to notify the method that it is an asynchronous method, rather than calling the next lifecycle method after executing the synchronous code sequentially. A function is returned and executed to indicate that the phase is complete.

2.4. Download the template

As described in 2.1., we chose to host the template on Github, so we need to download the corresponding files before generating the project code. This can be done quickly using download-git-repo.

class WebpackKickoffGenerator extends Generator {
    / /...
    _downloadTemplate() {
        return new Promise((resolve, reject) = > {
            const dirPath = this.destinationPath(this.dirName, '.tmp');
            download('alienzhou/webpack-kickoff-template', dirPath, err => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve();
            });
        });
    }
    / /...
}
Copy the code

Here we use the this.destinationPath() method, which is mainly used to get the path. If no parameter is passed, the directory of the current command line is returned. If multiple parameters are received, the path is concatenated.

Also, if you’re careful, you’ll notice that the _downloadTemplate() method is prefixed with an underscore. This is a convention in Yeoman: There is a default stage in the Yeoman execution order, which contains all user-defined class methods. However, you can add an underscore prefix if there are methods that you don’t want to be called directly by Yeoman’s scaffolding process, but instead are provided as utility methods to other class methods. This naming method is ignored in the default phase.

2.5. Template file copy

Once the project template has been downloaded, you can now copy the related directories and files to the target folder. All of this can be done in the Writing phase. In this case, you need to traverse all directories in the template to fill and copy all files in the template. The traversal mode is as follows:

class WebpackKickoffGenerator extends Generator {
    / /...
    _walk(filePath, templateRoot) {
        if (fs.statSync(filePath).isDirectory()) {
            fs.readdirSync(filePath).forEach(name= > {
                this._walk(path.resolve(filePath, name), templateRoot);
            });
            return;
        }

        const relativePath = path.relative(templateRoot, filePath);
        const destination = this.destinationPath(this.dirName, relativePath);
        this.fs.copyTpl(filePath, destination, {
            dirName: this.dirName
        });
    }
    / /...
}
Copy the code

The this.fs.copytpl () method is used, which supports file copying and specifies template parameters, and automatically prints information on the console if duplicate names are overwritten.

Finally, the writing phase is completed by integrating the download with the copy.

class WebpackKickoffGenerator extends Generator {
    / /...
    writing() {
        const done = this.async();
        this._downloadTemplate()
            .then((a)= > {
                const templateRoot = this.destinationPath(this.dirName, '.tmp');
                this._walk(templateRoot, templateRoot);
                fs.removeSync(templateRoot);
                done();
            })
            .catch(err= > {
                this.env.error(err);
            });
    }
    / /...
}
Copy the code

2.6. Dependent installation

So far, scaffolding has helped us prepare the configuration, directory structure, and dependency lists needed for project development. This further helps the developer to set up dependencies so that after the scaffolding creation project is complete, the developer can work directly on it.

Yeoman also provides the this.npminstall () method to implement the installation of the NPM package:

class WebpackKickoffGenerator extends Generator {
    / /...
    install() {
        this.npmInstall(' ', {}, {
            cwd: this.destinationPath(this.dirName)
        });
    }
    / /...
}
Copy the code

At this point, the scaffolding’s core function is complete. We are ready to use our generator to quickly create projects. Very simple ~

The complete code can be found at generator-webpack-Kickoff.

3. Use scaffolding 🚀

Using this scaffold will require both Yeoman and the Yeoman-Generator we just created above. One caveat, of course, is that both Yeoman and the Generator need to be installed globally. There is no problem with the global Yeoman installation (NPM install-g yo). There are several ways to handle generator-webpack-kickoff:

  1. Publish directly to NPM, then install globally as normal
  2. Manually copy directly to global node_modules
  3. usenpm linkLink a directory globally

According to section 2.2., our generator is named generator-webpack-kickoff. Since my package has been sent to NPM, to use this scaffold you can run the following command:

Install it once
npm i -g yo
npm i -g generator-webpack-kickoff

# Start scaffolding
yo webpack-kickoff
Copy the code

4. The optimization

As you can see from the example above, implementing a scaffold is very simple. The examples are small, but they also cover the main parts of scaffolding development. Of course, this article has omitted some “tuning” features for simplicity. For example,

  • Project directory duplication detection, when generating a project, check whether the directory already exists, and prompt a warning
  • Cache of project templates. Although we use Github hosting, we can also consider not having to re-download every time. We can put a local cache and update it daily or weekly.
  • CLI optimization.The full versionIt will also include richer CLI uses, such as the loading effects seen in the GIFs and the information panels displayed at the beginning and end. These tools include
    • Ora, used to create the spinner, the loading effect described above
    • Chalk, used to print color information
    • Update-notifier, used to check the online and local versions of packages
    • Beeper, you can beep you, for example, if you make a mistake
    • Boxen, create that little “panel” at the top and bottom
  • Version check. As mentioned above, update-notifier can be used to check the version. Therefore, versioning can be done during the initializing phase, prompting the user to update the scaffold.

The last

This article uses a simple example to show you how to quickly create scaffolding using Yeoman. To learn more about the development and use of yeoman-Generators, refer to the various types of generators written in the community. There are currently over 8,000 Yeoman-Generators on NPM and you might just have your dish.

See generator-webpack-Kickoff for the code completed in this article.