Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.

Adjust engineering structure according to module division

  • Core module: Core
  • Command module: Command
  • Model module: Model
  • Tool module: Utils
HZW cli - dev ├ ─ ─ the command// Command module├ ─ ─ the core// Core module├ ─ ─ utils// Tool module├ ─ ─ models// Model module├ ─ ─ node_modules/ / rely on├─ Package-lock ├─ package-lock, ├─ package-lock, ├─ download.json// Lerna configuration file

Copy the code

Preparation phase Flow chart

import-local

// core\cli\bin\index.js
#! /usr/bin/env node

const importLocal = require('import-local')

// If there is a scaffolding command in node_modules in the current project, there is a scaffolding command in the global Node environment
// import-local takes precedence over the node_modules version of the project and prints the log
if (importLocal(__filename)) {
  require('npmlog').info('tip'.'Using the version of HZW -cli-dev in the current project')}else {
  // Use the scaffolding command under global
  require('.. /lib')(process.argv.slice(2))}Copy the code

Require supports loading module types.js/.json/.node.

When loading the.js module, you need to export using module.exports/exports.

When the.json module is loaded, json.parse is called to parse the module and an object is returned.

When the.node module is loaded, a c++ plug-in is used.

When a file module of any type is loaded, it will be executed as.js, and an error will be reported if the content is not JS code.

The log tool

Create the log package

// Use lerna to create packages
lerna create @hzw-cli-dev/log
// Use lerna to install dependencies for the log package
lerna add npmlog  utils/log
// Change the entry file name to index.js
// Change the main field in package.json to lib/index.js
// Add dependencies to core clipackage. json
"dependencies": {
    "@hzw-cli-dev/utils": "^" 1.0.1."@hzw-cli-dev/log": "^" 1.0.1
}
// Install local dependencies using lerna link (file:xx)
lerna link
Copy the code

Custom log

'use strict';

// Introduce the nPMlog module
const log = require('npmlog');

// Read log.level from the environment variable
// log.level takes effect only when the weight set by level is exceeded
log.level = process.env.LOG_LEVEL || 'info';

// Specify log level parameters: (name, weight, configuration)
log.addLevel('success'.2000, { fg: 'red'.bg: 'yellow'.bold: true });

// Customize the prefix of log
log.heading = 'hzw';
// Customize the style of the log prefix
log.headingStyle = { fg: 'blue'.bg: 'green'.bold: true };

module.exports = log;
Copy the code

Calls to the log

// core\cli\lib\index.js
// Introduce our packaged nPMlog tool
const log = require('@hzw-cli-dev/log');
log.success('test'.'success... ');
Copy the code

It looks like this, which is kind of fun, so you can customize your log to your liking

Checking the Version number

'use strict';

// Import the package.json file of the current scaffold
const pkg = require('.. /package.json');

// Introduce our packaged nPMlog tool
const log = require('@hzw-cli-dev/log');

/ * * *@description: Core method *@param {*}
 * @return {*}* /
function core() {
  // Check the version number
  checkPkgVersion();
}

/ * * *@description: Check the version version in package.json *@param {*}
 * @return {*}* /
function checkPkgVersion() {
  log.success('Reminder, the current version is :', pkg.version);
}

module.exports = core;
Copy the code

Checking the Node Version

  • Install a third-party library for version comparisonsemver
  • Install a library that defines the output colors of scaffoldingcolors
lerna add semver core/cli
lerna add colors core/cli
Copy the code

Define the lowest node version number

// core\cli\lib\const.js
const LOWEST_NODE_VERSION = '17.0.0';

module.exports = {
  LOWEST_NODE_VERSION,
};

Copy the code

Check whether the node version meets requirements

// core\cli\lib\index.js
'use strict';

// Introduce the version comparison library semver
const semver = require('semver');
// Introduce the color library colors
const colors = require('colors/safe');
// Import the package.json file of the current scaffold
const pkg = require('.. /package.json');
// Introduce our packaged nPMlog tool
const log = require('@hzw-cli-dev/log');
// Import the configuration file
const constant = require('./const');

/ * * *@description: Core method *@param {*}
 * @return {*}* /
