From ea0dd9c0defa384a061bae39321617191df370ec Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Mon, 24 Jun 2019 22:30:56 -0400 Subject: [PATCH] prevent top-level text being discarded - fixes #3027 --- src/compiler/compile/nodes/Text.ts | 3 --- .../render_dom/wrappers/Element/index.ts | 2 +- .../compile/render_dom/wrappers/Fragment.ts | 9 ++++----- .../compile/render_dom/wrappers/Text.ts | 17 ++++++++++++++++- test/runtime/samples/isolated-text/_config.js | 6 ++++++ test/runtime/samples/isolated-text/main.svelte | 5 +++++ 6 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 test/runtime/samples/isolated-text/_config.js create mode 100644 test/runtime/samples/isolated-text/main.svelte diff --git a/src/compiler/compile/nodes/Text.ts b/src/compiler/compile/nodes/Text.ts index a4514f56f2..17883a0cdb 100644 --- a/src/compiler/compile/nodes/Text.ts +++ b/src/compiler/compile/nodes/Text.ts @@ -6,7 +6,6 @@ import { INode } from './interfaces'; export default class Text extends Node { type: 'Text'; data: string; - use_space = false; constructor(component: Component, parent: INode, scope: TemplateScope, info: any) { super(component, parent, scope, info); @@ -20,8 +19,6 @@ export default class Text extends Node { } node = node.parent; } - - this.use_space = true; } } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index e66e6c7e4f..9179225e29 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -335,7 +335,7 @@ export default class ElementWrapper extends Wrapper { function to_html(wrapper: ElementWrapper | TextWrapper) { if (wrapper.node.type === 'Text') { - if (wrapper.node.use_space) return ' '; + if ((wrapper as TextWrapper).use_space()) return ' '; const parent = wrapper.node.parent as Element; diff --git a/src/compiler/compile/render_dom/wrappers/Fragment.ts b/src/compiler/compile/render_dom/wrappers/Fragment.ts index bd20c9107f..2eadf3e915 100644 --- a/src/compiler/compile/render_dom/wrappers/Fragment.ts +++ b/src/compiler/compile/render_dom/wrappers/Fragment.ts @@ -17,6 +17,7 @@ import { INode } from '../../nodes/interfaces'; import Renderer from '../Renderer'; import Block from '../Block'; import { trim_start, trim_end } from '../../../utils/trim'; +import TextWrapper from './Text'; const wrappers = { AwaitBlock, @@ -47,7 +48,7 @@ function trimmable_at(child: INode, next_sibling: Wrapper): boolean { // The child and its sibling share a common nearest each block (not at an each block boundary) // The next sibling's previous node is an each block return (next_sibling.node.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/)) || next_sibling.node.prev.type === 'EachBlock'; -} +} export default class FragmentWrapper { nodes: Wrapper[]; @@ -91,8 +92,7 @@ export default class FragmentWrapper { // *unless* there is no whitespace between this node and its next sibling if (this.nodes.length === 0) { const should_trim = ( - // @ts-ignore todo: probably error, should it be next_sibling.node.data? - next_sibling ? (next_sibling.node.type === 'Text' && /^\s/.test(next_sibling.data) && trimmable_at(child, next_sibling)) : !child.has_ancestor('EachBlock') + next_sibling ? (next_sibling.node.type === 'Text' && /^\s/.test(next_sibling.node.data) && trimmable_at(child, next_sibling)) : !child.has_ancestor('EachBlock') ); if (should_trim) { @@ -103,8 +103,7 @@ export default class FragmentWrapper { // glue text nodes (which could e.g. be separated by comments) together if (last_child && last_child.node.type === 'Text') { - // @ts-ignore todo: probably error, should it be last_child.node.data? - last_child.data = data + last_child.data; + (last_child as TextWrapper).data = data + (last_child as TextWrapper).data; continue; } diff --git a/src/compiler/compile/render_dom/wrappers/Text.ts b/src/compiler/compile/render_dom/wrappers/Text.ts index ceacae29ba..e51ab5c370 100644 --- a/src/compiler/compile/render_dom/wrappers/Text.ts +++ b/src/compiler/compile/render_dom/wrappers/Text.ts @@ -49,12 +49,27 @@ export default class TextWrapper extends Wrapper { this.var = this.skip ? null : 't'; } + use_space() { + if (this.renderer.component.component_options.preserveWhitespace) return false; + if (/[\S\u00A0]/.test(this.data)) return false; + + let node = this.parent && this.parent.node; + while (node) { + if (node.type === 'Element' && node.name === 'pre') { + return false; + } + node = node.parent; + } + + return true; + } + render(block: Block, parent_node: string, parent_nodes: string) { if (this.skip) return; block.add_element( this.var, - this.node.use_space ? `@space()` : `@text(${stringify(this.data)})`, + this.use_space() ? `@space()` : `@text(${stringify(this.data)})`, parent_nodes && `@claim_text(${parent_nodes}, ${stringify(this.data)})`, parent_node ); diff --git a/test/runtime/samples/isolated-text/_config.js b/test/runtime/samples/isolated-text/_config.js new file mode 100644 index 0000000000..ec4332be5c --- /dev/null +++ b/test/runtime/samples/isolated-text/_config.js @@ -0,0 +1,6 @@ +export default { + html: ` + before +

after

+ ` +}; \ No newline at end of file diff --git a/test/runtime/samples/isolated-text/main.svelte b/test/runtime/samples/isolated-text/main.svelte new file mode 100644 index 0000000000..fa48dea46e --- /dev/null +++ b/test/runtime/samples/isolated-text/main.svelte @@ -0,0 +1,5 @@ +before + + + +

after

\ No newline at end of file