Share 12 code snippets about byte front-end writing, what is the difference?

b026ec4ce161e1545c70f7c78c35c899.jpeg

cpu: What is the difference between you and them?

How to download project templates using the interactive command-line tool

This part of the code implements a user-interactive GitHub template download tool. First of all, you need to create a project on github, and then use the code described below to pull it locally with the command line and decompress it.

It uses  enquirer the library to prompt the user for the repository's creator, name, branch, and target directory, then uses  downloadTemplate a function to download the template, and finally uses  fs-extra the library to store the downloaded files. printFunction wraps the logging function.

The specific implementation of the code is as follows:

  1. Introduce dependencies: fs-extra, enquirer, downloadTemplate and  print. (The print function will be implemented below)

import fs from 'fs-extra';
import enquirer from 'enquirer';
import downloadTemplate from './download';
import print from './print';
复制代码
  1. Define the interface  IRepo and  IAnswers, used to describe the warehouse information and the answers entered by the user.

type IRepo = {
  owner: string;
  name: string;
  branch: string;
};
type IAnswers = IRepo & {
  targetDir?: string;
};
复制代码
  1. Define a function  githubDownloadUrlto construct the download URL of the warehouse.

function githubDownloadUrl(repo: IRepo) {
  return 'https://github.com/' + repo.owner + '/' + repo.name + '/archive/refs/heads/' + repo.branch + '.zip';
}
复制代码
  1. Defines an array of questions, containing the questions that need to be prompted for user input and their validation logic.

  • type: Indicates the type of question, such as input, selection, confirmation, etc. The problem here is all about the input type.

  • name: The key that represents the result value generated by the question, for example, the value you enter when answering the question will be stored in the answer object with name as the key.

  • message: A prompt indicating the question, such as "Please enter the creator of the warehouse".

  • default: Indicates the default value of the question, if the user does not enter an answer, the default value is used.

  • validate: Indicates the validation function of the question, used to verify whether the answer entered by the user is legal. If the answer is invalid, an error message can be returned, prompting the user to re-enter.

  • The questions array contains four question objects, and each question object has the following attributes:

  • These questions will be used to prompt the user for input, and based on the answers entered by the user, the URL to download the template and the directory to store the file will be calculated.

const questions = [
  {
    type: 'input', // type为交互的类型
    name: 'owner', // 产生的值的key,比如你输入''
    message: '请输入仓库的创建者(example: "lio-mengxiang")', // 提示语
    default: 'lio-mengxiang',
    validate(val) {
      if (!val) {
        return '请输入文件名'; // 验证一下输入是否不为空
      }
      if (fs.accessSync(val, fs.constants.F_OK)) {
        return '文件已存在'; // 判断文件是否存在
      } else {
        return true;
      }
    },
  },
  {
    type: 'input',
    name: 'name',
    message: '请输入仓库名称(example: "react")',
    default: 'react-pnpm-monorepo-subTemplate',
    validate(val) {
      if (!val) {
        return '请输入仓库名'; // 验证一下输入是否不为空
      }
      return true;
    },
  },
  {
    type: 'input',
    name: 'branch',
    message: '请输入分支名(example: "main")',
    default: 'main',
    validate(val) {
      if (!val) {
        return '请输入分支名'; // 验证一下输入是否不为空
      }
      return true;
    },
  },
  {
    type: 'input',
    name: 'targetDir',
    message: '请输入放文件的目录(默认当前目录: "./")',
    default: './',
  },
];
复制代码
  1. The usage  enquirer.prompt method prompts the user for input and processes the answers entered by the user. If the input is wrong, output an error message and exit the program.

enquirer
  .prompt(questions)
  .then((answers: IAnswers) => {
    // 获取用户输入值
    const owner = answers.owner;
    const name = answers.name;
    const branch = answers.branch;
    const targetDir = answers.targetDir;
    downloadTemplate({ url: githubDownloadUrl({ owner, name, branch }), targetDir });
  })
  .catch((err) => {
    print.error(err);
    process.exit(1);
  });
复制代码

If the answer entered by the user is valid, use  downloadTemplate a function to download the template and use  fs-extra the storage file.

import download from 'download';
import compressing from 'compressing';
import print from './print';