function core() {
  try {
    // Check the version number
    checkPkgVersion();
    // Check the node version
    checkNodeVersion();
  } catch(error) { log.error(error.message); }}/ * * *@description: Check the current node version to prevent errors using the latest API *@param {*}
 * @return {*}* /
function checkNodeVersion() {
  // Get the current Node version
  const currentVersion = process.version;
  log.info('Note that the current node version is :', process.version);
  // Get the lowest node version
  const lowestVersion = constant.LOWEST_NODE_VERSION;
  // Compare the lowest node version
  if(! semver.gte(currentVersion, lowestVersion)) {throw new Error(colors.red('Error: Node version too old')); }}module.exports = core;
Copy the code

Results the following

Check the root account startup

Install the root-check library, specify the version, otherwise 2.0 is written with es module, error will be reported.

lerna add root-check@1.0. 0 core/cli/
Copy the code
// core\cli\lib\index.js
/ * * *@description: Checks the root account and automatically demotes *@param {*}
 * @return {*}* /
function checkRoot() {
  // Check root level and demote automatically
  const rootCheck = require('root-check');
  rootCheck();
}
Copy the code

Check the user home directory

Install the third-party library user-home to obtain the user home directory across operating systems.

Install the third-party library PAth-exists and check whether the file exists.

lerna add user-home core/cli/
lerna add path-exists@4.0. 0 core/cli/
Copy the code
// core\cli\lib\index.js
/ * * *@description: Check the user home directory *@param {*}
 * @return {*}* /
function checkUserHome() {
  // Import user-home to get the user home directory across the operating system
  const userHome = require('user-home');
  // Check whether the file exists
  const pathExists = require('path-exists').sync;
  // If the home directory does not exist, an exception is thrown
  if(! userHome || ! pathExists(userHome)) {throw new Error(colors.red('Current logon user home directory does not exist')); }}Copy the code

Check into the reference

Install third-party library minimist and parse parameters.

lerna add minimist core/cli/
Copy the code
/ * * *@description: Parses parameters, determines whether to enable the debug mode, and sets the log level * in the global variable@param {*}
 * @return {*}* /
let args;
function checkInputArgs() {
  const minimist = require('minimist');
  args = minimist(process.argv.slice(2));
  // Check whether debug mode is enabled and set the log level in the global variable
  checkArgs();
}

/ * * *@description: Checks whether the debug mode is enabled and sets the log level * in the global variable@param {*}
 * @return {*}* /
function checkArgs() {
  if (args.debug) {
    process.env.LOG_LEVEL = 'verbose';
  } else {
    process.env.LOG_LEVEL = 'info';
  }
  // Set the log level
  log.level = process.env.LOG_LEVEL;
}
Copy the code

Checking Environment Variables

Install the third-party library Dotenv.

lerna add dotenv core/cli/
Copy the code
/ * * *@description: Check environment variables *@param {*}
 * @return {*}* /
function checkEnv() {
  // Introduce the library dotenv for parsing environment variables
  const dotenv = require('dotenv');
  // The path of the environment variable
  const dotenvPath = path.resolve(userHome, '.env');
  // If the path exists
  if (pathExists(dotenvPath)) {
    // Place the.env environment variable in process.env
    dotenv.config({
      path: dotenvPath,
    });
  }
  // Create a default environment variable configuration
  createDefaultConfig();
  log.verbose('Environment variables', process.env.CLI_HOME_PATH);
}

/ * * *@description: Creates the default environment variable configuration *@param {*}
 * @return {*}* /
function createDefaultConfig() {
  const cliConfig = {
    home: userHome,
  };
  // Use CLI_HOME if CLI_HOME exists
  if (process.env.CLI_HOME) {
    cliConfig['cliHome'] = path.join(userHome, process.env.CLI_HOME);
  } else {
    // Use the default configuration if CLI_HOME does not exist
    cliConfig['cliHome'] = path.join(userHome, constant.DEFAULT_CLI_HOME);
  }
  / / set the process. The env. CLI_HOME_PATH
  process.env.CLI_HOME_PATH = cliConfig.cliHome;
}

Copy the code

Generic NPM API module encapsulation

Through the NPM API: https://registry.npmjs.org/ module name

