• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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