"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.anyToDiagnostic = anyToDiagnostic;
exports.convertSourceLocationToHighlight = convertSourceLocationToHighlight;
exports.default = void 0;
exports.encodeJSONKeyComponent = encodeJSONKeyComponent;
exports.errorToDiagnostic = errorToDiagnostic;
exports.escapeMarkdown = escapeMarkdown;
exports.generateJSONCodeHighlights = generateJSONCodeHighlights;
exports.getJSONHighlightLocation = getJSONHighlightLocation;
exports.getJSONSourceLocation = getJSONSourceLocation;
exports.md = md;
function _assert() {
  const data = _interopRequireDefault(require("assert"));
  _assert = function () {
    return data;
  };
  return data;
}
function _nullthrows() {
  const data = _interopRequireDefault(require("nullthrows"));
  _nullthrows = function () {
    return data;
  };
  return data;
}
function _jsonSourcemap() {
  const data = require("@mischnic/json-sourcemap");
  _jsonSourcemap = function () {
    return data;
  };
  return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** These positions are 1-based (so <code>1</code> is the first line/column) */
/**
 * Note: A tab character is always counted as a single character
 * This is to prevent any mismatch of highlighting across machines
 */
/**
 * Describes how to format a code frame.
 * A code frame is a visualization of a piece of code with a certain amount of
 * code highlights that point to certain chunk(s) inside the code.
 */
/** A JSON object (as in "map") */
/**
 * A style agnostic way of emitting errors, warnings and info.
 * Reporters are responsible for rendering the message, codeframes, hints, ...
 */
// This type should represent all error formats Parcel can encounter...
/** Something that can be turned into a diagnostic. */
/** Normalize the given value into a diagnostic. */
function anyToDiagnostic(input) {
  if (Array.isArray(input)) {
    return input.flatMap(e => anyToDiagnostic(e));
  } else if (input instanceof ThrowableDiagnostic) {
    return input.diagnostics;
  } else if (input instanceof Error) {
    return errorToDiagnostic(input);
  } else if (typeof input === 'string') {
    return [{
      message: input
    }];
  } else if (typeof input === 'object') {
    return [input];
  } else {
    return errorToDiagnostic(input);
  }
}

/** Normalize the given error into a diagnostic. */
function errorToDiagnostic(error, defaultValues) {
  let codeFrames = undefined;
  if (typeof error === 'string') {
    return [{
      origin: (defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.origin) ?? 'Error',
      message: escapeMarkdown(error)
    }];
  }
  if (error instanceof ThrowableDiagnostic) {
    return error.diagnostics.map(d => {
      return {
        ...d,
        origin: d.origin ?? (defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.origin) ?? 'unknown'
      };
    });
  }
  if (error.loc && error.source != null) {
    codeFrames = [{
      filePath: error.filePath ?? error.fileName ?? (defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.filePath) ?? undefined,
      code: error.source,
      codeHighlights: [{
        start: {
          line: error.loc.line,
          column: error.loc.column
        },
        end: {
          line: error.loc.line,
          column: error.loc.column
        }
      }]
    }];
  }
  return [{
    origin: (defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.origin) ?? 'Error',
    message: escapeMarkdown(error.message),
    name: error.name,
    stack: codeFrames == null ? error.highlightedCodeFrame ?? error.codeFrame ?? error.stack : undefined,
    codeFrames
  }];
}
/**
 * An error wrapper around a diagnostic that can be <code>throw</code>n (e.g. to signal a
 * build error).
 */
class ThrowableDiagnostic extends Error {
  constructor(opts) {
    let diagnostics = Array.isArray(opts.diagnostic) ? opts.diagnostic : [opts.diagnostic];

    // Construct error from diagnostics
    super(diagnostics[0].message);
    // @ts-ignore
    this.stack = diagnostics[0].stack ?? super.stack;
    // @ts-ignore
    this.name = diagnostics[0].name ?? super.name;
    this.diagnostics = diagnostics;
  }
}

/**
 * Turns a list of positions in a JSON5 file with messages into a list of diagnostics.
 * Uses <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>.
 *
 * @param code the JSON code
 * @param ids A list of JSON keypaths (<code>key: "/some/parent/child"</code>) with corresponding messages, \
 * <code>type</code> signifies whether the key of the value in a JSON object should be highlighted.
 */
exports.default = ThrowableDiagnostic;
function generateJSONCodeHighlights(data, ids) {
  let map = typeof data == 'string' ? (0, _jsonSourcemap().parse)(data, undefined, {
    dialect: 'JSON5',
    tabWidth: 1
  }) : data;
  return ids.map(({
    key,
    type,
    message
  }) => {
    let pos = (0, _nullthrows().default)(map.pointers[key]);
    return {
      ...getJSONHighlightLocation(pos, type),
      message
    };
  });
}

/**
 * Converts entries in <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>'s
 * <code>result.pointers</code> array.
 */
function getJSONHighlightLocation(pos, type) {
  let key = 'key' in pos ? pos.key : undefined;
  let keyEnd = 'keyEnd' in pos ? pos.keyEnd : undefined;
  if (!type && key && pos.value) {
    // key and value
    return {
      start: {
        line: key.line + 1,
        column: key.column + 1
      },
      end: {
        line: pos.valueEnd.line + 1,
        column: pos.valueEnd.column
      }
    };
  } else if (type == 'key' || !pos.value) {
    (0, _assert().default)(key && keyEnd);
    return {
      start: {
        line: key.line + 1,
        column: key.column + 1
      },
      end: {
        line: keyEnd.line + 1,
        column: keyEnd.column
      }
    };
  } else {
    return {
      start: {
        line: pos.value.line + 1,
        column: pos.value.column + 1
      },
      end: {
        line: pos.valueEnd.line + 1,
        column: pos.valueEnd.column
      }
    };
  }
}

/** Result is 1-based, but end is exclusive */
function getJSONSourceLocation(pos, type) {
  let v = getJSONHighlightLocation(pos, type);
  return {
    start: v.start,
    end: {
      line: v.end.line,
      column: v.end.column + 1
    }
  };
}
function convertSourceLocationToHighlight({
  start,
  end
}, message) {
  return {
    message,
    start,
    end: {
      line: end.line,
      column: end.column - 1
    }
  };
}

/** Sanitizes object keys before using them as <code>key</code> in generateJSONCodeHighlights */
function encodeJSONKeyComponent(component) {
  return component.replace(/~/g, '~0').replace(/\//g, '~1');
}
const escapeCharacters = ['\\', '*', '_', '~'];
function escapeMarkdown(s) {
  let result = s;
  for (const char of escapeCharacters) {
    result = result.replace(new RegExp(`\\${char}`, 'g'), `\\${char}`);
  }
  return result;
}
const mdVerbatim = Symbol();
function md(strings, ...params) {
  let result = [];
  for (let i = 0; i < params.length; i++) {
    result.push(strings[i]);
    let param = params[i];
    if (Array.isArray(param)) {
      for (let j = 0; j < param.length; j++) {
        var _param$j;
        result.push(((_param$j = param[j]) === null || _param$j === void 0 ? void 0 : _param$j[mdVerbatim]) ?? escapeMarkdown(`${param[j]}`));
        if (j < param.length - 1) {
          result.push(', ');
        }
      }
    } else {
      result.push((param === null || param === void 0 ? void 0 : param[mdVerbatim]) ?? escapeMarkdown(`${param}`));
    }
  }
  return result.join('') + strings[strings.length - 1];
}
md.bold = function (s) {
  // $FlowFixMe[invalid-computed-prop]
  return {
    [mdVerbatim]: '**' + escapeMarkdown(`${s}`) + '**'
  };
};
md.italic = function (s) {
  // $FlowFixMe[invalid-computed-prop]
  return {
    [mdVerbatim]: '_' + escapeMarkdown(`${s}`) + '_'
  };
};
md.underline = function (s) {
  // $FlowFixMe[invalid-computed-prop]
  return {
    [mdVerbatim]: '__' + escapeMarkdown(`${s}`) + '__'
  };
};
md.strikethrough = function (s) {
  // $FlowFixMe[invalid-computed-prop]
  return {
    [mdVerbatim]: '~~' + escapeMarkdown(`${s}`) + '~~'
  };
};