Skip to content

Commit de0dc65

Browse files
committed
fix(mock-doc): do not pretty print whitespace senstive elements
1 parent 10ea2fb commit de0dc65

File tree

2 files changed

+111
-19
lines changed

2 files changed

+111
-19
lines changed

src/mock-doc/serialize-node.ts

+75-19
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,14 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
8484
const ignoreTag = opts.excludeTags != null && opts.excludeTags.includes(tagName);
8585

8686
if (ignoreTag === false) {
87-
if (opts.newLines) {
87+
const isWithinWhitespaceSensitiveNode =
88+
opts.newLines || opts.indentSpaces > 0 ? isWithinWhitespaceSensitive(node) : false;
89+
if (opts.newLines && !isWithinWhitespaceSensitiveNode) {
8890
output.text.push('\n');
8991
output.currentLineWidth = 0;
9092
}
9193

92-
if (opts.indentSpaces > 0) {
94+
if (opts.indentSpaces > 0 && !isWithinWhitespaceSensitiveNode) {
9395
for (let i = 0; i < output.indent; i++) {
9496
output.text.push(' ');
9597
}
@@ -100,7 +102,10 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
100102
output.currentLineWidth += tagName.length + 1;
101103

102104
const attrsLength = (node as HTMLElement).attributes.length;
103-
const attributes = opts.prettyHtml && attrsLength > 1 ? cloneAttributes((node as HTMLElement).attributes as any, true) : (node as Element).attributes;
105+
const attributes =
106+
opts.prettyHtml && attrsLength > 1
107+
? cloneAttributes((node as HTMLElement).attributes as any, true)
108+
: (node as Element).attributes;
104109

105110
for (let i = 0; i < attrsLength; i++) {
106111
const attr = attributes.item(i);
@@ -173,7 +178,10 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
173178
if ((node as Element).hasAttribute('style')) {
174179
const cssText = (node as HTMLElement).style.cssText;
175180

176-
if (opts.approximateLineWidth > 0 && output.currentLineWidth + cssText.length + 10 > opts.approximateLineWidth) {
181+
if (
182+
opts.approximateLineWidth > 0 &&
183+
output.currentLineWidth + cssText.length + 10 > opts.approximateLineWidth
184+
) {
177185
output.text.push(`\nstyle="${cssText}">`);
178186
output.currentLineWidth = 0;
179187
} else {
@@ -194,7 +202,10 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
194202

195203
if (
196204
opts.newLines &&
197-
(node.childNodes.length === 0 || (node.childNodes.length === 1 && node.childNodes[0].nodeType === NODE_TYPES.TEXT_NODE && node.childNodes[0].nodeValue.trim() === ''))
205+
(node.childNodes.length === 0 ||
206+
(node.childNodes.length === 1 &&
207+
node.childNodes[0].nodeType === NODE_TYPES.TEXT_NODE &&
208+
node.childNodes[0].nodeValue.trim() === ''))
198209
) {
199210
output.text.push('\n');
200211
output.currentLineWidth = 0;
@@ -207,11 +218,16 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
207218
}
208219

209220
if (opts.excludeTagContent == null || opts.excludeTagContent.includes(tagName) === false) {
210-
const childNodes = tagName === 'template' ? (((node as any) as HTMLTemplateElement).content.childNodes as any) : node.childNodes;
221+
const childNodes =
222+
tagName === 'template' ? (((node as any) as HTMLTemplateElement).content.childNodes as any) : node.childNodes;
211223
const childNodeLength = childNodes.length;
212224

213225
if (childNodeLength > 0) {
214-
if (childNodeLength === 1 && childNodes[0].nodeType === NODE_TYPES.TEXT_NODE && (typeof childNodes[0].nodeValue !== 'string' || childNodes[0].nodeValue.trim() === '')) {
226+
if (
227+
childNodeLength === 1 &&
228+
childNodes[0].nodeType === NODE_TYPES.TEXT_NODE &&
229+
(typeof childNodes[0].nodeValue !== 'string' || childNodes[0].nodeValue.trim() === '')
230+
) {
215231
// skip over empty text nodes
216232
} else {
217233
if (opts.indentSpaces > 0 && ignoreTag === false) {
@@ -223,12 +239,14 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
223239
}
224240

225241
if (ignoreTag === false) {
226-
if (opts.newLines) {
242+
const isWithinWhitespaceSensitiveNode =
243+
opts.newLines || opts.indentSpaces > 0 ? isWithinWhitespaceSensitive(node) : false;
244+
if (opts.newLines && !isWithinWhitespaceSensitiveNode) {
227245
output.text.push('\n');
228246
output.currentLineWidth = 0;
229247
}
230248

231-
if (opts.indentSpaces > 0) {
249+
if (opts.indentSpaces > 0 && !isWithinWhitespaceSensitiveNode) {
232250
output.indent = output.indent - opts.indentSpaces;
233251
for (let i = 0; i < output.indent; i++) {
234252
output.text.push(' ');
@@ -287,12 +305,14 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
287305
}
288306
} else {
289307
// this text node has text content
290-
if (opts.newLines) {
308+
const isWithinWhitespaceSensitiveNode =
309+
opts.newLines || opts.indentSpaces > 0 || opts.prettyHtml ? isWithinWhitespaceSensitive(node) : false;
310+
if (opts.newLines && !isWithinWhitespaceSensitiveNode) {
291311
output.text.push('\n');
292312
output.currentLineWidth = 0;
293313
}
294314

295-
if (opts.indentSpaces > 0) {
315+
if (opts.indentSpaces > 0 && !isWithinWhitespaceSensitiveNode) {
296316
for (let i = 0; i < output.indent; i++) {
297317
output.text.push(' ');
298318
}
@@ -303,7 +323,10 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
303323
if (textContentLength > 0) {
304324
// this text node has text content
305325

306-
const parentTagName = node.parentNode != null && node.parentNode.nodeType === NODE_TYPES.ELEMENT_NODE ? node.parentNode.nodeName : null;
326+
const parentTagName =
327+
node.parentNode != null && node.parentNode.nodeType === NODE_TYPES.ELEMENT_NODE
328+
? node.parentNode.nodeName
329+
: null;
307330
if (NON_ESCAPABLE_CONTENT.has(parentTagName)) {
308331
// this text node cannot have its content escaped since it's going
309332
// into an element like <style> or <script>
@@ -316,7 +339,7 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
316339
output.currentLineWidth += textContentLength;
317340
} else {
318341
// this text node is going into a normal element and html can be escaped
319-
if (opts.prettyHtml) {
342+
if (opts.prettyHtml && !isWithinWhitespaceSensitiveNode) {
320343
// pretty print the text node
321344
output.text.push(escapeString(textContent.replace(/\s\s+/g, ' ').trim(), false));
322345
output.currentLineWidth += textContentLength;
@@ -334,7 +357,10 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
334357
textContentLength = textContent.length;
335358
if (textContentLength > 1) {
336359
if (/\s/.test(textContent.charAt(textContentLength - 1))) {
337-
if (opts.approximateLineWidth > 0 && output.currentLineWidth + textContentLength > opts.approximateLineWidth) {
360+
if (
361+
opts.approximateLineWidth > 0 &&
362+
output.currentLineWidth + textContentLength > opts.approximateLineWidth
363+
) {
338364
textContent = textContent.trimRight() + '\n';
339365
output.currentLineWidth = 0;
340366
} else {
@@ -365,12 +391,14 @@ function serializeToHtml(node: Node, opts: SerializeNodeToHtmlOptions, output: S
365391
}
366392
}
367393

368-
if (opts.newLines) {
394+
const isWithinWhitespaceSensitiveNode =
395+
opts.newLines || opts.indentSpaces > 0 ? isWithinWhitespaceSensitive(node) : false;
396+
if (opts.newLines && !isWithinWhitespaceSensitiveNode) {
369397
output.text.push('\n');
370398
output.currentLineWidth = 0;
371399
}
372400

373-
if (opts.indentSpaces > 0) {
401+
if (opts.indentSpaces > 0 && !isWithinWhitespaceSensitiveNode) {
374402
for (let i = 0; i < output.indent; i++) {
375403
output.text.push(' ');
376404
}
@@ -419,9 +447,26 @@ function isWithinWhitespaceSensitive(node: Node) {
419447
return false;
420448
}
421449

422-
/*@__PURE__*/ export const NON_ESCAPABLE_CONTENT = new Set(['STYLE', 'SCRIPT', 'IFRAME', 'NOSCRIPT', 'XMP', 'NOEMBED', 'NOFRAMES', 'PLAINTEXT']);
450+
/*@__PURE__*/ export const NON_ESCAPABLE_CONTENT = new Set([
451+
'STYLE',
452+
'SCRIPT',
453+
'IFRAME',
454+
'NOSCRIPT',
455+
'XMP',
456+
'NOEMBED',
457+
'NOFRAMES',
458+
'PLAINTEXT',
459+
]);
423460

424-
/*@__PURE__*/ export const WHITESPACE_SENSITIVE = new Set(['CODE', 'OUTPUT', 'PLAINTEXT', 'PRE', 'TEMPLATE', 'TEXTAREA']);
461+
/*@__PURE__*/ export const WHITESPACE_SENSITIVE = new Set([
462+
'CODE',
463+
'OUTPUT',
464+
'PLAINTEXT',
465+
'PRE',
466+
'SCRIPT',
467+
'TEMPLATE',
468+
'TEXTAREA',
469+
]);
425470

426471
/*@__PURE__*/ const EMPTY_ELEMENTS = new Set([
427472
'area',
@@ -491,7 +536,18 @@ function isWithinWhitespaceSensitive(node: Node) {
491536
'visible',
492537
]);
493538

494-
/*@__PURE__*/ const STRUCTURE_ELEMENTS = new Set(['html', 'body', 'head', 'iframe', 'meta', 'link', 'base', 'title', 'script', 'style']);
539+
/*@__PURE__*/ const STRUCTURE_ELEMENTS = new Set([
540+
'html',
541+
'body',
542+
'head',
543+
'iframe',
544+
'meta',
545+
'link',
546+
'base',
547+
'title',
548+
'script',
549+
'style',
550+
]);
495551

496552
interface SerializeOutput {
497553
currentLineWidth: number;

src/mock-doc/test/serialize-node.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,42 @@ describe('serializeNodeToHtml', () => {
2525
expect(html).toBe(`<div><span>var </span><b> value </b><span> =</span><code> 88 </code>;</div>`);
2626
});
2727

28+
it('do not pretty print <pre><code>', () => {
29+
const elm = doc.createElement('div');
30+
31+
elm.innerHTML = `<pre><code><span>88</span></code></pre>`;
32+
33+
const html = serializeNodeToHtml(elm, { prettyHtml: true });
34+
expect(html).toBe(`<pre><code><span>88</span></code></pre>`);
35+
});
36+
37+
it('do not pretty print <pre><code> w/ highlights and new', () => {
38+
const elm = doc.createElement('div');
39+
40+
elm.innerHTML = `<pre><code><span>install</span> cordova-plugin-purchase\nnpx cap update</code></pre>`;
41+
42+
const html = serializeNodeToHtml(elm, { prettyHtml: true });
43+
expect(html).toBe(`<pre><code><span>install</span> cordova-plugin-purchase\nnpx cap update</code></pre>`);
44+
});
45+
46+
it('do not pretty print <pre><code> w/ html comments', () => {
47+
const elm = doc.createElement('div');
48+
49+
elm.innerHTML = `<pre><code><span><!--a-->88</span>c<!--b--></code></pre>`;
50+
51+
const html = serializeNodeToHtml(elm, { prettyHtml: true });
52+
expect(html).toBe(`<pre><code><span><!--a-->88</span>c<!--b--></code></pre>`);
53+
});
54+
55+
it('do not pretty print <script>', () => {
56+
const elm = doc.createElement('div');
57+
58+
elm.innerHTML = `<script>value = '';</script>`;
59+
60+
const html = serializeNodeToHtml(elm, { prettyHtml: true });
61+
expect(html).toBe(`<script>value = '';</script>`);
62+
});
63+
2864
it('do not remove whitespace within <code>', () => {
2965
const elm = doc.createElement('div');
3066

0 commit comments

Comments
 (0)