dont render undefined/null attributes in SSR

pull/1815/head
Rich Harris 6 years ago
parent 8642ef17a4
commit b5945e13ea

@ -1,4 +1,4 @@
import { escape, escapeTemplate, stringify } from '../../utils/stringify'; import { stringify } from '../../utils/stringify';
import addToSet from '../../utils/addToSet'; import addToSet from '../../utils/addToSet';
import Component from '../Component'; import Component from '../Component';
import Node from './shared/Node'; import Node from './shared/Node';
@ -16,6 +16,7 @@ export default class Attribute extends Node {
name: string; name: string;
isSpread: boolean; isSpread: boolean;
isTrue: boolean; isTrue: boolean;
isConcatenated: boolean;
isDynamic: boolean; isDynamic: boolean;
isSynthetic: boolean; isSynthetic: boolean;
shouldCache: boolean; shouldCache: boolean;
@ -38,6 +39,7 @@ export default class Attribute extends Node {
this.isDynamic = true; // TODO not necessarily this.isDynamic = true; // TODO not necessarily
this.shouldCache = false; // TODO does this mean anything here? this.shouldCache = false; // TODO does this mean anything here?
this.isConcatenated = false;
} }
else { else {
@ -65,6 +67,8 @@ export default class Attribute extends Node {
? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name) ? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name)
: true : true
: false; : false;
this.isConcatenated = this.chunks.length > 1;
} }
} }
@ -99,16 +103,4 @@ export default class Attribute extends Node {
? this.chunks[0].data ? this.chunks[0].data
: ''; : '';
} }
stringifyForSsr() {
return this.chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data).replace(/"/g, '"'));
}
return '${@escape(' + chunk.snippet + ')}';
})
.join('');
}
} }

@ -1,5 +1,8 @@
import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../utils/quoteIfNecessary'; import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../utils/quoteIfNecessary';
import isVoidElementName from '../../../utils/isVoidElementName'; import isVoidElementName from '../../../utils/isVoidElementName';
import Attribute from '../../nodes/Attribute';
import Node from '../../nodes/shared/Node';
import { escapeTemplate } from '../../../utils/stringify';
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 // source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
const boolean_attributes = new Set([ const boolean_attributes = new Set([
@ -71,7 +74,7 @@ export default function(node, renderer, options) {
args.push(attribute.expression.snippet); args.push(attribute.expression.snippet);
} else { } else {
if (attribute.name === 'value' && node.name === 'textarea') { if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = attribute.stringifyForSsr(); textareaContents = stringifyAttribute(attribute);
} else if (attribute.isTrue) { } else if (attribute.isTrue) {
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: true }`); args.push(`{ ${quoteNameIfNecessary(attribute.name)}: true }`);
} else if ( } else if (
@ -82,18 +85,18 @@ export default function(node, renderer, options) {
// a boolean attribute with one non-Text chunk // a boolean attribute with one non-Text chunk
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: ${attribute.chunks[0].snippet} }`); args.push(`{ ${quoteNameIfNecessary(attribute.name)}: ${attribute.chunks[0].snippet} }`);
} else { } else {
args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${attribute.stringifyForSsr()}\` }`); args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${stringifyAttribute(attribute)}\` }`);
} }
} }
}); });
openingTag += "${@spread([" + args.join(', ') + "])}"; openingTag += "${@spread([" + args.join(', ') + "])}";
} else { } else {
node.attributes.forEach((attribute: Node) => { node.attributes.forEach((attribute: Attribute) => {
if (attribute.type !== 'Attribute') return; if (attribute.type !== 'Attribute') return;
if (attribute.name === 'value' && node.name === 'textarea') { if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = attribute.stringifyForSsr(); textareaContents = stringifyAttribute(attribute);
} else if (attribute.isTrue) { } else if (attribute.isTrue) {
openingTag += ` ${attribute.name}`; openingTag += ` ${attribute.name}`;
} else if ( } else if (
@ -105,9 +108,14 @@ export default function(node, renderer, options) {
openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }'; openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }';
} else if (attribute.name === 'class' && classExpr) { } else if (attribute.name === 'class' && classExpr) {
addClassAttribute = false; addClassAttribute = false;
openingTag += ` class="\${[\`${attribute.stringifyForSsr()}\`, ${classExpr}].join(' ').trim() }"`; openingTag += ` class="\${[\`${stringifyAttribute(attribute)}\`, ${classExpr}].join(' ').trim() }"`;
} else if (attribute.isConcatenated || !attribute.isDynamic) {
openingTag += ` ${attribute.name}="${stringifyAttribute(attribute)}"`;
} else { } else {
openingTag += ` ${attribute.name}="${attribute.stringifyForSsr()}"`; const { name } = attribute;
const { snippet } = attribute.chunks[0];
openingTag += '${(v => v == null ? "" : ` ' + name + '=${' + snippet + '}`)(' + snippet + ')}';
} }
}); });
} }
@ -130,3 +138,15 @@ export default function(node, renderer, options) {
renderer.append(`</${node.name}>`); renderer.append(`</${node.name}>`);
} }
} }
function stringifyAttribute(attribute: Attribute) {
return attribute.chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data).replace(/"/g, '&quot;'));
}
return '${@escape(' + chunk.snippet + ')}';
})
.join('');
}

@ -82,7 +82,7 @@ export function removeListener(node, event, handler) {
} }
export function setAttribute(node, attribute, value) { export function setAttribute(node, attribute, value) {
if (value == null) removeAttribute(node, attribute); if (value == null) node.removeAttribute(attribute);
else node.setAttribute(attribute, value); else node.setAttribute(attribute, value);
} }
@ -104,14 +104,10 @@ export function setCustomElementData(node, prop, value) {
} else if (value) { } else if (value) {
setAttribute(node, prop, value); setAttribute(node, prop, value);
} else { } else {
removeAttribute(node, prop); node.removeAttribute(prop);
} }
} }
export function removeAttribute(node, attribute) {
node.removeAttribute(attribute);
}
export function setXlinkAttribute(node, attribute, value) { export function setXlinkAttribute(node, attribute, value) {
node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value); node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
} }

@ -1,5 +1,5 @@
export default { export default {
'skip-ssr': true, html: '<div></div>',
html: '<div></div>' ssrHtml: '<div foo=1></div>'
}; };

Loading…
Cancel
Save