You can get information about a module.

Can also be replaced by other mirror Such as taobao source module name at https://registry.npmmirror.com/

// Create a new package with lerna and place it under utils
lerna create @hzw-cli-dev/get-npm-info ./utils/get-npm-info
// Change the file name and main property to index.js
// Core module is introduced
// Lerna link installs local dependencies
// Install axios to initiate network requests
lerna add axios utils/get-npm-info
// Install url-join to help join urls
lerna add url-join utils/get-npm-info
// Install semver for version comparison
lerna add semver utils/get-npm-info
Copy the code

Package get-Npm-info

// utils\get-npm-info\lib\index.js
'use strict';

const axios = require('axios');
const urlJoin = require('url-join');
const semver = require('semver');

/ * * *@description: Get information about the NPM module *@param {*} NpmName NPM module name *@param {*} Register NPM Image address *@return {*}* /
async function getNpmInfo(npmName, register) {
  // Return if npmName does not exist
  if(! npmName) {return null;
  }
  // Get the mirror address. If no parameter is passed, NPM source is used by default
  const registerUrl = register || getRegister('taobao');
  / / stitching url
  const npmInfoUrl = urlJoin(registerUrl, npmName);
  // Call NPM API to get data
  return axios
    .get(npmInfoUrl)
    .then((res) = > {
      if (res.status === 200) {
        return res.data;
      }
      return null;
    })
    .catch((e) = > {
      return Promise.reject(e);
    });
}

/ * * *@description: Gets the NPM mirror address *@param {*} Source of origin *@return {*} Mirror address */
function getRegister(origin) {
  const originList = {
    npm: 'https://registry.npmjs.org/'.taobao: 'https://registry.npmmirror.com/'};return originList[origin];
}

/ * * *@description: Gets the module version number array *@param {*} NpmName NPM module name *@param {*} Register NPM Image address *@return {*} Module version number */
async function getNpmVersions(npmName, register) {
  const data = await getNpmInfo(npmName, register);
  if (data) {
    return Object.keys(data.versions);
  }
  return [];
}

/ * * *@description: Obtains the eligible version number (greater than the current version) *@param {*} BaseVersion Current version *@param {*} Versions Version number array *@return {*} An array of version numbers greater than the current version */
function getNpmSemverVersions(baseVersion, versions) {
  if(! versions || versions.length ===0) {
    return [];
  }
  return versions
    .filter((version) = > semver.satisfies(version, ` > = 1.0.0 `))
    .sort((a, b) = > semver.gt(b, a));
}

/ * * *@description: Obtain the eligible version number (greater than the latest version number of the current version) * from NPM@param {*} NpmName NPM module name *@param {*} Register NPM Image address *@param {*} BaseVersion Current version *@return {*} Latest version */
async function getNpmSemverVersion(baseVersion, npmName, register) {
  const versions = await getNpmVersions(npmName, register);
  const newVersions = getNpmSemverVersions(baseVersion, versions);
  return newVersions[0] | |null;
}

module.exports = {
  getNpmInfo,
  getNpmVersions,
  getNpmSemverVersion,
};

Copy the code

Get information about the NPM package

// core\cli\lib\index.js

/ * * *@description: Check whether global update is required * 1. Obtain the current version number and module name 2. Call NPM API to get all version numbers 3. Extract all version numbers and compare which version numbers are greater than the current version 4. Gets the latest version number, prompting the user to update to that version *@param {*}
 * @return {*}* /
async function checkGlobalUpdate() {
  const currentVersion = pkg.version;
  const npmName = pkg.name;
  const { getNpmSemverVersion } = require('@hzw-cli-dev/get-npm-info');
  // getNpmInfo(npmName);
  const lastVersion = await getNpmSemverVersion(currentVersion, 'vue');
  // If the latest version exists and is larger than the current version
  if (lastVersion && semver.gt(lastVersion, currentVersion)) {
    log.warn(
      'Friendly tips',
      colors.yellow('Please update version: the current version is :', lastVersion),
    );
    log.warn(
      'Friendly tips',
      colors.yellow('Update command :'.`npm install -g ${npmName}`)); }}Copy the code