text: /**
   * @typedef {import('unist').Node} Node
   * @typedef {import('unist').Position} Position
   * @typedef {import('unist').Point} Point
   *
   * @typedef {'ascii'|'utf8'|'utf-8'|'utf16le'|'ucs2'|'ucs-2'|'base64'|'latin1'|'binary'|'hex'} BufferEncoding
   *   Encodings supported by the buffer class.
   *   This is a copy of the typing from Node, copied to prevent Node globals from
   *   being needed.
   *   Copied from: <https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a2bc1d8/types/node/globals.d.ts#L174>
   *
   * @typedef {string|Uint8Array} VFileValue Contents of the file.
   *   Can either be text, or a Buffer like structure.
   *   This does not directly use type `Buffer`, because it can also be used in a
   *   browser context.
   *   Instead this leverages `Uint8Array` which is the base type for `Buffer`,
   *   and a native JavaScript construct.
   *
   * @typedef {VFileValue|VFileOptions|VFile} VFileCompatible Things that can be
   *   passed to the constructor.
   *
   * @typedef VFileCoreOptions
   * @property {VFileValue} [value]
   * @property {string} [cwd]
   * @property {Array.<string>} [history]
   * @property {string} [path]
   * @property {string} [basename]
   * @property {string} [stem]
   * @property {string} [extname]
   * @property {string} [dirname]
   * @property {Object.<string, unknown>} [data]
   *
   * @typedef {{[key: string]: unknown} & VFileCoreOptions} VFileOptions
   *   Configuration: a bunch of keys that will be shallow copied over to the new
   *   file.
   *
   * @typedef {Object.<string, unknown>} VFileReporterSettings
   * @typedef {<T = VFileReporterSettings>(files: VFile[], options: T) => string} VFileReporter
   */
  
  import buffer from 'is-buffer'
  import {path} from './minpath.js'
  import {proc} from './minproc.js'
  import {VFileMessage} from 'vfile-message'
  
  // Order of setting (least specific to most), we need this because otherwise
  // `{stem: 'a', path: '~/b.js'}` would throw, as a path is needed before a
  // stem can be set.
  var order = ['history', 'path', 'basename', 'stem', 'extname', 'dirname']
  
  export class VFile {
    /**
     * Create a new virtual file.
     *
     * If `options` is `string` or `Buffer`, treats it as `{value: options}`.
     * If `options` is a `VFile`, shallow copies its data over to the new file.
     * All other given fields are set on the newly created `VFile`.
     *
     * Path related properties are set in the following order (least specific to
     * most specific): `history`, `path`, `basename`, `stem`, `extname`,
     * `dirname`.
     *
     * It’s not possible to set either `dirname` or `extname` without setting
     * either `history`, `path`, `basename`, or `stem` as well.
     *
     * @param {VFileCompatible} [value]
     */
    constructor(value) {
      var index = -1
      /** @type {VFileOptions} */
      var options
      /** @type {string} */
      var prop
  
      if (!value) {
        options = {}
      } else if (typeof value === 'string' || buffer(value)) {
        // @ts-ignore Looks like a buffer.
        options = {value}
      } else {
        // @ts-ignore Looks like file or options.
        options = value
      }
  
      /**
       * Place to store custom information.
       * It’s OK to store custom data directly on the file, moving it to `data`
       * gives a little more privacy.
       * @type {Object.<string, unknown>}
       */
      this.data = {}
  
      /**
       * List of messages associated with the file.
       * @type {Array.<VFileMessage>}
       */
      this.messages = []
  
      /**
       * List of file paths the file moved between.
       * @type {Array.<string>}
       */
      this.history = []
  
      /**
       * Base of `path`.
       * Defaults to `process.cwd()` (`/` in browsers).
       * @type {string}
       */
      this.cwd = proc.cwd()
  
      /* eslint-disable no-unused-expressions */
      /**
       * Raw value.
       * @type {VFileValue}
       */
      this.value
  
      // The below are non-standard, they are “well-known”.
      // As in, used in seeveral tools.
  
      /**
       * Whether a file was saved to disk.
       * @type {boolean}
       */
      this.stored
      /* eslint-enable no-unused-expressions */
  
      // Set path related properties in the correct order.
      while (++index < order.length) {
        prop = order[index]
  
        // Note: we specifically use `in` instead of `hasOwnProperty` to accept
        // `vfile`s too.
        if (prop in options && options[prop] !== undefined) {
          this[prop] = prop === 'history' ? options[prop].concat() : options[prop]
        }
      }
  
      // Set non-path related properties.
      for (prop in options) {
        if (!order.includes(prop)) {
          this[prop] = options[prop]
        }
      }
    }
  
    /**
     * Access full path (`~/index.min.js`).
     */
    get path() {
      return this.history[this.history.length - 1]
    }
  
    /**
     * Set full path (`~/index.min.js`).
     * Cannot be nullified.
     */
    set path(path) {
      assertNonEmpty(path, 'path')
  
      if (this.path !== path) {
        this.history.push(path)
      }
    }
  
    /**
     * Access parent path (`~`).
     */
    get dirname() {
      return typeof this.path === 'string' ? path.dirname(this.path) : undefined
    }
  
    /**
     * Set parent path (`~`).
     * Cannot be set if there's no `path` yet.
     */
    set dirname(dirname) {
      assertPath(this.path, 'dirname')
      this.path = path.join(dirname || '', this.basename)
    }
  
    /**
     * Access basename (including extname) (`index.min.js`).
     */
    get basename() {
      return typeof this.path === 'string' ? path.basename(this.path) : undefined
    }
  
    /**
     * Set basename (`index.min.js`).
     * Cannot contain path separators.
     * Cannot be nullified either (use `file.path = file.dirname` instead).
     */
    set basename(basename) {
      assertNonEmpty(basename, 'basename')
      assertPart(basename, 'basename')
      this.path = path.join(this.dirname || '', basename)
    }
  
    /**
     * Access extname (including dot) (`.js`).
     */
    get extname() {
      return typeof this.path === 'string' ? path.extname(this.path) : undefined
    }
  
    /**
     * Set extname (including dot) (`.js`).
     * Cannot be set if there's no `path` yet and cannot contain path separators.
     */
    set extname(extname) {
      assertPart(extname, 'extname')
      assertPath(this.path, 'extname')
  
      if (extname) {
        if (extname.charCodeAt(0) !== 46 /* `.` */) {
          throw new Error('`extname` must start with `.`')
        }
  
        if (extname.includes('.', 1)) {
          throw new Error('`extname` cannot contain multiple dots')
        }
      }
  
      this.path = path.join(this.dirname, this.stem + (extname || ''))
    }
  
    /**
     * Access stem (w/o extname) (`index.min`).
     */
    get stem() {
      return typeof this.path === 'string'
        ? path.basename(this.path, this.extname)
        : undefined
    }
  
    /**
     * Set stem (w/o extname) (`index.min`).
     * Cannot be nullified, and cannot contain path separators.
     */
    set stem(stem) {
      assertNonEmpty(stem, 'stem')
      assertPart(stem, 'stem')
      this.path = path.join(this.dirname || '', stem + (this.extname || ''))
    }
  
    /**
     * Serialize the file.
     *
     * @param {BufferEncoding} [encoding='utf8'] If `file.value` is a buffer, `encoding` is used to serialize buffers.
     * @returns {string}
     */
    toString(encoding) {
      // @ts-ignore string’s don’t accept the parameter, but buffers do.
      return (this.value || '').toString(encoding)
    }
  
    /**
     * Create a message and associates it w/ the file.
     *
     * @param {string|Error} reason Reason for message (`string` or `Error`). Uses the stack and message of the error if given.
     * @param {Node|Position|Point} [place] Place at which the message occurred in a file (`Node`, `Position`, or `Point`, optional).
     * @param {string} [origin] Place in code the message originates from (`string`, optional).
     * @returns {VFileMessage}
     */
    message(reason, place, origin) {
      var message = new VFileMessage(reason, place, origin)
  
      if (this.path) {
        message.name = this.path + ':' + message.name
        message.file = this.path
      }
  
      message.fatal = false
  
      this.messages.push(message)
  
      return message
    }
  
    /**
     * Info: create a message, associate it with the file, and mark the fatality
     * as `null`.
     * Calls `message()` internally.
     *
     * @param {string|Error} reason Reason for message (`string` or `Error`). Uses the stack and message of the error if given.
     * @param {Node|Position|Point} [place] Place at which the message occurred in a file (`Node`, `Position`, or `Point`, optional).
     * @param {string} [origin] Place in code the message originates from (`string`, optional).
     * @returns {VFileMessage}
     */
    info(reason, place, origin) {
      var message = this.message(reason, place, origin)
  
      message.fatal = null
  
      return message
    }
  
    /**
     * Fail: create a message, associate it with the file, mark the fatality as
     * `true`.
     * Note: fatal errors mean a file is no longer processable.
     * Calls `message()` internally.
     *
     * @param {string|Error} reason Reason for message (`string` or `Error`). Uses the stack and message of the error if given.
     * @param {Node|Position|Point} [place] Place at which the message occurred in a file (`Node`, `Position`, or `Point`, optional).
     * @param {string} [origin] Place in code the message originates from (`string`, optional).
     * @returns {never}
     */
    fail(reason, place, origin) {
      var message = this.message(reason, place, origin)
  
      message.fatal = true
  
      throw message
    }
  }
  
  /**
   * Assert that `part` is not a path (as in, does not contain `path.sep`).
   *
   * @param {string} part
   * @param {string} name
   * @returns {void}
   */
  function assertPart(part, name) {
    if (part && part.includes(path.sep)) {
      throw new Error(
        '`' + name + '` cannot be a path: did not expect `' + path.sep + '`'
      )
    }
  }
  
  /**
   * Assert that `part` is not empty.
   *
   * @param {string} part
   * @param {string} name
   * @returns {void}
   */
  function assertNonEmpty(part, name) {
    if (!part) {
      throw new Error('`' + name + '` cannot be empty')
    }
  }
  
  /**
   * Assert `path` exists.
   *
   * @param {string} path
   * @param {string} name
   * @returns {void}
   */
  function assertPath(path, name) {
    if (!path) {
      throw new Error('Setting `' + name + '` requires `path` to be set too')
    }
  }
  