collapse consecutive whitespace characters in the absense of options.preserveWhitespace or a <pre>

pull/2258/head
Richard Harris 6 years ago
parent 21d56c9ee3
commit 2b1aa77f29

@ -25,6 +25,7 @@ type ComponentOptions = {
tag?: string; tag?: string;
immutable?: boolean; immutable?: boolean;
accessors?: boolean; accessors?: boolean;
preserveWhitespace?: boolean;
}; };
// We need to tell estree-walker that it should always // We need to tell estree-walker that it should always
@ -1195,7 +1196,8 @@ function process_component_options(component: Component, nodes) {
immutable: component.compile_options.immutable || false, immutable: component.compile_options.immutable || false,
accessors: 'accessors' in component.compile_options accessors: 'accessors' in component.compile_options
? component.compile_options.accessors ? component.compile_options.accessors
: !!component.compile_options.customElement : !!component.compile_options.customElement,
preserveWhitespace: !!component.compile_options.preserveWhitespace
}; };
const node = nodes.find(node => node.name === 'svelte:options'); const node = nodes.find(node => node.name === 'svelte:options');
@ -1271,6 +1273,7 @@ function process_component_options(component: Component, nodes) {
case 'accessors': case 'accessors':
case 'immutable': case 'immutable':
case 'preserveWhitespace':
const code = `invalid-${name}-value`; const code = `invalid-${name}-value`;
const message = `${name} attribute must be true or false` const message = `${name} attribute must be true or false`
const value = get_value(attribute, code, message); const value = get_value(attribute, code, message);
@ -1291,7 +1294,7 @@ function process_component_options(component: Component, nodes) {
else { else {
component.error(attribute, { component.error(attribute, {
code: `invalid-options-attribute`, code: `invalid-options-attribute`,
message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors' and 'immutable' attributes` message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes`
}); });
} }
}); });

