This article started on Github, welcome issue/FXXK.

preface

If you’ve written vue, React, or Angular, you’re familiar with the following libraries:

  • vue-cli
  • create-react-app
  • angular-cli

For those of you who have experimented with these tools, have you noticed that they all have one thing in common – they provide a boilerplate for rapid development? This article will start with boilerplate. In this article, you will learn:

  1. aCLIThe problem the tool needs to solve;
  2. Write aCLIThe basic idea of;
  3. Write aCLIWhat preparations need to be made;

PS: Since the scaffolding is too long, I will use the cuter CLI instead.

preheating

Due to space constraints, this section will use create-react-app and vue-cli as examples (it’s really hard to convince people I started ng…). , review its use process:

Create-react-app creates a basic project and opens the directory as follows:

My - app ├ ─ ─ the README. Md ├ ─ ─ node_modules ├ ─ ─ package. The json ├ ─ ─ the gitignore ├ ─ ─ public │ └ ─ ─ the favicon. Ico │ └ ─ ─ index. The HTML │ └ ─ ─ the manifest. Json └ ─ ─ the SRC └ ─ ─ App. CSS └ ─ ─ App. Js └ ─ ─ App. Test, js └ ─ ─ index. The CSS └ ─ ─ index. The js └ ─ ─ logo. The SVG └ ─ ─ registerServiceWorker.jsCopy the code

Two interesting points:

  • public/manifest.json: This is part of the PWA that describes information about an application. Previously developedcordovaThis is also used to do overheat updates.
  • src/registerServiceWorker.js: Install the Service Workers file.

I haven’t used it for a long time, but it already supports PWA by default, so nice, if you don’t know how to do that, I won’t expand it here.

Next, let’s open package.json and see what it is:

{
  "name": "my-app"."version": "0.1.0 from"."private": true."dependencies": {
    "react": "^ 16.2.0"."react-dom": "^ 16.2.0"."react-scripts": 1.1.1 ""
  },
  "scripts": {
    "start": "react-scripts start"."build": "react-scripts build"."test": "react-scripts test --env=jsdom"."eject": "react-scripts eject"}}Copy the code

It’s pretty neat, but there’s a react-scripts. It may seem strange, but if I tell you that its dependencies include the usual front-end hooligan Babel, Webpack, webpack-dev-server, and Autoprefixer, I think you get a good idea of what React-Scripts does:

Wrap webPack and webpack-dev-server, provide a default configuration (built-in CSS, file, SVG, etc.), Run these configurations directly at react-scripts start and react-scripts build.

Create React apps with no Build configuration.😂

Of course, zero configuration is not suitable for large custom projects, and React-Scripts is like a giant black box where there are always scenarios where there are no apis or instructions to start with. React-scripts is certainly not stupid, and provides a fancy directive:

  • react-scripts eject: Decompress all the tools (configuration files and package.json dependencies) to the application’s path.

Amazing 😅, to run a look:

When all the dependencies were exposed, package.json exploded, making me nostalgic for the good old days.

Ok, so create-react-app solves this problem:

  1. According to the boilerplate file to generate a unified project skeleton, so that developers quickly put into development;
  2. Preset with an easy to developreact-scriptsTo save developers the painful configuration process. (webpackConfiguration engineer looks to be out of a job 😇)

Next, review vue-CLI again. As of the date of this writing, [email protected] is still in beta, so in this article we will use 2.x.x as an example to create a Vue project called my-vue-app with the template webpack-Simple:

Vue-cli adds a very important step before generating a project — Prompt, or query, and ultimately generate your project based on what is being asked. Two FYI:

  1. Vue-cli queries are done using the Inquirer. Js library.
  2. You can find webpack-simple here.

To conclude this section, a scaffold usually consists of the following basic parts:

  1. Prompt.
  2. Boilerplate
  3. Generate file

In actual combat

After the last section, you probably have a general idea, but let’s take vue-CLI as an example and jump right into action.

The basic idea

  1. Define the rules of a template package, using vue-CLI rules: template is the source file, meta. Js /meta. Json is the configuration entry file.
My-first-package ├─ meta. Js ├─ templateCopy the code
  1. Prompts. Parse the meta-js of the package, get the question to ask, and run it, dispensing the end user’s answer to a context object;
  2. Read the contents of the template and render it with the context object you just fetched.

Pseudocode implementation

Here is a pseudo-code implementation of CLI:

// 10 lines of pseudocode to implement a CLI
function CLI(packageSourcePath) {
    const context = {}
    const meta = require(path.join(packageSourcePath, 'meta.js'))
    const templatePath = path.join(packageSourcePath, 'template')
    const { prompts } = meta
    return promptsRunner(prompts).then(anwsers= > {
        Object.assign(context, anwsers)
        return generateFiles(templatePath, context)
    })
    .then((a)= >  console.log('[OK]'))
    .error((a)= > console.log('[Error]'))}Copy the code

