index.js

import { basename, dirname } from 'path';
import { cwd, stdout } from 'process';
import { eastAsianWidth } from 'get-east-asian-width';
import chalkPipe, { chalk } from 'chalk-pipe';

// src/core/PrintError.ts

// src/helpers/get-address.ts
function getAddress(text) {
  let index = 0;
  let start = -1;
  let end = -1;
  while (index >= 0) {
    const nextIndex = text.slice(Math.max(0, index)).indexOf("(");
    index = nextIndex > 0 ? nextIndex + index + 1 : -1;
    if (nextIndex > 0) {
      start = index;
    }
  }
  if (start === -1)
    return "";
  index = text.slice(Math.max(0, start)).indexOf(")");
  if (index === -1)
    return "";
  end = index + start;
  return text.slice(start, end);
}
var get_address_default = getAddress;

// src/helpers/is-rgb.ts
function isRgb(value) {
  const rgbRegular = /^\(\d{1,3},\d{1,3},\d{1,3}\)$/;
  const validRgb = rgbRegular.test(value.replace(/\s/g, ""));
  if (!validRgb)
    return false;
  const numberRegular = /\d{1,3}/g;
  const numberMatch = value.match(numberRegular);
  return Boolean(numberMatch?.every((item) => Number(item) <= 255));
}
var is_rgb_default = isRgb;

// src/helpers/is-bg-rgb.ts
function isBgRgb(value) {
  if (!value.startsWith("bg"))
    return false;
  const rgbValue = value.slice(2).replace(/\s/g, "");
  return is_rgb_default(rgbValue);
}
var is_bg_rgb_default = isBgRgb;

// src/helpers/is-string.ts
function isString(value) {
  return typeof value === "string";
}
var is_string_default = isString;

// src/helpers/normalize-number.ts
function normalizeNumber(value) {
  return is_string_default(value) ? Number.parseInt(value, 10) : value ?? Number.NaN;
}
var normalize_number_default = normalizeNumber;

// src/helpers/normalize-path.ts
function normalizePath(path) {
  return path.replace(/\\{1,2}/g, "/");
}
var normalize_path_default = normalizePath;

// src/helpers/normalize-rgb.ts
function normalizeRgb(color, prefix = "") {
  const length = color.startsWith(prefix) ? prefix.length : 0;
  const rgb = color.replace(/[\s()]/g, "").slice(Math.max(0, length));
  const [r, g, b] = rgb.split(",").map(Number);
  return [r, g, b];
}
var normalize_rgb_default = normalizeRgb;

// src/helpers/normalize-track.ts
function normalizeTrack(string_, start, end) {
  return string_.slice(Math.max(0, start), Math.max(0, end)).trim();
}
var normalize_track_default = normalizeTrack;
var PaletteError = class extends Error {
  /**
   * Constructs a new PaletteError instance.
   * @constructor
   * @param {(string | Error)} message - The error message or an Error object.
   */
  constructor(message) {
    super(is_string_default(message) ? message : message?.message || "");
    this.name = this.constructor.name;
  }
  /**
   * Returns a Chalk instance with specified styles applied.
   * @param {string} styles - The styles to apply.
   * @param {ChalkInstance} chain - The Chalk instance to chain with.
   * @returns {ChalkInstance} A Chalk instance with the specified styles.
   */
  palette(styles, chain = chalk) {
    if (!is_string_default(styles)) {
      styles = "";
    }
    const list = styles.split(".").map((style) => {
      const _isRgb = is_rgb_default(style);
      const _isBgRgb = is_bg_rgb_default(style);
      if (!_isRgb && !_isBgRgb)
        return style;
      const rgb = normalize_rgb_default(style, "bg");
      chain = _isRgb ? chain.rgb(...rgb) : chain.bgRgb(...rgb);
      return void 0;
    }).filter(Boolean);
    return chalkPipe(list.join("."), chain);
  }
  /**
   * Adds padding around the provided content.
   * @param {string} content - The content to pad.
   * @returns {string} The padded content.
   */
  padding(content) {
    return ` ${content} `;
  }
  /**
   * Generates a formatted message with optional styles.
   * @param {string} styles - The styles to apply.
   * @returns {string} The formatted message.
   */
  info(styles) {
    if (!is_string_default(styles)) {
      styles = "";
    }
    const title = this.padding(this.name);
    const stylish = this.palette(styles);
    return ` ${stylish(title)} ${this.message}`;
  }
};

