From 638724d2323a49fb30876e31662900f4b5cc893a Mon Sep 17 00:00:00 2001 From: Kevin Deng Date: Thu, 13 Nov 2025 04:15:44 +0800 Subject: [PATCH] feat: rewrite v2 --- src/browser.ts | 125 ++++++++++-------- src/core.ts | 271 ++++++++++++++++++-------------------- src/node.ts | 148 +++++++++++---------- src/types-v2.ts | 48 ------- src/types.ts | 81 ++---------- tests/basic.test.ts | 54 ++++---- tests/conformance.test.ts | 49 ------- tests/node.test.ts | 26 ++-- 8 files changed, 324 insertions(+), 478 deletions(-) delete mode 100644 src/types-v2.ts delete mode 100644 tests/conformance.test.ts diff --git a/src/browser.ts b/src/browser.ts index bb06dfd..22c725a 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,6 +1,12 @@ -import { setup } from './core.ts' -import { humanize } from './utils.ts' -import type { Debug, Debugger } from './types.ts' +import { + createDebug as _createDebug, + enable as _enable, + disable, + enabled, + namespaces, +} from './core.ts' +import { humanize, selectColor } from './utils.ts' +import type { Debugger, DebugOptions } from './types.ts' const colors: string[] = [ '#0000CC', @@ -81,14 +87,14 @@ const colors: string[] = [ '#FFCC33', ] -function useColors(): boolean { - return true -} - /** * Colorize log arguments if enabled. */ -export function formatArgs(this: Debugger, args: [string, ...any[]]): void { +function formatArgs( + this: Debugger, + diff: number, + args: [string, ...any[]], +): void { const { useColors } = this args[0] = `${ (useColors ? '%c' : '') + @@ -96,7 +102,7 @@ export function formatArgs(this: Debugger, args: [string, ...any[]]): void { (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') - }+${humanize(this.diff!)}` + }+${humanize(diff)}` if (!useColors) { return @@ -131,7 +137,43 @@ export function formatArgs(this: Debugger, args: [string, ...any[]]): void { * If `console.debug` is not available, falls back * to `console.log`. */ -export const log: Debugger['log'] = console.debug || console.log || (() => {}) +const log = console.debug || console.log || (() => {}) + +// Use non-null assertion operator because +// we handle the case where storage is undefined in load/save. +const storage = localstorage()! + +const defaultOptions: Omit, 'color'> = { + useColors: true, + + formatArgs, + formatters: { + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + j(v) { + try { + return JSON.stringify(v) + } catch (error: any) { + return `[UnexpectedJSONParseError]: ${error.message}` + } + }, + }, + inspectOpts: {}, + + log, +} + +export function createDebug( + namespace: string, + options?: DebugOptions, +): Debugger { + const color = (options && options.color) ?? selectColor(colors, namespace) + return _createDebug( + namespace, + Object.assign(defaultOptions, { color }, options), + ) +} /** * Localstorage attempts to return the localstorage. @@ -150,30 +192,7 @@ function localstorage(): Storage | undefined { // XXX (@Qix-) should we be logging these? } } -// Use non-null assertion operator because -// we handle the case where storage is undefined in load/save. -const storage = localstorage()! -/** - * Save `namespaces`. - */ -function save(namespaces: string) { - try { - if (namespaces) { - storage.setItem('debug', namespaces) - } else { - storage.removeItem('debug') - } - } catch { - // Swallow - // XXX (@Qix-) should we be logging these? - } -} - -/** - * Load `namespaces`. - * @return returns the previously persisted debug modes - */ function load(): string { let r: string | null | undefined try { @@ -191,32 +210,26 @@ function load(): string { return r || '' } -export const createDebug: Debug = setup( - useColors(), - colors, - log, - load, - save, - formatArgs, -) - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ -createDebug.formatters.j = function (v) { +function save(namespaces: string) { try { - return JSON.stringify(v) - } catch (error: any) { - return `[UnexpectedJSONParseError]: ${error.message}` + if (namespaces) { + storage.setItem('debug', namespaces) + } else { + storage.removeItem('debug') + } + } catch { + // Swallow + // XXX (@Qix-) should we be logging these? } } -export default createDebug -export type * from './types.ts' +function enable(namespaces: string): void { + save(namespaces) + _enable(namespaces) +} -// @ts-expect-error -createDebug.default = createDebug -// @ts-expect-error -createDebug.debug = createDebug +// side-effect +_enable(load()) -export { createDebug as 'module.exports' } +export type * from './types.ts' +export { disable, enable, enabled, namespaces } diff --git a/src/core.ts b/src/core.ts index d821ed4..772d74c 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,168 +1,153 @@ -import { coerce, matchesTemplate, selectColor } from './utils.ts' -import type { Debug, Debugger } from './types.ts' - -export function setup( - useColors: boolean, - colors: (string | number)[], - log: Debug['log'], - load: () => string, - save: (namespaces: string) => void, - formatArgs: Debug['formatArgs'], - init?: (debug: Debugger) => void, -): Debug { - const createDebug: Debug = (namespace: string): Debugger => { - let prevTime: number | undefined - let enableOverride: boolean | undefined - let namespacesCache: string | undefined - let enabledCache: boolean | undefined - - const debug: Debugger = (...args: any[]) => { - if (!debug.enabled) { - return - } +import { coerce, matchesTemplate } from './utils.ts' +import type { Debugger, DebugOptions } from './types.ts' - const curr = Date.now() - const ms = curr - (prevTime || curr) - debug.diff = ms - debug.prev = prevTime - debug.curr = curr - prevTime = curr - - args[0] = coerce(args[0]) - if (typeof args[0] !== 'string') { - // Anything else let's inspect with %O - args.unshift('%O') - } +let globalNamespaces: string = '' - // Apply any `formatters` transformations - let index = 0 - args[0] = (args[0] as string).replace(/%([a-z%])/gi, (match, format) => { - // If we encounter an escaped % then don't increase the array index - if (match === '%%') return '%' - - index++ - const formatter = createDebug.formatters[format] - if (typeof formatter === 'function') { - const value = args[index] - match = formatter.call(debug, value) - - // Now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1) - index-- - } - return match - }) - - // Apply env-specific formatting (colors, etc.) - createDebug.formatArgs.call(debug, args as [string, ...any[]]) - - const logFn = debug.log || createDebug.log - logFn.apply(debug, args) - } +export function namespaces(): string { + return globalNamespaces +} - function extend(this: Debugger, namespace: string, delimiter = ':') { - const newDebug = createDebug(this.namespace + delimiter + namespace) - newDebug.log = this.log - return newDebug +export function createDebug( + namespace: string, + options: Required, +): Debugger { + let prevTime: number | undefined + let enableOverride: boolean | undefined + let namespacesCache: string | undefined + let enabledCache: boolean | undefined + + const debug: Debugger = (...args: any[]) => { + if (!debug.enabled) { + return } - debug.namespace = namespace - debug.useColors = useColors - debug.color = selectColor(colors, namespace) - debug.extend = extend - debug.log = log + const curr = Date.now() + const ms = curr - (prevTime || curr) + const diff = ms + prevTime = curr - // Never run the code below, this is just to make TypeScript happy - // eslint-disable-next-line no-constant-condition - if (false) { - debug.enabled = false + args[0] = coerce(args[0]) + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O') } - Object.defineProperty(debug, 'enabled', { - enumerable: true, - configurable: false, - get: () => { - if (enableOverride != null) { - return enableOverride - } - if (namespacesCache !== createDebug.namespaces) { - namespacesCache = createDebug.namespaces - enabledCache = createDebug.enabled(namespace) - } - - return enabledCache - }, - set: (v) => { - enableOverride = v - }, + + // Apply any `formatters` transformations + let index = 0 + args[0] = (args[0] as string).replace(/%([a-z%])/gi, (match, format) => { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') return '%' + + index++ + const formatter = options.formatters[format] + if (typeof formatter === 'function') { + const value = args[index] + match = formatter.call(debug, value) + + // Now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1) + index-- + } + return match }) - // Env-specific initialization logic for debug instances - init && init(debug) + // Apply env-specific formatting (colors, etc.) + options.formatArgs.call(debug, diff, args as [string, ...any[]]) - return debug + debug.log(...args) } + debug.extend = function (this: Debugger, namespace: string, delimiter = ':') { + return createDebug(this.namespace + delimiter + namespace, { + useColors: this.useColors, + color: this.color, + formatArgs: this.formatArgs, + formatters: this.formatters, + inspectOpts: this.inspectOpts, + log: this.log, + }) + } + Object.assign(debug, options) + + debug.namespace = namespace + Object.defineProperty(debug, 'enabled', { + enumerable: true, + configurable: false, + get: () => { + if (enableOverride != null) { + return enableOverride + } + if (namespacesCache !== globalNamespaces) { + namespacesCache = globalNamespaces + enabledCache = enabled(namespace) + } - function enable(namespaces: string) { - save(namespaces) - createDebug.namespaces = namespaces + return enabledCache + }, + set: (v) => { + enableOverride = v + }, + }) + + // Never run the code below, this is just to make TypeScript happy + // eslint-disable-next-line no-constant-condition + if (false) { + debug.useColors = true + debug.color = 0 + debug.formatArgs = () => {} + debug.formatters = {} + debug.inspectOpts = {} + debug.log = () => {} + debug.enabled = false + } - createDebug.names = [] - createDebug.skips = [] + return debug +} - const split = namespaces - .trim() - .replace(/\s+/g, ',') - .split(',') - .filter(Boolean) +let names: string[] = [] +let skips: string[] = [] - for (const ns of split) { - if (ns[0] === '-') { - createDebug.skips.push(ns.slice(1)) - } else { - createDebug.names.push(ns) - } - } - } +export function enable(namespaces: string): void { + globalNamespaces = namespaces - function disable() { - const namespaces = [ - ...createDebug.names, - ...createDebug.skips.map((namespace) => `-${namespace}`), - ].join(',') - createDebug.enable('') - return namespaces - } + names = [] + skips = [] - function enabled(name: string): boolean { - for (const skip of createDebug.skips) { - if (matchesTemplate(name, skip)) { - return false - } - } + const split = globalNamespaces + .trim() + .replace(/\s+/g, ',') + .split(',') + .filter(Boolean) - for (const ns of createDebug.names) { - if (matchesTemplate(name, ns)) { - return true - } + for (const ns of split) { + if (ns[0] === '-') { + skips.push(ns.slice(1)) + } else { + names.push(ns) } - - return false } +} - createDebug.namespaces = '' - createDebug.formatters = {} - createDebug.enable = enable - createDebug.disable = disable - createDebug.enabled = enabled +export function disable(): string { + const namespaces = [ + ...names, + ...skips.map((namespace) => `-${namespace}`), + ].join(',') + enable('') + return namespaces +} - // deprecated - createDebug.names = [] - createDebug.skips = [] - createDebug.selectColor = (ns) => selectColor(colors, ns) - createDebug.formatArgs = formatArgs - createDebug.log = log +export function enabled(name: string): boolean { + for (const skip of skips) { + if (matchesTemplate(name, skip)) { + return false + } + } - createDebug.enable(load()) + for (const ns of names) { + if (matchesTemplate(name, ns)) { + return true + } + } - return createDebug + return false } diff --git a/src/node.ts b/src/node.ts index 97184ce..b2f0070 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,19 +1,18 @@ import { createRequire } from 'node:module' import { isatty } from 'node:tty' import { formatWithOptions, inspect } from 'node:util' -import { setup } from './core.ts' -import { humanize as _humanize } from './utils.ts' -import type { Debug, Debugger, InspectOptions } from './types.ts' +import { + createDebug as _createDebug, + enable as _enable, + disable, + enabled, + namespaces, +} from './core.ts' +import { humanize as _humanize, selectColor } from './utils.ts' +import type { Debugger, DebugOptions, InspectOptions } from './types.ts' const require = createRequire(import.meta.url) -/** - * Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr. - */ -export function log(...args: any[]): void { - process.stderr.write(`${formatWithOptions(inspectOpts, ...args)}\n`) -} - const colors: number[] = process.stderr.getColorDepth && process.stderr.getColorDepth() > 2 ? [ @@ -26,9 +25,7 @@ const colors: number[] = : [6, 2, 3, 4, 5, 1] const inspectOpts: InspectOptions = Object.keys(process.env) - .filter((key) => { - return /^debug_/i.test(key) - }) + .filter((key) => /^debug_/i.test(key)) .reduce( (obj, key) => { // Camel-case @@ -41,8 +38,10 @@ const inspectOpts: InspectOptions = Object.keys(process.env) let value: any = process.env[key] if (value === 'null') { value = null + // TODO perf: don't use regex } else if (/^yes|on|true|enabled$/i.test(value)) { value = true + // TODO perf: don't use regex } else if (/^no|off|false|disabled$/i.test(value)) { value = false } else { @@ -55,23 +54,6 @@ const inspectOpts: InspectOptions = Object.keys(process.env) {} as Record, ) -/** - * Load `namespaces`. - */ -function load(): string { - return process.env.DEBUG || '' -} - -function save(namespaces: string): void { - if (namespaces) { - process.env.DEBUG = namespaces - } else { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG - } -} - /** * Is stdout a TTY? Colored output is enabled when `true`. */ @@ -88,10 +70,21 @@ try { humanize = _humanize } +function getDate(): string { + if (inspectOpts.hideDate) { + return '' + } + return `${new Date().toISOString()} ` +} + /** * Adds ANSI color escape codes if enabled. */ -export function formatArgs(this: Debugger, args: [string, ...any[]]): void { +export function formatArgs( + this: Debugger, + diff: number, + args: [string, ...any[]], +): void { const { namespace: name, useColors } = this if (useColors) { @@ -100,60 +93,73 @@ export function formatArgs(this: Debugger, args: [string, ...any[]]): void { const prefix = ` ${colorCode};1m${name} \u001B[0m` args[0] = prefix + args[0].split('\n').join(`\n${prefix}`) - args.push(`${colorCode}m+${humanize(this.diff!)}\u001B[0m`) + args.push(`${colorCode}m+${humanize(diff)}\u001B[0m`) } else { args[0] = `${getDate()}${name} ${args[0]}` } } -function getDate(): string { - if (inspectOpts.hideDate) { - return '' - } - return `${new Date().toISOString()} ` +function log(this: Debugger, ...args: any[]): void { + process.stderr.write(`${formatWithOptions(this.inspectOpts, ...args)}\n`) } -function init(debug: Debugger): void { - debug.inspectOpts = Object.assign({}, inspectOpts) -} +const defaultOptions: Omit, 'color'> = { + useColors: useColors(), -export const createDebug: Debug = setup( - useColors(), - colors, - log, - load, - save, formatArgs, - init, -) + formatters: { + /** + * Map %o to `util.inspect()`, all on a single line. + */ + o(v) { + this.inspectOpts.colors = this.useColors + return inspect(v, this.inspectOpts) + .split('\n') + .map((str) => str.trim()) + .join(' ') + }, -createDebug.inspectOpts = inspectOpts + /** + * Map %O to `util.inspect()`, allowing multiple lines if needed. + */ + O(v) { + this.inspectOpts.colors = this.useColors + return inspect(v, this.inspectOpts) + }, + }, + inspectOpts, -/** - * Map %o to `util.inspect()`, all on a single line. - */ -createDebug.formatters.o = function (v) { - this.inspectOpts!.colors = this.useColors - return inspect(v, this.inspectOpts) - .split('\n') - .map((str) => str.trim()) - .join(' ') + log, } -/** - * Map %O to `util.inspect()`, allowing multiple lines if needed. - */ -createDebug.formatters.O = function (v) { - this.inspectOpts!.colors = this.useColors - return inspect(v, this.inspectOpts) +export function createDebug( + namespace: string, + options?: DebugOptions, +): Debugger { + const color = (options && options.color) ?? selectColor(colors, namespace) + return _createDebug( + namespace, + Object.assign(defaultOptions, { color }, options), + ) } -export default createDebug -export type * from './types.ts' +function save(namespaces: string): void { + if (namespaces) { + process.env.DEBUG = namespaces + } else { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG + } +} -// @ts-expect-error -createDebug.default = createDebug -// @ts-expect-error -createDebug.debug = createDebug +function enable(namespaces: string): void { + save(namespaces) + _enable(namespaces) +} + +// side-effect +_enable(process.env.DEBUG || '') -export { createDebug as 'module.exports' } +export type * from './types.ts' +export { disable, enable, enabled, namespaces } diff --git a/src/types-v2.ts b/src/types-v2.ts deleted file mode 100644 index 24974bc..0000000 --- a/src/types-v2.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { InspectOptions as NodeInspectOptions } from 'node:util' - -/** - * Disable debug output. - */ -export declare function disable(): string -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - */ -export declare function enable(namespaces: string): void -/** - * Returns true if the given mode name is enabled, false otherwise. - */ -export declare function enabled(namespaces: string): boolean -/** - * Returns an array of the currently enabled debug namespaces. - */ -export declare function namespaces(): string[] - -export interface InspectOptions extends NodeInspectOptions { - hideDate?: boolean -} - -export interface Formatters { - [formatter: string]: (this: Debugger, v: any) => string -} - -export interface Debugger extends Required { - (formatter: any, ...args: any[]): void - - namespace: string - enabled: boolean - - extend: (namespace: string, delimiter?: string) => Debugger -} - -export interface DebugOptions { - useColors?: boolean - color?: string | number - - formatArgs?: (this: Debugger, diff: number, args: [string, ...any[]]) => void - formatters?: Formatters - /** Node.js only */ - inspectOpts?: InspectOptions - - log?: (this: Debugger, ...args: any[]) => void -} diff --git a/src/types.ts b/src/types.ts index 7b77051..22636e7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,82 +4,27 @@ export interface InspectOptions extends NodeInspectOptions { hideDate?: boolean } -export interface Debugger { +export interface Formatters { + [formatter: string]: (this: Debugger, v: any) => string +} + +export interface Debugger extends Required { (formatter: any, ...args: any[]): void - useColors: boolean - color: string | number - enabled: boolean namespace: string - inspectOpts?: InspectOptions + enabled: boolean - log: (...args: any[]) => any extend: (namespace: string, delimiter?: string) => Debugger - - /** - * @internal - * @deprecated It will removed in the next major version. This is internal cache. - */ - diff?: number - /** - * @internal - * @deprecated It will removed in the next major version. This is internal cache. - */ - prev?: number - /** - * @internal - * @deprecated It will removed in the next major version. This is internal cache. - */ - curr?: number } -export interface Formatters { - [formatter: string]: (this: Debugger, v: any) => string -} - -export interface Debug { - (namespace: string): Debugger +export interface DebugOptions { + useColors?: boolean + color?: string | number - namespaces: string - - /** - * Disable debug output. - */ - disable: () => string - /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - */ - enable: (namespaces: string) => void - /** - * Returns true if the given mode name is enabled, false otherwise. - */ - enabled: (namespaces: string) => boolean - - formatters: Formatters + formatArgs?: (this: Debugger, diff: number, args: [string, ...any[]]) => void + formatters?: Formatters + /** Node.js only */ inspectOpts?: InspectOptions - /** - * @deprecated It will removed in the next major version. - */ - formatArgs: (this: Debugger, args: [string, ...any[]]) => void - /** - * @deprecated It will removed in the next major version. - */ - selectColor: (namespace: string) => string | number - /** - * @deprecated It will removed in the next major version. - */ - log: (...args: any[]) => void - - /** - * @internal - * @deprecated It will removed in the next major version. This is internal cache. - */ - names: string[] - /** - * @internal - * @deprecated It will removed in the next major version. This is internal cache. - */ - skips: string[] + log?: (this: Debugger, ...args: any[]) => void } diff --git a/tests/basic.test.ts b/tests/basic.test.ts index 5286af6..94f0d61 100644 --- a/tests/basic.test.ts +++ b/tests/basic.test.ts @@ -1,12 +1,12 @@ import { assert, describe, it } from 'vitest' -const { createDebug: debug } = await import( +const { createDebug, enable, disable } = (await import( globalThis.process ? '../src/node' : '../src/browser' -) +)) as typeof import('../src/node') describe('basic', () => { it('passes a basic sanity check', () => { - const log = debug('test') + const log = createDebug('test') log.enabled = true log.log = () => {} @@ -14,16 +14,16 @@ describe('basic', () => { }) it('honors global debug namespace enable calls', () => { - assert.deepStrictEqual(debug('test:12345').enabled, false) - assert.deepStrictEqual(debug('test:67890').enabled, false) + assert.deepStrictEqual(createDebug('test:12345').enabled, false) + assert.deepStrictEqual(createDebug('test:67890').enabled, false) - debug.enable('test:12345') - assert.deepStrictEqual(debug('test:12345').enabled, true) - assert.deepStrictEqual(debug('test:67890').enabled, false) + enable('test:12345') + assert.deepStrictEqual(createDebug('test:12345').enabled, true) + assert.deepStrictEqual(createDebug('test:67890').enabled, false) }) it('uses custom log function', () => { - const log = debug('test') + const log = createDebug('test') log.enabled = true const messages = [] @@ -38,7 +38,7 @@ describe('basic', () => { describe('extend namespace', () => { it('should extend namespace', () => { - const log = debug('foo') + const log = createDebug('foo') log.enabled = true log.log = () => {} @@ -47,7 +47,7 @@ describe('basic', () => { }) it('should extend namespace with custom delimiter', () => { - const log = debug('foo') + const log = createDebug('foo') log.enabled = true log.log = () => {} @@ -56,7 +56,7 @@ describe('basic', () => { }) it('should extend namespace with empty delimiter', () => { - const log = debug('foo') + const log = createDebug('foo') log.enabled = true log.log = () => {} @@ -65,7 +65,7 @@ describe('basic', () => { }) it('should keep the log function between extensions', () => { - const log = debug('foo') + const log = createDebug('foo') log.log = () => {} const logBar = log.extend('bar') @@ -75,40 +75,34 @@ describe('basic', () => { describe('rebuild namespaces string (disable)', () => { it('handle names, skips, and wildcards', () => { - debug.enable('test,abc*,-abc') - const namespaces = debug.disable() + enable('test,abc*,-abc') + const namespaces = disable() assert.deepStrictEqual(namespaces, 'test,abc*,-abc') }) it('handles empty', () => { - debug.enable('') - const namespaces = debug.disable() + enable('') + const namespaces = disable() assert.deepStrictEqual(namespaces, '') - assert.deepStrictEqual(debug.names, []) - assert.deepStrictEqual(debug.skips, []) }) it('handles all', () => { - debug.enable('*') - const namespaces = debug.disable() + enable('*') + const namespaces = disable() assert.deepStrictEqual(namespaces, '*') }) it('handles skip all', () => { - debug.enable('-*') - const namespaces = debug.disable() + enable('-*') + const namespaces = disable() assert.deepStrictEqual(namespaces, '-*') }) it('names+skips same with new string', () => { - debug.enable('test,abc*,-abc') - const oldNames = [...debug.names] - const oldSkips = [...debug.skips] - const namespaces = debug.disable() + enable('test,abc*,-abc') + const namespaces = disable() assert.deepStrictEqual(namespaces, 'test,abc*,-abc') - debug.enable(namespaces) - assert.deepStrictEqual(oldNames.map(String), debug.names.map(String)) - assert.deepStrictEqual(oldSkips.map(String), debug.skips.map(String)) + enable(namespaces) }) // it('handles re-enabling existing instances', () => { diff --git a/tests/conformance.test.ts b/tests/conformance.test.ts deleted file mode 100644 index 7135585..0000000 --- a/tests/conformance.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { createRequire } from 'node:module' -import { describe, expect, test } from 'vitest' - -const require = createRequire(import.meta.url) - -const removedAPIs = new Set([ - 'destroy', //deprecated - 'coerce', // private API - 'humanize', // private API - 'init', // private API - 'save', // private API - 'load', // private API - 'colors', // private API - 'useColors', // private API - 'inspectOpts', // private API - 'module.exports', -]) - -describe('conformance', () => { - test('require: same exports', () => { - const debug = require('debug') - const obugNode = require('../dist/node') - const debugKeys = Object.keys(debug) - .filter((key) => !removedAPIs.has(key)) - .sort() - const obugKeys = Object.keys(obugNode) - .filter((k) => k !== 'inspectOpts') - .sort() - expect(obugKeys).toEqual(debugKeys) - - const obugBrowser = require('../dist/browser') - expect(Object.keys(obugBrowser).sort()).toEqual(obugKeys) - }) - - test('import: same exports', async () => { - const debug = await import('debug') - const obug = await import('../dist/node') - const debugKeys = Object.keys(debug) - .filter((key) => !removedAPIs.has(key)) - .sort() - const obugKeys = Object.keys(obug) - .filter((key) => key !== 'createDebug' && key !== 'module.exports') - .sort() - expect(obugKeys).toEqual(debugKeys) - - const obugBrowser = await import('../dist/browser') - expect(Object.keys(obugBrowser).sort()).toEqual(Object.keys(obug).sort()) - }) -}) diff --git a/tests/node.test.ts b/tests/node.test.ts index d76bdc6..71a22f2 100644 --- a/tests/node.test.ts +++ b/tests/node.test.ts @@ -1,7 +1,6 @@ -import { afterEach } from 'node:test' import { formatWithOptions } from 'node:util' -import { describe, expect, it, vi } from 'vitest' -import debug from '../src/node' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { createDebug, enable } from '../src/node' import type { InspectOptions } from '../src/types' vi.mock('node:util', async (importActual) => { @@ -11,23 +10,24 @@ vi.mock('node:util', async (importActual) => { formatWithOptions: vi.fn(actual.formatWithOptions), } }) + vi.spyOn(process.stderr, 'write').mockImplementation(() => true) -afterEach(() => { - vi.resetAllMocks() +beforeEach(() => { + vi.clearAllMocks() }) describe('debug node', () => { describe('formatting options', () => { it('calls util.formatWithOptions', () => { - debug.enable('*') - const log = debug('formatting options') + enable('*') + const log = createDebug('formatting options') log('hello world') expect(formatWithOptions).toBeCalledTimes(1) }) it('calls util.formatWithOptions with inspectOpts', () => { - debug.enable('*') + enable('*') const options: InspectOptions = { hideDate: true, @@ -35,15 +35,15 @@ describe('debug node', () => { depth: 10, showHidden: true, } - Object.assign(debug.inspectOpts!, options) - - const log = debug('format with inspectOpts') + const log = createDebug('format with inspectOpts', { + inspectOpts: options, + }) log('hello world2') - // assert.deepStrictEqual(util.formatWithOptions.getCall(0).args[0], options) + expect(formatWithOptions).toHaveBeenNthCalledWith( 1, options, - expect.stringMatching(/.+ formatting options hello world$/), + expect.stringMatching(/.+ format with inspectOpts hello world2$/), ) }) }) -- 2.51.2