CLI

็Žฐๅœจๅ‰้ข

ๆœฌๆ–‡ๆ˜ฏไฝœ่€…ๅœจๆญๅปบ CLI ๆ—ถ็š„ไธ€ไบ›็ฌ”่ฎฐ ้€‚ๅˆๆƒณ่ฆๅญฆไน  CLI ๆญๅปบ็š„่Œๆ–ฐไปฌไบ†่งฃๆ•ดไธชๆต็จ‹

CLI ็š„ๆญๅปบๅ…ถๅฎžๅนถไธๅคๆ‚ ๅ› ไธบๆ ธๅฟƒๅŠŸ่ƒฝๅœจ npm ไธŠๅทฒ็ปๆœ‰ไบ†ๅพˆๅคšๆˆ็†Ÿ็š„ๆจกๅ—ไบ†

ไฝœ่€…ๅฎž็Žฐ็š„ CLI ๆ˜ฏไธ€ไธชๅธฆๆจก็‰ˆ็ผ–่ฏ‘็š„็ฑปไผผ CRA ็š„ๅทฅๅ…ท(ๅฝ“็„ถๆ˜ฏๆœ€ๅŸบ็ก€็š„ๅŠŸ่ƒฝ ๐Ÿ˜ฌ)

ๅ› ไธบๆ˜ฏๅœจ node ็Žฏๅขƒไธ‹่ฟ่กŒ็š„้กน็›ฎ ๆ‰€ไปฅไธ‹ๆ–‡็š„ demo ไฝฟ็”จ็š„้ƒฝๆ˜ฏ cjs ๆจกๅ—

ๅฅฝๅ•ฆ CLI ๅ…ถๅฎžๆฒกๆœ‰ไป€ไนˆ้ป‘้ญ”ๆณ• ๆˆ‘ไปฌๅผ€ๅง‹ๅง๏ฝž๏ฝž๏ฝž ๐Ÿ˜๐Ÿ˜๐Ÿ˜

้ข„่งˆๅ›พๅ’Œไปฃ็ ๅฆ‚ไธ‹ ๐Ÿ‘‡๐Ÿ‘‡๐Ÿ‘‡

Preview

ไป“ๅบ“ๅœฐๅ€ https://github.com/LuckyChou710/code-traveling/tree/main/09-node-training-camp/cli

ไธ‹ๆ–‡่ฏดๅˆฐ็š„YepGym xxxๅฐฑๆ˜ฏๅœจๆ‰ง่กŒ CLI

ๅทฅๆฌฒๅ–„ๅ…ถไบ‹๏ผŒๅฟ…ๅ…ˆๅˆฉๅ…ถๅ™จ

ๅทฅๆฌฒๅ–„ๅ…ถไบ‹๏ผŒๅฟ…ๅ…ˆๅˆฉๅ…ถๅ™จ ้ฆ–ๅ…ˆๅธฆๅคงๅฎถไบ†่งฃไธ€ไบ› CLI ๅˆถไฝœๅฟ…ๅค‡็š„ npm ๅŒ…

ไปฅไธ‹็š†ไธบๆœ€ๅŸบ็ก€็š„ไฝฟ็”จๆ–นๆณ• ๅ…ทไฝ“็š„็”จๆณ•ๅฏๅŽปๅฏนๅบ”็š„ๅฎ˜็ฝ‘ๆŸฅ็œ‹

PS๏ผšๆœ‰ๅพˆๅคšๅŒ…ๅทฒๆ›ดๆ–ฐๆˆไบ† ESM ๆจกๅ— ๅฆ‚ๆžœ่ฆไฝฟ็”จ CJS ๆจกๅ—็š„่ฏ ่ฏทๅฎ‰่ฃ…ๅฏนๅบ”็š„็‰ˆๆœฌ

commander

https://www.npmjs.com/package/commander

ๅˆ’้‡็‚น ๐Ÿ“ commander ๆ˜ฏ CLI ๆญๅปบไธญ็š„ไธ€ไธชๆœ€ๆ ธๅฟƒ็š„ๅŒ…

ๅฎƒๅฏไปฅไธบไฝ ็š„ CLI ๅ‘ฝไปคๆณจๅ…ฅๅพˆๅคšๅ‚ๆ•ฐ ไพ‹ๅฆ‚

  • YepGym create xxx ๅฏไปฅๅˆ›ๅปบไธ€ไธชๆจก็‰ˆ

  • YepGym help ๅฏไปฅ่Žทๅ– CLI ็š„ๆ‰€ๆœ‰ๅ‘ฝไปค่ฏดๆ˜Ž

  • ......

