aMarCruz/jscc

View on GitHub
src/parse-helper.ts

Summary

Maintainability
A
0 mins
Test Coverage
import remapVars = require('./remap-vars')

/**
 * Matches line-ending of win, mac, and unix type
 */
const EOLS = /[^\r\n]+/g

/**
 * Helper class for the `parseChunks` function.
 */
class ParseHelper {

  private lastPos = 0     // keep the next offset to flush
  private output = true   // output state, starts "active"

  /**
   * @param source Original source
   * @param props jscc properties
   */
  constructor (private source: string, private props: JsccProps) {
  }

  /**
   * Final flush. The final output is always in "active" state.
   */
  public flush () {
    return this.commit(this.lastPos, this.source.length)
  }

  /**
   * Write pending changes.
   *
   * _IMPORTANT:_ `Parser.parse` can change the current jscc values,
   *   so this function _MUST BE_ called before the parsing to make any
   *   replacements with the current values.
   *
   * @param offset Starting position of the matched line
   */
  public flushPrev (offset: number) {
    if (this.output) {
      this.commit(this.lastPos, offset)
    }
  }

  /**
   * A line was processed, flush buffers as necessary.
   *
   * @param start Starting position of the chunk into the original buffer
   * @param end Position of the character following the chunk
   * @param output The updated output state
   * @returns The updated position where to continue the search.
   */
  public flushLine (start: number, end: number, output: boolean) {

    // Find the start of the next line in the buffer.
    if (end < this.source.length) {
      end += this.source.substr(end, 2) === '\r\n' ? 2 : 1
    }

    if (output !== this.output) {
      this.output = output
      this.flushit(start, end)

    } else if (output) {
      // flushPrev was already called, so no need to commit
      this.remove(start, end)
    }

    return end
  }

  /**
   * If the parsed `chunk` seems to contain varnames to replace, call the
   * remapVars function which will make the replacement and store the chunk
   * into the MagicString intance. Otherwise, do nothing.
   *
   * _NOTE:_ This function updates `this.lastPos`
   *
   * @param start Starting position of the chunk into the original buffer
   * @param end Position of the character following the chunk
   * @returns `true` if the chunk was changed
   */
  private commit (start: number, end: number) {

    if (start >= end) {
      return false
    }

    this.lastPos = end

    // Get the fragment of source where to search varnames to replace
    const chunk = this.source.slice(start, end)

    // Call remapVars only if a memvar prefix exists in the chunk
    return chunk.indexOf('$_') > -1 && remapVars(this.props, chunk, start)
  }

  /**
   * Removes the block from the `start` to the `end` position, inclusive.
   *
   * _NOTE:_ This function updates `this.lastPos`
   *
   * @param start Starting position of the chunk into the original buffer
   * @param end Position of the character following the chunk
   * @returns Position of the character following the removed block.
   */
  private remove (start: number, end: number) {

    this.lastPos = end

    const block = this.props.keepLines
      ? this.source.slice(start, end).replace(EOLS, '') : ''

    this.props.magicStr.overwrite(start, end, block)
  }

  /**
   * The output state changed, flush the buffer.
   *
   * @param start Start of current line
   * @param end End of current line
   */
  private flushit (start: number, end: number) {
    if (this.output) {
      // Output begins, remove previous hidden block.
      this.remove(this.lastPos, end)

    } else {
      // Output ends, flush the already processed block.
      this.commit(this.lastPos, start)
    }
  }
}

export = ParseHelper