current position:Home>CLI command line detection tool

CLI command line detection tool

2022-05-15 07:50:13User 4669777

When we are doing front-end development , In order to ensure the quality of code submitted by team members , Generally, code quality inspection and code beautification will be carried out on the code , The usual approach is to make a series of configurations , With the help of eslintprettierlint-stagedhusky And other tools to realize the detection of code . But this process involves many , Configuration is also cumbersome , Moreover, repeated configuration is required for different projects , It undoubtedly increases everyone's workload , So what I want to solve is this problem , A command line tool is provided to encapsulate the above detection tool , Simplify configuration steps .

How to develop a command line tool

First of all, you should understand what is a command-line tool , The English name of the command line tool is command-line interface, abbreviation ,CLI,( Hereinafter referred to as CLI)CLI Users can interact with the system in real time , Get real-time information of the system , Complete user-defined functions . such as gitcreate-react-appvue-cli And so on are the command-line tools that we usually contact .

So back to the title , How to develop a command line tool ?

oclif

brief introduction

Here I use oclif, One can help us quickly build CLI Framework , And can support Javascript perhaps Typescript Language .

oclif There are two types of command-line tools ,Single-command and Multi-command.Single-command such as ls perhaps curl command ,Multi-command such as git, Multiple commands can be provided , Be similar to git addgit commit etc. . Here, as needed , I chose Single-command.

Quick creation

Run the following command line quickly Single-command Command line :

$ npx oclif single big-lint

Then follow the prompt on the command line , Enter some basic information , You can directly help us create a CLI engineering .

Execute the following command and enter the following information , It means a CLI It was created successfully . Here I choose to use Typescript To create CLI, After all 2020 Years. , No more Typescript It's a little hard to say .

$ cd big-lint
$ ./bin/run
hello world from ./src/index.js!
$ ./bin/run --help
Describe the command here

describe the command here

USAGE
  $ big-lint [FILE]

OPTIONS
  -f, --force
  -h, --help       show CLI help
  -n, --name=name  name to print
  -v, --version    show CLI version

tips: What needs to be noted here is , It's for you CLI When you name it , It can be executed npm view packageName To see if the name has been occupied by others , Because we developed CLI The tool will eventually be released to npm Upper , If the name is occupied, it cannot be published , It will be more troublesome to change it then .

Now open package.json file , You can see the main Field :

{
    "main": "src/index.js",
}

main Field specifies an entry file , It's what we do $ ./bin/run The file executed when the command is executed .

src/index.js

import {Command, flags} from '@oclif/command'

class HappyLint extends Command {
  static description = 'describe the command here'

  static flags = {
    // add --version flag to show CLI version
    version: flags.version({char: 'v'}),
    help: flags.help({char: 'h'}),
    // flag with a value (-n, --name=VALUE)
    name: flags.string({char: 'n', description: 'name to print'}),
    // flag with no value (-f, --force)
    force: flags.boolean({char: 'f'}),
  }

  static args = [{name: 'file'}]

  async run() {
    const {args, flags} = this.parse(HappyLint)

    const name = flags.name || 'world'
    this.log(`hello ${name} from ./src/index.ts`)
    if (args.file && flags.force) {
      this.log(`you input --force and --file: ${args.file}`)
    }
  }
}

export = HappyLint

If you want to use big-lint Instead of ./bin/run command , It can be executed under the project directory npm link command , Then you can use it happily big-lint --help See the effect .

Come here , A simple CLI The tool is finished , Now let's start our topic , How to implement code detection .

How to realize code detection and code beautification

Detection tools

In order to ensure that each submission to git Standardization of warehouse code , Front end solutions generally use eslintprettierlint-staged coordination git hook and husky, stay git commit When you're done with the code eslint Specification verification . Next, you need to install the above dependent packages , And then in package.json File to configure :