Options:
  -V, --version   output the version number
  -h, --help      display help for command

Commands:
  create|c        create a project
  help [command]  display help for command

้€š่ฟ‡ๅผ•ๅ…ฅcommander่ฟ™ไธชๅบ“ ๅฐฑๅฏไปฅๅฟซ้€ŸๅธฎๅŠฉๆˆ‘ไปฌๅฎž็Žฐ่ฟ™ไธชๆ•ˆๆžœ

const { program } = require('commander');

program
  .command('create')
  .alias('c')
  .description('create a project')
  .action(() => {
    // do something
  });

program.parse();

inquirer or prompts

https://www.npmjs.com/package/inquirer

https://www.npmjs.com/package/prompts

่ฟ™ไธคไธชๅบ“้ƒฝๅฏไปฅๅœจๅ‘ฝไปค่กŒ็”Ÿๆˆไธ€ไบ›ๅฏไพ›็”จๆˆท้€‰ๆ‹ฉ็š„ไบคไบ’

ไพ‹ๅฆ‚

? Select Compiler (Press <space> to select, <a> to toggle all, <i> to invert sel
ection, and <enter> to proceed)
โฏโ—ฏ Babel
 โ—ฏ TypeScript
const inquirer = require('inquirer');

inquirer
  .prompt([
    {
      type: 'checkbox',
      message: 'Select Compiler',
      name: 'compiler',
      choices: [
        {
          name: 'Babel',
        },
        {
          name: 'TypeScript',
        },
      ],
      validate(answer) {
        if (answer.length < 1) {
          return 'You must choose at least one topping.';
        }

        return true;
      },
    },
  ])
  .then((answers) => {
    console.log(JSON.stringify(answers));
  });

้€š่ฟ‡ๆ”นๅ˜็ฌฌไธ€ไธช type ๅฑžๆ€ง ๆˆ‘ไปฌๅฏไปฅ่ฎพ็ฝฎๆˆ input / list / ......

download-git-repo

https://www.npmjs.com/package/download-git-repo

็›ดๆŽฅไธŠไปฃ็ ๅง ๐Ÿ˜›

const download = require('download-git-repo');

download('YepGym/react-v17-template', 'test/tmp', { clone: false }, (err) => {
  console.log(err ? 'Error' : 'Success');
});

ไธŠ่ฟฐไปฃ็ ๅฐฑไผšๅœจๅฝ“ๅ‰็›ฎๅ‰ไธ‹็š„ test/temp ไธ‹ๆ‹‰ๅŽป่ฟœ็จ‹ไป“ๅบ“ YepGym/react-v17-template ไธ‹็š„ไปฃ็ 

ๆ›ดๅคš็š„็”จๆณ•ๅฏไปฅๅ‚่€ƒไธŠ่ฟฐๆ–‡ๆกฃ

ejs

https://ejs.bootcss.com/

https://www.npmjs.com/package/ejs

ejs ๆ˜ฏไธ€็ง็ฎ€ๆด็š„ๆจก็‰ˆ่ฏญๆณ• ไพ‹ๅฆ‚ ๆˆ‘ไปฌๆœ‰ไธ€ไปฝ package.json

{
  "name": "<%= projectName %>",
  "description": "<%= projectDescription %>",
  "author": "<%= projectAuthor %>",
  "version": "1.0.0",
  "main": "index.js"
}

้‡Œ้ขๆœ‰ไธ€ไบ›็ฑปไผผๅ˜้‡็š„ๅœฐๆ–น ็”จ ejs ่ฏญๆณ•่ฟ›่กŒๅ ไฝ

้€š่ฟ‡ ejs ็š„็ผ–่ฏ‘ ๅฐฑๅฏไปฅๅฐ†่ฟ™ไธชๅ˜้‡ๅกซๅ……่ฟ›ๅŽป ็”Ÿๆˆๆœ€็ปˆ็š„ๆ–‡ไปถ

็”จๆณ•ๅฆ‚ไธ‹

let template = ejs.compile(str, options);
template(data);
// => ่พ“ๅ‡บๆธฒๆŸ“ๅŽ็š„ HTML ๅญ—็ฌฆไธฒ

ejs.render(str, data, options);
// => ่พ“ๅ‡บๆธฒๆŸ“ๅŽ็š„ HTML ๅญ—็ฌฆไธฒ

ejs.renderFile(filename, data, options, function (err, str) {
  // str => ่พ“ๅ‡บๆธฒๆŸ“ๅŽ็š„ HTML ๅญ—็ฌฆไธฒ
});

