“This is my 33rd day of participating in the First Challenge 2022. For details: First Challenge 2022.”

The background,

I wish you to become a distinguished Audi owner!

Write a command line tool Joymax query a platform development environment SMS verification code; Finally, we prepare the technical points to realize these functions:

  1. package.jsonbinField with go global install;
  2. Command design andcommanderOrganize commands and parse command parameters;
  3. esbuildPackage script;
  4. Copy to clipboardpbcopyCommand;

Second, the joymax. Ts

import { Command } from 'commander';

const program = new Command();

program.version(require('.. /package').version)
 .usage('<command> [options]')
 .command('q'.'get challenge sms code from xxplatform') // joymax q
 .command('add'.'add some alias:phone to config.json') // joymax add
 .command('ccfg'.'modify xxxx config') // joymax ccfg
 .command('ls'.'list cfg.json in path ~') // joymax ls
 .parse(process.argv)
Copy the code

This file serves as the command entry, in which the q, CCFG, ls commands are registered. The main work is as follows:

  1. withcommanderInitialize theprogram;
  2. throughprogram.versionregisteredjoymax --version.
  3. throughprogram.commandRegisters subcommands, the first argument being command, i.eJoymax Q, Joymax Add, JOYmax CCFG, joymax LSThe second argument is a prompt about the command.
  4. callpromgram.parseParse process parameters;

Third, joymax – q.t s

import { cfgJsonObj } from './cfg'; // Read the config configuration file

import { runCommand } from './utils'; // Run system commands

import querySmsCodeInfo from './fetch'; // Send a network request to obtain the SMS verification code

import chalk from 'chalk'; 

// Initialize program
import { Command } from 'commander';
const program = new Command();

program
 .usage('[phone or phone alias]') // Describe the usage
 .description('lookup sms challenge code and write to Clipboard, just CMD + V; ') // Describe the function of the command
 .argument('<string>'.'phone number or a alias of phone') Joymax q parameter
 .option('-z, --alipay'.'lookup alipay challenge code from ') // Command option -z
 .on('--help'.() = > {
  // Listen for --help and call joymax q --help to execute the callback
  console.log()
  console.log(' Examples:')
  console.log()
  console.log(chalk.gray(' # looking up by phone alias, added by: $ joymax add -n [phoneName] -p [phoneN]'))
  console.log(' $ joymax q someAlias')
  console.log()
  console.log(chalk.gray(' # looking up by phone number: 00016001234'))
  console.log(' $ joymax q 00016001234')
  console.log()
 }).parse(process.argv);

// program.opts() gets options
let { alipay: isAlipay } = program.opts();
let [ str = ' ' ] = program.args; // Get command parameters

// async IIFE to use await and return ; (async() = > {let code;

