“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:
package.json
的bin
Field with go global install;- Command design and
commander
Organize commands and parse command parameters; esbuild
Package script;- Copy to clipboard
pbcopy
Command;
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:
- with
commander
Initialize theprogram
; - through
program.version
registeredjoymax --version
. - through
program.command
Registers subcommands, the first argument being command, i.eJoymax Q, Joymax Add, JOYmax CCFG, joymax LS
The second argument is a prompt about the command. - call
promgram.parse
Parse 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:
- Read local
config.json
The file contains necessary configurations for accessing the SMS verification code platform and aliases for user configurations: mobile phone number mapping information; - with
commander
Initialization commandprogram
And then callUseage, description,
Method Describes the usage and purpose of a command - call
program.argument
The statementjoymax q xxx
Parameters,xxx
Referred to asq
Command parameters, which will be saved in theprogram.arg
In this array, there are multiple parameters - call
program.option
The statementq
Command options,-z
和--alipay
.-z
Is short,--alipay
It is written in full and will be used when accessing this parameter last timealipay
As akey
Access; Call later if you want to get the optionprogram.opts()
Method to retrieve command execution options;-z
It is because our business has a login scene in Alipay, and the verification code is passed at this timeopen-api
The parameters of are slightly different; - 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 is
11
Digit check directly, otherwise when alias processing from the configuration to get the phone number; - use
pbcopy
Write 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:
- To obtain
cfg.json
And updatecfg.json
The method ofloadJsonObj
; - Gets the alias from the option
name
And phonephone
, used tocfg.json
To add the configuration; - Handles duplicate aliases and updates
cfg.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.js
The 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:
package.json bin
Fields are guaranteed to be implemented during global installationjoymax
;commander
registeredQ, LS, ADD, CCFG
Commands, parsing parameters, options;npm
Lifecycle scriptpostinstall
Handle 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!