// src/core/TraceError.ts
var TraceError = class extends PaletteError {
  /**
   * Extracts trace information from the error stack.
   * @returns {TraceOption[]} An array of trace options.
   */
  trace() {
    const track = [];
    if (!this.stack)
      return track;
    for (const line of this.stack.split("\n")) {
      const text = line.trim();
      if (text === "")
        continue;
      const result = this.parse(text);
      if (result && this.funnel(result)) {
        track.push(result);
      }
    }
    return track;
  }
  /**
   * Filters out trace options with addresses starting with 'node:'.
   * @private
   * @param {TraceOption} option - The trace option to check.
   * @returns {boolean} Returns true if the option should be included.
   */
  funnel(option) {
    return !option.address?.startsWith("node:");
  }
  /**
   * Parses a single line of the error stack trace.
   * @private
   * @param {string} text - The line of stack trace text.
   * @returns {TraceOption | undefined} A trace option object, or undefined if parsing fails.
   */
  parse(text) {
    const original = text;
    if (!text.startsWith("at "))
      return;
    text = text.replace(/^at /, "");
    if (text === "Error (<anonymous>)" || text === "Error (<anonymous>:null:null)") {
      return void 0;
    }
    const isEval = text.startsWith("eval");
    let name;
    let address;
    let filepath;
    let directory;
    let file;
    let line;
    let col;
    let packageName = "[current]";
    const addressMatch = get_address_default(text);
    if (addressMatch) {
      address = addressMatch.trim();
      name = isEval ? "eval" : text.slice(0, Math.max(0, text.length - address.length - 2)).trim();
    } else {
      address = text;
      name = "anonymous";
    }
    address = normalize_path_default(address);
    const lineMatch = /:(\d+):(\d+)\)?$/.exec(address);
    if (lineMatch) {
      line = normalize_number_default(lineMatch[1]);
      col = normalize_number_default(lineMatch[2]);
      filepath = normalize_track_default(address, 0, address.length - lineMatch[0].length);
    }
    if (filepath) {
      file = basename(filepath);
      directory = dirname(filepath);
      file = normalize_path_default(file);
      directory = normalize_path_default(directory);
    }
    if (directory) {
      const match = /node_modules\/([^/]+)(?!.*node_modules.*)/.exec(directory);
      if (match)
        packageName = match[1];
    }
    return {
      original,
      name,
      address,
      file,
      line,
      col,
      packageName
    };
  }
};

