Skip to content

Commit b5945e1

Browse files
committed
dont render undefined/null attributes in SSR
1 parent 8642ef1 commit b5945e1

File tree

4 files changed

+35
-27
lines changed

4 files changed

+35
-27
lines changed

src/compile/nodes/Attribute.ts

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { escape, escapeTemplate, stringify } from '../../utils/stringify';
1+
import { stringify } from '../../utils/stringify';
22
import addToSet from '../../utils/addToSet';
33
import Component from '../Component';
44
import Node from './shared/Node';
@@ -16,6 +16,7 @@ export default class Attribute extends Node {
1616
name: string;
1717
isSpread: boolean;
1818
isTrue: boolean;
19+
isConcatenated: boolean;
1920
isDynamic: boolean;
2021
isSynthetic: boolean;
2122
shouldCache: boolean;
@@ -38,6 +39,7 @@ export default class Attribute extends Node {
3839

3940
this.isDynamic = true; // TODO not necessarily
4041
this.shouldCache = false; // TODO does this mean anything here?
42+
this.isConcatenated = false;
4143
}
4244

4345
else {
@@ -65,6 +67,8 @@ export default class Attribute extends Node {
6567
? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name)
6668
: true
6769
: false;
70+
71+
this.isConcatenated = this.chunks.length > 1;
6872
}
6973
}
7074

@@ -99,16 +103,4 @@ export default class Attribute extends Node {
99103
? this.chunks[0].data
100104
: '';
101105
}
102-
103-
stringifyForSsr() {
104-
return this.chunks
105-
.map((chunk: Node) => {
106-
if (chunk.type === 'Text') {
107-
return escapeTemplate(escape(chunk.data).replace(/"/g, '"'));
108-
}
109-
110-
return '${@escape(' + chunk.snippet + ')}';
111-
})
112-
.join('');
113-
}
114106
}

src/compile/render-ssr/handlers/Element.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../utils/quoteIfNecessary';
22
import isVoidElementName from '../../../utils/isVoidElementName';
3+
import Attribute from '../../nodes/Attribute';
4+
import Node from '../../nodes/shared/Node';
5+
import { escapeTemplate } from '../../../utils/stringify';
36

47
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
58
const boolean_attributes = new Set([
@@ -71,7 +74,7 @@ export default function(node, renderer, options) {
7174
args.push(attribute.expression.snippet);
7275
} else {
7376
if (attribute.name === 'value' && node.name === 'textarea') {
74-
textareaContents = attribute.stringifyForSsr();
77+
textareaContents = stringifyAttribute(attribute);
7578
} else if (attribute.isTrue) {
7679
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: true }`);
7780
} else if (
@@ -82,18 +85,18 @@ export default function(node, renderer, options) {
8285
// a boolean attribute with one non-Text chunk
8386
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: ${attribute.chunks[0].snippet} }`);
8487
} else {
85-
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${attribute.stringifyForSsr()}\` }`);
88+
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${stringifyAttribute(attribute)}\` }`);
8689
}
8790
}
8891
});
8992

9093
openingTag += "${@spread([" + args.join(', ') + "])}";
9194
} else {
92-
node.attributes.forEach((attribute: Node) => {
95+
node.attributes.forEach((attribute: Attribute) => {
9396
if (attribute.type !== 'Attribute') return;
9497

9598
if (attribute.name === 'value' && node.name === 'textarea') {
96-
textareaContents = attribute.stringifyForSsr();
99+
textareaContents = stringifyAttribute(attribute);
97100
} else if (attribute.isTrue) {
98101
openingTag += ` ${attribute.name}`;
99102
} else if (
@@ -105,9 +108,14 @@ export default function(node, renderer, options) {
105108
openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }';
106109
} else if (attribute.name === 'class' && classExpr) {
107110
addClassAttribute = false;
108-
openingTag += ` class="\${[\`${attribute.stringifyForSsr()}\`, ${classExpr}].join(' ').trim() }"`;
111+
openingTag += ` class="\${[\`${stringifyAttribute(attribute)}\`, ${classExpr}].join(' ').trim() }"`;
112+
} else if (attribute.isConcatenated || !attribute.isDynamic) {
113+
openingTag += ` ${attribute.name}="${stringifyAttribute(attribute)}"`;
109114
} else {
110-
openingTag += ` ${attribute.name}="${attribute.stringifyForSsr()}"`;
115+
const { name } = attribute;
116+
const { snippet } = attribute.chunks[0];
117+
118+
openingTag += '${(v => v == null ? "" : ` ' + name + '=${' + snippet + '}`)(' + snippet + ')}';
111119
}
112120
});
113121
}
@@ -129,4 +137,16 @@ export default function(node, renderer, options) {
129137
if (!isVoidElementName(node.name)) {
130138
renderer.append(`</${node.name}>`);
131139
}
140+
}
141+
142+
function stringifyAttribute(attribute: Attribute) {
143+
return attribute.chunks
144+
.map((chunk: Node) => {
145+
if (chunk.type === 'Text') {
146+
return escapeTemplate(escape(chunk.data).replace(/"/g, '&quot;'));
147+
}
148+
149+
return '${@escape(' + chunk.snippet + ')}';
150+
})
151+
.join('');
132152
}

src/shared/dom.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function removeListener(node, event, handler) {
8282
}
8383

8484
export function setAttribute(node, attribute, value) {
85-
if (value == null) removeAttribute(node, attribute);
85+
if (value == null) node.removeAttribute(attribute);
8686
else node.setAttribute(attribute, value);
8787
}
8888

@@ -104,14 +104,10 @@ export function setCustomElementData(node, prop, value) {
104104
} else if (value) {
105105
setAttribute(node, prop, value);
106106
} else {
107-
removeAttribute(node, prop);
107+
node.removeAttribute(prop);
108108
}
109109
}
110110

111-
export function removeAttribute(node, attribute) {
112-
node.removeAttribute(attribute);
113-
}
114-
115111
export function setXlinkAttribute(node, attribute, value) {
116112
node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
117113
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export default {
2-
'skip-ssr': true,
2+
html: '<div></div>',
33

4-
html: '<div></div>'
4+
ssrHtml: '<div foo=1></div>'
55
};

0 commit comments

Comments
 (0)