@ -23,7 +23,8 @@ const valid_options = [
'customElement', 'customElement',
'tag', 'tag',
'css', 'css',
'preserveComments' 'preserveComments',
'preserveWhitespace'
]; ];
function validate_options(options: CompileOptions, warnings: Warning[]) { function validate_options(options: CompileOptions, warnings: Warning[]) {

@ -1,11 +1,26 @@
import Node from './shared/Node'; import Node from './shared/Node';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
export default class Text extends Node { export default class Text extends Node {
type: 'Text'; type: 'Text';
data: string; data: string;
use_space = false;
constructor(component, parent, scope, info) { constructor(component: Component, parent: Node, scope: TemplateScope, info: any) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.data = info.data; this.data = info.data;
if (!component.component_options.preserveWhitespace && !/\S/.test(info.data)) {
let node = parent;
while (node) {
if (node.type === 'Element' && node.name === 'pre') {
return;
}
node = node.parent;
}
this.use_space = true;
}
} }
} }

@ -54,7 +54,7 @@ export default class TextWrapper extends Wrapper {
block.add_element( block.add_element(
this.var, this.var,
`@text(${stringify(this.data)})`, this.node.use_space ? `@space()` : `@text(${stringify(this.data)})`,
parent_nodes && `@claim_text(${parent_nodes}, ${stringify(this.data)})`, parent_nodes && `@claim_text(${parent_nodes}, ${stringify(this.data)})`,
parent_node parent_node
); );

@ -57,7 +57,8 @@ export interface CompileOptions {
tag?: string; tag?: string;
css?: boolean; css?: boolean;
preserveComments?: boolean | false; preserveComments?: boolean;
preserveWhitespace?: boolean;
} }
export interface Visitor { export interface Visitor {

@ -46,6 +46,10 @@ export function text(data) {
return document.createTextNode(data); return document.createTextNode(data);
} }
export function space() {
return text(' ');
}
export function comment() { export function comment() {
return document.createComment(''); return document.createComment('');
} }

@ -85,11 +85,11 @@ function cleanChildren(node) {
node.removeChild(child); node.removeChild(child);
} }
child.data = child.data.replace(/\s{2,}/g, '\n'); child.data = child.data.replace(/\s+/g, '\n');
if (previous && previous.nodeType === 3) { if (previous && previous.nodeType === 3) {
previous.data += child.data; previous.data += child.data;
previous.data = previous.data.replace(/\s{2,}/g, '\n'); previous.data = previous.data.replace(/\s+/g, '\n');
node.removeChild(child); node.removeChild(child);
child = previous; child = previous;

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -24,7 +25,7 @@ function create_fragment(ctx) {
t0 = text("Hello "); t0 = text("Hello ");
t1 = text(ctx.name); t1 = text(ctx.name);
t2 = text("!"); t2 = text("!");
t3 = text("\n"); t3 = space();
debugger; debugger;
add_location(h1, file, 4, 0, 38); add_location(h1, file, 4, 0, 38);
}, },

@ -11,6 +11,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -30,7 +31,7 @@ function create_each_block(ctx) {
c: function create() { c: function create() {
span = element("span"); span = element("span");
t0 = text(t0_value); t0 = text(t0_value);
t1 = text("\n\t"); t1 = space();
{ {
const { foo, bar, baz, thing } = ctx; const { foo, bar, baz, thing } = ctx;
@ -84,7 +85,7 @@ function create_fragment(ctx) {
each_blocks[i].c(); each_blocks[i].c();
} }
t0 = text("\n\n"); t0 = space();
p = element("p"); p = element("p");
t1 = text("foo: "); t1 = text("foo: ");
t2 = text(ctx.foo); t2 = text(ctx.foo);

@ -11,6 +11,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -30,7 +31,7 @@ function create_each_block(ctx) {
c: function create() { c: function create() {
span = element("span"); span = element("span");
t0 = text(t0_value); t0 = text(t0_value);
t1 = text("\n\t"); t1 = space();
{ {
const { foo } = ctx; const { foo } = ctx;
@ -84,7 +85,7 @@ function create_fragment(ctx) {
each_blocks[i].c(); each_blocks[i].c();
} }
t0 = text("\n\n"); t0 = space();
p = element("p"); p = element("p");
t1 = text("foo: "); t1 = text("foo: ");
t2 = text(ctx.foo); t2 = text(ctx.foo);

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -22,7 +23,7 @@ function create_fragment(ctx) {
c: function create() { c: function create() {
p = element("p"); p = element("p");
t0 = text(t0_value); t0 = text(t0_value);
t1 = text("\n\t"); t1 = space();
t2 = text(ctx.bar); t2 = text(ctx.bar);
add_location(p, file, 7, 0, 67); add_location(p, file, 7, 0, 67);
}, },

@ -7,7 +7,7 @@ import {
insert, insert,
noop, noop,
safe_not_equal, safe_not_equal,
text space
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
@ -16,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
div0 = element("div"); div0 = element("div");
t = text("\n"); t = space();
div1 = element("div"); div1 = element("div");
div0.dataset.foo = "bar"; div0.dataset.foo = "bar";
div1.dataset.foo = ctx.bar; div1.dataset.foo = ctx.bar;

@ -8,7 +8,7 @@ import {
insert, insert,
noop, noop,
safe_not_equal, safe_not_equal,
text space
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
@ -17,7 +17,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
div0 = element("div"); div0 = element("div");
t = text("\n"); t = space();
div1 = element("div"); div1 = element("div");
attr(div0, "data-foo", "bar"); attr(div0, "data-foo", "bar");
attr(div1, "data-foo", ctx.bar); attr(div1, "data-foo", ctx.bar);

@ -11,6 +11,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -30,13 +31,13 @@ function create_each_block(ctx) {
div = element("div"); div = element("div");
strong = element("strong"); strong = element("strong");
t0 = text(ctx.i); t0 = text(ctx.i);
t1 = text("\n\n\t\t"); t1 = space();
span = element("span"); span = element("span");
t2 = text(t2_value); t2 = text(t2_value);
t3 = text(" wrote "); t3 = text(" wrote ");
t4 = text(t4_value); t4 = text(t4_value);
t5 = text(" ago:"); t5 = text(" ago:");
t6 = text("\n\n\t\t"); t6 = space();
raw_before = element('noscript'); raw_before = element('noscript');
span.className = "meta"; span.className = "meta";
div.className = "comment"; div.className = "comment";
@ -97,7 +98,7 @@ function create_fragment(ctx) {
each_blocks[i].c(); each_blocks[i].c();
} }
t0 = text("\n\n"); t0 = space();
p = element("p"); p = element("p");
t1 = text(ctx.foo); t1 = text(ctx.foo);
}, },

@ -11,8 +11,8 @@ import {
prevent_default, prevent_default,
run_all, run_all,
safe_not_equal, safe_not_equal,
stop_propagation, space,
text stop_propagation
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
@ -23,10 +23,10 @@ function create_fragment(ctx) {
div = element("div"); div = element("div");
button0 = element("button"); button0 = element("button");
button0.textContent = "click me"; button0.textContent = "click me";
t1 = text("\n\t"); t1 = space();
button1 = element("button"); button1 = element("button");
button1.textContent = "or me"; button1.textContent = "or me";
t3 = text("\n\t"); t3 = space();
button2 = element("button"); button2 = element("button");
button2.textContent = "or me!"; button2.textContent = "or me!";
dispose = [ dispose = [

@ -7,7 +7,7 @@ import {
insert, insert,
noop, noop,
safe_not_equal, safe_not_equal,
text space
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
@ -16,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
div0 = element("div"); div0 = element("div");
t = text("\n"); t = space();
div1 = element("div"); div1 = element("div");
div0.style.cssText = ctx.style; div0.style.cssText = ctx.style;
div1.style.cssText = div1_style_value = "" + ctx.key + ": " + ctx.value; div1.style.cssText = div1_style_value = "" + ctx.key + ": " + ctx.value;

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -20,7 +21,7 @@ function create_fragment(ctx) {
c() { c() {
button = element("button"); button = element("button");
button.textContent = "foo"; button.textContent = "foo";
t1 = text("\n\n"); t1 = space();
p = element("p"); p = element("p");
t2 = text("x: "); t2 = text("x: ");
t3 = text(ctx.x); t3 = text(ctx.x);

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -20,7 +21,7 @@ function create_fragment(ctx) {
c() { c() {
button = element("button"); button = element("button");
button.textContent = "foo"; button.textContent = "foo";
t1 = text("\n\n"); t1 = space();
p = element("p"); p = element("p");
t2 = text("number of things: "); t2 = text("number of things: ");
t3 = text(t3_value); t3 = text(t3_value);

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -20,7 +21,7 @@ function create_fragment(ctx) {
c() { c() {
button = element("button"); button = element("button");
button.textContent = "foo"; button.textContent = "foo";
t1 = text("\n\n"); t1 = space();
p = element("p"); p = element("p");
t2 = text("x: "); t2 = text("x: ");
t3 = text(ctx.x); t3 = text(ctx.x);

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -20,7 +21,7 @@ function create_fragment(ctx) {
c() { c() {
button = element("button"); button = element("button");
button.textContent = "foo"; button.textContent = "foo";
t1 = text("\n\n"); t1 = space();
p = element("p"); p = element("p");
t2 = text("number of things: "); t2 = text("number of things: ");
t3 = text(t3_value); t3 = text(t3_value);

@ -7,7 +7,7 @@ import {
mount_component, mount_component,
noop, noop,
safe_not_equal, safe_not_equal,
text space
} from "svelte/internal"; } from "svelte/internal";
import Imported from "Imported.svelte"; import Imported from "Imported.svelte";
@ -21,7 +21,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
imported.$$.fragment.c(); imported.$$.fragment.c();
t = text("\n"); t = space();
nonimported.$$.fragment.c(); nonimported.$$.fragment.c();
}, },

@ -9,7 +9,7 @@ import {
insert, insert,
noop, noop,
safe_not_equal, safe_not_equal,
text space
} from "svelte/internal"; } from "svelte/internal";
// (10:1) {#if a} // (10:1) {#if a}
@ -139,19 +139,19 @@ function create_fragment(ctx) {
c() { c() {
div = element("div"); div = element("div");
if (if_block0) if_block0.c(); if (if_block0) if_block0.c();
t0 = text("\n\n\t"); t0 = space();
p0 = element("p"); p0 = element("p");
p0.textContent = "this can be used as an anchor"; p0.textContent = "this can be used as an anchor";
t2 = text("\n\n\t"); t2 = space();
if (if_block1) if_block1.c(); if (if_block1) if_block1.c();
t3 = text("\n\n\t"); t3 = space();
if (if_block2) if_block2.c(); if (if_block2) if_block2.c();
t4 = text("\n\n\t"); t4 = space();
p1 = element("p"); p1 = element("p");
p1.textContent = "so can this"; p1.textContent = "so can this";
t6 = text("\n\n\t"); t6 = space();
if (if_block3) if_block3.c(); if (if_block3) if_block3.c();
t7 = text("\n\n"); t7 = space();
if (if_block4) if_block4.c(); if (if_block4) if_block4.c();
if_block4_anchor = comment(); if_block4_anchor = comment();
}, },

@ -22,10 +22,16 @@ export default {
input.checked = false; input.checked = false;
await input.dispatchEvent(event); await input.dispatchEvent(event);
assert.equal(target.innerHTML, `<input type="checkbox">\n<p>false</p>`); assert.htmlEqual(target.innerHTML, `
<input type="checkbox">
<p>false</p>
`);
component.foo = true; component.foo = true;
assert.equal(input.checked, true); assert.equal(input.checked, true);
assert.equal(target.innerHTML, `<input type="checkbox">\n<p>true</p>`); assert.htmlEqual(target.innerHTML, `
<input type="checkbox">
<p>true</p>
`);
} }
}; };

@ -5,13 +5,25 @@ export default {
compound: 'piece of', compound: 'piece of',
go: { deeper: 'core' } go: { deeper: 'core' }
}, },
html: `<div><p>foo: lol</p>\n<p>baz: 42 (number)</p>\n<p>qux: this is a piece of string</p>\n<p>quux: core</p></div>`,
html: `
<div><p>foo: lol</p>
<p>baz: 42 (number)</p>
<p>qux: this is a piece of string</p>
<p>quux: core</p></div>
`,
test({ assert, component, target }) { test({ assert, component, target }) {
component.bar = 'wut'; component.bar = 'wut';
component.x = 3; component.x = 3;
component.compound = 'rather boring'; component.compound = 'rather boring';
component.go = { deeper: 'heart' }; component.go = { deeper: 'heart' };
assert.equal( target.innerHTML, `<div><p>foo: wut</p>\n<p>baz: 43 (number)</p>\n<p>qux: this is a rather boring string</p>\n<p>quux: heart</p></div>` ); assert.htmlEqual(target.innerHTML, `
<div><p>foo: wut</p>
<p>baz: 43 (number)</p>
<p>qux: this is a rather boring string</p>
<p>quux: heart</p></div>
`);
} }
}; };

@ -1,5 +1,8 @@
export default { export default {
html: '<button>+1</button>\n\n<p>0</p>', html: `
<button>+1</button>
<p>0</p>
`,
async test({ assert, component, target, window }) { async test({ assert, component, target, window }) {
const button = target.querySelector('button'); const button = target.querySelector('button');
@ -7,11 +10,17 @@ export default {
await button.dispatchEvent(event); await button.dispatchEvent(event);
assert.equal(component.counter, 1); assert.equal(component.counter, 1);
assert.equal(target.innerHTML, '<button>+1</button>\n\n<p>1</p>'); assert.htmlEqual(target.innerHTML, `
<button>+1</button>
<p>1</p>
`);
await button.dispatchEvent(event); await button.dispatchEvent(event);
assert.equal(component.counter, 2); assert.equal(component.counter, 2);
assert.equal(target.innerHTML, '<button>+1</button>\n\n<p>2</p>'); assert.htmlEqual(target.innerHTML, `
<button>+1</button>
<p>2</p>
`);
assert.equal(component.foo(), 42); assert.equal(component.foo(), 42);
} }

Loading…
Cancel
Save