/**
 * 下载远程项目模板的方法
 */
export default function downloadTemplate({ url, targetDir = './' }: { url: string; targetDir?: string }): Promise<any> {
  print.info('download start, please wait...');
  // 通过get方法下载
  return download(url, targetDir)
    .on('end', () => {
      print.success('download done');
    })
    .then((stream) => {
      return compressing.zip.uncompress(stream, './');
    })
    .catch((err) => {
      print.error(err);
    });
}

复制代码

grouping function

For example, suppose we have an array [1, 2, 3, 4, 5, 6], if we call group([1, 2, 3, 4, 5, 6], 2), then this function will return a new An array of [[1, 2], [3, 4], [5, 6]].

export function group(array: any[], subGroupLength: number) {
  let index = 0;
  const newArray = [];

  while (index < array.length) {
    newArray.push(array.slice(index, (index += subGroupLength)));
  }
  return newArray;
}
复制代码

Node quickly executes linux commands

This code defines a  function execQuick that  spawn executes a command using a subprocess. spawn The advantage of subprocess is that it is  exec more efficient than , because it does not need to create a new shell environment, and does not cause errors due to exceeding the limit of the maximum buffer.

execQuick The function takes a command and some options as parameters and returns a Promise object containing the result of the command execution.

  • If the user specifies  time an option, execQuick the time spent executing the command will be printed after the command is executed;

  • If the user specifies  silent an option, execQuick it suppresses the printing of the command's standard output and standard error output.

import { spawn } from 'child_process';
import print from './print';

/**
 * spawn优于exec的点
 * 1是在于不用新建shell,减少性能开销
 * 2是没有maxbuffer的限制
 */
export default async function execQuick(
  command: string,
  options: {
    cwd?: string;
    time?: boolean;
    silent?: boolean;
  } = {}
): Promise<{ pid: number; code: number; stdout: string; stderr: string }> {
  return new Promise((resolve) => {
    const silent = options.silent !== false;
    const begin = new Date().getTime();
    const result = {
      pid: null,
      code: null,
      stdout: '',
      stderr: '',
    };

    const { stdout, stderr, pid } = spawn(command, {
      cwd: options.cwd,
      shell: true,
    }).on('close', (code) => {
      if (options.time) {
        const end = new Date().getTime();
        const waste = ((end - begin) / 1000).toFixed(2);
        print.info(command, `Command executed in ${waste} ms.`);
      }

      if (code !== 0 && !silent) {
        print.error(command, 'Command executed failed');
      }

      result.code = code;
      resolve(result);
    });

    result.pid = pid;

    stdout.on('data', (data) => {
      const dataStr = data.toString();
      if (!silent) {
        print.info(dataStr);
      }
      result.stdout += dataStr;
    });

    stderr.on('data', (data) => {
      const dataStr = data.toString();
      if (!silent) {
        print.error(dataStr);
      }
      result.stderr += dataStr;
    });
  });
}

复制代码

simple logging function

This code defines a function that prints the log named  log. It uses  chalk the library to set the color of the log.

log The function takes any number of arguments and prints them to standard output. It also defines four printing functions corresponding to different colors, namely  log.info, log.warn, , log.error and  log.success.

These functions print their arguments in different colors. For example, log.success('成功') the string will  '成功' be printed in green.

In addition, log a function called is defined  log.divider , which can print a separator line, which can be used to distinguish different logs. The color of the divider can  level be specified by parameter, default is  'info'.

/**
 * 更改颜色
 * example chalk.green('成功') 文字显示绿色
 */
import chalk from 'chalk';

type ILevel = 'info' | 'warn' | 'success' | 'error';

function print(color: string, ...args: string[]) {
  if (args.length > 1) {
    log(chalk[`bg${color.replace(/^\w/, (w) => w.toUpperCase())}`](` ${args[0]} `), chalk[color](args.slice(1)));
  } else {
    log(chalk[color](...args));
  }
}

function log(...args) {
  console.log(...args);
}

log.info = print.bind(null, 'gray');
log.warn = print.bind(null, 'yellow');
log.error = print.bind(null, 'red');
log.success = print.bind(null, 'green');
log.chalk = chalk;

