字节前端架构组工程化代码片段

news2024/9/21 0:38:08

如何使用交互式的命令行工具下载项目模板

这部分代码实现了一个用户交互的 GitHub 模板下载工具。首先你需要在github上创建一个项目,然后使用下面介绍的代码就可以用命令行拉取到本地,并解压了。

它使用 enquirer 库提示用户输入仓库的创建者、名称、分支、和目标目录,然后使用 downloadTemplate 函数下载模板,最后使用 fs-extra 库存储下载的文件。 print函数封装了日志记录的函数。

代码的具体实现如下:

1.引入依赖:fs-extraenquirerdownloadTemplateprint。(print函数实现下面会有)

import fs from 'fs-extra';
import enquirer from 'enquirer';
import downloadTemplate from './download';
import print from './print'; 

2.定义接口 IRepoIAnswers,用来描述仓库信息和用户输入的答案。

type IRepo = {owner: string;name: string;branch: string;
};
type IAnswers = IRepo & {targetDir?: string;
}; 

3.定义函数 githubDownloadUrl,用来构造仓库的下载 URL。

function githubDownloadUrl(repo: IRepo) {return 'https://github.com/' + repo.owner + '/' + repo.name + '/archive/refs/heads/' + repo.branch + '.zip';
} 

4.定义一个问题数组,包含需要提示用户输入的问题及其验证逻辑。* questions 数组包含了四个问题对象,每个问题对象都有以下几个属性:* type:表示问题的类型,例如输入、选择、确认等。这里的问题都是输入类型。* name:表示问题产生的结果值的 key,例如当你在回答问题时输入的值会以 name 作为 key 存储在答案对象中。* message:表示问题的提示语,例如 “请输入仓库的创建者”。* default:表示问题的默认值,如果用户没有输入答案,则使用默认值。* validate:表示问题的验证函数,用来验证用户输入的答案是否合法。如果答案不合法,可以返回一个错误消息,提示用户重新输入。* 这些问题将用于提示用户输入,并根据用户输入的答案计算下载模板的 URL 和存储文件的目录。

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: './',},
]; 

3.使用 enquirer.prompt 方法提示用户输入,并处理用户输入的答案。如果输入有误,则输出错误信息并退出程序。

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);}); 

如果用户输入的答案合法,则使用 downloadTemplate 函数下载模板并使用 fs-extra 存储文件。

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);});
} 

分组函数

例如,假设我们有一个数组 [1, 2, 3, 4, 5, 6],如果我们调用 group([1, 2, 3, 4, 5, 6], 2),那么这个函数会返回一个新的数组 [[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快速执行linux命令

这段代码定义了一个 execQuick 函数,它使用 spawn 子进程的方式执行一条命令。spawn 子进程的优点是比 exec 更高效,因为它不需要创建新的 shell 环境,并且不会因超出最大缓冲区的限制而导致错误。

execQuick 函数接受一条命令和一些选项作为参数,并返回一个包含命令执行结果的 Promise 对象。

  • 如果用户指定了 time 选项,execQuick 会在执行完命令后打印出命令执行所花费的时间;
  • 如果用户指定了 silent 选项,execQuick 会禁止打印出命令的标准输出和标准错误输出。
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;});});
} 

简单的日志记录函数

这段代码定义了一个打印日志的函数,名为 log。它使用了 chalk 库来设置日志的颜色。

log 函数接受任意数量的参数,并将它们打印到标准输出。它也定义了四个分别对应不同颜色的打印函数,分别是 log.infolog.warnlog.errorlog.success

这些函数会把它们的参数以不同的颜色打印出来。例如,log.success('成功') 会把字符串 '成功' 以绿色打印出来。

此外,log 还定义了一个名为 log.divider 的函数,它可以打印一条分隔线,用于区分不同的日志。分隔线的颜色可以通过 level 参数来指定,默认为 '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; 

判断类型函数

这些函数用于检查 JavaScript 中的对象是否属于特定的类型。例如,函数 isArray() 可以用来检查传入的对象是否为数组类型。isObject() 函数可以用来检查对象是否为对象类型,isString() 函数可以用来检查对象是否为字符串类型,以此类推。

主要基于的是一下函数去做判断

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

这个函数也是大家从juqery代码里学来的,一直沿用到现在,也是极为推崇的判断类型的方法,因为它非常准确。

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;
} 

精简版classnames函数

这段代码实现了一个名为cs的函数,该函数能够将一组字符串类型的参数合并成一个字符串,并返回合并后的字符串。这个函数可以接受多个参数,并且支持字符串、字符串数组、对象等多种参数类型。在合并字符串时,会自动去除重复的字符串,并将所有字符串用空格隔开。

例如,对于以下调用:

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

会返回字符串:

'a b c d e' 

该函数可以用来替代类似的库(例如classnames),用于合并一组字符串并作为一个类名使用。

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函数

omit函数,它接受两个参数:一个对象和一个数组。函数会返回一个新对象,该对象为传入的对象的浅拷贝,并删除了数组中列出的所有属性。

