'use strict'; const SAXParser = require('parse5-sax-parser'); const { escapeString } = require('parse5/lib/serializer'); class RewritingStream extends SAXParser { constructor() { super({ sourceCodeLocationInfo: true }); this.posTracker = this.locInfoMixin.posTracker; } _transformChunk(chunk) { // NOTE: ignore upstream return value as we want to push to // the Writable part of Transform stream ourselves. super._transformChunk(chunk); } _getRawHtml(location) { const droppedBufferSize = this.posTracker.droppedBufferSize; const start = location.startOffset - droppedBufferSize; const end = location.endOffset - droppedBufferSize; return this.tokenizer.preprocessor.html.slice(start, end); } // Events _handleToken(token) { if (!super._handleToken(token)) { this.emitRaw(this._getRawHtml(token.location)); } // NOTE: don't skip new lines after
and other tags, // otherwise we'll have incorrect raw data. this.parserFeedbackSimulator.skipNextNewLine = false; } // Emitter API _emitToken(eventName, token) { this.emit(eventName, token, this._getRawHtml(token.sourceCodeLocation)); } emitDoctype(token) { let res = `'; this.push(res); } emitStartTag(token) { let res = `<${token.tagName}`; const attrs = token.attrs; for (let i = 0; i < attrs.length; i++) { res += ` ${attrs[i].name}="${escapeString(attrs[i].value, true)}"`; } res += token.selfClosing ? '/>' : '>'; this.push(res); } emitEndTag(token) { this.push(`${token.tagName}>`); } emitText({ text }) { this.push(escapeString(text, false)); } emitComment(token) { this.push(``); } emitRaw(html) { this.push(html); } } module.exports = RewritingStream;