preface

The company needs a lot of new projects, so the director asked me to prepare to build a general framework and put it on Git, so that the new projects can be directly applied. I can’t help but think that if you were to create a new project every time, the process would be to create a new folder (or a new git repository), copy the git address of the generic framework, pull it into that folder, and start developing. This operation process seems cumbersome and inefficient. When you need to copy, you must first open the Git repository, then find the git address of the general framework, and then create a new project. The git address may not be remembered, and you also need to obtain the Git repository. This is not line, so I think before very 🔥 cli tool, before I saw a lot of god to write the article, feel very cool also very worship, thought oneself also want to write a, but for a long time no opportunity, mainly is not needed, also don’t know how to write. This is not, now the opportunity to come, so began to try ~ 😎

Most CLI tools require plug-in tools, which means you need to read a lot of documentation, so a patient ❤️ is required.

Ready Coffe, Let’s start

START

Tools required for cli 🔧

The name of the Introduction to the The document address
commander Command line custom commands, such as -v, -c Github.com/tj/commande…
chalk Beautify the style and highlight the font github.com/chalk/chalk
inquirer Interactive answer Github.com/SBoudrias/I…
figlet Art words Github.com/patorjk/fig…
ora Loaded animation effects Github.com/sindresorhu…
download-git-repo Downloading a Remote Template www.npmjs.com/package/dow…
handlebars You can replace dynamic strings in templates Handlebarsjs.com/zh/guide/#%… |

Create a new project 🆕

  • Create a new folder 📁
mkdir fency-cli   // Create a new folder with an arbitrary name
cd fency-cli     // Go to the folder
npm init -y     // Quickly generate package.json
Copy the code
  • Install all of the libraries mentioned earlier ⬇️
yarn add commander chalk inquirer figlet ora download-git-repo handlebars -D
Copy the code

Ora, I am using version 5.6, above 6 use import

  • Create the cli entry file /bin/cli.js 📃
#! /usr/bin/env node // Script for the interpreter console.log('Hello fe-cli') // To test whether it worksCopy the code
  • Add import file bin field 🏠 to package.json file
{
    "name": "fency-cli"."version": "1.0.0"."description": "Scaffolding tools"."bin": {
        "fe-cli": "./bin/cli.js"},... }Copy the code

Add bin for normal use of “NPM link”,” fe-cli “as command line name, we can test ~

  • This command is then mapped to global 🔗
npm link
Copy the code
  • The result is ✅

  • To test, type fe-cli on the command line and run ⚙️

Hello fency-cli is the output of the CLI file, and when you change a file, it will be updated globally.

Obtain the version number 🏷

  • Generally, a project has a version number, and the version number represents the iteration of a function, so let’s make the CLI version number first. The version number is related to the version in package.json, see 🌰 below
const { program } = require('commander')
const package=require('.. /package.json')

// Get the version number of package.json
program.version(package.version)

// Parse command line instructions, must be added, otherwise no information printed
program.parse(process.argv)
Copy the code
  • To test, type fe-cli -v or fe-cli –version

Program.version (package.version, ‘-v, –version’). If you want to support -v, add a second parameter, such as program.version(package.version, ‘-v, –version’).

New business project

When we create a new business project, the process goes something like this: create a new folder and name it, copy the generic framework to the project, and push the project to the Git repository. Now we will automate these processes ~ 👀

Before cloning the git address of a generic framework or pushing it to a Git repository, SSH must be set up, otherwise it will not work.

Without further ado, start ~ 🙈

  • Prepare interactive responses

Because our project will have two types, one is the front desk of the framework, the framework of the other one is the background, so I am so ready, let the user use to create the command line, then enter the folder name and description (after input to determine whether to have the same name, if you have to remind the user name), then choose a framework that can start copy after selection.

Create a new question.js file in the SRC folder

const fse=require('fs-extra')

