chalk和ora源码重点知识
chalk
知识点1:自定义 imports 引用
在 package.json 中定义:
json
{
"imports": {
"#ansi-styles": "./source/vendor/ansi-styles/index.js",
"#supports-color": {
"node": "./source/vendor/supports-color/index.js",
"default": "./source/vendor/supports-color/browser.js"
}
}
}
应用:
js
import ansiStyles from '#ansi-styles';
import supportsColor from '#supports-color';
知识点2:批量生成方法和构造者模式应用
根据 ansiStyles 配置批量生成构造者方法:
js
for (const [styleName, style] of Object.entries(ansiStyles)) {
styles[styleName] = {
get() {
const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
Object.defineProperty(this, styleName, { value: builder });
return builder;
},
};
}
通过 createBuilder 生成构造者对象:
js
const createBuilder = (self, _styler, _isEmpty) => {
const builder = (...arguments_) => applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
Object.setPrototypeOf(builder, proto);
builder[GENERATOR] = self;
builder[STYLER] = _styler;
builder[IS_EMPTY] = _isEmpty;
return builder;
};
知识点3:在对象的原型(prototype)上新增属性
js
Object.defineProperties(createChalk.prototype, styles);
知识点4:使用工厂模块快速生成 chalk 实例
定义:
js
function createChalk(options) {
return chalkFactory(options);
}
应用:
js
const chalk = createChalk();
export default chalk;
知识点5:替换对象的原型
js
const chalkFactory = options => {
const chalk = (...strings) => strings.join(' ');
applyOptions(chalk, options);
Object.setPrototypeOf(chalk, createChalk.prototype);
return chalk;
};
知识点6:向\n两侧注入转义字符
js
export function stringEncaseCRLFWithFirstIndex(
string, // 带\n的初始字符串
prefix, // 闭合字符串,\n左侧注入
postfix, // 开启字符串,\n右侧注入
index // 第一个\n位置序号
) {
let endIndex = 0;
let returnValue = '';
do {
// 是否存在\r
const gotCR = string[index - 1] === '\r';
// 向\n两侧注入转移字符
returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix;
// 获取\n后面一个字符的位置
endIndex = index + 1;
// 获取下一个\n的序号
index = string.indexOf('\n', endIndex);
} while (index !== -1); // 如果存在\n则继续循环
// 获取\n后面的字符串进行拼接
returnValue += string.slice(endIndex);
return returnValue;
}
代码演示
js
import chalk, { Chalk } from "chalk";
// 简单调用
console.log('chalk--->', chalk.red('hello chalk'))
// 混合调用
console.log('chalk--->', `${chalk.red('hello chalk')}!${chalk.green('chalk learn')}`)
// 链式调用
console.log('chalk--->', chalk.red.bgBlue.bold('hello chalk'))
// 多个参数
console.log('chalk--->', chalk.red('hello chalk', 'learn'))
// 嵌套的调用
console.log('chalk--->', chalk.red('hello chalk', chalk.underline('learn')))
// 超越256的色值进行定义
console.log('chalk--->', chalk.rgb(255, 255, 0).underline('hello chalk'))
console.log('chalk--->', chalk.hex('#ff0000').bold('hello chalk'))
console.log('chalk--->', chalk.hex('#ff0000')('hello chalk'))
// 场景一:日志的打印
const error = (...text) => console.log(chalk.bold.hex('ff0000')(text))
const warning = (...text) => console.log(chalk.bold.hex('ffa500')(text))
error('Error')
warning('Warning')
// 自定义Chalk类
const customChalk = new Chalk({ level: 0 })
console.log(customChalk.red('hello chalk'))
// 链式调用
class Test {
one() {
console.log(1)
return this;
}
two() {
console.log(2)
return this;
}
}
function createBuilder () {
return new Test()
}
const builder = createBuilder()
builder.one().two()
ora
知识点7:Class私有属性
js
class Test {
#name = 'sam';
#getName() {
return this.#name;
}
}
const t = new Test();
console.log(t.#getName()); // error
console.log(t.#name); // error
知识点8:输入流缓冲
js
import readline from 'node:readline';
import { BufferListStream } from 'bl';
#mutedStream = new BufferListStream();
this.#mutedStream.pipe(process.stdout);
this.#rl = readline.createInterface({
input: process.stdin,
output: this.#mutedStream,
});
知识点9:命令行光标隐藏和显示
js
console.log('\u001B[?25l'); // 光标隐藏
console.log('\u001B[?25h'); // 光标显示
import cliCursor from 'cli-cursor';
cliCursor.show(stream);
cliCursor.hide(stream);
知识点10:命令行清屏操作
js
this.#stream.cursorTo(0); // 光标移动到初始位置
for (let index = 0; index < this.#linesToClear; index++) {
if (index > 0) {
this.#stream.moveCursor(0, -1); // 如果清除行数大于1,光标上移一行
}
this.#stream.clearLine(1); // 清除一行
}
知识点11:打印成功字符
使用log-symbols获取成功字符
js
import logSymbols from 'log-symbols';
console.log(logSymbols.success); // ✔
代码演示
js
import ora, { oraPromise } from 'ora'
const spinner = ora().start();
spinner.color = 'red'
spinner.text = 'Reading...';
setTimeout(() => {
spinner.stop()
}, 3000);
// oraPromise 支持异步任务
(async function () {
const promise = new Promise(resolve => {
console.log('doing something...');
setTimeout(() => {
resolve();
}, 3000)
});
await oraPromise(promise, {
successText: 'success',
failText: 'failed',
prefixText: 'Download chalk'
})
})()
手动实现ora
js
import cliSpinners from 'cli-spinners' // https://www.npmjs.com/package/cli-spinners
import cliCursor from 'cli-cursor'; // https://www.npmjs.com/package/cli-cursor
import { BufferListStream } from 'bl'; // https://www.npmjs.com/package/bl
import * as readline from 'node:readline'; // https://nodejs.org/api/readline.html#readline
const spinners = cliSpinners.dots2; // 组件库
const text = 'loading' // 默认文本
const stream = process.stderr; // 输出流
let frameIndex = 0; // 当前帧
const frames = spinners.frames; // 每一帧的内容
const interval = spinners.interval; // 每一帧的间隔
const mutedStream = new BufferListStream();
mutedStream.pipe(process.stdout);
const rl = readline.createInterface({
input: process.stdin,
output: mutedStream
});
function clear() {
stream.cursorTo(0);
stream.clearLine(1);
}
function render() {
clear()
const readerText = frames[frameIndex] + ' ' + text;
stream.write(readerText);
frameIndex = ++frameIndex % frames.length;
}
let i = setInterval(render, interval)
function stop() {
clearInterval(i);
i = undefined;
clear();
frameIndex = 0;
rl.close()
}
cliCursor.hide(stream);
setTimeout(() => {
stop();
}, 3000)