猫史档案馆


【更好的控制台】你是否在为神岛的控制台无法输出完整的对象/列表而烦恼?

用户:小苏打_小苏打_查看:0 回复:4 评论:0 创建时间:2024-08-05T00:22:58


/**
 * 得到更好的控制台输出
 *  - 清晰的层级显示,让输出更美观
 *  - 自动展开对象、数组,让debug更简单
 *  - 自定义最大展开深度、最大长度、占位符、tab字符等
 *  - 循环引用检测
 * @example Rich.print(obj)
 * @todo 稀疏数组支持
 */
class Rich {
    static config = {
        maxDepth: 5,
        maxLength: 40,
        placeholder: '(...)',
        tabChar: '|---'
    };
    static handlers = [
        {
            condition: obj => obj === null,
            handler: () => 'null',
        },
        {
            condition: obj => obj === undefined,
            handler: () => 'undefined'
        },
        {
            condition: obj => typeof obj === 'string',
            handler: obj => obj,
            cat: true
        },
        {
            condition: obj => typeof obj === 'number'
                || typeof obj === 'bigint'
                || typeof obj === 'boolean'
                || typeof obj === 'symbol',
            handler: obj => obj.toString(),
            cat: true
        },
        {
            condition: (_, depth) => depth >= Rich.config.maxDepth,
            handler: _ => '...'
        },
        {
            condition: obj => obj instanceof Array,
            handler: (obj, depth, fn) => '[' + obj.map(v => fn(v, depth)).join(', ') + ']'
        },
        {
            condition: obj => obj instanceof Date,
            handler: obj => obj.toISOString()
        },
        {
            condition: obj => obj instanceof Function,
            handler: obj => `Function ${obj.name}`
        },
        {
            condition: obj => obj instanceof Object,
            handler: (obj, depth, fn) => {
                if (Rich.visitedObj.has(obj)) {
                    return '(Circular Reference)';
                }
                Rich.visitedObj.add(obj);
                const keys = Object.keys(obj);
                if (keys.length == 0) {
                    return '{}';
                }
                const ctxTab = Rich.config.tabChar.repeat(depth + 1);
                const ctx = keys
                    .map(key => `${key}: ${fn(obj[key], depth + 1)}`)
                    .join(`,\n${ctxTab}`);
                Rich.visitedObj.delete(obj);
                return `{\n${ctxTab}${ctx}\n${Rich.config.tabChar.repeat(depth)}}`;
            }
        }
    ];
    static visitedObj = new WeakSet();
    static getRiched(obj, depth = 0) {
        for (const handler of Rich.handlers) {
            if (!handler.condition(obj, depth)) {
                continue;
            }
            let ctx = handler.handler(obj, depth, Rich.getRiched);
            if (handler.cat && ctx.length >= Rich.config.maxLength) {
                ctx = ctx.slice(0, Rich.config.maxLength / 2) + '...' + ctx.slice(-Rich.config.maxLength / 2);
            }
            return ctx;
        }
        return `${typeof obj}(${obj.toString() ?? '...'})`;
    }
    static print(obj) {
        console.log(Rich.getRiched(obj));
    }
}
exports.Rich = Rich;


回复

上一页1 页 / 共 1下一页
孤僻的血翼蝠bawa孤僻的血翼蝠bawa

酷!

点赞1


评论


追梦ez追梦ez

好东西必须ddd

点赞1


评论


小奕小奕小奕小奕小奕小奕

tql

点赞1


评论


小苏打_小苏打_

示例用法:

const obj = {
  a: {
    c: {
      f: 123,
      g: [3, 3, null, 4, 5],
      h: 2n**1024n
    },
    i: "A quick brown fox jumps over a lazy dog."
  }
}
Rich.print(obj);

输出效果(注:由于box3控制台会合并所有空格,只能使用一些字符撑起宽度。可以在Rich.config中配置替代字符。):

{
|---a: {
|---|---c: {
|---|---|---f: 123,
|---|---|---g: [3, 3, null, 4, 5],
|---|---|---h: 179769313486231...329624224137216
|---|---},
|---|---i: A quick brown f...ver a lazy dog.
|---}
}


typescript源文件:

interface ConvertHandler {
  condition: (obj: any, depth: number) => boolean;
  handler: (obj: any, depth: number, fn: (obj: any, depth: number) => string) => string;
  cat?: boolean
}

interface RichConfig {
  maxDepth: number;
  maxLength: number;
  placeholder: string;
  tabChar: string;
}

/**
 * 得到更好的控制台输出
 * @example Rich.print(obj)
 * @todo 稀疏数组支持
 */
export abstract class Rich {
  static config: RichConfig = {
    maxDepth: 5,
    maxLength: 30,
    placeholder: '(...)',
    tabChar: '|---'
  };

  static handlers: ConvertHandler[] = [
    {
      condition: obj => obj === null,
      handler: () => 'null',
    },
    {
      condition: obj => obj === undefined,
      handler: () => 'undefined'
    },
    {
      condition: obj => typeof obj === 'string',
      handler: obj => obj,
      cat: true
    },
    {
      condition: obj => typeof obj === 'number'
        || typeof obj === 'bigint'
        || typeof obj === 'boolean'
        || typeof obj === 'symbol',
      handler: obj => obj.toString(),
      cat: true
    },
    {
      condition: (_, depth) => depth >= Rich.config.maxDepth,
      handler: _ => '...'
    },
    {
      condition: obj => obj instanceof Array,
      handler: (obj, depth, fn) => '[' + (obj as Array<any>).map(v => fn(v, depth)).join(', ') + ']'
    },
    {
      condition: obj => obj instanceof Object,
      handler: (obj, depth, fn) => {
        const ctxTab = Rich.config.tabChar.repeat(depth+1);
        const ctx = Object.keys(obj)
          .map(key => `${key}: ${fn(obj[key], depth + 1)}`)
          .join(`,\n${ctxTab}`);
        return `{\n${ctxTab}${ctx}\n${Rich.config.tabChar.repeat(depth)}}`;
      }
    }
  ]

  static getRiched(obj: any, depth: number = 0): string {
    for (const handler of Rich.handlers) {
      if(!handler.condition(obj, depth)){
        continue;
      }
      let ctx = handler.handler(obj, depth, Rich.getRiched);
      if(handler.cat && ctx.length >= Rich.config.maxLength){
        ctx = ctx.slice(0, Rich.config.maxLength / 2) + '...' + ctx.slice(-Rich.config.maxLength / 2);
      }
      return ctx;
    }
    return `${typeof obj}(${obj.string})`;
  }

  static print(obj: any){
    console.log(Rich.getRiched(obj));
  }
}

点赞0


评论