็ป“ๅˆไธŠๆ–‡็š„ demo ๆˆ‘ไปฌ้œ€่ฆ็ผ–่ฏ‘ package.json ็”จๆณ•ๅฆ‚ไธ‹

const ejs = require('ejs');
const fs = require('fs');

const projectName = 'chou-cli';
const projectDescription = 'description ......';
const projectAuthor = 'chou';

ejs
  .renderFile('./package.json', {
    projectName,
    projectDescription,
    projectAuthor,
  })
  .then((res) => {
    fs.writeFile('./package.json', res, (err) => throw err);
  });

ejs.renderFile ๅฏไปฅ่ฏปๅ…ฅไธ€ไธชๆ–‡ไปถ ็„ถๅŽ็ฌฌไบŒไธชๅ‚ๆ•ฐๅฏไปฅไผ ้€’ๆจก็‰ˆ้‡Œ้œ€่ฆ็š„ๅ˜้‡

github api

https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api

github ไธบๆˆ‘ไปฌๆไพ›ไบ† restful api ไพ‹ๅฆ‚ไป“ๅบ“็š„่Žทๅ– ็”จๆˆทไฟกๆฏ็š„่Žทๅ–็ญ‰็ญ‰

ๅ…ทไฝ“็š„ๆ–‡ๆกฃๅฏไปฅๅ‚่€ƒๅฎ˜ๆ–นๆ–‡ๆกฃ

้”ฆไธŠๆทป่Šฑ

chalk

https://www.npmjs.com/package/chalk

chalk ๆ˜ฏ็ฒ‰็ฌ”็š„ๆ„ๆ€ ้กพๅๆ€ไน‰ ๅฎƒๅฏไปฅ่ฎฉๆˆ‘ไปฌ็š„่พ“ๅ‡บ(console)ๅธฆไธŠ้ขœ่‰ฒ

็›ดๆŽฅ้€š่ฟ‡ chalk.็š„ๆ–นๆณ•ๅฐฑๅฏไปฅไธบไฝ ็š„ console ๆณจๅ…ฅ้ขœ่‰ฒๅ•ฆ๏ฝž๏ฝž

const chalk = require('chalk');

console.log(chalk.blue.bold('Hello Chou!'));
console.log(chalk.blue.bold.bgWhiteBright('Hello Chou!'));

boxen

https://www.npmjs.com/package/boxen

ไพ‹ๅฆ‚ๅฝ“ๆŸไธช npm ๅŒ…ๆœ‰ไบ†ๆ–ฐ็š„็‰ˆๆœฌ้œ€่ฆๅ‡็บงๆ—ถ ๆˆ–่€…ๆˆ‘ไปฌๅฏๅŠจไธ€ไธช้กน็›ฎๆ—ถ ็ปๅธธไผšๅ‘็Žฐไธ‹้ข็š„ๆ•ˆๆžœ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                             โ”‚
โ”‚    New version available 4.5.13 โ†’ 5.0.1     โ”‚
โ”‚   Run yarn global add @vue/cli to update!   โ”‚
โ”‚                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
const boxen = require('boxen');
const chalk = require('chalk');

const upgradeMessage = `New version available ${chalk.magenta(
  '4.5.13'
)} โ†’ ${chalk.green('5.0.1')} \nRun ${chalk.yellow(
  'yarn global add @vue/cli'
)} to update!`;

console.log(
  boxen(upgradeMessage, {
    align: 'center',
    borderColor: 'green',
    dimBorder: true,
    padding: 1,
  })
);

ora

https://www.npmjs.com/package/ora

่ฟ™ไธชๅบ“ๅฏไปฅ็”Ÿๆˆไธ€ไธช loading ็š„ spinner

โ  Fetching...

ๅฝ“ๆˆ‘ไปฌๆ‰ง่กŒไธ€ไบ›่€—ๆ—ถ็š„ๆ“ไฝœๆ—ถ ไพ‹ๅฆ‚ api ่ฏทๆฑ‚ๆ—ถ ๅฐฑๅฏไปฅๅธฆไธŠ่ฟ™ไธชๅบ“่ฎฉ็”จๆˆทๆœ‰ๆ›ดๅฅฝ็š„ไฝ“้ชŒ

const ora = require('ora');

const spinner = ora('Fetching...');

spinner.start();

setTimeout(() => {
  spinner.succeed('success');
}, 2000);

figlet

https://www.npmjs.com/package/figlet

