src/parse-helper.ts
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