例如,如果传入的对象为 { a: 1, b: 2, c: 3 },数组为 [‘a’, ‘c’],则返回的对象为 { 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;
} 

获取项目文件,以命令输入的目录为根目录

这个函数定义了一个 getProjectPath() 函数。它接受一个目录路径作为参数,并返回这个目录在项目中的绝对路径。如果没有提供目录路径,默认使用当前工作目录作为目录路径。

这个函数可以用来根据相对路径获取文件在项目中的绝对路径。

例如,如果工作目录为 /home/user/project,传入目录路径为 ‘./src’,则返回值为 ‘/home/user/project/src’。

import path from 'path';

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

更改主题的方法

通过更改css变量达到更换主题的目的

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]);});}
} 

自动化发布git脚本之检测git仓库是否初始化的代码

这个函数定义了一个 checkGitRemote() 函数。它首先会使用 getGitRootPath() 函数检测当前目录是否为 Git 仓库。

如果是,它会执行 git remote -v 命令,然后检查命令的输出中是否包含 push。如果包含,则打印空行;

如果不包含,则打印错误信息,并退出程序。如果检测到的当前目录不是 Git 仓库,则打印错误信息,并退出程序

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);}
} 

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

这个函数定义了一个 compose() 函数,它接受一个包含一组中间件对象的数组作为参数。

每个中间件对象都有一个名称和一个函数。

compose() 函数会按照数组中的顺序执行每个中间件函数。每个中间件函数执行完毕后,会更新一个名为 middlewareData 的对象,该对象包含了每个中间件函数处理后的数据。

最终返回的 middlewareData 对象可以用来在多个中间件之间共享数据。