ไพ‹ๅฆ‚่ฟ™ไธชๅบ“ ๆˆ‘ไปฌๅฏไปฅๅœจ็ปˆ็ซฏๆ‰“ๅฐๅ‡บๅพˆๅฅฝ็œ‹็š„ๆ–‡ๅญ—ๆ•ˆๆžœ ไพ‹ๅฆ‚ๆ–‡็ซ ๅผ€ๅคด้ข„่งˆๅ›พ็š„ๆ•ˆๆžœ

 __    __              ____                        __
/\ \  /\ \            /\  _`\                     /\ \
\ `\`\\/'/  __   _____\ \ \L\_\  __  __    ___ ___\ \ \
 `\ `\ /' /'__`\/\ '__`\ \ \L_L /\ \/\ \ /' __` __`\ \ \
   `\ \ \/\  __/\ \ \L\ \ \ \/, \ \ \_\ \/\ \/\ \/\ \ \_\
     \ \_\ \____\\ \ ,__/\ \____/\/`____ \ \_\ \_\ \_\/\_\
      \/_/\/____/ \ \ \/  \/___/  `/___/> \/_/\/_/\/_/\/_/
                   \ \_\             /\___/
                    \/_/             \/__/
const figlet = require('figlet');

figlet.text(
  'YepGym!',
  {
    font: 'Larry 3D',
    horizontalLayout: 'default',
    verticalLayout: 'default',
    width: 80,
    whitespaceBreak: true,
  },
  (err, data) => {
    if (!err) console.log(data);
  }
);

figlet ๅ†…็ฝฎไบ†ๅพˆๅคš็ง font ๅฏไพ›้€‰ๆ‹ฉ

http://www.figlet.org/examples.html

ๅฏไปฅ้€š่ฟ‡่ฟ™ไธช็ฝ‘ๅ€ ๆŸฅ็œ‹ examples

็ป“ๅˆ chalk ๅ’Œ่ฏฅๅบ“ ๅฐฑๅฏไปฅๅฎž็Žฐไบ”้ขœๅ…ญ่‰ฒ็š„ logo sign ๅ•ฆ

clear

clear ๅฏไปฅ็”จๆธ…้™คไฝ ็š„ๅ‘ฝไปค่กŒ ๅŠŸ่ƒฝๅ’Œไฝ ็›ดๆŽฅๅœจ cmd / bash ่พ“ๅ…ฅ clear ็š„ๆ•ˆๆžœไธ€ๆ ท

ๅฝ“ๆˆ‘ไปฌๅœจๅฑๅน•ไธŠๆœ‰ๆฏ”่พƒๅคš็š„ไฟกๆฏ ๆƒณ่ฆๆธ…ไธ€ไธ‹ๅฑ็š„ๆ—ถๅ€™ ๅฐฑๅฏไปฅไฝฟ็”จ่ฟ™ไธชๅบ“

> https://www.npmjs.com/package/clear

const clear = require('clear');

clear();

validate-npm-package-name

https://www.npmjs.com/package/validate-npm-package-name

ๅœจ vue-cli ไธญ ๅฐฑๆ˜ฏๅผ•ๅ…ฅไบ†่ฟ™ไธชๅบ“ๅŽปๅˆคๆ–ญ่ฆๅˆ›ๅปบ็š„้กน็›ฎๅๆ˜ฏๅฆๅˆๆณ•

const validateProjectName = require('validate-npm-package-name');
const result = validateProjectName(' leading-space:and:weirdchars');
console.log(result);
// {
//   validForNewPackages: false,
//   validForOldPackages: false,
//   errors: [
//     'name cannot contain leading or trailing spaces',
//     'name can only contain URL-friendly characters'
//   ]
// }

YepGym

้ฆ–ๅ…ˆๆˆ‘ไปฌ่ฆๅš็š„ๅฐฑๆ˜ฏๅฐ†ๆˆ‘ไปฌ็š„ CLI ๅ‘ฝไปค ๅœจ็ปˆ็ซฏๆ‰ง่กŒ่ตทๆฅ

ๅœจ้กน็›ฎ็š„ package.json ไธญๆทปๅŠ  bin ๅญ—ๆฎต ้…็ฝฎไธŠไฝ ็š„ CLI ๅ‘ฝไปคๅ’Œๅฏนๅบ”็š„ๆ–‡ไปถ่ทฏๅพ„

  "bin": {
    "YepGym": "./bin/YepGym.js"
  },

ๆณจๆ„ โš ๏ธ๏ผš ่ฏฅๆ–‡ไปถ็š„ๆ–‡ไปถๅคดๅฟ…้กปๅŠ ไธŠ

#! /usr/bin/env node

่กจๆ˜Ž่ฏฅๆ–‡ไปถ่ฆไปฅ node ็š„็ŽฏๅขƒๅŽป่ฟ่กŒ

็„ถๅŽๅœจ้กน็›ฎ็›ฎๅฝ•ไธ‹ๆ‰ง่กŒ npm link ่ฟ™ไธชๅ‘ฝไปคๅฐฑไผš่ฝฏ้“พๆŽฅๅœจๅ…จๅฑ€็š„ bin ็›ฎๅฝ•ไธ‹

ๆ‰ง่กŒ npm list -g ๅฏไปฅ็œ‹ๅˆฐ

/opt/homebrew/lib
โ”œโ”€โ”€ nodemon@2.0.15
โ”œโ”€โ”€ npm@8.3.1
โ”œโ”€โ”€ nrm@1.2.5
โ””โ”€โ”€ yep-gym@1.0.0 -> package.jsonไธ‹้…็ฝฎ็š„ๆ–‡ไปถไฝ็ฝฎ

ๆญคๆ—ถ ๆˆ‘ไปฌๅœจๅ‘ฝไปค่กŒไธ‹ๆ‰ง่กŒ YepGym ๆ—ถ ๅฐฑไผšๅŽปๆ‰พๅˆฐๅฏนๅบ”็š„ๆ–‡ไปถไฝ็ฝฎ ๅนถไปฅ node ็Žฏๅขƒ่ฟ่กŒ่ฏฅๆ–‡ไปถ

cli ๆต็จ‹

ๆ•ดไธช้กน็›ฎ็ป“ๆž„ๅฆ‚ไธ‹ ไป“ๅบ“้“พๆŽฅ ๐Ÿ”— ๅœจๆ–‡็ซ ๅผ€ๅคด

ไปฃ็ ๆฏ”่พƒ็ฎ€ๅ• ๆ„Ÿๅ…ด่ถฃ็š„ๅฏไปฅ่‡ชๅทฑๆŸฅ็œ‹ ๐Ÿถ

YepGym-CLI
โ”œโ”€ .eslintrc.js
โ”œโ”€ README.md
โ”œโ”€ bin
โ”‚  โ””โ”€ YepGym.js
โ”œโ”€ lib
โ”‚  โ”œโ”€ commander.js
โ”‚  โ”œโ”€ create.js
โ”‚  โ”œโ”€ main.js
โ”‚  โ””โ”€ utils
โ”‚     โ”œโ”€ constants.js
โ”‚     โ”œโ”€ generator.js // ็ผ–่ฏ‘ๆจก็‰ˆๆ–‡ไปถ
โ”‚     โ”œโ”€ loadCustomPreset.js // ๅŠ ่ฝฝ็”จๆˆท่‡ชๅฎšไน‰้…็ฝฎ
โ”‚     โ”œโ”€ loadRemotePreset.js // ๅŠ ่ฝฝ่ฟœ็จ‹ไป“ๅบ“ๆจก็‰ˆ
โ”‚     โ”œโ”€ prompt.js // ็”จๆˆท่‡ชๅฎšไน‰ไฟกๆฏ่พ“ๅ…ฅ
โ”‚     โ”œโ”€ request.js  //  github api request
โ”‚     โ””โ”€ sign.js
โ”œโ”€ package.json
โ””โ”€ yarn.lock

ๅœจ่ฟ™้‡Œ ๅชๅšไธ€ไบ›ๆต็จ‹ไธŠ็š„่ฏดๆ˜Ž

  1. ้€š่ฟ‡ loadCustomPreset ๅŽปๅŠ ่ฝฝ็”จๆˆท็š„ไธ€ไบ›้…็ฝฎ

  2. ๆ นๆฎ้€‰ๆ‹ฉ็š„ไป“ๅบ“ ็”จ download-git-repoๅŽปๆ‹‰ๅ–ๅฏนๅบ”็š„ๆจก็‰ˆๅˆฐๆœฌๅœฐ

  3. ๅฆ‚ๆžœๆ˜ฏๅธฆๆจก็‰ˆ่ฏญๆณ•็š„ ้œ€่ฆ่ฟ›่กŒๆจก็‰ˆ่ฏญๆณ•็ผ–่ฏ‘็š„ ๅˆ™ๅ†่ฟ›ๅ…ฅ generator ๆ–นๆณ• ๅœจ่ฟ™้‡Œ ๆˆ‘ไฝฟ็”จ็š„ๆ˜ฏ ejs ๆจก็‰ˆ่ฏญๆณ•

Last updated

Was this helpful?