diff --git a/node_modules/minipass/index.js b/node_modules/minipass/index.js index c072352..6dba800 100644 --- a/node_modules/minipass/index.js +++ b/node_modules/minipass/index.js @@ -1,537 +1,689 @@ 'use strict' -const EE = require('events') -const Yallist = require('yallist') -const SD = require('string_decoder').StringDecoder - -const EOF = Symbol('EOF') -const MAYBE_EMIT_END = Symbol('maybeEmitEnd') -const EMITTED_END = Symbol('emittedEnd') -const EMITTING_END = Symbol('emittingEnd') -const CLOSED = Symbol('closed') -const READ = Symbol('read') -const FLUSH = Symbol('flush') -const FLUSHCHUNK = Symbol('flushChunk') -const ENCODING = Symbol('encoding') -const DECODER = Symbol('decoder') -const FLOWING = Symbol('flowing') -const PAUSED = Symbol('paused') -const RESUME = Symbol('resume') -const BUFFERLENGTH = Symbol('bufferLength') -const BUFFERPUSH = Symbol('bufferPush') -const BUFFERSHIFT = Symbol('bufferShift') -const OBJECTMODE = Symbol('objectMode') -const DESTROYED = Symbol('destroyed') - -// TODO remove when Node v8 support drops +const EE = require('events') // 引入 Node.js 的 'events' 模块,事件发射器的基类 +const Yallist = require('yallist') // 引入 'yallist' 模块,一个用于实现双向链表的数据结构 +const SD = require('string_decoder').StringDecoder // 引入 'string_decoder' 模块中的 StringDecoder,用于解码 Buffer 数据 + +// 定义一些符号常量,用于标识流的不同状态 +const EOF = Symbol('EOF') // 表示流结束的标志 +const MAYBE_EMIT_END = Symbol('maybeEmitEnd') // 可能发出 'end' 事件的标志 +const EMITTED_END = Symbol('emittedEnd') // 表示已经发出 'end' 事件 +const EMITTING_END = Symbol('emittingEnd') // 正在发出 'end' 事件的标志 +const CLOSED = Symbol('closed') // 表示流已关闭的标志 +const READ = Symbol('read') // 表示流正在读取的状态 +const FLUSH = Symbol('flush') // 表示需要刷新流的状态 +const FLUSHCHUNK = Symbol('flushChunk') // 表示正在刷新数据块的状态 +const ENCODING = Symbol('encoding') // 用于标识编码方式的符号 +const DECODER = Symbol('decoder') // 解码器,用于解码流中的数据 +const FLOWING = Symbol('flowing') // 表示流处于流动状态的标志 +const PAUSED = Symbol('paused') // 表示流处于暂停状态的标志 +const RESUME = Symbol('resume') // 表示流恢复的标志 +const BUFFERLENGTH = Symbol('bufferLength') // 用于跟踪缓冲区长度的符号 +const BUFFERPUSH = Symbol('bufferPush') // 表示缓冲区正在被推送数据的状态 +const BUFFERSHIFT = Symbol('bufferShift') // 表示缓冲区数据正在被移除的状态 +const OBJECTMODE = Symbol('objectMode') // 标志流是否处于对象模式的状态 +const DESTROYED = Symbol('destroyed') // 表示流已被销毁的标志 + +// 用于决定是否支持异步迭代器,考虑到兼容性 const doIter = global._MP_NO_ITERATOR_SYMBOLS_ !== '1' const ASYNCITERATOR = doIter && Symbol.asyncIterator - || Symbol('asyncIterator not implemented') + || Symbol('asyncIterator not implemented') // 如果支持异步迭代器,则使用 Symbol.asyncIterator,否则使用自定义标识符 const ITERATOR = doIter && Symbol.iterator - || Symbol('iterator not implemented') + || Symbol('iterator not implemented') // 同理,处理同步迭代器 -// Buffer in node 4.x < 4.5.0 doesn't have working Buffer.from -// or Buffer.alloc, and Buffer in node 10 deprecated the ctor. -// .M, this is fine .\^/M.. +// 在较旧版本的 Node.js 中(如 v4.x),没有 `Buffer.alloc` 和 `Buffer.from` 方法,因此需要使用 'safe-buffer' 模块 const B = Buffer.alloc ? Buffer - : /* istanbul ignore next */ require('safe-buffer').Buffer + : /* istanbul ignore next */ require('safe-buffer').Buffer // 根据 Node.js 版本选择合适的 Buffer 类 -// events that mean 'the stream is over' -// these are treated specially, and re-emitted -// if they are listened for after emitting. +// 事件处理:确定流何时结束('end'、'finish' 或 'prefinish' 事件) const isEndish = ev => - ev === 'end' || - ev === 'finish' || - ev === 'prefinish' + ev === 'end' || + ev === 'finish' || + ev === 'prefinish' // 这些事件表示流的结束状态,必须特别处理 +// 判断一个对象是否是 ArrayBuffer 实例 const isArrayBuffer = b => b instanceof ArrayBuffer || - typeof b === 'object' && - b.constructor && - b.constructor.name === 'ArrayBuffer' && - b.byteLength >= 0 + typeof b === 'object' && + b.constructor && + b.constructor.name === 'ArrayBuffer' && + b.byteLength >= 0 // 确保对象是 ArrayBuffer 实例,且有合法的 byteLength -const isArrayBufferView = b => !B.isBuffer(b) && ArrayBuffer.isView(b) +// 判断一个对象是否是 ArrayBuffer 的视图(如 Uint8Array、Int32Array 等) +const isArrayBufferView = b => !B.isBuffer(b) && ArrayBuffer.isView(b) // 确保不是 Buffer,且是 ArrayBuffer 的视图 +// 导出 Minipass 类,该类继承自 EventEmitter(EE) module.exports = class Minipass extends EE { constructor (options) { - super() - this[FLOWING] = false - // whether we're explicitly paused - this[PAUSED] = false - this.pipes = new Yallist() - this.buffer = new Yallist() - this[OBJECTMODE] = options && options.objectMode || false + super() // 调用父类(EventEmitter)的构造函数,初始化事件处理功能 + + this[FLOWING] = false // 流状态标志,表示是否正在流动 + this[PAUSED] = false // 流是否处于暂停状态 + this.pipes = new Yallist() // 使用 Yallist(双向链表)实现管道机制 + this.buffer = new Yallist() // 使用 Yallist 存储数据块 + this[OBJECTMODE] = options && options.objectMode || false // 是否启用对象模式,默认为 false if (this[OBJECTMODE]) - this[ENCODING] = null + this[ENCODING] = null // 如果是对象模式,则没有编码方式 else - this[ENCODING] = options && options.encoding || null + this[ENCODING] = options && options.encoding || null // 如果不是对象模式,设置编码方式(默认为 null) + + // 如果编码方式是 'buffer',则设置为 null,因为在此模式下流中的数据是 Buffer 类型 if (this[ENCODING] === 'buffer') this[ENCODING] = null - this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null - this[EOF] = false - this[EMITTED_END] = false - this[EMITTING_END] = false - this[CLOSED] = false - this.writable = true - this.readable = true - this[BUFFERLENGTH] = 0 - this[DESTROYED] = false + + this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null // 根据编码方式初始化解码器(如果需要) + + // 一些流状态初始化 + this[EOF] = false // 流是否已结束的标志 + this[EMITTED_END] = false // 是否已经发出了 'end' 事件 + this[EMITTING_END] = false // 是否正在发出 'end' 事件 + this[CLOSED] = false // 流是否已关闭 + this.writable = true // 流是否可写 + this.readable = true // 流是否可读 + this[BUFFERLENGTH] = 0 // 初始化缓冲区长度 + this[DESTROYED] = false // 流是否已销毁 } + // 获取当前缓冲区的长度 get bufferLength () { return this[BUFFERLENGTH] } + // 获取和设置编码方式 get encoding () { return this[ENCODING] } set encoding (enc) { - if (this[OBJECTMODE]) + if (this[OBJECTMODE]) // 如果是对象模式,不能设置编码 throw new Error('cannot set encoding in objectMode') + // 如果已经有编码,并且新编码不同,且流已经有数据或解码器未完成,抛出错误 if (this[ENCODING] && enc !== this[ENCODING] && (this[DECODER] && this[DECODER].lastNeed || this[BUFFERLENGTH])) throw new Error('cannot change encoding') + // 设置新的编码方式,并且更新解码器 if (this[ENCODING] !== enc) { - this[DECODER] = enc ? new SD(enc) : null + this[DECODER] = enc ? new SD(enc) : null // 创建新的解码器 if (this.buffer.length) - this.buffer = this.buffer.map(chunk => this[DECODER].write(chunk)) + this.buffer = this.buffer.map(chunk => this[DECODER].write(chunk)) // 如果有数据,进行解码 } - this[ENCODING] = enc + this[ENCODING] = enc // 更新编码方式 } + // 设置编码方式的便捷方法 setEncoding (enc) { - this.encoding = enc + this.encoding = enc // 调用上面的 setter } +} - get objectMode () { return this[OBJECTMODE] } - set objectMode (ॐ ) { this[OBJECTMODE] = this[OBJECTMODE] || !!ॐ } +// 获取对象模式属性值的getter方法,返回存储在this[OBJECTMODE]中的值 +get objectMode () { return this[OBJECTMODE] } - write (chunk, encoding, cb) { - if (this[EOF]) - throw new Error('write after end') +// 设置对象模式属性值的setter方法, +// 如果当前this[OBJECTMODE]未设置(为假值),则根据传入的参数ॐ 来确定是否设置为真(通过!!ॐ 转换为布尔值) +set objectMode (ॐ ) { this[OBJECTMODE] = this[OBJECTMODE] ||!!ॐ } + +// write方法,用于向流中写入数据 +write (chunk, encoding, cb) { + // 如果已经标记为流结束(EOF)状态,抛出错误,表示不能在结束后再写入 + if (this[EOF]) + throw new Error('write after end') - if (this[DESTROYED]) { - this.emit('error', Object.assign( + // 如果流已经被销毁(DESTROYED为真),则触发'error'事件并返回true + if (this[DESTROYED]) { + this.emit('error', Object.assign( new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' } - )) - return true - } + )) + return true + } - if (typeof encoding === 'function') - cb = encoding, encoding = 'utf8' - - if (!encoding) - encoding = 'utf8' - - // convert array buffers and typed array views into buffers - // at some point in the future, we may want to do the opposite! - // leave strings and buffers as-is - // anything else switches us into object mode - if (!this[OBJECTMODE] && !B.isBuffer(chunk)) { - if (isArrayBufferView(chunk)) - chunk = B.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) - else if (isArrayBuffer(chunk)) - chunk = B.from(chunk) - else if (typeof chunk !== 'string') - // use the setter so we throw if we have encoding set - this.objectMode = true - } + // 如果传入的第二个参数encoding是函数类型,则将其作为回调函数cb,同时将encoding默认设置为'utf8' + if (typeof encoding === 'function') + cb = encoding, encoding = 'utf8' + + // 如果encoding未传入(为假值),将其默认设置为'utf8' + if (!encoding) + encoding = 'utf8' + + // 以下代码块用于处理不同类型的数据转换为合适的格式,特别是针对非对象模式下的情况 + // 如果当前不是对象模式且传入的数据chunk不是Buffer类型 + if (!this[OBJECTMODE] &&!B.isBuffer(chunk)) { + // 如果chunk是ArrayBufferView类型,将其转换为Buffer类型 + if (isArrayBufferView(chunk)) + chunk = B.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) + // 如果chunk是ArrayBuffer类型,将其转换为Buffer类型 + else if (isArrayBuffer(chunk)) + chunk = B.from(chunk) + // 如果chunk既不是字符串也不是Buffer类型,切换到对象模式(通过调用setter方法,它会进行相应的错误处理,如果有encoding设置的话) + else if (typeof chunk!=='string') + this.objectMode = true + } - // this ensures at this point that the chunk is a buffer or string - // don't buffer it up or send it to the decoder - if (!this.objectMode && !chunk.length) { - const ret = this.flowing - if (this[BUFFERLENGTH] !== 0) - this.emit('readable') - if (cb) - cb() - return ret - } + // 到这里确保了chunk要么是Buffer类型要么是字符串类型 + // 如果当前不是对象模式且chunk长度为0 + if (!this.objectMode &&!chunk.length) { + const ret = this.flowing + // 如果缓冲区长度(BUFFERLENGTH)不为0,触发'readable'事件 + if (this[BUFFERLENGTH]!== 0) + this.emit('readable') + // 如果有回调函数cb,执行回调 + if (cb) + cb() + return ret + } - // fast-path writing strings of same encoding to a stream with - // an empty buffer, skipping the buffer/decoder dance - if (typeof chunk === 'string' && !this[OBJECTMODE] && - // unless it is a string already ready for us to use - !(encoding === this[ENCODING] && !this[DECODER].lastNeed)) { - chunk = B.from(chunk, encoding) - } + // 以下是针对写入字符串的快速处理路径 + // 如果传入的chunk是字符串类型,且当前不是对象模式,并且不符合特定条件(除非它已经是符合我们使用要求的字符串,即编码匹配且解码器没有剩余需求) + if (typeof chunk ==='string' &&!this[OBJECTMODE] && + // unless it is a string already ready for us to use + !(encoding === this[ENCODING] &&!this[ENCODING].lastNeed)) { + // 将字符串转换为Buffer类型(使用指定的encoding) + chunk = B.from(chunk, encoding) + } - if (B.isBuffer(chunk) && this[ENCODING]) - chunk = this[DECODER].write(chunk) + // 如果chunk是Buffer类型且存在编码(ENCODING)设置,通过解码器对chunk进行处理 + if (B.isBuffer(chunk) && this[ENCODING]) + chunk = this[DECODER].write(chunk) - try { - return this.flowing + try { + // 如果流处于流动(flowing)状态,触发'data'事件并返回当前流动状态,否则将chunk压入缓冲区(BUFFERPUSH)并返回false + return this.flowing ? (this.emit('data', chunk), this.flowing) : (this[BUFFERPUSH](chunk), false) - } finally { - if (this[BUFFERLENGTH] !== 0) - this.emit('readable') - if (cb) - cb() - } + } finally { + // 如果缓冲区长度(BUFFERLENGTH)不为0,触发'readable'事件 + if (this[BUFFERLENGTH]!== 0) + this.emit('readable') + // 如果有回调函数cb,执行回调 + if (cb) + cb() } +} - read (n) { - if (this[DESTROYED]) - return null - - try { - if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) - return null - - if (this[OBJECTMODE]) - n = null - - if (this.buffer.length > 1 && !this[OBJECTMODE]) { - if (this.encoding) - this.buffer = new Yallist([ - Array.from(this.buffer).join('') - ]) - else - this.buffer = new Yallist([ - B.concat(Array.from(this.buffer), this[BUFFERLENGTH]) - ]) - } +// read方法,用于从流中读取数据 +read (n) { + // 如果流已经被销毁(DESTROYED为真),返回null + if (this[DESTROYED]) + return null - return this[READ](n || null, this.buffer.head.value) - } finally { - this[MAYBE_EMIT_END]() - } - } + try { + // 如果缓冲区长度为0,或者读取的字节数n为0或者大于缓冲区长度,返回null + if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) + return null - [READ] (n, chunk) { - if (n === chunk.length || n === null) - this[BUFFERSHIFT]() - else { - this.buffer.head.value = chunk.slice(n) - chunk = chunk.slice(0, n) - this[BUFFERLENGTH] -= n + // 如果处于对象模式,将读取字节数n设置为null + if (this[OBJECTMODE]) + n = null + + // 如果缓冲区中元素数量大于1且不是对象模式 + if (this.buffer.length > 1 &&!this[OBJECTMODE]) { + // 如果存在编码设置,将缓冲区内容合并为一个字符串元素的列表 + if (this.encoding) + this.buffer = new Yallist([ + Array.from(this.buffer).join('') + ]) + // 否则将缓冲区内容合并为一个Buffer类型元素的列表(根据缓冲区长度进行合并) + else + this.buffer = new Yallist([ + B.concat(Array.from(this.buffer), this[BUFFERLENGTH]) + ]) } - this.emit('data', chunk) - - if (!this.buffer.length && !this[EOF]) - this.emit('drain') - - return chunk + // 调用内部的[READ]方法进行实际的读取操作,并返回读取到的数据 + return this[READ](n || null, this.buffer.head.value) + } finally { + // 执行可能的结束(EOF)相关的触发操作(具体由[MAYBE_EMIT_END]方法定义) + this[MAYBE_EMIT_END]() } +} - end (chunk, encoding, cb) { - if (typeof chunk === 'function') - cb = chunk, chunk = null - if (typeof encoding === 'function') - cb = encoding, encoding = 'utf8' - if (chunk) - this.write(chunk, encoding) - if (cb) - this.once('end', cb) - this[EOF] = true - this.writable = false - - // if we haven't written anything, then go ahead and emit, - // even if we're not reading. - // we'll re-emit if a new 'end' listener is added anyway. - // This makes MP more suitable to write-only use cases. - if (this.flowing || !this[PAUSED]) - this[MAYBE_EMIT_END]() - return this +// [READ]方法,内部用于实际处理读取数据并更新缓冲区等相关操作 +[READ] (n, chunk) { + // 如果要读取的字节数n等于chunk长度或者n为null(读取全部),则从缓冲区移除已读取的数据(通过BUFFERSHIFT方法) + if (n === chunk.length || n === null) + this[BUFFERSHIFT]() + else { + // 否则更新缓冲区头部元素(剩余未读取部分),并更新chunk为已读取部分,同时减少缓冲区长度记录 + this.buffer.head.value = chunk.slice(n) + chunk = chunk.slice(0, n) + this[BUFFERLENGTH] -= n } - // don't let the internal resume be overwritten - [RESUME] () { - if (this[DESTROYED]) - return + // 触发'data'事件,传递读取到的数据chunk + this.emit('data', chunk) - this[PAUSED] = false - this[FLOWING] = true - this.emit('resume') - if (this.buffer.length) - this[FLUSH]() - else if (this[EOF]) - this[MAYBE_EMIT_END]() - else - this.emit('drain') - } + // 如果缓冲区为空且未到流结束(EOF)状态,触发'drain'事件 + if (!this.buffer.length &&!this[EOF]) + this.emit('drain') - resume () { - return this[RESUME]() - } + return chunk +} - pause () { - this[FLOWING] = false - this[PAUSED] = true - } +// end方法,用于标记流的结束操作 +end (chunk, encoding, cb) { + // 如果传入的第一个参数chunk是函数类型,将其赋值给cb(作为回调函数),并将chunk设置为null + if (typeof chunk === 'function') + cb = chunk, chunk = null + // 如果传入的第二个参数encoding是函数类型,将其赋值给cb(作为回调函数),并将encoding默认设置为'utf8' + if (typeof encoding === 'function') + cb = encoding, encoding = 'utf8' + // 如果存在要写入的数据chunk,调用write方法将其写入流中(使用指定的encoding) + if (chunk) + this.write(chunk, encoding) + // 如果有回调函数cb,监听一次'end'事件,当事件触发时执行cb回调 + if (cb) + this.once('end', cb) + // 标记流已经到达结束状态(EOF为真) + this[EOF] = true + // 设置流不可写 + this.writable = false + + // 如果流处于流动状态(flowing为真)或者没有暂停(PAUSED为假),则执行可能的结束相关触发操作(通过[MAYBE_EMIT_END]方法) + // 这样即使没有进行读取操作,若之前没写入过内容也会触发,而且如果后续添加新的'end'监听器也会重新触发,使得该模块更适合只写的用例 + if (this.flowing ||!this[PAUSED]) + this[MAYBE_EMIT_END]() + return this +} - get destroyed () { - return this[DESTROYED] - } +// [RESUME]方法,内部用于恢复流的操作,不允许被外部重写 +[RESUME] () { + // 如果流已经被销毁(DESTROYED为真),直接返回,不进行后续操作 + if (this[DESTROYED]) + return + + // 将暂停状态(PAUSED)设置为假,表示流不再暂停 + this[PAUSED] = false + // 将流动状态(FLOWING)设置为真,表示流处于流动可操作状态 + this[FLOWING] = true + // 触发'resume'事件,表示流已恢复 + this.emit('resume') + // 如果缓冲区中有数据,调用[FLUSH]方法进行刷新处理 + if (this.buffer.length) + this[FLUSH]() + // 如果已经到达流结束状态(EOF为真),执行可能的结束相关触发操作(通过[MAYBE_EMIT_END]方法) + else if (this[EOF]) + this[MAYBE_EMIT_END]() + // 否则触发'drain'事件,表示缓冲区已排空,可以继续写入等操作 + else + this.emit('drain') +} - get flowing () { - return this[FLOWING] - } +// resume方法,对外提供的恢复流操作的接口,调用内部的[RESUME]方法来实现具体功能 +resume () { + return this[RESUME]() +} - get paused () { - return this[PAUSED] - } +// pause方法,用于暂停流的操作,将流动状态(FLOWING)设置为假,暂停状态(PAUSED)设置为真 +pause () { + this[FLOWING] = false + this[PAUSED] = true +} - [BUFFERPUSH] (chunk) { - if (this[OBJECTMODE]) - this[BUFFERLENGTH] += 1 - else - this[BUFFERLENGTH] += chunk.length - return this.buffer.push(chunk) - } +// 获取流是否已被销毁的属性访问器,返回存储在this[DESTROYED]中的值 +get destroyed () { + return this[DESTROYED] +} - [BUFFERSHIFT] () { - if (this.buffer.length) { - if (this[OBJECTMODE]) - this[BUFFERLENGTH] -= 1 - else - this[BUFFERLENGTH] -= this.buffer.head.value.length - } - return this.buffer.shift() - } +// 获取流是否处于流动状态的属性访问器,返回存储在this[FLOWING]中的值 +get flowing () { + return this[FLOWING] +} - [FLUSH] () { - do {} while (this[FLUSHCHUNK](this[BUFFERSHIFT]())) +// 获取流是否处于暂停状态的属性访问器,返回存储在this[PAUSED]中的值 +get paused () { + return this[PAUSED] +} - if (!this.buffer.length && !this[EOF]) - this.emit('drain') - } +// [BUFFERPUSH]方法,用于向缓冲区中推送数据chunk +// 根据是否处于对象模式(OBJECTMODE)来更新缓冲区长度记录,并将chunk添加到缓冲区中 +[BUFFERPUSH] (chunk) { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] += 1 + else + this[BUFFERLENGTH] += chunk.length + return this.buffer.push(chunk) +} - [FLUSHCHUNK] (chunk) { - return chunk ? (this.emit('data', chunk), this.flowing) : false +// [BUFFERSHIFT]方法,用于从缓冲区移除数据(通常是已处理过的数据) +// 根据缓冲区中是否有数据以及是否处于对象模式来相应地减少缓冲区长度记录,并移除缓冲区头部元素(返回移除的元素) +[BUFFERSHIFT] () { + if (this.buffer.length) { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] -= 1 + else + this[BUFFERLENGTH] -= this.buffer.head.value.length } + return this.buffer.shift() +} - pipe (dest, opts) { - if (this[DESTROYED]) - return +// [FLUSH]方法,用于刷新缓冲区中的数据,不断调用[FLUSHCHUNK]方法处理缓冲区头部的数据,直到缓冲区为空 +// 如果缓冲区排空且未到达流结束(EOF为假)状态,触发'drain'事件 +[FLUSH] () { + do {} while (this[FLUSHCHUNK](this[BUFFERSHIFT]())) - const ended = this[EMITTED_END] - opts = opts || {} - if (dest === process.stdout || dest === process.stderr) - opts.end = false - else - opts.end = opts.end !== false + if (!this.buffer.length &&!this[EOF]) + this.emit('drain') +} - const p = { dest: dest, opts: opts, ondrain: _ => this[RESUME]() } - this.pipes.push(p) +// [FLUSHCHUNK]方法,用于处理单个缓冲区数据块chunk +// 如果chunk存在,触发'data'事件并返回流的流动状态(flowing),否则返回false +[FLUSHCHUNK] (chunk) { + return chunk? (this.emit('data', chunk), this.flowing) : false +} - dest.on('drain', p.ondrain) - this[RESUME]() - // piping an ended stream ends immediately - if (ended && p.opts.end) - p.dest.end() - return dest - } +// pipe方法,用于将当前流的数据导向另一个目标流dest +pipe (dest, opts) { + // 如果当前流已经被销毁(DESTROYED为真),直接返回,不进行管道连接操作 + if (this[DESTROYED]) + return + + const ended = this[EMITTED_END] + opts = opts || {} + // 如果目标流是标准输出(process.stdout)或者标准错误输出(process.stderr),则设置opts.end为false,表示不自动结束目标流 + if (dest === process.stdout || dest === process.stderr) + opts.end = false + else + opts.end = opts.end!== false + + const p = { dest: dest, opts: opts, ondrain: _ => this[RESUME]() } + // 将管道相关配置信息添加到pipes数组中,用于管理管道连接情况 + this.pipes.push(p) + + // 监听目标流的'drain'事件,当触发时调用当前流的[RESUME]方法恢复操作 + dest.on('drain', p.ondrain) + // 恢复当前流的操作(调用[RESUME]方法) + this[RESUME]() + // 如果当前流已经结束(ended为真)且管道配置中要求自动结束(opts.end为真),则结束目标流 + if (ended && p.opts.end) + p.dest.end() + return dest +} - addListener (ev, fn) { - return this.on(ev, fn) - } +// addListener方法,实际上就是调用on方法来添加事件监听器,只是对外提供了另一种命名方式 +addListener (ev, fn) { + return this.on(ev, fn) +} - on (ev, fn) { - try { - return super.on(ev, fn) - } finally { - if (ev === 'data' && !this.pipes.length && !this.flowing) - this[RESUME]() - else if (isEndish(ev) && this[EMITTED_END]) { - super.emit(ev) - this.removeAllListeners(ev) - } +// on方法,用于添加指定事件ev的监听器fn +// 先尝试调用父类(通过super关键字,假设存在继承关系)的on方法来添加监听器,然后根据不同情况进行额外处理 +on (ev, fn) { + try { + return super.on(ev, fn) + } finally { + // 如果添加的是'data'事件监听器,且当前没有管道连接(pipes长度为0)且流未处于流动状态(flowing为假),则恢复流的操作(调用[RESUME]方法) + if (ev === 'data' &&!this.pipes.length &&!this.flowing) + this[RESUME]() + // 如果添加的是类似结束相关的事件(通过isEndish函数判断,这里假设其有相应逻辑)且流已经触发过结束(EMITTED_END为真), + // 则再次触发该事件(通过super.emit),并移除该事件的所有监听器(通过removeAllListeners方法) + else if (isEndish(ev) && this[EMITTED_END]) { + super.emit(ev) + this.removeAllListeners(ev) } } +} - get emittedEnd () { - return this[EMITTED_END] - } +// emittedEnd属性访问器,用于获取流是否已经触发过结束事件(返回存储在this[EMITTED_END]中的值) +get emittedEnd () { + return this[EMITTED_END] +} - [MAYBE_EMIT_END] () { - if (!this[EMITTING_END] && - !this[EMITTED_END] && - !this[DESTROYED] && - this.buffer.length === 0 && - this[EOF]) { - this[EMITTING_END] = true - this.emit('end') - this.emit('prefinish') - this.emit('finish') - if (this[CLOSED]) - this.emit('close') - this[EMITTING_END] = false - } +// [MAYBE_EMIT_END]方法,用于在满足一定条件下触发与流结束相关的多个事件 +[MAYBE_EMIT_END] () { + // 以下条件都满足时才会执行后续的事件触发操作: + // 1. 当前没有正在触发结束事件(EMITTING_END为假) + // 2. 之前没有触发过结束事件(EMITTED_END为假) + // 3. 流没有被销毁(DESTROYED为假) + // 4. 缓冲区长度为0(buffer.length === 0) + // 5. 已经标记流到达结束状态(EOF为真) + if (!this[EMITTING_END] && + !this[EMITTED_END] && + !this[DESTROYED] && + this.buffer.length === 0 && + this[EOF]) { + // 标记正在触发结束事件 + this[EMITTING_END] = true + // 依次触发'end'、'prefinish'、'finish'事件 + this.emit('end') + this.emit('prefinish') + this.emit('finish') + // 如果流已经关闭(CLOSED为真),触发'close'事件 + if (this[CLOSED]) + this.emit('close') + // 标记结束事件触发完毕 + this[EMITTING_END] = false } +} - emit (ev, data) { - // error and close are only events allowed after calling destroy() - if (ev !== 'error' && ev !== 'close' && ev !== DESTROYED && this[DESTROYED]) +// emit方法,用于触发指定的事件,并根据不同事件类型进行相应的处理 +emit (ev, data) { + // 如果触发的事件不是'error'、'close'以及DESTROYED(这里假设DESTROYED是一个特定的表示销毁的标识常量), + // 且流已经被销毁(DESTROYED为真),则直接返回,不进行事件触发操作 + if (ev!== 'error' && ev!== 'close' && ev!== DESTROYED && this[DESTROYED]) + return + else if (ev === 'data') { + // 如果触发的是'data'事件,且传入的数据data为空(假值),则直接返回,不做处理 + if (!data) return - else if (ev === 'data') { - if (!data) - return - if (this.pipes.length) - this.pipes.forEach(p => + // 如果存在管道连接(pipes长度大于0),遍历每个管道配置对象p + // 调用目标流的write方法写入数据,如果返回false(表示目标流缓冲区已满等情况),则暂停当前流(调用pause方法) + if (this.pipes.length) + this.pipes.forEach(p => p.dest.write(data) === false && this.pause()) - } else if (ev === 'end') { - // only actual end gets this treatment - if (this[EMITTED_END] === true) - return - - this[EMITTED_END] = true - this.readable = false - - if (this[DECODER]) { - data = this[DECODER].end() - if (data) { - this.pipes.forEach(p => p.dest.write(data)) - super.emit('data', data) - } - } - - this.pipes.forEach(p => { - p.dest.removeListener('drain', p.ondrain) - if (p.opts.end) - p.dest.end() - }) - } else if (ev === 'close') { - this[CLOSED] = true - // don't emit close before 'end' and 'finish' - if (!this[EMITTED_END] && !this[DESTROYED]) - return - } + } else if (ev === 'end') { + // 如果触发的是'end'事件,且已经标记过结束事件(EMITTED_END为真),则直接返回,不重复处理 + if (this[EMITTED_END] === true) + return - // TODO: replace with a spread operator when Node v4 support drops - const args = new Array(arguments.length) - args[0] = ev - args[1] = data - if (arguments.length > 2) { - for (let i = 2; i < arguments.length; i++) { - args[i] = arguments[i] + // 标记已经触发过结束事件 + this[EMITTED_END] = true + // 设置流不可读 + this.readable = false + + // 如果存在解码器(DECODER),调用其end方法获取最终的数据 + if (this[DECODER]) { + data = this[DECODER].end(); + // 如果有返回的数据,遍历管道配置对象,将数据写入目标流,并通过父类的emit方法触发'data'事件 + if (data) { + this.pipes.forEach(p => p.dest.write(data)); + super.emit('data', data); } } - try { - return super.emit.apply(this, args) - } finally { - if (!isEndish(ev)) - this[MAYBE_EMIT_END]() - else - this.removeAllListeners(ev) - } + // 遍历管道配置对象,移除目标流对'drain'事件的监听器, + // 如果管道配置中要求自动结束(opts.end为真),则结束目标流 + this.pipes.forEach(p => { + p.dest.removeListener('drain', p.ondrain); + if (p.opts.end) + p.dest.end(); + }); + } else if (ev === 'close') { + // 如果触发的是'close'事件,标记流已关闭 + this[CLOSED] = true; + // 如果还没有触发过结束事件(EMITTED_END为假)且流没有被销毁(DESTROYED为假),则直接返回,不触发'close'事件, + // 即确保在'end'和'finish'事件之后才触发'close'事件 + if (!this[EMITTED_END] &&!this[DESTROYED]) + return; } - // const all = await stream.collect() - collect () { - const buf = [] - buf.dataLength = 0 - this.on('data', c => { - buf.push(c) - buf.dataLength += c.length - }) - return this.promise().then(() => buf) + // 创建一个与传入参数arguments长度相同的数组args,用于后续传递给父类的emit方法 + // (这里这么做可能是考虑到不同版本Node.js对参数传递方式的兼容性等问题,后续计划用展开运算符替换,如注释所述) + const args = new Array(arguments.length); + args[0] = ev; + args[1] = data; + if (arguments.length > 2) { + for (let i = 2; i < arguments.length; i++) { + args[i] = arguments[i]; + } } - // const data = await stream.concat() - concat () { - return this[OBJECTMODE] - ? Promise.reject(new Error('cannot concat in objectMode')) - : this.collect().then(buf => - this[OBJECTMODE] - ? Promise.reject(new Error('cannot concat in objectMode')) - : this[ENCODING] ? buf.join('') : B.concat(buf, buf.dataLength)) + try { + // 尝试调用父类的emit方法触发指定事件,并传递整理好的参数args,返回父类emit方法的执行结果 + return super.emit.apply(this, args); + } finally { + // 如果触发的事件不是类似结束相关的事件(通过isEndish函数判断,这里假设其有相应逻辑), + // 调用[MAYBE_EMIT_END]方法,检查是否需要触发结束相关事件 + if (!isEndish(ev)) + this[MAYBE_EMIT_END](); + // 如果是类似结束相关的事件,移除该事件的所有监听器 + else + this.removeAllListeners(ev); } +} - // stream.promise().then(() => done, er => emitted error) - promise () { - return new Promise((resolve, reject) => { - this.on(DESTROYED, () => reject(new Error('stream destroyed'))) - this.on('end', () => resolve()) - this.on('error', er => reject(er)) - }) - } +// collect方法,用于收集流中的数据,通常可以配合异步操作使用 +// 例如可以像这样使用:const all = await stream.collect(); +collect () { + // 创建一个数组buf用于存储收集到的数据 + const buf = []; + // 记录收集到的数据总长度 + buf.dataLength = 0; + // 监听'data'事件,每当有数据到来时,将数据添加到buf数组中,并更新数据总长度 + this.on('data', c => { + buf.push(c); + buf.dataLength += c.length; + }); + // 返回一个Promise,当流结束(通过promise方法监听'end'事件)后,返回收集到的数据buf数组 + return this.promise().then(() => buf); +} - // for await (let chunk of stream) - [ASYNCITERATOR] () { - const next = () => { - const res = this.read() - if (res !== null) - return Promise.resolve({ done: false, value: res }) - - if (this[EOF]) - return Promise.resolve({ done: true }) - - let resolve = null - let reject = null - const onerr = er => { - this.removeListener('data', ondata) - this.removeListener('end', onend) - reject(er) - } - const ondata = value => { - this.removeListener('error', onerr) - this.removeListener('end', onend) - this.pause() - resolve({ value: value, done: !!this[EOF] }) - } - const onend = () => { - this.removeListener('error', onerr) - this.removeListener('data', ondata) - resolve({ done: true }) - } - const ondestroy = () => onerr(new Error('stream destroyed')) - return new Promise((res, rej) => { - reject = rej - resolve = res - this.once(DESTROYED, ondestroy) - this.once('error', onerr) - this.once('end', onend) - this.once('data', ondata) - }) - } +// concat方法,用于将流中的数据连接起来(如果支持的话) +// 例如可以像这样使用:const data = await stream.concat(); +concat () { + // 如果处于对象模式(OBJECTMODE为真),直接返回一个被拒绝的Promise, + // 因为在对象模式下不支持连接操作,并给出相应错误提示 + if (this[OBJECTMODE]) + return Promise.reject(new Error('cannot concat in objectMode')); + // 调用collect方法收集数据,然后基于收集到的数据进行后续处理 + return this.collect().then(buf => + // 再次检查是否处于对象模式,如果是则同样返回被拒绝的Promise及相应错误提示 + this[OBJECTMODE] + ? Promise.reject(new Error('cannot concat in objectMode')) + : // 如果存在编码(ENCODING)设置,将收集到的buf数组中的数据连接为字符串返回,否则使用B.concat方法(这里假设B有相应的处理逻辑) + // 根据数据总长度buf.dataLength将数据连接起来并返回 + this[ENCODING]? buf.join('') : B.concat(buf, buf.dataLength)); +} - return { next } - } +// promise方法,返回一个Promise,用于监听流的几个关键状态事件 +// 例如可以像这样使用:stream.promise().then(() => done, er => emitted error); +promise () { + return new Promise((resolve, reject) => { + // 监听流被销毁(DESTROYED)事件,若触发则拒绝Promise,并给出相应错误提示 + this.on(DESTROYED, () => reject(new Error('stream destroyed'))); + // 监听流结束('end')事件,若触发则将Promise状态置为已解决(resolve) + this.on('end', () => resolve()); + // 监听流出现错误('error')事件,若触发则将Promise状态置为已拒绝,并传递错误对象er + this.on('error', er => reject(er)); + }); +} - // for (let chunk of stream) - [ITERATOR] () { - const next = () => { - const value = this.read() - const done = value === null - return { value, done } - } - return { next } - } +// [ASYNCITERATOR]方法,用于实现流的异步迭代器功能,使得可以使用 `for await (let chunk of stream)` 语法来迭代流中的数据 +[ASYNCITERATOR] () { + const next = () => { + // 调用read方法从流中读取数据 + const res = this.read(); + // 如果读取到的数据不为null,说明有可用数据,将其包装在一个已解决的Promise中返回,标记迭代未完成(done: false),并传递读取到的数据作为值(value) + if (res!== null) + return Promise.resolve({ done: false, value: res }); - destroy (er) { - if (this[DESTROYED]) { - if (er) - this.emit('error', er) - else - this.emit(DESTROYED) - return this - } + // 如果已经到达流的结束位置(EOF为真),返回一个已解决的Promise,标记迭代已完成(done: true) + if (this[EOF]) + return Promise.resolve({ done: true }); + + // 以下定义了用于处理各种流相关事件的回调函数以及Promise相关的控制变量 + let resolve = null; + let reject = null; + const onerr = er => { + // 当出现错误时,移除对'data'、'end'事件的监听器,然后通过reject拒绝Promise,并传递错误对象er + this.removeListener('data', ondata); + this.removeListener('end', onend); + reject(er); + }; + const ondata = value => { + // 当接收到数据时,移除对'error'、'end'事件的监听器,暂停流(调用pause方法), + // 然后通过resolve解决Promise,传递包含接收到的数据(value)以及根据EOF状态判断的迭代是否完成(done)的对象 + this.removeListener('error', onerr); + this.removeListener('end', onend); + this.pause(); + resolve({ value: value, done:!!this[EOF] }); + }; + const onend = () => { + // 当流结束时,移除对'error'、'data'事件的监听器,然后通过resolve解决Promise,标记迭代已完成(done: true) + this.removeListener('error', onerr); + this.removeListener('data', ondata); + resolve({ done: true }); + }; + const ondestroy = () => onerr(new Error('stream destroyed')); + + // 创建一个新的Promise,用于等待流相关事件的触发来决定Promise的状态(解决或拒绝) + return new Promise((res, rej) => { + reject = rej; + resolve = res; + // 监听流被销毁(DESTROYED)事件,若触发则调用ondestroy函数处理 + this.once(DESTROYED, ondestroy); + // 监听流出现错误('error')事件,若触发则调用onerr函数处理 + this.once('error', onerr); + // 监听流结束('end')事件,若触发则调用onend函数处理 + this.once('end', onend); + // 监听流有新数据到来('data')事件,若触发则调用ondata函数处理 + this.once('data', ondata); + }); + }; + + // 返回一个包含next方法的对象,符合异步迭代器协议要求,使得流可以被异步迭代 + return { next }; +} - this[DESTROYED] = true +// [ITERATOR]方法,用于实现流的普通迭代器功能,使得可以使用 `for (let chunk of stream)` 语法来迭代流中的数据(非异步情况) +[ITERATOR] () { + const next = () => { + // 调用read方法从流中读取数据 + const value = this.read(); + // 根据读取到的数据是否为null来判断迭代是否完成,将结果(包含读取到的数据和迭代完成标记)作为对象返回 + const done = value === null; + return { value, done }; + }; + // 返回一个包含next方法的对象,符合迭代器协议要求,使得流可以被迭代 + return { next }; +} - // throw away all buffered data, it's never coming out - this.buffer = new Yallist() - this[BUFFERLENGTH] = 0 +// destroy方法,用于彻底销毁流对象,释放相关资源,执行清理操作等 +destroy (er) { + // 如果流已经被销毁(DESTROYED为真),根据是否传入错误对象er来决定是否触发'error'事件或者DESTROYED事件,然后返回当前流对象 + if (this[DESTROYED]) { + if (er) + this.emit('error', er); + else + this.emit(DESTROYED); + return this; + } - if (typeof this.close === 'function' && !this[CLOSED]) - this.close() + // 标记流已经被销毁 + this[DESTROYED] = true; - if (er) - this.emit('error', er) - else // if no error to emit, still reject pending promises - this.emit(DESTROYED) + // 清空缓冲区数据,将缓冲区替换为一个新的空的Yallist实例(这里假设Yallist是一种数据结构,用于存储缓冲区数据),并将缓冲区长度设置为0 + this.buffer = new Yallist(); + this[BUFFERLENGTH] = 0; - return this - } + // 如果存在close方法(是一个函数)且流还未关闭(CLOSED为假),调用close方法执行关闭相关的操作(具体由close方法内部逻辑决定) + if (typeof this.close === 'function' &&!this[CLOSED]) + this.close(); + + // 如果传入了错误对象er,触发'error'事件并传递该错误对象, + // 否则(没有错误要触发),仍然触发DESTROYED事件,用于通知相关的Promise等外部逻辑流已被销毁(例如可能会导致关联的Promise被拒绝等情况) + if (er) + this.emit('error', er); + else // if no error to emit, still reject pending promises + this.emit(DESTROYED); + + // 返回当前流对象 + return this; +} - static isStream (s) { - return !!s && (s instanceof Minipass || s instanceof EE && ( +// 静态方法isStream,用于判断给定的对象s是否是一个流对象 +// 通过检查对象是否存在以及是否满足特定的类型或具备特定的方法来进行判断 +static isStream (s) { + return!!s && (s instanceof Minipass || s instanceof EE && ( + // 如果对象s是Minipass的实例,或者是EE的实例且具备pipe方法(通常表示可读流), + // 或者具备write和end方法(通常表示可写流),则认为它是一个流对象 typeof s.pipe === 'function' || // readable (typeof s.write === 'function' && typeof s.end === 'function') // writable - )) - } + )); } diff --git a/node_modules/minizlib/constants.js b/node_modules/minizlib/constants.js index 641ebc7..672b6d0 100644 --- a/node_modules/minizlib/constants.js +++ b/node_modules/minizlib/constants.js @@ -2,114 +2,228 @@ // Node v6 didn't export this, so we just hard code the version and rely // on all the other hard-coded values from zlib v4736. When node v6 // support drops, we can just export the realZlibConstants object. +// 尝试获取 'zlib' 模块中的 'constants' 属性,如果获取失败(例如 'zlib' 模块不存在或没有 'constants' 属性), +// 则使用一个包含 'ZLIB_VERNUM: 4736' 的对象作为替代(这里使用了 istanbul ignore next 注释,可能是用于在测试覆盖时忽略该行, +// 表示该行在测试中不需要特别关注其执行情况) const realZlibConstants = require('zlib').constants || - /* istanbul ignore next */ { ZLIB_VERNUM: 4736 } + /* istanbul ignore next */ { ZLIB_VERNUM: 4736 }; +// 使用 Object.freeze 冻结对象,防止其被修改,通过 Object.assign 将多个对象合并为一个新对象,并将其作为模块的导出内容。 +// 首先创建一个继承自 null 的空对象(Object.create(null))作为基础对象,然后将一系列属性添加到这个基础对象上。 module.exports = Object.freeze(Object.assign(Object.create(null), { + // 以下是定义的一系列常量及其对应的值,这些常量大概率与 zlib 相关的压缩、解压操作及配置等方面有关。 + + // 表示不进行刷新操作的常量,常用于 zlib 相关的流操作等场景,值为 0 Z_NO_FLUSH: 0, + // 表示部分刷新的常量,在某些特定的流数据处理中间阶段可能会用到,值为 1 Z_PARTIAL_FLUSH: 1, + // 表示同步刷新的常量,用于强制将缓冲数据立即刷新处理,值为 2 Z_SYNC_FLUSH: 2, + // 表示完全刷新的常量,可能涉及更彻底的数据处理和刷新,值为 3 Z_FULL_FLUSH: 3, + // 用于标记压缩或解压操作完成的常量,值为 4 Z_FINISH: 4, + // 可能与数据块相关的一个标识常量,具体含义需结合 zlib 具体使用场景,值为 5 Z_BLOCK: 5, + // 表示操作成功的常量,通常用于判断 zlib 相关函数调用的返回结果,值为 0 Z_OK: 0, + // 表示流已经到达结束状态的常量,比如在解压完成等情况时使用,值为 1 Z_STREAM_END: 1, + // 表示需要字典(可能用于特定的压缩算法辅助解压等情况)的常量,值为 2 Z_NEED_DICT: 2, + // 表示出现了系统错误(errno 相关,通常关联底层系统调用错误情况)的常量,值为 -1 Z_ERRNO: -1, + // 表示流出现错误的常量,比如流的初始化、配置等方面出现问题,值为 -2 Z_STREAM_ERROR: -2, + // 表示数据出现错误的常量,可能是传入的数据格式不符合要求等情况,值为 -3 Z_DATA_ERROR: -3, + // 表示内存相关错误的常量,比如内存分配不足等情况,值为 -4 Z_MEM_ERROR: -4, + // 表示缓冲区相关错误的常量,例如缓冲区溢出、不足等问题,值为 -5 Z_BUF_ERROR: -5, + // 表示版本相关错误的常量,可能是使用的 zlib 版本与期望不符等情况,值为 -6 Z_VERSION_ERROR: -6, + // 表示不进行压缩的配置常量,对应压缩等级等相关设置,值为 0 Z_NO_COMPRESSION: 0, + // 表示以最快速度进行压缩的配置常量,牺牲一定的压缩率来换取速度,值为 1 Z_BEST_SPEED: 1, + // 表示以最佳压缩效果进行压缩的配置常量,可能会消耗更多时间和资源来获得最高的压缩率,值为 9 Z_BEST_COMPRESSION: 9, + // 表示使用默认压缩配置的常量,具体的默认压缩程度由 zlib 内部定义,值为 -1 Z_DEFAULT_COMPRESSION: -1, + // 可能是一种压缩策略相关的常量,具体含义取决于 zlib 具体实现中的压缩算法逻辑,值为 1 Z_FILTERED: 1, + // 可能表示仅使用哈夫曼编码(一种数据编码方式常用于压缩)的常量,值为 2 Z_HUFFMAN_ONLY: 2, + // 可能是与行程长度编码(一种简单的数据压缩编码方式)相关的常量,值为 3 Z_RLE: 3, + // 可能是一种固定模式(具体针对压缩算法中的某些固定配置情况)的常量,值为 4 Z_FIXED: 4, + // 表示默认压缩策略的常量,对应压缩算法中的策略选择,值为 0 Z_DEFAULT_STRATEGY: 0, + // 表示使用 deflate 压缩算法的标识常量,常用于指定具体的压缩方法,值为 1 DEFLATE: 1, + // 表示使用 inflate 解压算法的标识常量,用于指定解压操作,值为 2 INFLATE: 2, + // 表示使用 gzip 格式进行压缩或解压相关操作的标识常量,gzip 是一种常见的压缩文件格式,值为 3 GZIP: 3, + // 表示对 gzip 格式文件进行解压操作的标识常量,与 GZIP 相对应,值为 4 GUNZIP: 4, + // 表示使用原始 deflate 算法(可能不包含 gzip 等格式的额外头部等信息)进行压缩的标识常量,值为 5 DEFLATERAW: 5, + // 表示使用原始 inflate 算法(对应 DEFLATERAW 进行解压操作)的标识常量,值为 6 INFLATERAW: 6, + // 可能是一个通用的解压相关的标识常量,具体含义取决于具体使用场景,值为 7 UNZIP: 7, + // 表示使用 Brotli 算法进行解码操作的标识常量,Brotli 是一种新的压缩算法,值为 8 BROTLI_DECODE: 8, + // 表示使用 Brotli 算法进行编码(压缩)操作的标识常量,值为 9 BROTLI_ENCODE: 9, + // 表示 Brotli 算法中最小窗口位数的常量,窗口位数与压缩算法中的数据处理范围等相关,值为 8 Z_MIN_WINDOWBITS: 8, + // 表示 Brotli 算法中最大窗口位数的常量,限定了窗口位数的上限,值为 15 Z_MAX_WINDOWBITS: 15, + // 表示 Brotli 算法中默认窗口位数的常量,通常在未指定时使用的窗口位数设置,值为 15 Z_DEFAULT_WINDOWBITS: 15, + // 表示 Brotli 算法中最小数据块大小的常量,在处理数据块时的下限,值为 64 Z_MIN_CHUNK: 64, + // 表示 Brotli 算法中最大数据块大小的常量,理论上可以处理的最大数据块尺寸(这里设置为无穷大),值为 Infinity Z_MAX_CHUNK: Infinity, + // 表示 Brotli 算法中默认数据块大小的常量,通常处理数据块时的默认尺寸,值为 16384 Z_DEFAULT_CHUNK: 16384, + // 表示 Brotli 算法中最小内存级别(可能与内存使用、分配策略相关)的常量,值为 1 Z_MIN_MEMLEVEL: 1, + // 表示 Brotli 算法中最大内存级别(限定内存使用相关策略的上限)的常量,值为 9 Z_MAX_MEMLEVEL: 9, + // 表示 Brotli 算法中默认内存级别(未指定时使用的内存级别设置)的常量,值为 8 Z_DEFAULT_MEMLEVEL: 8, + // 表示 Brotli 算法中最小压缩级别(类似压缩程度的设置,负数可能有特殊含义)的常量,值为 -1 Z_MIN_LEVEL: -1, + // 表示 Brotli 算法中最大压缩级别(限定压缩程度上限)的常量,值为 9 Z_MAX_LEVEL: 9, + // 表示 Brotli 算法中默认压缩级别(未指定时使用的压缩级别设置)的常量,值为 -1 Z_DEFAULT_LEVEL: -1, + // 表示 Brotli 算法中处理操作类型为常规处理的常量,对应具体操作阶段的标识,值为 0 BROTLI_OPERATION_PROCESS: 0, + // 表示 Brotli 算法中处理操作类型为刷新操作的常量,可能用于及时刷新缓冲数据等情况,值为 1 BROTLI_OPERATION_FLUSH: 1, + // 表示 Brotli 算法中处理操作类型为完成操作(类似结束整个压缩或解压流程)的常量,值为 2 BROTLI_OPERATION_FINISH: 2, + // 表示 Brotli 算法中处理操作类型为发出元数据(可能在特定场景下向外提供相关数据信息)的常量,值为 3 BROTLI_OPERATION_EMIT_METADATA: 3, + // 表示 Brotli 算法中通用模式(可能适用于多种类型数据的一种默认模式)的常量,值为 0 BROTLI_MODE_GENERIC: 0, + // 表示 Brotli 算法中文本模式(针对文本数据进行优化处理的模式)的常量,值为 1 BROTLI_MODE_TEXT: 1, + // 表示 Brotli 算法中字体模式(可能针对字体文件等特定数据类型优化)的常量,值为 2 BROTLI_MODE_FONT: 2, + // 表示 Brotli 算法中默认模式(未指定具体模式时使用的模式设置)的常量,值为 0 BROTLI_DEFAULT_MODE: 0, + // 表示 Brotli 算法中最小质量(可能与压缩后数据质量、还原效果等相关)的常量,值为 0 BROTLI_MIN_QUALITY: 0, + // 表示 Brotli 算法中最大质量(限定质量相关指标上限)的常量,值为 11 BROTLI_MAX_QUALITY: 11, + // 表示 Brotli 算法中默认质量(未指定质量时使用的质量设置)的常量,值为 11 BROTLI_DEFAULT_QUALITY: 11, + // 表示 Brotli 算法中最小窗口位数(与前面类似,可能在不同场景下的窗口位数限制)的常量,值为 10 BROTLI_MIN_WINDOW_BITS: 10, + // 表示 Brotli 算法中最大窗口位数(限定窗口位数范围上限)的常量,值为 24 BROTLI_MAX_WINDOW_BITS: 24, + // 表示 Brotli 算法中较大的最大窗口位数(可能用于特殊情况或更高要求场景下的窗口位数设置)的常量,值为 30 BROTLI_LARGE_MAX_WINDOW_BITS: 30, + // 表示 Brotli 算法中默认窗口(可能是综合考虑各种因素后的常用窗口设置)的常量,值为 22 BROTLI_DEFAULT_WINDOW: 22, + // 表示 Brotli 算法中最小输入块位数(与输入数据块大小、处理方式相关的设置)的常量,值为 16 BROTLI_MIN_INPUT_BLOCK_BITS: 16, + // 表示 Brotli 算法中最大输入块位数(限定输入块位数上限)的常量,值为 24 BROTLI_MAX_INPUT_BLOCK_BITS: 24, + // 表示 Brotli 算法中参数类型为模式相关(用于设置算法模式相关参数)的常量,值为 0 BROTLI_PARAM_MODE: 0, + // 表示 Brotli 算法中参数类型为质量相关(用于调整压缩质量相关参数)的常量,值为 1 BROTLI_PARAM_QUALITY: 1, + // 表示 Brotli 算法中参数类型为窗口大小相关(用于设置窗口位数等相关参数)的常量,值为 2 BROTLI_PARAM_LGWIN: 2, + // 表示 Brotli 算法中参数类型为块大小相关(用于设置数据块相关参数)的常量,值为 3 BROTLI_PARAM_LGBLOCK: 3, + // 表示 Brotli 算法中参数类型为禁用字面量上下文建模(可能是一种高级的压缩算法特性控制参数)的常量,值为 4 BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: 4, + // 表示 Brotli 算法中参数类型为大小提示(可能用于提前告知算法期望的数据大小等情况)的常量,值为 5 BROTLI_PARAM_SIZE_HINT: 5, + // 表示 Brotli 算法中参数类型为大窗口(与前面大窗口相关设置关联)的常量,值为 6 BROTLI_PARAM_LARGE_WINDOW: 6, + // 表示 Brotli 算法中参数类型为后缀数量(可能在特定数据处理中有相关意义)的常量,值为 7 BROTLI_PARAM_NPOSTFIX: 7, + // 表示 Brotli 算法中参数类型为直接数量(具体含义取决于算法内部逻辑)的常量,值为 8 BROTLI_PARAM_NDIRECT: 8, + // 表示 Brotli 解码器出现错误结果的常量,用于判断解码结果是否为错误情况,值为 0 BROTLI_DECODER_RESULT_ERROR: 0, + // 表示 Brotli 解码器成功的常量,用于判断解码操作是否成功完成,值为 1 BROTLI_DECODER_RESULT_SUCCESS: 1, + // 表示 Brotli 解码器需要更多输入数据才能继续解码的常量,用于反馈解码状态,值为 2 BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 2, + // 表示 Brotli 解码器需要更多输出空间(可能是缓冲区等已满,需要更多空间来存放解码后的数据)才能继续解码的常量,值为 3 BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 3, + // 表示 Brotli 解码器参数类型为禁用环形缓冲区重新分配(可能涉及内存管理、缓冲区优化相关设置)的常量,值为 0 BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: 0, + // 表示 Brotli 解码器参数类型为大窗口(与前面大窗口相关设置关联)的常量,值为 1 BROTLI_DECODER_PARAM_LARGE_WINDOW: 1, + // 表示 Brotli 解码器没有错误的常量,用于判断解码器当前状态是否无错,值为 0 BROTLI_DECODER_NO_ERROR: 0, + // 表示 Brotli 解码器成功的常量,用于判断解码器操作是否成功,值为 1 BROTLI_DECODER_SUCCESS: 1, + // 表示 Brotli 解码器需要更多输入数据才能继续操作的常量,用于反馈解码器的状态需求,值为 2 BROTLI_DECODER_NEEDS_MORE_INPUT: 2, + // 表示 Brotli 解码器需要更多输出空间(类似前面需要更多输出情况)才能继续操作的常量,值为 3 BROTLI_DECODER_NEEDS_MORE_OUTPUT: 3, + // 表示 Brotli 解码器出现格式错误(具体是过度的半字节相关问题,可能涉及数据格式解析异常)的常量,值为 -1 BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: -1, + // 表示 Brotli 解码器出现格式错误(涉及保留字段相关问题,可能是数据格式不符合规范)的常量,值为 -2 BROTLI_DECODER_ERROR_FORMAT_RESERVED: -2, + // 表示 Brotli 解码器出现格式错误(过度的元数据半字节相关问题,可能在处理元数据时格式异常)的常量,值为 -3 BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: -3, + // 表示 Brotli 解码器出现格式错误(简单哈夫曼字母表相关问题,可能是哈夫曼编码相关数据格式不符合要求)的常量,值为 -4 BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: -4, + // 表示 Brotli 解码器出现格式错误(简单哈夫曼相同相关问题,可能是哈夫曼编码重复等格式异常情况)的常量,值为 -5 BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: -5, + // 表示 Brotli 解码器出现格式错误(上下文映射空间相关问题,可能是数据结构中上下文映射部分格式不符合要求)的常量,值为 -6 BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: -6, + // 表示 Brotli 解码器出现格式错误(哈夫曼空间相关问题,可能是哈夫曼编码相关数据结构格式不符合要求)的常量,值为 -7 BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: -7, + // 表示 Brot + // 表示Brotli解码器出现格式错误(具体是上下文映射重复相关问题,可能是在处理数据时上下文映射部分出现不符合规范的重复情况)的常量,值为 -8 BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: -8, +// 表示Brotli解码器出现格式错误(与数据块长度相关的第一种错误情况,具体错误原因需结合Brotli解码器对块长度的格式要求来确定)的常量,值为 -9 BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: -9, +// 表示Brotli解码器出现格式错误(与数据块长度相关的第二种错误情况,同样需依据具体的块长度格式规范来判断错误缘由)的常量,值为 -10 BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: -10, +// 表示Brotli解码器出现格式错误(与转换相关的问题,可能是在对数据进行某种转换操作时,数据格式不符合要求等情况)的常量,值为 -11 BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: -11, +// 表示Brotli解码器出现格式错误(与字典相关的问题,比如字典数据格式不符合要求或者字典使用方面出现异常等情况)的常量,值为 -12 BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: -12, +// 表示Brotli解码器出现格式错误(与窗口位数相关的问题,可能是传入的窗口位数参数不符合要求或者数据中体现的窗口位数信息有错误等情况)的常量,值为 -13 BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: -13, +// 表示Brotli解码器出现格式错误(与填充相关的第一种错误情况,具体的填充格式不符合Brotli解码器的要求等情况)的常量,值为 -14 BROTLI_DECODER_ERROR_FORMAT_PADDING_1: -14, +// 表示Brotli解码器出现格式错误(与填充相关的第二种错误情况,类似第一种填充错误,只是具体错误细节有所不同)的常量,值为 -15 BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -15, +// 表示Brotli解码器出现格式错误(与距离相关的问题,比如在数据处理中涉及到的距离参数、指针距离等不符合格式要求等情况)的常量,值为 -16 BROTLI_DECODER_ERROR_FORMAT_DISTANCE: -16, +// 表示Brotli解码器出现字典未设置的错误(可能在需要字典辅助解码但却没有正确设置字典的情况下出现该错误)的常量,值为 -19 BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -19, +// 表示Brotli解码器出现参数无效的错误(即传入的参数不符合解码器对参数的合法性要求,比如参数类型、范围等不正确)的常量,值为 -20 BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: -20, +// 表示Brotli解码器出现分配上下文模式相关内存失败的错误(可能是在为上下文模式相关的数据结构分配内存时出现内存不足等问题)的常量,值为 -21 BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: -21, +// 表示Brotli解码器出现分配树组相关内存失败的错误(在为树组相关的数据结构分配内存时遇到内存相关的问题,导致无法正常分配)的常量,值为 -22 BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: -22, +// 表示Brotli解码器出现分配上下文映射相关内存失败的错误(涉及到上下文映射的数据结构在内存分配环节出现问题,无法成功分配内存)的常量,值为 -25 BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: -25, +// 表示Brotli解码器出现分配环形缓冲区相关内存失败的第一种错误情况(可能是第一次尝试分配环形缓冲区内存时遇到问题,比如内存不足等原因)的常量,值为 -26 BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: -26, +// 表示Brotli解码器出现分配环形缓冲区相关内存失败的第二种错误情况(与第一种类似,但可能是后续再次分配或者另一种相关的内存分配问题导致的错误)的常量,值为 -27 BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: -27, +// 表示Brotli解码器出现分配块类型树相关内存失败的错误(在为块类型树相关的数据结构分配内存时出现了内存方面的异常情况)的常量,值为 -30 BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: -30, +// 表示Brotli解码器出现不可达(可能是代码执行流程进入到了本不应到达的逻辑部分,意味着程序出现了逻辑错误或者异常情况)的错误的常量,值为 -31 BROTLI_DECODER_ERROR_UNREACHABLE: -31, }, realZlibConstants)) diff --git a/node_modules/minizlib/index.js b/node_modules/minizlib/index.js index 295047b..236cc22 100644 --- a/node_modules/minizlib/index.js +++ b/node_modules/minizlib/index.js @@ -1,320 +1,421 @@ 'use strict' -const assert = require('assert') -const Buffer = require('buffer').Buffer -const realZlib = require('zlib') - -const constants = exports.constants = require('./constants.js') -const Minipass = require('minipass') - -const OriginalBufferConcat = Buffer.concat - +// 引入Node.js的内置断言模块,用于编写测试用例时进行各种断言检查,确保代码符合预期的行为 +const assert = require('assert'); +// 引入Node.js的Buffer模块,用于处理二进制数据,如创建、操作字节缓冲区等 +const Buffer = require('buffer').Buffer; +// 引入Node.js的zlib模块,用于进行数据的压缩和解压缩操作 +const realZlib = require('zlib'); + +// 引入自定义的常量模块(./constants.js),并将其赋值给当前模块的exports.constants,方便其他模块使用这些常量 +const constants = exports.constants = require('./constants.js'); +// 引入Minipass模块,Minipass可能是一个实现了流相关功能的基础类,用于构建自定义的流对象(此处为推测,具体功能依赖其自身实现) +const Minipass = require('minipass'); + +// 保存原始的Buffer.concat方法,后续代码中会对Buffer.concat进行临时修改,这里先保存以便恢复 +const OriginalBufferConcat = Buffer.concat; + +// 自定义的错误类ZlibError,继承自Error类,用于包装zlib相关操作出现的错误,提供更详细和统一的错误信息格式 class ZlibError extends Error { constructor (err) { - super('zlib: ' + err.message) - this.code = err.code - this.errno = err.errno + // 调用父类(Error)的构造函数,传入格式化后的错误消息,消息前缀为 'zlib: ',后面跟上原始错误的消息内容 + super('zlib: ' + err.message); + // 将原始错误的错误码赋值给当前错误对象的code属性,方便外部根据错误码判断具体错误类型 + this.code = err.code; + // 将原始错误的系统错误号(errno)赋值给当前错误对象的errno属性,用于更底层的错误追踪和处理(如果适用) + this.errno = err.errno; /* istanbul ignore if */ + // 以下代码块在测试覆盖时可能会被忽略(通常是一些特殊情况或者难以触发的逻辑), + // 如果当前错误对象没有设置错误码(code属性为空),则将错误码默认设置为 'ZLIB_ERROR' if (!this.code) - this.code = 'ZLIB_ERROR' + this.code = 'ZLIB_ERROR'; - this.message = 'zlib: ' + err.message - Error.captureStackTrace(this, this.constructor) + // 再次设置错误消息,确保消息格式符合预期,这里与构造函数开头设置的消息内容一致,可能是为了代码的清晰性和一致性 + this.message = 'zlib: ' + err.message; + // 捕获当前错误对象的堆栈信息,方便在调试时查看错误发生的调用栈情况,第二个参数指定了构造函数作为起始位置 + Error.captureStackTrace(this, this.constructor); } + // 重写name属性的访问器,返回自定义的错误名称 'ZlibError',用于更清晰地标识错误类型 get name () { - return 'ZlibError' + return 'ZlibError'; } } -// the Zlib class they all inherit from -// This thing manages the queue of requests, and returns -// true or false if there is anything in the queue when -// you call the .write() method. -const _opts = Symbol('opts') -const _flushFlag = Symbol('flushFlag') -const _finishFlushFlag = Symbol('finishFlushFlag') -const _fullFlushFlag = Symbol('fullFlushFlag') -const _handle = Symbol('handle') -const _onError = Symbol('onError') -const _sawError = Symbol('sawError') -const _level = Symbol('level') -const _strategy = Symbol('strategy') -const _ended = Symbol('ended') -const _defaultFullFlush = Symbol('_defaultFullFlush') - +// 以下定义了一系列Symbol类型的私有属性名,用于在ZlibBase类中标记一些内部使用的属性, +// 通过Symbol可以创建唯一的标识符,避免属性名冲突,实现一定程度的封装和数据隐藏 +const _opts = Symbol('opts'); +const _flushFlag = Symbol('flushFlag'); +const _finishFlushFlag = Symbol('finishFlushFlag'); +const _fullFlushFlag = Symbol('fullFlushFlag'); +const _handle = Symbol('handle'); +const _onError = Symbol('onError'); +const _sawError = Symbol('sawError'); +const _level = Symbol('level'); +const _strategy = Symbol('strategy'); +const _ended = Symbol('ended'); +const _defaultFullFlush = Symbol('_defaultFullFlush'); + +// ZlibBase类,继承自Minipass类,作为其他与zlib相关的类的基类,用于管理压缩/解压操作的请求队列等功能 class ZlibBase extends Minipass { constructor (opts, mode) { - if (!opts || typeof opts !== 'object') - throw new TypeError('invalid options for ZlibBase constructor') - - super(opts) - this[_ended] = false - this[_opts] = opts - - this[_flushFlag] = opts.flush - this[_finishFlushFlag] = opts.finishFlush - // this will throw if any options are invalid for the class selected + // 首先检查传入的opts参数,如果不存在或者不是对象类型,抛出TypeError异常,提示传入的ZlibBase构造函数参数无效 + if (!opts || typeof opts!== 'object') + throw new TypeError('invalid options for ZlibBase constructor'); + + // 调用父类(Minipass)的构造函数,传入opts参数,完成父类的初始化操作 + super(opts); + // 标记当前对象是否已经结束(初始化为false,表示尚未结束),通过私有属性 _ended 来记录 + this[_ended] = false; + // 将传入的opts参数保存到私有属性 _opts 中,方便后续在类中使用这些配置选项 + this[_opts] = opts; + + // 将opts中的flush属性值赋给私有属性 _flushFlag,可能用于控制数据的刷新方式(具体依赖zlib相关逻辑) + this[_flushFlag] = opts.flush; + // 将opts中的finishFlush属性值赋给私有属性 _finishFlushFlag,可能与结束时的刷新操作相关(同样依赖具体逻辑) + this[_finishFlushFlag] = opts.finishFlush; + + // 尝试根据传入的mode创建对应的zlib实例(通过realZlib[mode],这里假设realZlib对象有根据不同模式创建实例的功能), + // 如果创建过程中出现错误,捕获异常并抛出一个经过包装的ZlibError错误对象,确保错误信息符合自定义的格式和处理逻辑 try { - this[_handle] = new realZlib[mode](opts) + this[_handle] = new realZlib[mode](opts); } catch (er) { // make sure that all errors get decorated properly - throw new ZlibError(er) + throw new ZlibError(er); } + // 定义一个内部的错误处理函数 _onError,用于处理zlib实例触发的错误事件 this[_onError] = (err) => { - this[_sawError] = true - // there is no way to cleanly recover. - // continuing only obscures problems. - this.close() - this.emit('error', err) - } - - this[_handle].on('error', er => this[_onError](new ZlibError(er))) - this.once('end', () => this.close) + // 标记已经出现错误,通过私有属性 _sawError 记录,后续可以根据此标记进行相应的处理判断 + this[_sawError] = true; + // 在出现错误的情况下,认为无法进行干净的恢复操作,直接关闭相关资源(调用close方法) + // 继续执行可能会掩盖问题,所以选择关闭并触发 'error' 事件向外传递错误信息 + this.close(); + this.emit('error', err); + }; + + // 监听zlib实例的 'error' 事件,当触发时调用内部的 _onError 函数来处理错误,将原始错误包装为ZlibError类型后再处理 + this[_handle].on('error', er => this[_onError](new ZlibError(er))); + // 监听当前对象(继承自Minipass,可能是流相关的结束事件)的 'end' 事件,当触发时调用close方法进行关闭相关操作 + this.once('end', () => this.close); } + // close方法,用于关闭与zlib相关的资源(可能是释放底层的句柄等操作) close () { + // 如果存在zlib实例的句柄(_handle属性不为null),则调用其close方法进行关闭操作, + // 然后将 _handle 属性设置为null,表示已经关闭,最后触发 'close' 事件通知外部相关操作已完成 if (this[_handle]) { - this[_handle].close() - this[_handle] = null - this.emit('close') + this[_handle].close(); + this[_handle] = null; + this.emit('close'); } } + // reset方法,用于重置zlib相关的状态(前提是没有出现过错误,通过 _sawError 属性判断), + // 如果没有错误且存在zlib实例的句柄,调用句柄的reset方法进行重置操作(具体重置的内容依赖zlib底层实现) reset () { if (!this[_sawError]) { - assert(this[_handle], 'zlib binding closed') - return this[_handle].reset() + assert(this[_handle], 'zlib binding closed'); + return this[_handle].reset(); } } + // flush方法,用于执行数据的刷新操作,根据传入的参数或默认的刷新标志来决定刷新方式 flush (flushFlag) { + // 如果已经标记为结束状态(ended为真),则直接返回,不进行刷新操作 if (this.ended) - return + return; - if (typeof flushFlag !== 'number') - flushFlag = this[_fullFlushFlag] - this.write(Object.assign(Buffer.alloc(0), { [_flushFlag]: flushFlag })) + // 如果传入的flushFlag参数不是数字类型,使用默认的完全刷新标志(_fullFlushFlag,具体值应在类的其他地方定义或初始化) + if (typeof flushFlag!== 'number') + flushFlag = this[_fullFlushFlag]; + // 创建一个空的Buffer对象,并将刷新标志(_flushFlag)作为属性添加到该对象上,然后调用write方法将其写入流中进行刷新操作 + this.write(Object.assign(Buffer.alloc(0), { [_flushFlag]: flushFlag })); } + // end方法,用于标记流的结束操作,并进行一些相关的清理和刷新操作 end (chunk, encoding, cb) { + // 如果传入了数据chunk,先调用write方法将其写入流中(进行相应的编码转换等处理,后面的代码会实现) if (chunk) - this.write(chunk, encoding) - this.flush(this[_finishFlushFlag]) - this[_ended] = true - return super.end(null, null, cb) + this.write(chunk, encoding); + // 调用flush方法进行结束时的刷新操作,使用 _finishFlushFlag 作为刷新标志 + this.flush(this[_finishFlushFlag]); + // 标记当前对象已经结束(通过私有属性 _ended 设置为true) + this[_ended] = true; + // 调用父类(Minipass)的end方法,传入null表示没有额外的数据要写入,结束流操作,并传递回调函数cb(如果有的话) + return super.end(null, null, cb); } + // ended属性访问器,用于获取当前对象是否已经结束的状态,返回存储在 _ended 属性中的值 get ended () { - return this[_ended] + return this[_ended]; } + // write方法,用于向流中写入数据,涉及到对数据的处理、调用zlib实例进行压缩/解压操作以及将结果写入父类流等复杂逻辑 write (chunk, encoding, cb) { - // process the chunk using the sync process - // then super.write() all the outputted chunks + // 如果传入的第二个参数encoding是函数类型,将其作为回调函数cb,同时将encoding默认设置为 'utf8', + // 这是一种常见的参数处理方式,用于统一参数格式,方便后续对数据进行处理 if (typeof encoding === 'function') - cb = encoding, encoding = 'utf8' + cb = encoding, encoding = 'utf8'; - if (typeof chunk === 'string') - chunk = Buffer.from(chunk, encoding) + // 如果传入的第一个参数chunk是字符串类型,将其转换为Buffer类型,使用指定的encoding进行编码转换 + if (typeof chunk ==='string') + chunk = Buffer.from(chunk, encoding); + // 如果已经出现过错误(_sawError为真),直接返回,不再进行写入操作 if (this[_sawError]) - return - assert(this[_handle], 'zlib binding closed') - - // _processChunk tries to .close() the native handle after it's done, so we - // intercept that by temporarily making it a no-op. - const nativeHandle = this[_handle]._handle - const originalNativeClose = nativeHandle.close - nativeHandle.close = () => {} - const originalClose = this[_handle].close - this[_handle].close = () => {} - // It also calls `Buffer.concat()` at the end, which may be convenient - // for some, but which we are not interested in as it slows us down. - Buffer.concat = (args) => args - let result + return; + // 通过断言确保存在zlib实例的句柄,如果不存在则抛出异常,提示zlib绑定已关闭,无法进行写入操作 + assert(this[_handle], 'zlib binding closed'); + + // 以下代码块对zlib实例的原生句柄(_handle._handle)进行一些临时操作,主要是拦截其close方法使其变为空操作(noop), + // 目的是避免在处理数据过程中意外关闭句柄,同时也保存了原始的close方法,方便后续恢复 + const nativeHandle = this[_handle]._handle; + const originalNativeClose = nativeHandle.close; + nativeHandle.close = () => {}; + const originalClose = this[_handle].close; + this[_handle].close = () => {}; + + // 临时修改Buffer.concat方法为一个简单返回参数的函数(这里修改的目的可能是为了优化性能或者避免一些不必要的操作, + // 因为原有的Buffer.concat可能会有一些额外的逻辑,如合并缓冲区等,而此处不需要这些功能) + Buffer.concat = (args) => args; + + let result; try { + // 根据传入的chunk数据是否包含 _flushFlag 属性以及其值来确定刷新标志,如果没有则使用类中保存的 _flushFlag 属性值 const flushFlag = typeof chunk[_flushFlag] === 'number' - ? chunk[_flushFlag] : this[_flushFlag] - result = this[_handle]._processChunk(chunk, flushFlag) - // if we don't throw, reset it back how it was - Buffer.concat = OriginalBufferConcat + ? chunk[_flushFlag] : this[_flushFlag]; + // 调用zlib实例的 _processChunk方法处理数据chunk,传入数据和刷新标志,获取处理结果(可能是经过压缩/解压后的缓冲区数据等) + result = this[_handle]._processChunk(chunk, flushFlag); + // 如果没有抛出异常,说明处理成功,将Buffer.concat方法恢复为原始的方法(之前保存的OriginalBufferConcat) + Buffer.concat = OriginalBufferConcat; } catch (err) { - // or if we do, put Buffer.concat() back before we emit error - // Error events call into user code, which may call Buffer.concat() - Buffer.concat = OriginalBufferConcat - this[_onError](new ZlibError(err)) + // 如果在处理过程中抛出异常,先将Buffer.concat方法恢复为原始的方法, + // 然后调用内部的 _onError 方法处理错误,将原始错误包装为ZlibError类型并触发 'error' 事件向外通知错误情况 + // 之所以要先恢复Buffer.concat方法,是因为后续的错误处理可能会涉及到用户代码调用Buffer.concat方法,如果不恢复可能导致错误 + Buffer.concat = OriginalBufferConcat; + this[_onError](new ZlibError(err)); } finally { if (this[_handle]) { - // Core zlib resets `_handle` to null after attempting to close the - // native handle. Our no-op handler prevented actual closure, but we - // need to restore the `._handle` property. - this[_handle]._handle = nativeHandle - nativeHandle.close = originalNativeClose - this[_handle].close = originalClose - // `_processChunk()` adds an 'error' listener. If we don't remove it - // after each call, these handlers start piling up. - this[_handle].removeAllListeners('error') + // 在finally块中,确保无论是否出现异常,都要进行一些清理和恢复操作 + // 将zlib实例的 _handle 属性重新指向原始的原生句柄(之前保存的nativeHandle),恢复原生句柄的close方法为原始的方法 + this[_handle]._handle = nativeHandle; + nativeHandle.close = originalNativeClose; + this[_handle].close = originalClose; + // 移除zlib实例对 'error' 事件的所有监听器,因为 _processChunk方法每次调用可能会添加 'error' 监听器, + // 如果不及时移除,这些监听器会不断累积,导致意外的行为 + this[_handle].removeAllListeners('error'); } } - let writeReturn + let writeReturn; + // 如果有处理结果result,进行后续的写入操作 if (result) { + // 如果处理结果是数组且长度大于0,说明可能有多个缓冲区数据需要写入 + // 先将第一个缓冲区(通常是zlib实例内部的输出缓冲区,可能会被复用,所以需要复制一份)通过父类的write方法写入流中, + // 然后遍历数组的其余元素,依次调用父类的write方法将数据写入流中 if (Array.isArray(result) && result.length > 0) { - // The first buffer is always `handle._outBuffer`, which would be - // re-used for later invocations; so, we always have to copy that one. - writeReturn = super.write(Buffer.from(result[0])) + writeReturn = super.write(Buffer.from(result[0])); for (let i = 1; i < result.length; i++) { - writeReturn = super.write(result[i]) + writeReturn = super.write(result[i]); } } else { - writeReturn = super.write(Buffer.from(result)) + // 如果结果不是数组或者数组长度为0,直接将结果(单个缓冲区数据)通过父类的write方法写入流中 + writeReturn = super.write(Buffer.from(result)); } } + // 如果有回调函数cb,执行回调函数(通常用于通知写入操作完成等情况) if (cb) - cb() - return writeReturn + cb(); + // 返回写入操作的返回值(可能是父类write方法的返回值,具体含义依赖父类的实现逻辑) + return writeReturn; } } - +// Zlib类,继承自ZlibBase类,用于进一步定制基于zlib库的压缩/解压相关操作,添加特定的默认参数设置等功能 class Zlib extends ZlibBase { constructor (opts, mode) { - opts = opts || {} - - opts.flush = opts.flush || constants.Z_NO_FLUSH - opts.finishFlush = opts.finishFlush || constants.Z_FINISH - super(opts, mode) - - this[_fullFlushFlag] = constants.Z_FULL_FLUSH - this[_level] = opts.level - this[_strategy] = opts.strategy + // 如果没有传入opts参数,则创建一个空对象作为默认值,确保后续对opts属性的访问不会出现问题 + opts = opts || {}; + + // 如果opts中没有设置flush属性,将其默认设置为 constants.Z_NO_FLUSH(从之前引入的常量模块中获取,可能表示不进行刷新操作的常量值) + opts.flush = opts.flush || constants.Z_NO_FLUSH; + // 如果opts中没有设置finishFlush属性,将其默认设置为 constants.Z_FINISH(可能表示完成时的刷新操作相关常量值) + opts.finishFlush = opts.finishFlush || constants.Z_FINISH; + + // 调用父类(ZlibBase)的构造函数,传入处理后的opts参数和mode参数,完成父类的初始化以及与zlib底层实例的创建等操作 + super(opts, mode); + + // 设置完全刷新标志(_fullFlushFlag)为 constants.Z_FULL_FLUSH(具体含义与zlib的刷新机制相关,可能是一种较为彻底的刷新方式对应的常量值) + this[_fullFlushFlag] = constants.Z_FULL_FLUSH; + // 将opts中的level属性值保存到私有属性 _level 中,可能用于记录当前压缩/解压操作的级别设置(具体依赖zlib相关逻辑) + this[_level] = opts.level; + // 将opts中的strategy属性值保存到私有属性 _strategy 中,可能用于记录当前采用的压缩/解压策略(同样依赖具体逻辑) + this[_strategy] = opts.strategy; } + // params方法,用于修改当前zlib实例的参数(如压缩级别、策略等),但需要满足一定条件并进行一些复杂的内部处理 params (level, strategy) { + // 如果已经出现过错误(_sawError为真),直接返回,不进行参数修改操作,可能是在出现错误后认为当前状态不适合修改参数 if (this[_sawError]) - return + return; + // 如果不存在zlib实例的句柄(_handle为null),抛出一个错误,提示在绑定已关闭的情况下无法切换参数 if (!this[_handle]) - throw new Error('cannot switch params when binding is closed') + throw new Error('cannot switch params when binding is closed'); + // 以下代码块在测试覆盖时可能会被忽略(通常是一些难以触发或者不适合在常规测试中执行的逻辑), + // 如果当前zlib实例不支持params方法(即 _handle.params不存在),抛出一个错误,提示在当前实现中不支持此操作 + // 这里可能是因为不同版本或者不同底层实现对参数修改的支持情况不一致导致的判断 // no way to test this without also not supporting params at all /* istanbul ignore if */ if (!this[_handle].params) - throw new Error('not supported in this implementation') - - if (this[_level] !== level || this[_strategy] !== strategy) { - this.flush(constants.Z_SYNC_FLUSH) - assert(this[_handle], 'zlib binding closed') - // .params() calls .flush(), but the latter is always async in the - // core zlib. We override .flush() temporarily to intercept that and - // flush synchronously. - const origFlush = this[_handle].flush + throw new Error('not supported in this implementation'); + + // 如果传入的新参数level或strategy与当前保存的参数(_level、_strategy)不一致,进行参数修改操作 + if (this[_level]!== level || this[_strategy]!== strategy) { + // 先调用flush方法进行同步刷新操作,使用 constants.Z_SYNC_FLUSH 作为刷新标志,确保数据处于合适的状态以便修改参数 + this.flush(constants.Z_SYNC_FLUSH); + // 通过断言确保存在zlib实例的句柄,如果不存在则抛出异常,提示zlib绑定已关闭,无法进行参数修改操作 + assert(this[_handle], 'zlib binding closed'); + + // 以下代码是为了处理核心zlib库中.params() 方法调用.flush() 方法且后者是异步的情况, + // 在这里临时重写zlib实例的.flush() 方法,使其在内部调用当前类的flush方法(实现同步刷新)并执行传入的回调函数cb, + // 这样可以拦截并改变原本异步的刷新行为,使其符合当前参数修改时的同步需求 + const origFlush = this[_handle].flush; this[_handle].flush = (flushFlag, cb) => { - this.flush(flushFlag) - cb() - } + this.flush(flushFlag); + cb(); + }; + try { - this[_handle].params(level, strategy) + // 调用zlib实例的.params() 方法,传入新的参数level和strategy,实际执行参数修改操作(具体修改的内容依赖zlib底层实现) + this[_handle].params(level, strategy); } finally { - this[_handle].flush = origFlush + // 在无论是否出现异常的情况下,都要将zlib实例的.flush() 方法恢复为原始的方法(之前保存的origFlush),确保后续操作不受影响 + this[_handle].flush = origFlush; } + /* istanbul ignore else */ + // 以下代码块在测试覆盖时可能会被默认忽略(可能是一些正常流程下必然会执行的逻辑), + // 如果zlib实例的句柄仍然存在(即没有在参数修改过程中出现意外关闭等情况),更新当前对象保存的参数(_level和 _strategy)为新传入的值 if (this[_handle]) { - this[_level] = level - this[_strategy] = strategy + this[_level] = level; + this[_strategy] = strategy; } } } } -// minimal 2-byte header +// Deflate类,继承自Zlib类,代表使用deflate压缩算法的相关操作,构造函数中传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'Deflate' +// 它可能会在创建实例时根据Zlib类以及更上层的ZlibBase类的逻辑,初始化与deflate算法相关的配置和底层zlib实例等内容 class Deflate extends Zlib { constructor (opts) { - super(opts, 'Deflate') + super(opts, 'Deflate'); } } +// Inflate类,继承自Zlib类,代表使用inflate解压算法的相关操作,构造函数中传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'Inflate' +// 用于初始化与inflate算法相关的配置和底层zlib实例等,以便后续进行解压相关的操作 class Inflate extends Zlib { constructor (opts) { - super(opts, 'Inflate') + super(opts, 'Inflate'); } } -// gzip - bigger header, same deflate compression +// Gzip类,继承自Zlib类,用于处理gzip格式的压缩/解压相关操作,构造函数中传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'Gzip' +// gzip是一种常见的带有特定头部格式的压缩文件格式,该类在初始化时会基于Zlib类的基础配置和对应模式来准备相关操作 class Gzip extends Zlib { constructor (opts) { - super(opts, 'Gzip') + super(opts, 'Gzip'); } } +// Gunzip类,继承自Zlib类,主要用于对gzip格式文件进行解压操作,构造函数中传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'Gunzip' class Gunzip extends Zlib { constructor (opts) { - super(opts, 'Gunzip') + super(opts, 'Gunzip'); } } -// raw - no header +// DeflateRaw类,继承自Zlib类,用于使用原始的deflate算法进行操作(可能不包含像gzip等格式那样的额外头部信息), +// 构造函数传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'DeflateRaw',以便初始化相关配置和底层实例 class DeflateRaw extends Zlib { constructor (opts) { - super(opts, 'DeflateRaw') + super(opts, 'DeflateRaw'); } } +// InflateRaw类,继承自Zlib类,用于使用原始的inflate算法进行解压操作(对应前面的DeflateRaw,去除格式头部等信息的解压情况), +// 构造函数传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'InflateRaw',来初始化相应的配置和底层实例 class InflateRaw extends Zlib { constructor (opts) { - super(opts, 'InflateRaw') + super(opts, 'InflateRaw'); } } -// auto-detect header. +// Unzip类,继承自Zlib类,用于自动检测文件头部并进行相应的解压操作(可以处理多种格式,通过自动识别头部来确定解压方式), +// 构造函数传入opts参数并调用父类(Zlib)的构造函数,指定模式为 'Unzip',以此来初始化相关配置和底层实例 class Unzip extends Zlib { constructor (opts) { - super(opts, 'Unzip') + super(opts, 'Unzip'); } } +// Brotli类,继承自ZlibBase类,用于基于Brotli压缩算法进行相关操作,和Zlib类类似,不过是针对Brotli算法的特定处理 class Brotli extends ZlibBase { constructor (opts, mode) { - opts = opts || {} + opts = opts || {}; - opts.flush = opts.flush || constants.BROTLI_OPERATION_PROCESS - opts.finishFlush = opts.finishFlush || constants.BROTLI_OPERATION_FINISH + // 如果opts中没有设置flush属性,将其默认设置为 constants.BROTLI_OPERATION_PROCESS(与Brotli算法的操作类型相关的常量,可能表示常规处理阶段的刷新方式) + opts.flush = opts.flush || constants.BROTLI_OPERATION_PROCESS; + // 如果opts中没有设置finishFlush属性,将其默认设置为 constants.BROTLI_OPERATION_FINISH(可能表示Brotli算法完成操作时的刷新方式相关常量值) + opts.finishFlush = opts.finishFlush || constants.BROTLI_OPERATION_FINISH; - super(opts, mode) + // 调用父类(ZlibBase)的构造函数,传入处理后的opts参数和mode参数,完成父类的初始化以及与Brotli相关的底层实例创建等操作 + super(opts, mode); - this[_fullFlushFlag] = constants.BROTLI_OPERATION_FLUSH + // 设置完全刷新标志(_fullFlushFlag)为 constants.BROTLI_OPERATION_FLUSH(与Brotli算法的刷新操作类型相关的常量值) + this[_fullFlushFlag] = constants.BROTLI_OPERATION_FLUSH; } } +// BrotliCompress类,继承自Brotli类,专门用于使用Brotli算法进行压缩操作,构造函数传入opts参数并调用父类(Brotli)的构造函数,指定模式为 'BrotliCompress', +// 以此来初始化与Brotli压缩相关的配置和底层实例等内容 class BrotliCompress extends Brotli { constructor (opts) { - super(opts, 'BrotliCompress') + super(opts, 'BrotliCompress'); } } +// BrotliDecompress类,继承自Brotli类,专门用于使用Brotli算法进行解压操作,构造函数传入opts参数并调用父类(Brotli)的构造函数,指定模式为 'BrotliDecompress', +// 用于初始化与Brotli解压相关的配置和底层实例等,以便后续进行解压相关的操作 class BrotliDecompress extends Brotli { constructor (opts) { - super(opts, 'BrotliDecompress') + super(opts, 'BrotliDecompress'); } } -exports.Deflate = Deflate -exports.Inflate = Inflate -exports.Gzip = Gzip -exports.Gunzip = Gunzip -exports.DeflateRaw = DeflateRaw -exports.InflateRaw = InflateRaw -exports.Unzip = Unzip +// 将上述定义的各个类作为模块的导出内容,方便其他模块引入并使用这些类来进行相应的压缩、解压等操作 +exports.Deflate = Deflate; +exports.Inflate = Inflate; +exports.Gzip = Gzip; +exports.Gunzip = Gunzip; +exports.DeflateRaw = DeflateRaw; +exports.InflateRaw = InflateRaw; +exports.Unzip = Unzip; + /* istanbul ignore else */ +// 以下代码块在测试覆盖时可能会被默认忽略(可能是根据不同Node.js版本的兼容性判断等情况), +// 如果Node.js的 realZlib 模块中存在 BrotliCompress 函数(即支持Brotli压缩算法),则将 BrotliCompress 和 BrotliDecompress 类作为模块导出内容, +// 否则创建一个在构造函数中抛出错误的类作为它们的导出内容,提示在当前版本的Node.js中不支持Brotli算法 if (typeof realZlib.BrotliCompress === 'function') { - exports.BrotliCompress = BrotliCompress - exports.BrotliDecompress = BrotliDecompress + exports.BrotliCompress = BrotliCompress; + exports.BrotliDecompress = BrotliDecompress; } else { exports.BrotliCompress = exports.BrotliDecompress = class { constructor () { - throw new Error('Brotli is not supported in this version of Node.js') + throw new Error('Brotli is not supported in this version of Node.js'); } - } -} + }; +} \ No newline at end of file diff --git a/node_modules/ms/index.js b/node_modules/ms/index.js index 6a522b1..622f17f 100644 --- a/node_modules/ms/index.js +++ b/node_modules/ms/index.js @@ -1,62 +1,86 @@ /** - * Helpers. + * 辅助变量定义部分。 + * 这里定义了一系列时间单位换算相关的变量,方便后续在时间格式化和解析等操作中使用。 */ - +// 定义变量 s 表示 1 秒对应的毫秒数,即 1000 毫秒 var s = 1000; +// 定义变量 m 表示 1 分钟对应的毫秒数,通过 1 分钟等于 60 秒来换算,即 60 * 1000 毫秒 var m = s * 60; +// 定义变量 h 表示 1 小时对应的毫秒数,通过 1 小时等于 60 分钟来换算,即 60 * 60 * 1000 毫秒 var h = m * 60; +// 定义变量 d 表示 1 天对应的毫秒数,通过 1 天等于 24 小时来换算,即 24 * 60 * 60 * 1000 毫秒 var d = h * 24; +// 定义变量 y 表示 1 年对应的毫秒数,这里取一年为 365.25 天进行换算,即 365.25 * 24 * 60 * 60 * 1000 毫秒 var y = d * 365.25; /** * Parse or format the given `val`. + * 此函数是模块对外暴露的主要接口,用于解析给定的 `val` 参数(如果是字符串)或者格式化给定的 `val` 参数(如果是数字)。 * * Options: * * - `long` verbose formatting [false] + * 可以传入一个可选的 `options` 对象,其中 `long` 属性用于控制是否采用详细(冗长)的格式化方式,默认值为 `false`。 * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} + * @param {String|Number} val 要处理的参数,可以是字符串或者数字类型。 + * @param {Object} [options] 可选的配置对象,用于控制格式化等相关行为。 + * @throws {Error} throw an error if val is not a non-empty string or a number 如果 `val` 既不是非空字符串也不是有效的数字,会抛出一个错误。 + * @return {String|Number} 根据 `val` 的类型以及 `options` 的配置,返回相应的解析或格式化后的结果,可能是字符串或者数字类型。 * @api public */ - module.exports = function(val, options) { + // 如果没有传入 `options` 参数,则创建一个空对象作为默认值,确保后续对 `options` 属性的访问不会出现问题 options = options || {}; + // 获取 `val` 的类型,通过 `typeof` 操作符判断是字符串、数字还是其他类型 var type = typeof val; - if (type === 'string' && val.length > 0) { + // 如果 `val` 是字符串类型且长度大于 0,说明需要进行解析操作,调用 `parse` 函数(内部私有函数)进行解析,并返回解析结果 + if (type ==='string' && val.length > 0) { return parse(val); } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); + // 如果 `val` 是数字类型且不是 `NaN`(即有效数字),根据 `options.long` 的值来决定调用 `fmtLong`(详细格式化)函数还是 `fmtShort`(简短格式化)函数进行格式化,并返回格式化后的结果 + return options.long? fmtLong(val) : fmtShort(val); } + // 如果 `val` 不符合上述两种合法情况(既不是非空字符串也不是有效数字),抛出一个错误,提示 `val` 的值不符合要求,并展示 `val` 的具体内容(通过 `JSON.stringify` 转换为字符串形式) throw new Error( - 'val is not a non-empty string or a valid number. val=' + + 'val is not a non-empty string or a valid number. val=' + JSON.stringify(val) ); }; /** * Parse the given `str` and return milliseconds. + * 此函数用于解析给定的字符串 `str`,尝试从字符串中提取时间相关的数值和单位信息,并将其转换为对应的毫秒数返回。 + * 如果字符串格式不符合要求或者无法正确解析,则返回 `undefined`。 * - * @param {String} str - * @return {Number} + * @param {String} str 要解析的字符串,期望包含时间数值和对应的时间单位表示(如 "1h" 表示 1 小时)。 + * @return {Number} 返回解析后的毫秒数,如果解析失败则返回 `undefined`。 * @api private */ - function parse(str) { + // 将传入的 `str` 参数强制转换为字符串类型,确保后续字符串操作的一致性 str = String(str); + // 如果字符串长度大于 100,可能认为是不合理的输入,直接返回,不进行后续解析操作(这里 100 只是一个人为设定的长度限制阈值) if (str.length > 100) { return; } + // 使用正则表达式来匹配字符串中的时间数值和单位部分,尝试提取相关信息。 + // 正则表达式的含义如下: + // ^ 表示匹配字符串的开头 + // (?:\d+)?\.?\d+ 表示匹配可选的整数部分(可能没有)、可选的小数点以及必须的小数部分,用于提取时间数值 + // * 表示前面的数值部分和后面的单位部分之间可以有零个或多个空格 + // (milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)? 表示匹配可选的时间单位部分,支持多种不同的时间单位缩写形式(如 "ms"、"s"、"m"、"h"、"d"、"y" 等),并且单位部分是不区分大小写的(通过 /i 修饰符指定) + // $ 表示匹配字符串的结尾 var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str + str ); if (!match) { return; } + // 提取匹配到的时间数值部分,并通过 `parseFloat` 转换为浮点数类型,存储在变量 `n` 中 var n = parseFloat(match[1]); + // 提取匹配到的时间单位部分,如果没有匹配到则默认使用 "ms"(毫秒),并将其转换为小写形式,存储在变量 `type` 中 var type = (match[2] || 'ms').toLowerCase(); + // 根据提取到的时间单位 `type`,通过 `switch` 语句进行不同情况的处理,将对应的时间数值转换为毫秒数并返回 switch (type) { case 'years': case 'year': @@ -99,54 +123,69 @@ function parse(str) { /** * Short format for `ms`. + * 此函数用于将给定的毫秒数 `ms` 转换为简短格式的时间表示字符串,根据毫秒数的大小选择合适的时间单位进行缩写显示。 * - * @param {Number} ms - * @return {String} + * @param {Number} ms 要格式化的毫秒数。 + * @return {String} 返回简短格式的时间表示字符串,如 "1h"、"30m"、"10s" 等形式。 * @api private */ - function fmtShort(ms) { + // 如果毫秒数大于等于 1 天对应的毫秒数(`d`),则将其除以 `d` 并取整,然后加上时间单位 "d"(天)返回,表示以天为单位的时间格式 if (ms >= d) { return Math.round(ms / d) + 'd'; } + // 如果毫秒数大于等于 1 小时对应的毫秒数(`h`),则将其除以 `h` 并取整,然后加上时间单位 "h"(小时)返回,表示以小时为单位的时间格式 if (ms >= h) { return Math.round(ms / h) + 'h'; } + // 如果毫秒数大于等于 1 分钟对应的毫秒数(`m`),则将其除以 `m` 并取整,然后加上时间单位 "m"(分钟)返回,表示以分钟为单位的时间格式 if (ms >= m) { return Math.round(ms / m) + 'm'; } + // 如果毫秒数大于等于 1 秒对应的毫秒数(`s`),则将其除以 `s` 并取整,然后加上时间单位 "s"(秒)返回,表示以秒为单位的时间格式 if (ms >= s) { return Math.round(ms / s) + 's'; } + // 如果毫秒数小于 1 秒对应的毫秒数,直接返回原始的毫秒数加上时间单位 "ms"(毫秒),表示以毫秒为单位的时间格式 return ms + 'ms'; } /** * Long format for `ms`. + * 此函数用于将给定的毫秒数 `ms` 转换为详细格式的时间表示字符串,会根据不同的时间范围使用更详细的表述方式(包含复数形式等)进行格式化。 * - * @param {Number} ms - * @return {String} + * @param {Number} ms 要格式化的毫秒数。 + * @return {String} 返回详细格式的时间表示字符串,如 "1 day"、"2 hours" 等形式,更符合自然语言的表述习惯。 * @api private */ - function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; + return ( + // 调用 `plural` 函数(内部私有函数)尝试按照以天为单位进行格式化,如果满足条件则返回相应的格式化字符串,否则继续后续的格式化判断 + plural(ms, d, 'day') || + // 调用 `plural` 函数尝试按照以小时为单位进行格式化,如果满足条件则返回相应的格式化字符串,否则继续后续的格式化判断 + plural(ms, h, 'hour') || + // 调用 `plural` 函数尝试按照以分钟为单位进行格式化,如果满足条件则返回相应的格式化字符串,否则继续后续的格式化判断 + plural(ms, h, 'minute') || + // 调用 `plural` 函数尝试按照以秒为单位进行格式化,如果满足条件则返回相应的格式化字符串,否则直接返回原始的毫秒数加上 " ms"(注意这里有空格)作为最后的格式化结果 + plural(ms, s, 'second') || + ms +'ms' + ); } /** * Pluralization helper. + * 此函数是一个辅助函数,用于根据给定的毫秒数 `ms`、时间单位对应的毫秒数 `n`(如 `d`、`h`、`m`、`s` 等)以及时间单位名称 `name`, + * 来判断是否需要使用复数形式进行格式化,并返回相应的格式化字符串。 + * 如果毫秒数小于对应的时间单位毫秒数 `n`,则直接返回 `undefined`,表示不满足格式化条件。 */ - function plural(ms, n, name) { if (ms < n) { return; } + // 如果毫秒数小于对应时间单位毫秒数的 1.5 倍,使用 `Math.floor` 向下取整,并按照单数形式(不带 "s")进行格式化,返回如 "1 day" 的格式字符串 if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; + return Math.floor(ms / n) +' '+ name; } - return Math.ceil(ms / n) + ' ' + name + 's'; -} + // 如果毫秒数大于等于对应时间单位毫秒数的 1.5 倍,使用 `Math.ceil` 向上取整,并按照复数形式(带 "s")进行格式化,返回如 "2 days" 的格式字符串 + return Math.ceil(ms / n) +' '+ name +'s'; +} \ No newline at end of file diff --git a/node_modules/multibase/src/index.js b/node_modules/multibase/src/index.js index 1c9ebe2..8c7b85a 100644 --- a/node_modules/multibase/src/index.js +++ b/node_modules/multibase/src/index.js @@ -4,101 +4,128 @@ */ 'use strict' -const { Buffer } = require('buffer') -const constants = require('./constants') - -exports = module.exports = multibase -exports.encode = encode -exports.decode = decode -exports.isEncoded = isEncoded -exports.names = Object.freeze(Object.keys(constants.names)) -exports.codes = Object.freeze(Object.keys(constants.codes)) - -const errNotSupported = new Error('Unsupported encoding') +// 从Node.js的 'buffer' 模块中解构出 'Buffer' 对象,用于处理二进制数据,例如创建、操作字节缓冲区等 +const { Buffer } = require('buffer'); +// 引入自定义的 'constants' 模块(推测其中包含了与多基数编码(Multibase)相关的常量定义,比如编码名称、编码代码等信息) +const constants = require('./constants'); + +// 将'multibase' 函数以及其他相关函数('encode'、'decode'、'isEncoded')作为模块的导出内容,方便其他模块引入并使用这些功能。 +// 同时,将 'names' 和 'codes' 属性也作为导出内容,分别对应从 'constants' 模块中获取的编码名称和编码代码的键名数组,并使用 'Object.freeze' 冻结,防止其被意外修改。 +exports = module.exports = multibase; +exports.encode = encode; +exports.decode = decode; +exports.isEncoded = isEncoded; +exports.names = Object.freeze(Object.keys(constants.names)); +exports.codes = Object.freeze(Object.keys(constants.codes)); + +// 创建一个表示不支持的编码错误的 'Error' 实例,用于在遇到不支持的编码情况时抛出相应错误信息 +const errNotSupported = new Error('Unsupported encoding'); /** * Create a new buffer with the multibase varint+code. + * 此函数用于创建一个带有多基数(Multibase)变长整数(varint)和编码代码的新缓冲区。 + * 它接收一个表示多基数名称或编码代码数字的参数以及一个要添加多基数前缀的数据缓冲区,返回处理后的缓冲区。 * - * @param {string|number} nameOrCode - The multibase name or code number. - * @param {Buffer} buf - The data to be prefixed with multibase. + * @param {string|number} nameOrCode - The multibase name or code number. 可以是多基数编码的名称(字符串形式)或者编码代码对应的数字,用于确定具体使用的编码方式。 + * @param {Buffer} buf - The data to be prefixed with multibase. 要添加多基数前缀的二进制数据缓冲区。 * @memberof Multibase - * @returns {Buffer} + * @returns {Buffer} 返回一个新的缓冲区,包含了多基数编码相关的前缀和原始的数据缓冲区内容拼接后的结果。 */ function multibase (nameOrCode, buf) { + // 如果没有传入要添加前缀的数据缓冲区(buf 为假值),则抛出一个错误,提示需要一个已编码的缓冲区作为参数 if (!buf) { - throw new Error('requires an encoded buffer') + throw new Error('requires an encoded buffer'); } - const base = getBase(nameOrCode) - const codeBuf = Buffer.from(base.code) - - const name = base.name - validEncode(name, buf) - return Buffer.concat([codeBuf, buf]) + // 通过 'getBase' 函数(内部函数,用于根据名称或代码获取对应的编码基础信息)获取具体的编码基础对象,包含编码相关的各种属性和方法 + const base = getBase(nameOrCode); + // 根据获取到的编码基础对象中的编码代码创建一个缓冲区,用于后续拼接在数据前面作为多基数前缀的一部分 + const codeBuf = Buffer.from(base.code); + + const name = base.name; + // 调用 'validEncode' 函数(内部函数,用于验证编码是否有效)验证当前编码名称和数据缓冲区是否匹配、能否正确编码,此处主要进行有效性验证但无返回值(返回值为 undefined) + validEncode(name, buf); + // 使用 'Buffer.concat' 方法将编码代码缓冲区和原始数据缓冲区拼接在一起,形成最终带有多基数前缀的新缓冲区,并返回该缓冲区 + return Buffer.concat([codeBuf, buf]); } /** * Encode data with the specified base and add the multibase prefix. + * 此函数用于使用指定的编码基础对数据进行编码,并添加多基数前缀,最终返回编码后带有前缀的缓冲区。 * - * @param {string|number} nameOrCode - The multibase name or code number. - * @param {Buffer} buf - The data to be encoded. - * @returns {Buffer} + * @param {string|number} nameOrCode - The multibase name or code number. 多基数编码的名称或编码代码对应的数字,用于指定具体的编码方式。 + * @param {Buffer} buf - The data to be encoded. 要进行编码的二进制数据缓冲区。 + * @returns {Buffer} 返回经过指定编码方式编码并添加多基数前缀后的缓冲区。 * @memberof Multibase */ function encode (nameOrCode, buf) { - const base = getBase(nameOrCode) - const name = base.name + // 通过 'getBase' 函数获取对应的编码基础对象,包含编码相关的属性和方法等信息 + const base = getBase(nameOrCode); + const name = base.name; - return multibase(name, Buffer.from(base.encode(buf))) + // 调用'multibase' 函数,先使用编码基础对象的 'encode' 方法对数据进行编码(将原始数据按照指定编码方式转换), + // 然后将编码后的结果作为数据添加多基数前缀,最终返回处理后的缓冲区 + return multibase(name, Buffer.from(base.encode(buf))); } /** * Takes a buffer or string encoded with multibase header, decodes it and * returns the decoded buffer + * 此函数用于接收一个带有多基数头部(header)编码的缓冲区或字符串,对其进行解码操作,并返回解码后的缓冲区。 * - * @param {Buffer|string} bufOrString - * @returns {Buffer} + * @param {Buffer|string} bufOrString 可以是已经编码的缓冲区或者对应的字符串形式,包含了多基数编码的相关信息。 + * @returns {Buffer} 返回解码后的二进制数据缓冲区。 * @memberof Multibase - * */ function decode (bufOrString) { + // 如果传入的参数是缓冲区类型,将其转换为字符串形式,方便后续按字符串逻辑进行处理(比如截取前缀等操作) if (Buffer.isBuffer(bufOrString)) { - bufOrString = bufOrString.toString() + bufOrString = bufOrString.toString(); } - const code = bufOrString.substring(0, 1) - bufOrString = bufOrString.substring(1, bufOrString.length) + // 截取输入字符串的第一个字符作为编码代码,用于后续确定使用哪种编码方式进行解码 + const code = bufOrString.substring(0, 1); + // 截取输入字符串中除去第一个字符(编码代码部分)后的剩余部分,作为要解码的实际数据内容 + bufOrString = bufOrString.substring(1, bufOrString.length); - if (typeof bufOrString === 'string') { - bufOrString = Buffer.from(bufOrString) + // 如果剩余的数据内容仍然是字符串类型,将其转换为缓冲区类型,以便后续使用缓冲区相关的解码方法进行操作 + if (typeof bufOrString ==='string') { + bufOrString = Buffer.from(bufOrString); } - const base = getBase(code) - return Buffer.from(base.decode(bufOrString.toString())) + // 通过 'getBase' 函数根据编码代码获取对应的编码基础对象,包含解码相关的方法等信息 + const base = getBase(code); + // 调用编码基础对象的 'decode' 方法对数据进行解码操作,将解码后的结果从字符串形式转换为缓冲区形式并返回 + return Buffer.from(base.decode(bufOrString.toString())); } /** * Is the given data multibase encoded? + * 此函数用于判断给定的数据(缓冲区或字符串形式)是否是经过多基数编码的。 * - * @param {Buffer|string} bufOrString - * @returns {boolean} + * @param {Buffer|string} bufOrString 要检查的二进制数据缓冲区或者对应的字符串形式的数据。 + * @returns {boolean} 如果是经过多基数编码的则返回 true,否则返回 false。 * @memberof Multibase */ function isEncoded (bufOrString) { + // 如果传入的参数是缓冲区类型,将其转换为字符串形式,方便后续按字符串逻辑进行处理(比如截取前缀等操作) if (Buffer.isBuffer(bufOrString)) { - bufOrString = bufOrString.toString() + bufOrString = bufOrString.toString(); } - // Ensure bufOrString is a string - if (Object.prototype.toString.call(bufOrString) !== '[object String]') { - return false + // 确保经过转换后的数据是字符串类型,如果不是(通过 'Object.prototype.toString.call' 判断类型不符合 '[object String]'),则直接返回 false,表示不是多基数编码的数据 + if (Object.prototype.toString.call(bufOrString)!== '[object String]') { + return false; } - const code = bufOrString.substring(0, 1) + // 截取输入字符串的第一个字符作为编码代码,用于尝试获取对应的编码基础对象来判断是否合法编码 + const code = bufOrString.substring(0, 1); try { - const base = getBase(code) - return base.name + // 通过 'getBase' 函数根据编码代码获取对应的编码基础对象,如果能成功获取到(说明编码代码合法),则进一步判断其是否有对应的编码名称(即是否是有效的已实现的编码),返回相应的布尔值结果 + const base = getBase(code); + return base.name; } catch (err) { - return false + // 如果在获取编码基础对象过程中出现错误(比如编码代码不被支持等情况),则返回 false,表示不是多基数编码的数据 + return false; } } @@ -107,26 +134,41 @@ function isEncoded (bufOrString) { * @param {Buffer} buf * @private * @returns {undefined} + * 此函数是一个私有函数,用于验证给定编码名称对应的编码方式能否对传入的数据缓冲区进行正确的编码操作。 + * 它通过调用对应的编码基础对象的 'decode' 方法进行验证,如果出现问题可能会抛出异常(由调用者处理),本身无返回值(返回 undefined)。 */ function validEncode (name, buf) { - const base = getBase(name) - base.decode(buf.toString()) + const base = getBase(name); + base.decode(buf.toString()); } +/** + * 此函数用于根据传入的多基数编码名称或编码代码获取对应的编码基础对象。 + * 如果传入的名称或代码在 'constants' 模块定义的编码名称或编码代码集合中能找到对应项,则返回相应的编码基础对象,否则抛出不支持的编码错误。 + * 同时,还会检查获取到的编码基础对象是否已经实现(通过 'isImplemented' 方法判断),如果未实现则抛出相应的错误信息。 + * + * @param {string|number} nameOrCode 多基数编码的名称或编码代码对应的数字。 + * @returns {Object} 返回对应的编码基础对象,包含编码相关的各种属性(如名称、编码代码等)和方法(如编码、解码方法等)。 + */ function getBase (nameOrCode) { - let base + let base; + // 首先检查传入的名称或代码是否在 'constants' 模块定义的编码名称集合中存在对应项,如果存在则获取对应的编码基础对象 if (constants.names[nameOrCode]) { - base = constants.names[nameOrCode] + base = constants.names[nameOrCode]; } else if (constants.codes[nameOrCode]) { - base = constants.codes[nameOrCode] + // 如果在编码名称集合中不存在,再检查是否在编码代码集合中存在对应项,如果存在则获取对应的编码基础对象 + base = constants.codes[nameOrCode]; } else { - throw errNotSupported + // 如果在两个集合中都找不到对应项,抛出表示不支持的编码错误(之前定义的 'errNotSupported' 错误对象) + throw errNotSupported; } + // 检查获取到的编码基础对象是否已经实现(通过 'isImplemented' 方法判断,具体实现逻辑应该在编码基础对象内部定义), + // 如果未实现,则抛出一个新的错误,提示对应的编码基础对象对应的编码还未实现 if (!base.isImplemented()) { - throw new Error('Base ' + nameOrCode + ' is not implemented yet') + throw new Error('Base'+ nameOrCode +'is not implemented yet'); } - return base -} + return base; +} \ No newline at end of file