1'use strict'; 2 3const SAXParser = require('parse5-sax-parser'); 4const { escapeString } = require('parse5/lib/serializer'); 5 6class RewritingStream extends SAXParser { 7 constructor() { 8 super({ sourceCodeLocationInfo: true }); 9 10 this.posTracker = this.locInfoMixin.posTracker; 11 } 12 13 _transformChunk(chunk) { 14 // NOTE: ignore upstream return value as we want to push to 15 // the Writable part of Transform stream ourselves. 16 super._transformChunk(chunk); 17 } 18 19 _getRawHtml(location) { 20 const droppedBufferSize = this.posTracker.droppedBufferSize; 21 const start = location.startOffset - droppedBufferSize; 22 const end = location.endOffset - droppedBufferSize; 23 24 return this.tokenizer.preprocessor.html.slice(start, end); 25 } 26 27 // Events 28 _handleToken(token) { 29 if (!super._handleToken(token)) { 30 this.emitRaw(this._getRawHtml(token.location)); 31 } 32 33 // NOTE: don't skip new lines after <pre> and other tags, 34 // otherwise we'll have incorrect raw data. 35 this.parserFeedbackSimulator.skipNextNewLine = false; 36 } 37 38 // Emitter API 39 _emitToken(eventName, token) { 40 this.emit(eventName, token, this._getRawHtml(token.sourceCodeLocation)); 41 } 42 43 emitDoctype(token) { 44 let res = `<!DOCTYPE ${token.name}`; 45 46 if (token.publicId !== null) { 47 res += ` PUBLIC "${token.publicId}"`; 48 } else if (token.systemId !== null) { 49 res += ' SYSTEM'; 50 } 51 52 if (token.systemId !== null) { 53 res += ` "${token.systemId}"`; 54 } 55 56 res += '>'; 57 58 this.push(res); 59 } 60 61 emitStartTag(token) { 62 let res = `<${token.tagName}`; 63 64 const attrs = token.attrs; 65 66 for (let i = 0; i < attrs.length; i++) { 67 res += ` ${attrs[i].name}="${escapeString(attrs[i].value, true)}"`; 68 } 69 70 res += token.selfClosing ? '/>' : '>'; 71 72 this.push(res); 73 } 74 75 emitEndTag(token) { 76 this.push(`</${token.tagName}>`); 77 } 78 79 emitText({ text }) { 80 this.push(escapeString(text, false)); 81 } 82 83 emitComment(token) { 84 this.push(`<!--${token.text}-->`); 85 } 86 87 emitRaw(html) { 88 this.push(html); 89 } 90} 91 92module.exports = RewritingStream; 93