const create = [
    {
        name:'conf'.type:'confirm'.message:'🆕 Do you want to create a new project? '}, {name:'name'.message:'👉 Please enter project name :'.validate:function(val){
            if(! val){return 'Dear, you forgot to enter the name of the project.'
            }
            if(fse.existsSync(val)){
                return 'A project with the same name already exists in the current directory, please change the project name'
            }
            return true
        },
        when: res= > Boolean(res.conf)
    },{
        name:'desc'.message:'💬 Please enter a project description :'.when:res= >Boolean(res.conf)
    },{
        name:'template'.type:'list'.message:'🔜 Please select a frame? '.choices:[
            {
                key:'a'.name:'General purpose framework'.value:' '.// The git address of the foreground common framework
            },
            {
                key:'b'.name:'Middle background Universal Framework'.value:' '.// This is the git address}].filter:function(val){
            return val.toLowerCase()
        },
        when: res= >{
            Boolean(res.conf)
        }
    }
]

module.exports={
    create
}
Copy the code
  • Add the logic to create the file and create a new create.js file under the SRC folder

const download = require('download-git-repo')
const ora = require('ora')
const fse = require('fs-extra')
const handlebars = require('handlebars')
const myChalk = require('.. /utils/chalk')

const { red, yellow, green } = myChalk

function createProject(project) {
    // Get user input, selected information
    const { template, name, desc } = project;
    const spinner = ora("Pulling the frame...");
    spinner.start();
    download(template, name, { clone: true }, async function (err) {
        if (err) {
            red(err);
            spinner.text = red('Pull failed.${err}`)
            spinner.fail()
            process.exit(1);
        } else {
            spinner.text = green('Pull success... `)
            spinner.succeed()
            spinner.text = yellow('Please wait.. Replacing project name, description in package.json... ')
            const multiMeta={
                project_name: name,
                project_desc: desc
            }
            const multiFiles=[
                `${name}/package.json`
            ]
            // Use conditional loops to replace template characters with files
            for (var i = 0; i < multiFiles.length; i++) {
                // Try {} catch {} to terminate the Spinner in case of an error
                try {
                    // Wait to read the file
                    const multiFilesContent = await fse.readFile(multiFiles[i], 'utf8')
                    // Wait to replace file, handlebars.compile(original file contents)(template character)
                    const multiFilesResult = await handlebars.compile(multiFilesContent)(multiMeta)
                    // Wait for the output file
                    await fse.outputFile(multiFiles[i], multiFilesResult)
                } catch (err) {
                    // If something goes wrong, Spinner changes the text message
                    spinner.text = red('Project creation failed.${err}`)
                    // Terminates the wait animation and displays the X flag
                    spinner.fail()
                    // Exit the process
                    process.exit(1)}}// If successful, Spinner changes the text message
            spinner.text = yellow('Project created successfully! `)
            // End the waiting animation and display the bookmark
            spinner.succeed()
        }
    });
}

module.exports = createProject
Copy the code
  • The create command line entered by the user is then processed

Cli.js adds a line of code

const inquirer = require('inquirer')
const package = require('.. /package.json')
const question = require('.. /src/question')
const myChalk = require('.. /utils/chalk')
const createProject = require('.. /src/create')

const { red } = myChalk
/** Create project */

program
    .command('create')
    .description('Create a project')
    .action(function(){
        inquirer.prompt(question.create).then(async answer => {
            if(answer.conf){
                createProject(answer)
            }else{
                red('🆘 you have terminated this operation 🆘')
            }
        }).catch(err= >{
            red('❌ program error ❌')
            process.exit(1); })})Copy the code
  • Finally, let’s look at the effect

Cool ~

Push the new project to git repository

Companies usually have their own Git repository for multiple partners to develop together, so you need to implement the automatic process of pushing new projects to the Git repository, but the prerequisite is to create a new repository in the remote repository, and then copy the Git address. (At present only this, small partners if there is a better way, might as well say, in this very thank you 🙏)

  • Question. Js adds an interactive answer
const pushGit=[
    {
        name:'url'.type:'input'.message:'🌲 please enter the remote warehouse address :',}]Copy the code
  • Add the ability to push remote repositories and create a new Git file in the SRC folder
const execa = require('execa')
const ora = require('ora')
const spinner = ora('git pushing... \n')
const myChalk = require('.. /utils/chalk')

const { red, green } =myChalk

async function push(gitRemote) {
    const runCMD = (command, args) = > {
        if(! args) { [command, ...args] = command.split(/\s+/);
        }
        return execa(command, args).catch((err) = > {
            spinner.fail(
                red("Push failed, please check the remote warehouse address is correct.")); }); };await runCMD("echo unicorns");
    await runCMD("git init");
    await runCMD(`git remote add origin ${gitRemote}`);
    await runCMD("git add .");
    await runCMD("git commit -m init");
    spinner.start();
    await runCMD("git push origin master").then((res) = > {
        if (res) {
            spinner.stop();
            console.log();
            console.log(
                green(
                    "🎉 push success hot ~ \n" +
                    " \n" +
                    "😀 can be happy to start typing code, may god beast bless you, write code never bug\n")); }}); }module.exports = {push} * Last added push command line cli.js added a new code` ``JavaScript const git = require('.. / SRC /git') / / program.command ('pushGit').description(' push to gitlab ').action(function(){ inquirer.prompt(question.pushGit).then(answer=>{ git.push(answer.url) }).catch(err => { red(`❌ program error ❌`) process.exit(1); })})Copy the code

That should push it to the remote warehouse, assuming nothing goes wrong.

Well, that’s it. If you need a new feature, you can expand it yourself. If I have time, I will update it synchronously

Fency – demo of cli

If there are deficiencies, please give me more advice ~ 🙏