{
  "lint-staged": {
    "src/**/*.js": [
      "eslint --fix --ext .js",
      "prettier --write",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
}

big-lint The meaning of existence is to simplify such a process , Omit complicated configuration .

Build your own CLI

Installation dependency

Now? cd To what we created before big-lint In Engineering , Execute the following command in the root directory , Install the appropriate package : $ yarn add husky eslint prettier lint-staged eslint-config-airbnb eslint-config-airbnb-base eslint-config-airbnb-typescript eslint-config-prettier eslint-formatter-pretty eslint-plugin-babel eslint-plugin-compat eslint-plugin-eslint-comments eslint-plugin-import eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-unicorn -S

General train of thought

big-lint Two detection modes will be provided :

  • Test the submitted code
  • By designation dir Parameters , Detect the code under the specified path

Parameter description :

big-lint [options] [dir]

Arguments:
dir                        Specify the path of verification ( Support node Of glob grammar )                     [string]

Flags:
--staged, -S              only lint git staged files                          [boolean] [default: false]
--prettier, -p            format code with prettier                           [boolean] [default: false]
--eslint, -e              enable lint javascript                              [boolean] [default: false]
--fix, -f                 fix all eslint and stylelint auto-fixable problems  [boolean] [default: false]
--format, -F              output format of console                            [string]  [default: stylish]
--cwd, -c                 current working directory                           [default: process.cwd()]

Test the submitted code

adopt --staged Parameters , To determine whether only the submitted code is currently detected , The way to achieve this is through lint-stagedhuskyeslint. Here's how to do it in code :

Definition OPTIONS

src/utils/options.ts

import {flags} from '@oclif/command'

const flagConfig = {
  staged: flags.boolean({
    char: 'S',
    default: false,
    description: 'only lint git staged files',
  }),
  prettier: flags.boolean({
    char: 'P',
    default: false,
    description: 'format code with prettier',
  }),
  eslint: flags.boolean({
    char: 'e',
    default: false,
    description: 'enabel lint javascript',
  }),
  fix: flags.boolean({
    char: 'f',
    default: false,
    description: 'fix all eslint and stylelint auto-fixable problems',
  }),
  cwd: flags.string({
    char: 'c',
    default: process.cwd(),
    description: 'current working directory',
  }),
  format: flags.string({
    char: 'F',
    default: 'stylish',
    description: 'output format of console',
  }),
}

export default flagConfig

Console line execution :

$ big-lint --help

You can see the following information , Explain what we set up flags It works :

 testing js(eslint)

USAGE
  $ big-lint [DIR]

ARGUMENTS
  DIR   Specify the path 

OPTIONS
  -F, --format=format  [default: stylish] output format of console
  -P, --prettier       format code with prettier
  -S, --staged         only lint git staged files
  -c, --cwd=cwd        [default: /Users/Documents/big-lint] current working directory
  -e, --eslint         enabel lint javascript
  -f, --fix            fix all eslint and stylelint auto-fixable problems

modify src/index.js Under the run Method , according to staged Parameters to determine which logic to follow .

async run() {
  const {args, flags} = this.parse(HappyLint)
  const {staged, ...rest} = flags
  const {dir} = args

  if (staged) {
    await this.lintStaged(rest)
  } else {
    await this.lint({...rest, dir})
  }
}

Let's take a look at lintStaged Code for :

async lintStaged(flags: any) {
  const {prettier, eslint, fix, format} = flags
  getEslintConfig()

  let eslintCommon = fix ? `${eslintPath} --fix` : eslintPath

  //  Add formatted output 
  if (format !== 'stylish') {
    eslintCommon = `${eslintCommon} -f ${format}`
  }

  const lintstagedrc = {
    ...(prettier && {
      '*.{js,jsx,ts,tsx,less,scss,sass,css}': [
          `${prettierPath} --write`,
          'git add',
        ],
    }),
    ...(eslint && {
        '*{.js,.jsx,.ts,.tsx}': [
          eslintCommon,
          'git add',
        ],
       }),
    }
    const rcPath = join(__dirname, '.lintstagedrc.json')
    writeFileSync(rcPath, JSON.stringify(lintstagedrc))

    try {
      const child = spawn(lintStagedPath, ['-c', rcPath], {stdio: 'inherit'})
      child.on('close', (code: any) => {
        process.exit(code) // eslint-disable-line
      })
    } catch (error) {

    }
}

getEslintConfig Method will first judge whether there is a default in the project root path .eslintrc.js.prettierrc.editorconfig The configuration file , If not, a configuration file will be automatically generated by default and placed in the project root directory .

It's used here cross-spawn To call... Under the project root path node_modules/.bin/lint-staged Carry out orders , spawn The second argument to is an array , Pass in the parameters for executing the command , -c The parameter specifies lint-staged Ordered Profile path .

spawn Function returns a child process , When the child process stdio Triggered when the stream has been closed close event , We need to monitor this event , because lint-staged Failed the inspection , We need to implement process.exite(code) Method to kill the process .

Come here , lintStaged The logic is over , Now you can test the effect .

Find a test project , stay package.json The file is configured as follows :

{
    "husky": {
        "hooks": {
          "pre-commit": "big-lint --eslint --staged"
        }
    },
}

Add... Under this project .eslintrc.js The configuration file , perform git add, git commit -m 'test', If there is eslint Report errors , You can see the following error message .

Next, let's look at lint Code for :

async lint(flags: any) {
    const {dir, cwd, prettier, eslint, fix, format} = flags
    if (dir === undefined) {
      this.error('please specify a path to lint')
    }
    //  Support multi-path , Separated by commas 
    let filePath: any

    if (dir.split(',').length !== 0) {
      filePath = dir.split(',')
    } else {
      filePath = dir
    }

    const allFiles = getFiles(filePath, cwd)
    try {
      if (eslint) {
        getEslintConfig()
        const eslintExtensions = ['.js', '.jsx', '.ts', '.tsx']
        const files = allFiles.filter(item => endsWithArray(item, eslintExtensions))
        if (files.length > 0) {
          let args = fix ? ['--fix', ...files] : [...files]
          args = format !== 'stylish' ? ['-f', format, ...args] : [...args]
          spawn.sync(eslintPath, args, {stdio: 'inherit'})
        }
      }

      if (prettier) {
        const prettierExtensions = ['.js', '.jsx', '.ts', '.tsx', '.css', '.less', '.scss', '.sass']
        const files = allFiles.filter(item =>
          endsWithArray(item, prettierExtensions),
        )
        if (files.length > 0) {
          spawn.sync(prettierPath, ['--write', ...files], {stdio: 'inherit'})
        }
      }
    } catch (error) {
      this.error(error)
    }
  }

adopt getFiles Method to get the data under the specified path node_modules All under js file , Check the code .

src/utils/utils.ts

export const getFiles = (patterns: any, cwd: any) => {
  const result = globby
  .sync(patterns, {
    gitignore: true,
    ignore: ['**/node_modules/**', '.git'],
    onlyFiles: true,
    dot: true,
  })
  .map((item: any) => {
    // ignore  Packages must use relative paths 
    return path.relative(cwd, item)
  })

  return ignore()
  .add(getIgnores(cwd))
  .filter(result)
}

In the test project pacakge.json The file is configured as follows :

{
    "scripts": {
        "lint": "big-lint --eslint 'src/'",
        "prettier": "big-lint --prettier 'src/'",
        "fix": "big-lintpy-lint --eslint --fix 'src/'"
      },
}

perform npm run lint command , It can be done to src Code in the directory eslint Rule checking , perform npm run prettier It can be done to src The code in the directory prettier Code beautification , perform npm run fix It can be done to src Automatic code repair of the code under the directory .

Publish your own CLI Tools

Release your own npm package , First of all npm Register one on the official website npm account number , Enter into CLI Directory , perform $ npm login, Fill in the login information .

modify package.json In the document keywords Field , Here you need to fill in the npm Package keyword information , If you want your npm If the package is searched and used by more people ,keywords Fields need to be described as accurately as possible .

name Fields and version Field is a required field ,name Only one , It can't be used by others ,homepage The field is yours npm Home page of package , Because of my npm The package is open source , So here you fill in github Address .

After the above information is confirmed to be correct , perform $ npm run prepack command , Re execution npm publish The order can be done npm The release of the package .

github Address

copyright notice
author[User 4669777],Please bring the original link to reprint, thank you.
https://en.chowdera.com/2022/131/202205102047501479.html

Random recommended