/**
 * 异步函数组合,是否调用下一个函数,完全由中间件自己决定
 * @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);
} 

命令行工具如何显示loading动画

我们封装了在命令行工具中常用ora这个库,ora 是一个 JavaScript 库,用于在命令行中显示 loading 指示器。

它可以用来提示用户在执行异步操作时的进度和结果。例如,可以使用 ora 库在执行某个异步任务时显示一个转圈圈的 loading 指示器,并在任务完成后显示成功或失败信息。

接着看我们封装的函数,如果函数执行成功,则 loading 指示器会显示成功信息,并将函数的返回值作为 Promise 的成功值;

如果函数执行失败,则 loading 指示器会显示失败信息,并将函数抛出的错误作为 Promise 的失败值。这个函数可以用来提示用户在执行异步操作时的进度和结果。

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);});});
} 

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/384030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

npm版本过低造成的各种问题汇总及升级方法

npm does not support Node.js 这个是因为npm和Node.js的版本不合适&#xff0c;版本对照表在这里&#xff1a;https://nodejs.org/zh-cn/download/releases/ 查看node和npm的版本&#xff1a; node -v npm -v试图装一个npm&#xff0c;结果报错&#xff1a;TypeError: Canno…

C++面向对象之多态性

文章目录C面向对象之多态性1.静态多态2.动态多态3.多态的好处3.1使用方法4.纯虚函数5.虚析构与纯虚析构5.1问题5.2解决6.其他知识点7.代码8.测试结果8.1父类中无虚函数&#xff0c;父类的指针指向子类对象&#xff0c;将调用父类中的函数&#xff0c;无法调用子类中的重写函数&…

Qt入门基础

目录 一 学习Qt的动机 二 创建第一个Qt程序 关于main.cpp QApplication Widget 关于对象树 关于Widget常用的API return a.exec() 一 学习Qt的动机 我在学习了C之后&#xff0c;想做图形化界面。实现前后端的交互。这时候就可以利用Qt进行编程。 二 创建第一个Qt程序 Qt…

【RocketMQ】消息的存储

Broker对消息的处理 BrokerController初始化的过程中&#xff0c;调用registerProcessor方法注册了处理器&#xff0c;在注册处理器的代码中可以看到创建了处理消息发送的处理器对象SendMessageProcessor&#xff0c;然后将其注册到远程服务中&#xff1a; public class Brok…

哨声吹响,与世界杯相关的欺诈也在激增

2022 年世界杯已经开始&#xff0c;通过虚假流媒体网站与彩票针对足球迷的诈骗激增。近日&#xff0c;Zscaler 发现与世界杯相关的新注册域名有所增加&#xff0c;尽管并非都是恶意的&#xff0c;也是值得警惕的。 流量趋势 随着世界杯的开赛&#xff0c;从 11 月 21 日流媒体…

Python set集合全部操作方法

文章目录一. 介绍1. 创建set集合2. 判断元素是否在集合内3. 集合推导式(Set comprehension)二. 集合基本操作1. add&#xff08;&#xff09;添加单个元素2. update&#xff08;&#xff09;添加列表&#xff0c;元组&#xff0c;字典等整体数据3. remove ( ) 移除元素&#xf…

PACS三维影像后处理系统源码 PACS源码

PACS源码 PACS3D影像后处理系统源码 一、系统概述&#xff1a; ​基于VC MSSQL开发的一套三甲医院医学影像PACS系统源码&#xff0c;集成3D影像后处理功能&#xff0c;包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分析等…

mysql数据库之视图

视图&#xff08;view&#xff09;是一种虚拟的存在&#xff0c;视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图之保存了查询的sql逻辑&#xff0c;不保存查询结…

【C语言】函数指针和指针函数

文章目录[TOC](文章目录)前言概述函数指针定义&#xff1a;使用&#xff1a;回调函数指针函数前言 今天学一下函数指针 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 概述 函数指针&#xff1a;是一个指向函数的指针&#xff0c;在内存空间中存放的…

关于小程序内存在违规内容的处理警告

遇到了问题&#xff0c;就记录一下解决过程吧 小程序涉及提供在线观看视频服务&#xff0c;请补充文娱-视频广场类目&#xff1b; 我们的小程序做了一个类似抖音上下滑动观看视频的页面&#xff0c;被警告了&#xff0c;查看了文档&#xff0c; https://developers.weixin.qq.…

C语言程序-计算闰年平年

#include <stdio.h> int main() { int year ,month,day,i1; printf("欢迎使用本程序&#xff0c;使用愉快\n") ; while(i<10){ printf("请输入您需要计算的年\n"); scanf("%d",&year); printf("…

安全高效 | AIRIOT智慧工地管理解决方案

建筑工地施工材料、机械设备、工程车、人员各个环节管理相对复杂、建筑业也是安全事故频发的高危行业&#xff0c;安全管控尤为重要。建筑施工单位想要保障安全生产&#xff0c;做好能源消耗管控降低生产成本&#xff0c;需要解决掉很多现状问题&#xff1a;1、工地施工作业人员…

【Leetcode 剑指Offer】第 11 天 双指针(简单)

双指针剑指 Offer 18. 删除链表的节点剑指 Offer 18. 删除链表的节点 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。 返回删除后的链表的头节点。 示例 1: 输入: head [4,5,1,9], val 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二…

服务器(centos7.6)已经安装了宝塔面板,想在里面安装一个SVN工具(subversion),应该如何操作呢?

首先&#xff0c;在登录进入宝塔面板&#xff0c;然后点击左侧终端&#xff0c;进入终端界面&#xff0c;如下图&#xff1a;------------------------------------------如果是第一次使用会弹出输入服务器用户名和密码&#xff0c;此时输入root账号和密码&#xff0c;即可进入…

2.基于Label studio的训练数据标注指南:(智能文档)文档抽取任务、PDF、表格、图片抽取标注等

文档抽取任务Label Studio使用指南 1.基于Label studio的训练数据标注指南&#xff1a;信息抽取&#xff08;实体关系抽取&#xff09;、文本分类等 2.基于Label studio的训练数据标注指南&#xff1a;&#xff08;智能文档&#xff09;文档抽取任务、PDF、表格、图片抽取标注等…

Python3-集合

Python3 集合 集合&#xff08;set&#xff09;是一个无序的不重复元素序列。 可以使用大括号 { } 或者 set() 函数创建集合&#xff0c;注意&#xff1a;创建一个空集合必须用 set() 而不是 { }&#xff0c;因为 { } 是用来创建一个空字典。 创建格式&#xff1a; parame …

奇淫技巧:熟练使用Fetch一个干翻PostMan的顶级技巧

一&#xff1a;如何使用fetch重新发送一个接口请求&#xff1f; 按照如下的请求进行复制 fetch("https://bip-test.yyuap.com/mdf-node/uniform/user/checkAuthByCode?terminalType1&serviceCodeorderList&codeuserdef_filterItem,userdef_schemaSetting"…

《C++ Primer》第十章 泛型算法

《C Primer》第十章 泛型算法 10.1 概述 大多数算法定义在头文件algorithm中&#xff0c;还有一些算法在numeric中。例如标准库算法find: int val 42;//即将查找的值 //如果在vec中找到想要的元素&#xff0c;则返回结果指向它&#xff0c;否则返回vec.cend() auto result …

小样本学习--学习记录

之前在做课题的时候&#xff0c;把数据不均衡和小样本的概念混淆了&#xff0c;昨天看了一篇论文&#xff1a;《 面向小样本数据的机器学习方法研究综述 &#xff08;陈良臣&#xff0c;傅德印&#xff09;》 &#xff0c;这篇论文写的非常清晰。推荐阅读。 网上的一些综述整理…

2月VR大数据:硬件份额变化不大,PS VR2首发游戏超50款

Hello大家好&#xff0c;每月一期的VR内容/硬件大数据统计又和大家见面了。 想了解VR软硬件行情么&#xff1f;关注这里就对了。我们会统计Steam平台的用户及内容等数据&#xff0c;每月初准时为你推送&#xff0c;不要错过喔&#xff01;本数据报告包含&#xff1a;Steam VR硬…