// src/core/PrintError.ts
var PrintError = class extends TraceError {
  /**
   * Constructs a new PrintError instance.
   * @constructor
   * @param {(string | Error)} message - The error message or an Error object.
   * @param {string} styles - The styles to apply to the error message.
   */
  constructor(message, styles) {
    super(message);
    const track = this.trace();
    const prefix = this.opening();
    const suffix = this.closing(styles);
    this.stack = [prefix, "", this.print(track), "", suffix].join("\n");
  }
  /**
   * Creates the opening part of the error stack trace.
   * @private
   * @param {number} [defaultLength] - The default length to use if the title length is not provided.
   * @returns {string} The opening part of the error stack trace.
   */
  opening(defaultLength) {
    let title = this.padding(this.message);
    const length = this.length(title);
    const width = this.calc((defaultLength ?? length) + 2);
    let halfWidth = Math.floor(width / 2);
    const isAlternate = halfWidth <= 0;
    if (isAlternate) {
      title = this.padding("Error Message");
      halfWidth = Math.floor((width + length - 15) / 2);
    }
    const prefixString = this.divide(halfWidth);
    const prefix = this.highlight("red", prefixString);
    const suffixString = this.divide(halfWidth);
    const suffix = this.highlight("red", suffixString);
    let output = `${prefix}${this.highlight("cyanBright", title)}${suffix}]`;
    if (isAlternate) {
      output += `
${this.message}`;
      const divider = this.divide((this.column() ?? 32) - 2);
      output += `
${this.highlight("grey", `[${divider}]`)}`;
    }
    return output;
  }
  /**
   * Formats the trace information for printing.
   * @private
   * @param {TraceOption[]} track - The array of trace options.
   * @returns {string} The formatted trace information.
   */
  print(track) {
    const root = basename(cwd());
    const { length } = track;
    return track.map((item, index) => {
      const titleStylish = this.palette("yellowBright");
      const title = titleStylish(`${item.file}:${item.line}`);
      const summary = `- ${title} ${item.name}`;
      const current = item.packageName.replace("[current]", root);
      const startIndex = item.address.indexOf(current);
      const shorthandAddress = item.address.slice(Math.max(0, startIndex));
      const descStylish = this.palette("gray");
      let description = descStylish(shorthandAddress.replace(current, `(${current})`));
      const isLatest = index === length - 1;
      if (!isLatest) {
        description += "\n";
      }
      return [summary, `  ${description}`].join("\n");
    }).filter(Boolean).join("\n");
  }
  /**
   * Creates the closing part of the error stack trace.
   * @private
   * @param {string} [styles] - The styles to apply to the error name.
   * @param {number} [defaultLength] - The default length to use if the title length is not provided.
   * @returns {string} The closing part of the error stack trace.
   */
  closing(styles, defaultLength) {
    let title = this.padding(this.name);
    const length = this.length(title);
    let width = this.calc((defaultLength ?? length) + 3);
    if (width <= 0) {
      title = this.padding("Exception");
      width += length - 9;
    }
    const prefix = this.highlight("red", this.divide(width));
    const suffix = this.highlight("red", this.divide(1));
    const stylish = this.palette(styles);
    return `[${prefix}${stylish(title)}${suffix}`;
  }
  /**
   * Calculates the display length of a string, considering East Asian Width rules.
   * @private
   * @param {string} content - The string content whose display length is to be calculated.
   * @returns {number} The total display length of the string, accounting for wide and narrow characters.
   */
  length(content) {
    let result = 0;
    for (const char of content) {
      const codePoint = char.codePointAt(0);
      if (typeof codePoint !== "number")
        continue;
      result += eastAsianWidth(codePoint);
    }
    return result;
  }
  /**
   * Calculates the width of the terminal.
   * @private
   * @param {number} length - The length of the content.
   * @param {number} [defaultLength=32] - The default length to use if the terminal width cannot be determined.
   * @returns {number} The calculated width.
   */
  calc(length, defaultLength = 32) {
    const columns = this.column();
    return (columns ?? defaultLength) - length;
  }
  /**
   * Retrieves the current width of the terminal in columns.
   * @private
   * @returns {number | undefined} The number of columns in the terminal, or `undefined` if the terminal width cannot be determined.
   */
  column() {
    return stdout?.columns;
  }
  /**
   * Applies a highlight color to the content.
   * @private
   * @param {string} color - The color to apply.
   * @param {string} content - The content to highlight.
   * @returns {string} The highlighted content.
   */
  highlight(color, content) {
    const stylish = this.palette(color);
    return stylish(content);
  }
  /**
   * Creates a string of a specified length filled with a separator character.
   * @private
   * @param {number} length - The length of the string.
   * @param {string} [separator='-'] - The character to repeat.
   * @returns {string} The string filled with the separator character.
   */
  divide(length, separator = "-") {
    const ls = Array.from({ length }).map(() => separator);
    return ls.join("");
  }
};

// src/core/Exception.ts
var Exception = class extends PrintError {
  /**
   * Constructs a new Exception instance.
   * @constructor
   * @param {(string | Error)} [message] - The error message or an Error object.
   * @param {string} [styles] - The styles to apply to the error message.
   */
  constructor(message, styles) {
    super(message ?? "", styles);
  }
  /**
   * Returns a string representation of the Exception.
   * @returns {string} A string containing the name and message of the Exception.
   */
  toString() {
    return `${this.name}: ${this.message}`;
  }
};
var Exception_default = Exception;

export { Exception_default as default };