/**
 * Print divider
 * @param {'info' | 'warn' | 'success' | 'error'} level
 */
log.divider = (level: ILevel = 'info') => {
  const logger = log[level] || log.info;
  logger('---------------------------------------------------------------------------------------');
};

export default log;

复制代码

Judgment type function

These functions are used to check whether an object in JavaScript is of a certain type. For example, the function isArray() can be used to check whether the object passed in is an array type. The isObject() function can be used to check whether an object is of type Object, the isString() function can be used to check whether an object is of type String, and so on.

Mainly based on the following functions to make judgments

const getType = (obj) => Object.prototype.toString.call(obj).slice(8, -1);
复制代码

This function is also learned from the juqery code, and it has been used until now. It is also a highly respected method of judging the type, because it is very accurate.

const getType = (obj) => Object.prototype.toString.call(obj).slice(8, -1);

export function isArray(obj: any): obj is any[] {
  return getType(obj) === 'Array';
}

export function isObject(obj: any): obj is { [key: string]: any } {
  return getType(obj) === 'Object';
}

export function isString(obj: any): obj is string {
  return getType(obj) === 'String';
}

export function isNumber(obj: any): obj is number {
  return getType(obj) === 'Number' && obj === obj;
}

export function isRegExp(obj: any) {
  return getType(obj) === 'RegExp';
}

export function isFile(obj: any): obj is File {
  return getType(obj) === 'File';
}

export function isBlob(obj: any): obj is Blob {
  return getType(obj) === 'Blob';
}

export function isUndefined(obj: any): obj is undefined {
  return obj === undefined;
}

export function isFunction(obj: any): obj is (...args: any[]) => any {
  return typeof obj === 'function';
}

export function isEmptyObject(obj: any): boolean {
  return isObject(obj) && Object.keys(obj).length === 0;
}


复制代码

Lite version classnames function

This code implements a csfunction called that combines a set of arguments of type string into a single string and returns the combined string. This function can accept multiple parameters, and supports multiple parameter types such as strings, string arrays, and objects. When merging strings, duplicate strings are automatically removed and all strings are separated by spaces.

For example, for the following call:

cs('a', 'b', ['c', 'd'], { e: true, f: false }, null, undefined);
复制代码

will return the string:

'a b c d e'
复制代码

This function can be used as an alternative to similar libraries (for example classnames) for merging a set of strings and using them as a class name.

import { isString, isArray, isObject } from './is';

type ClassNamesArg = string | string[] | { [key: string]: any } | undefined | null | boolean;

/**
 * 代替classnames库,样式合并的方法
 */
export default function cs(...args: ClassNamesArg[]): string {
  const length = args.length;
  let classNames: string[] = [];
  for (let i = 0; i < length; i++) {
    const v = args[i];
    if (!v) {
      continue;
    }
    if (isString(v)) {
      classNames.push(v);
    } else if (isArray(v)) {
      classNames = classNames.concat(v);
    } else if (isObject(v)) {
      Object.keys(v).forEach((k) => {
        if (v[k]) {
          classNames.push(k);
        }
      });
    }
  }
  return [...new Set(classNames)].join(' ');
}

复制代码

omit function

omit function, which accepts two parameters: an object and an array. The function returns a new object that is a shallow copy of the passed object with all properties listed in the array removed.

For example, if the object passed in is { a: 1, b: 2, c: 3 } and the array is ['a', 'c'], the returned object is { b: 2 }.

/**
 * delete keys from object
 */
export default function omit<T extends Record<string | number, any>, K extends keyof T>(
  obj: T,
  keys: Array<K | string> // string 为了某些没有声明的属性被omit
): Omit<T, K> {
  const clone = {
    ...obj,
  };
  keys.forEach((key) => {
    if ((key as K) in clone) {
      delete clone[key as K];
    }
  });
  return clone;
}

复制代码

Get the project file, take the directory entered by the command as the root directory

This function defines a getProjectPath() function. It takes a directory path as an argument and returns the absolute path of this directory within the project. If no directory path is provided, the current working directory is used as the directory path by default.

This function can be used to get the absolute path of the file in the project based on the relative path.

For example, if the working directory is /home/user/project and the incoming directory path is './src', the return value is '/home/user/project/src'.