PromptsRunner is used to query, and generateFiles is used to render and generateFiles. A: wow! It was so simple.

Technology selection

This is vuE-CLI technology selection:

  1. Information: Inquirer. Js
  2. Command line parsing: commander. Js
  3. Template rendering: handlebars.js
  4. File generation: Metalsmith

Keen to see the world you, have been itching to try it?

More and more

Of course, if it’s just a CLI like this, which is obviously just a toy, you might consider supporting the following cute features:

  1. Inject some default properties into the Context (git username)
  2. Supports file filtering (by Context)
  3. Support for file renaming (not directly supported by VUe-CLI by default, but via metalsmith’s plug-in)
  4. Support package management (e.g. Package generation, caching, pull, update, delete, automated testing)
  5. Provide lifecycle hooks (such as beforePrompt, beforeRender, beforeExit, and so on)
  6. Dynamic output paths (this is useful for me)

Of course, there’s more, if you can name it.

Sweet moment

It’s time to introduce you to some desserts.

SAO

Github portal: github.com/saojs/sao

A very sexy name, this is a library written by our lovely EGOIST that basically implements all the features I mentioned above.

Crucially, SAO now provides a large number of high-quality boilerplate files:

name description
template Template for scaffolding out an SAO template
lass Lass scaffolds a modern package boilerplate for node
lad Lad scaffolds a Koa webapp and API framework for node
vue Kickstart a Vue project with Poi
gi Generate .gitignore file in your project
nm Scaffold out a node module
vue-webpack Vue.js offcial webpack template (SAO port)
basic Basic project skeleton
react SAO template for react with vbuild
micro-service Scaffolding out a micro-service
node-cli Scaffold a node cli tool
next Scaffold out a Next.js project
electron Scaffold out an Electron project
expo Scaffold out an Expo app

It’s so powerful. That’s probably why I have to love EGOIST.

poz

Github portal: github.com/ulivz/poz

Because of the need to support renaming and dynamic output paths in actual production, I wrote the LIBRARY POZ, based on the predecessors, based on a completely different implementation, to achieve the same function, and add a little special effects, this is probably the fun of building wheels. Welcome to Fxxk/Issue.

POZ is also planning to start developing stable version 1.0 of the RoadMap. See if that RoadMap is ambitious.

alphax

Github portal: github.com/ulivz/alpha…

This is a poZ base library, based on Stream and inspired by Metalsmith and Majo from SAO. I recently completely rewrote the library to give it the following lovely features:

  1. A minimalist API;
  2. Task flow control;
  3. The middleware;
  4. File filtering based on pure function or JSON;
  5. Renaming files based on pure functions or JSON;
  6. Support not to write hard disk, easy to test;

It works like this:

alphax()
  .src('* *')
  .task(task1)
  .task(task2)
  .task(task3)
  .use(file= > file.content += Date.now())
  .rename(filepath= > filepath.replace('{name}', name))
  .rename(filepath= > filepath.replace('{age}', age))
  .transform(content= > content.replace('{name}', name))
  .filter(filepath= > filepath.endWith('.js'))
  .filter(filepath= >! filepath.startWith('test'))
  .dest('dist')
  .then(files= > console.log(files))
  .catch(error= > console.log(error))
Copy the code

If you don’t like functions, you can also configure them:

const config = {
  tasks: [task1, task3, task3],
  use: file= > file.content += Date.now(),
  rename: {
    '{name}': name,
    '{age}': age
  },
  filter: {
    'app.js': true.'test.js': false
  },
  transform(content) {
    return content.replace('{name}', name)
  }
}

alphax()
  .src('* *', config)
  .dest('dist')
  .then(files= > console.log(files))
  .catch(error= > console.log(error))
Copy the code

This lib has solved most of your META. Js API design problems 😄. Attach the Documentation address, written in the lovely Docute: Documentation

conclusion

A CLI is ultimately about productivity and uniformity, but the over-encapsulation of create-React-app, and the over-looseness of [email protected], doesn’t seem to be the best solution. Less configuration and more scalability are two contradictory topics, and in the long run, the choice of CLI will depend on the specific scenario, but as a CLI developer, it’s worth thinking about how to strike a better balance.

This article only talked about the first part of writing a CLI tool, the basic idea is relatively simple, but there will be many optimization points and error catch 😅, Good luck!

In the following sections, I will continue to explain the basic implementation principles and ideas of react-scripts similar to create-react-app. In fact, this is also a feature of [email protected] and POI, so stay focused.

Above, the full text.)