// If parameter
 if (/^\d{11}$/.test(str)) { 
  // The parameter is the mobile phone number, and the verification code is directly transmitted
  code = await querySmsCodeInfo(str, isAlipay)
 } else {
  // Otherwise, it is an alias
  let p = cfgJsonObj.alias[str];
  code = (p ? await querySmsCodeInfo(p, isAlipay) : ' ')}let data = code ? code.data : null
 
 if (data.errno === 0) {
  let xcode = data.result[0];
  // Run commands to write to the MacOS clipboard
  await runCommand(`echo ${xcode} | pbcopy`.null);
  // Otherwise output to the command line
  console.log(` challenge code"${xcode}】 From XXX has been copied, just CMD+V ')}else {
  console.error('challenge code failed!!!! ');
  console.log(data)
 }
})();
Copy the code

This file implements the TS file of the Joymax Q command. The main logic is as follows:

  1. Read localconfig.jsonThe file contains necessary configurations for accessing the SMS verification code platform and aliases for user configurations: mobile phone number mapping information;
  2. withcommanderInitialization commandprogramAnd then callUseage, description,Method Describes the usage and purpose of a command
  3. callprogram.argumentThe statementjoymax q xxxParameters,xxxReferred to asqCommand parameters, which will be saved in theprogram.argIn this array, there are multiple parameters
  4. callprogram.optionThe statementqCommand options,-z--alipay.-zIs short,--alipayIt is written in full and will be used when accessing this parameter last timealipayAs akeyAccess; Call later if you want to get the optionprogram.opts()Method to retrieve command execution options;-zIt is because our business has a login scene in Alipay, and the verification code is passed at this timeopen-apiThe parameters of are slightly different;
  5. Different processing is performed according to different parameters to support the scenario that the mobile phone number and its corresponding alias are passed in at the same time. If the match is11Digit check directly, otherwise when alias processing from the configuration to get the phone number;
  6. usepbcopyWrite the verification code to the clipboard, and output the verification code to the command line, which is convenient for copying from the command line when writing fails.

Four, joymax – add ts

// Get the CFG. Json configuration, which has been refreshed
import { cfgJsonObj, loadCfgJSON } from './cfg';
import { Command } from 'commander';
import chalk from 'chalk';

const program = new Command();

program
 .usage('[phoneAlias:phone]')
 .description('add a alias:phone to cfg.json')
 .option('-n, --name <char>'.'alias for the phone') / / - n alias
 .option('-p, --phone <char>'.'phone number') // -p Phone number
 .on('--help'.() = > {
  / / -- help output
  console.log()
  console.log(' Examples:')
  console.log()
  console.log(chalk.gray(' # add a alias:phone to cfg.json'))
  console.log(' $ joymax add -n someAlias -p 00016001234')
  console.log()
 }).parse(process.argv);

// Get the value of the option with the full name, i.e. --name, --phone
let { name, phone } = program.opts()


if (cfgJsonObj.alias[name]) {
 // The same alias cannot be set repeatedly
 console.error(`${name} existed already, it's value is :${cfgJsonObj.alias[name]}`)}else {
 // Verify the mobile phone number format
 if (/^\d{11}$/.test(phone)) {
  cfgJsonObj.alias[name] = phone
  loadCfgJSON(cfgJsonObj); // loadCfgJson updates the cfg.json file by passing in a new configuration object
  console.log(`${name} has been added to cfg.json`);
 } else {
  console.error('phone number should be tested by /^\d{11}$/')}}Copy the code

Implementation of joymax add command ts file, the main logic is as follows:

  1. To obtaincfg.jsonAnd updatecfg.jsonThe method ofloadJsonObj;
  2. Gets the alias from the optionnameAnd phonephone, used tocfg.jsonTo add the configuration;
  3. Handles duplicate aliases and updatescfg.json;

Fifth, joymax – CCFG

import { loadCfgJSON, cfgJsonObj } from './cfg';

import { Command } from 'commander';
import chalk from 'chalk'

const program = new Command();

program
 .usage('[xxxKey][xxxxValue]')
 .description('modify armid/dev/appid/zappid/key xxxx options')
 .option('-a, --armid <char>'.'xxxx armid')
 .option('-d, --dev <char>'.'xxxxx dev')
 .option('-a, --appid <char>'.'xxxx appid')
 .option('-z, --zappid <char>'.'xxxx alipay appid')
 .option('-k, --key <char>'.'xxxx key')
 .on('--help'.() = > {
  console.log()
  console.log(' Examples:')
  console.log()
  console.log(chalk.gray(' # modify armid/dev/appid/zappid/key xxxxx options'))
  console.log(' $ joymax someAlias')
  console.log()
 }).parse(process.argv);

// Get options
let options = program.opts()

Object.keys(options).forEach((fg) = > {
 / / update the CFG. Json
 cfgJsonObj[fg] = options[fg]
})

// Write cfg.json and refresh the cfgJsonObj object
loadCfgJSON(cfgJsonObj)
Copy the code

The implementation of joymax CCFG command, which is used to modify the configuration of cfg.json for verification code open API, the verification code platform is common to the group, and our team has different business, so it needs to reuse, to reuse these parameters have to accept configuration; This part does not say much, if there is a need to do, do not need to ignore it;

Six, joymax – ls. Ts

This file is the ts file that implements the joymax ls command:

import { cfgJsonObj } from './cfg'; import { Command } from 'commander'; import chalk from 'chalk'; const program = new Command(); program .usage('[config]') .description('list cfg.json') .on('--help', () => { console.log() console.log(' Examples:') console.log() console.log(' $ joymax ls') console.log() }) Parse (process.argv) // Simply output cfg.json to the command line console.log(json.stringify (cfgJsonObj, null, 2));Copy the code

7. Configuration file problems

In the last post, we mentioned a question:

The problem with cfg.json, where we put the configuration file cfg.json in the package directory, obviously has a fatal problem;

After the upgrade, when the user installs the package locally, the CFG. Json of the new package will overwrite the CFG. Json of the user’s previous installation, resulting in the loss of the user’s configuration, such as alias and open-API parameters.

How do you solve this? Look at the official thinking 2s;

/Users/ XXX is the home directory of MacOS, XXX is the user name. If there is a home directory, please merge it. If there is a local directory, it has a higher priority. If the package is not in the home directory, it indicates that this is the first installation, this time to copy the package directory CFG. Json home directory is good;

So what’s the good of that? In this way, the user’s configuration will not be lost when the user upgrades the package. In addition, if the configuration items are added later in the new package, the user only needs to upgrade the package version and does not need to pay attention to these configurations.

So how do you do that?

7.1 npm hooks

Does it say that configuration files should be handled after installation? What ❓❓❓ how do I know when….

In fact, NPM provides this capability — Life Cycle Scripts, which have to be 🌈 to brag about design, is a kind of hook like existence, We just need to set a postinstall script in the scripts of package.json for this package;

Postinstall is called after the package is installed, and preinstall is called before the package is installed. So we add this to package.json:

{

  "scripts": {
    "test": "bin/joymax.js"."build": "./esbiubiu"."postinstall": "./mergeCfg" // This is it
  },
  "bin": {
    "joymax": "bin/joymax.js"
  },
  "keywords": [
    "challenge code"],}Copy the code

7.2 mergeCfg script

Create a new js file in the root directory of the package and add executable permissions to it:

$ touch mergeCfg.js
$ chmod +x mergeCfg.js
Copy the code
  • mergeCfg.jsThe script content is as follows:
#! /usr/bin/env node
const path = require('path');
const fs = require('fs').promises;
const os = require('os');
// const cp = require('child_process');

const CFG_NAME = 'cfg.json';

// Get the User's home directory on the Macbook, such as /User/Mr. Right
const HOME_PATH = os.homedir() || process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;

// Splice the configuration file path in the home directory
const HOME_CFG_PATH = path.join(HOME_PATH, CFG_NAME);

// The configuration file path in the package directory
const PKG_CFG_PATH = path.resolve(__dirname, CFG_NAME);

const CHARSET = 'utf8'

// async IIFE to use await & return; (async() = > {let homeCfgStat;
  let homeCfgJSON;
  let alreadyHome;
  let finalCfg;
  let homeCfgJSONString;
  try {
    // Check whether there are configuration files in the user's home directory
    // If yes, read
    homeCfgStat = await fs.stat(HOME_CFG_PATH);
    alreadyHome = homeCfgStat.isFile()
    homeCfgJSONString = await fs.readFile(HOME_CFG_PATH, CHARSET)
    homeCfgJSON = homeCfgJSONString ? JSON.parse(homeCfgJSONString) : {}
  } catch (e) {
    // fs.stat does not exist
    homeCfgJSONString = await fs.readFile(PKG_CFG_PATH, CHARSET)
    homeCfgJSON = homeCfgJSONString ? JSON.parse(homeCfgJSONString) : {}
  }

  homeCfgJSONString = null

  const pkgCfgJSON = JSON.parse(await fs.readFile(PKG_CFG_PATH, CHARSET));

  if (alreadyHome) {
     // exist to merge
     // merge, the priority local is higher
    finalCfg = Object.assign({}, pkgCfgJSON, homeCfgJSON)
  } else {
    // Copy the home directory if it does not exist
    // copy to ~
    finalCfg = pkgCfgJSON
  }

  // Write to home directory
  await fs.writeFile(HOME_CFG_PATH, JSON.stringify(finalCfg, null.2));

  // cp.exec(`chmod ug +w ${HOME_CFG_PATH}`);

  console.log('MERGE CFG.JSON FINISHED!!!! '); }) ();Copy the code

Eight, summary

This essay completes all the previous instructions, let’s review:

  1. package.json binFields are guaranteed to be implemented during global installationjoymax;
  2. commanderregisteredQ, LS, ADD, CCFGCommands, parsing parameters, options;
  3. npmLifecycle scriptpostinstallHandle the relationship between user local configuration and configuration files in the package to ensure that user configuration is not lost.

This is an Intranet tool, no open source, please forgive me, but the source code has been posted; Some people say that your company does not have SMS verification code open-API?

Opportunity is not come, as long as can check the verification code, write a service is not on the line, this performance is not up, roll up!

Query captcha with an alias:

Finally, I wish you to become a distinguished Audi owner!