diff --git a/src/generators/nodes/Attribute.ts b/src/generators/nodes/Attribute.ts index f774713868..887963e343 100644 --- a/src/generators/nodes/Attribute.ts +++ b/src/generators/nodes/Attribute.ts @@ -6,6 +6,7 @@ import addToSet from '../../utils/addToSet'; import { DomGenerator } from '../dom/index'; import Node from './shared/Node'; import Element from './Element'; +import Text from './Text'; import Block from '../dom/Block'; import Expression from './shared/Expression'; @@ -22,37 +23,51 @@ export default class Attribute extends Node { compiler: DomGenerator; parent: Element; name: string; + isSpread: boolean; isTrue: boolean; isDynamic: boolean; - chunks: Node[]; + expression?: Expression; + chunks: (Text | Expression)[]; dependencies: Set; - expression: Node; constructor(compiler, parent, scope, info) { super(compiler, parent, scope, info); - this.name = info.name; - this.isTrue = info.value === true; + if (info.type === 'Spread') { + this.name = null; + this.isSpread = true; + this.isTrue = false; - this.dependencies = new Set(); + this.expression = new Expression(compiler, this, scope, info.expression); + this.dependencies = this.expression.dependencies; + this.chunks = null; - this.chunks = this.isTrue - ? [] - : info.value.map(node => { - if (node.type === 'Text') return node; + this.isDynamic = true; // TODO not necessarily + } + + else { + this.name = info.name; + this.isTrue = info.value === true; - const expression = new Expression(compiler, this, scope, node.expression); + this.dependencies = new Set(); - addToSet(this.dependencies, expression.dependencies); - return expression; - }); + this.chunks = this.isTrue + ? [] + : info.value.map(node => { + if (node.type === 'Text') return node; - this.isDynamic = this.chunks.length === 1 - ? this.chunks[0].type !== 'Text' - : this.chunks.length > 1; + const expression = new Expression(compiler, this, scope, node.expression); - // TODO this would be better, but it breaks some stuff - // this.isDynamic = this.dependencies.size > 0; + addToSet(this.dependencies, expression.dependencies); + return expression; + }); + + // TODO this would be better, but it breaks some stuff + // this.isDynamic = this.dependencies.size > 0; + this.isDynamic = this.chunks.length === 1 + ? this.chunks[0].type !== 'Text' + : this.chunks.length > 1; + } } getValue() { diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts index e9f9665bb0..90456e136f 100644 --- a/src/generators/nodes/Element.ts +++ b/src/generators/nodes/Element.ts @@ -57,6 +57,7 @@ export default class Element extends Node { break; case 'Attribute': + case 'Spread': // special case if (node.name === 'xmlns') this.namespace = node.value[0].data; @@ -151,18 +152,6 @@ export default class Element extends Node { block.outros += 1; } - this.attributes.forEach(attribute => { - if (attribute.type === 'Attribute' && attribute.value !== true) { - // removed - } else { - if (this.parent) this.parent.cannotUseInnerHTML(); - - if (attribute.type === 'Spread') { - block.addDependencies(attribute.metadata.dependencies); - } - } - }); - const valueAttribute = this.attributes.find((attribute: Attribute) => attribute.name === 'value'); if (this.name === 'textarea') { @@ -526,23 +515,20 @@ export default class Element extends Node { this.attributes .filter(attr => attr.type === 'Attribute' || attr.type === 'Spread') .forEach(attr => { - if (attr.type === 'Attribute') { - const { dynamic, value, dependencies } = mungeAttribute(attr, block); + const condition = attr.dependencies.size > 0 + ? [...attr.dependencies].map(d => `changed.${d}`).join(' || ') + : null; + + if (attr.isSpread) { + const { snippet, dependencies } = attr.expression; - const snippet = `{ ${quoteIfNecessary(attr.name)}: ${value} }`; initialProps.push(snippet); - const condition = dependencies && dependencies.map(d => `changed.${d}`).join(' || '); updates.push(condition ? `${condition} && ${snippet}` : snippet); - } - - else { - block.contextualise(attr.expression); // TODO gah - const { snippet, dependencies } = attr.metadata; - + } else { + const snippet = `{ ${quoteIfNecessary(attr.name)}: ${attr.getValue()} }`; initialProps.push(snippet); - const condition = dependencies && dependencies.map(d => `changed.${d}`).join(' || '); updates.push(condition ? `${condition} && ${snippet}` : snippet); } }); diff --git a/src/generators/nodes/shared/mungeAttribute.ts b/src/generators/nodes/shared/mungeAttribute.ts index 9609f58d4b..296e8e2faf 100644 --- a/src/generators/nodes/shared/mungeAttribute.ts +++ b/src/generators/nodes/shared/mungeAttribute.ts @@ -14,8 +14,7 @@ type MungedAttribute = { export default function mungeAttribute(attribute: Node, block: Block): MungedAttribute { if (attribute.type === 'Spread') { - block.contextualise(attribute.expression); // TODO remove - const { dependencies, snippet } = attribute.metadata; + const { dependencies, snippet } = attribute.expression; return { spread: true, diff --git a/src/generators/server-side-rendering/visitors/Element.ts b/src/generators/server-side-rendering/visitors/Element.ts index 0cd10cfbbe..a031344da5 100644 --- a/src/generators/server-side-rendering/visitors/Element.ts +++ b/src/generators/server-side-rendering/visitors/Element.ts @@ -35,13 +35,12 @@ export default function visitElement( appendTarget.slots[slotName] = ''; } - if (node.attributes.find(attr => attr.type === 'Spread')) { + if (node.attributes.find(attr => attr.isSpread)) { // TODO dry this out const args = []; node.attributes.forEach((attribute: Node) => { if (attribute.type === 'Spread') { - block.contextualise(attribute.expression); - args.push(attribute.metadata.snippet); + args.push(attribute.expression.snippet); } else if (attribute.type === 'Attribute') { if (attribute.name === 'value' && node.name === 'textarea') { textareaContents = stringifyAttributeValue(block, attribute.value); @@ -53,8 +52,7 @@ export default function visitElement( attribute.value[0].type !== 'Text' ) { // a boolean attribute with one non-Text chunk - block.contextualise(attribute.value[0].expression); - args.push(`{ ${quoteIfNecessary(attribute.name)}: ${attribute.value[0].metadata.snippet} }`); + args.push(`{ ${quoteIfNecessary(attribute.name)}: ${attribute.value[0].expression.snippet} }`); } else { args.push(`{ ${quoteIfNecessary(attribute.name)}: \`${stringifyAttributeValue(block, attribute.value)}\` }`); }