import path from 'path';

/**
 * 获取项目文件,以命令输入的目录为根目录
 */
export default function getProjectPath(dir = './'): string {
  return path.join(process.cwd(), dir);
}

复制代码

How to change the theme

Change the theme by changing the css variable

import { isObject } from './is';

/**
 * 更换css变量的方法
 */
export function setCssVariables(variables: Record<string, any>, root = document.body) {
  if (variables && isObject(variables)) {
    Object.keys(variables).forEach((themKey) => {
      root.style.setProperty(themKey, variables[themKey]);
    });
  }
}

复制代码

Automatic release of git scripts to detect whether the git warehouse is initialized code

This function defines a checkGitRemote() function. It will first use the getGitRootPath() function to detect whether the current directory is a Git repository.

If so, it executes the git remote -v command, then checks to see if push is included in the output of the command. If included, print an empty line;

If not, print an error message and exit the program. If the detected current directory is not a Git warehouse, print an error message and exit the program

import execQuick from './execQuick';
import getGitRootPath from './getGitRootPath';
import print from './print';

export default async function checkGitRemote() {
  if (getGitRootPath()) {
    const { code, stdout } = await execQuick('git remote -v');
    if (code === 0 && stdout.match('(push)')) {
      print();
    } else {
      print.error(['publish'], '在指定 git remote 前,您无法发布代码,请手动添加 git remote。');
      process.exit(1);
    }
  } else {
    print.error(['publish'], '没有检测到 Git 仓库。');
    process.exit(1);
  }
}

复制代码

Asynchronous function composition, whether to call the next function is completely determined by the middleware itself

This function defines a compose() function that takes an array containing an array of middleware objects as an argument.

Each middleware object has a name and a function.

The compose() function will execute each middleware function in the order in the array. After each middleware function is executed, an object named middlewareData will be updated, which contains the data processed by each middleware function.

The returned middlewareData object can be used to share data among multiple middlewares.

/**
 * 异步函数组合,是否调用下一个函数,完全由中间件自己决定
 * @param middleware 中间件
 */

type IMiddleware = {
  name: string;
  fn: ({ middlewareData, next }: { middlewareData: Record<string, any>; next: () => void }) => Promise<{ data: Record<string, any> }>;
};

export default function compose(middleware: IMiddleware[]) {
  let middlewareData: Record<string, any> = {};
  async function dispatch(index: number) {
    if (index === middleware.length) return;
    const { name, fn } = middleware[index];
    const { data } = await fn({
      middlewareData,
      next: () => {
        dispatch(++index);
      },
    });
    middlewareData = {
      ...middlewareData,
      [name]: {
        ...middlewareData[name],
        ...data,
      },
    };
  }
  dispatch(0);
}

复制代码

How command line tools display loading animations

We encapsulate the ora library, which is commonly used in command line tools, ora is a JavaScript library for displaying loading indicators on the command line.

It can be used to notify the user of the progress and results of an asynchronous operation. For example, you can use the ora library to display a spinning loading indicator while executing an asynchronous task, and display success or failure information after the task is completed.

Then look at the function we encapsulated. If the function is executed successfully, the loading indicator will display the success message, and the return value of the function will be used as the success value of the Promise;

If the function fails to execute, the loading indicator will display the failure message and use the error thrown by the function as the failure value of the Promise. This function can be used to notify the user of the progress and results of an asynchronous operation.

import ora from 'ora';
import print from './print';

export default function withOra(
  promiseFn: () => Promise<any>,
  { text, successText, failText, startText }: { text: string; successText: string; failText: string; startText?: string }
) {
  return new Promise((resolve, reject) => {
    const spinner = ora(text).start();
    startText && print.info(startText);

    promiseFn()
      .then((result) => {
        spinner.succeed(`✅ ${successText}`);
        resolve(result);
      })
      .catch((err) => {
        spinner.fail(`❎ ${failText}`);
        reject(err);
      });
  });
}

复制代码

Author: Meng Xiang_Chengdu
Link: https://juejin.cn/post/7176935575302668346
Source: Nuggets

Guess you like

Origin blog.csdn.net/Ed7zgeE9X/article/details/130376809