diff --git a/mocha.opts b/mocha.opts index 427b029758..af6b17a845 100644 --- a/mocha.opts +++ b/mocha.opts @@ -1 +1,2 @@ +--bail test/test.js \ No newline at end of file diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index 6a8624a601..a2e3bb281f 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -100,9 +100,10 @@ export default function dom( generator.stylesheet.warnOnUnusedSelectors(options.onwarn); - parsed.html.children.forEach((node: Node) => { - visit(generator, block, state, node, [], []); - }); + // parsed.html.children.forEach((node: Node) => { + // visit(generator, block, state, node, [], []); + // }); + parsed.html.build(); const builder = new CodeBuilder(); const computationBuilder = new CodeBuilder(); diff --git a/src/generators/nodes/Attribute.ts b/src/generators/nodes/Attribute.ts new file mode 100644 index 0000000000..daaa9e6b7f --- /dev/null +++ b/src/generators/nodes/Attribute.ts @@ -0,0 +1,7 @@ +import Node from './shared/Node'; + +export default class Attribute extends Node { + name: string; + value: true | Node[] + expression: Node +} \ No newline at end of file diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts index 66c9f84be9..f659dbd50f 100644 --- a/src/generators/nodes/Element.ts +++ b/src/generators/nodes/Element.ts @@ -1,11 +1,17 @@ import Node from './shared/Node'; import Block from '../dom/Block'; import State from '../dom/State'; +import Attribute from './Attribute'; +import * as namespaces from '../../utils/namespaces'; + +const meta: Record = { + ':Window': {}, // TODO this should be dealt with in walkTemplate +}; export default class Element extends Node { type: 'Element'; name: string; - attributes: Node[]; // TODO have more specific Attribute type + attributes: Attribute[]; // TODO have more specific Attribute type children: Node[]; init( @@ -21,7 +27,7 @@ export default class Element extends Node { this.cannotUseInnerHTML(); } - this.attributes.forEach((attribute: Node) => { + this.attributes.forEach(attribute => { if (attribute.type === 'Attribute' && attribute.value !== true) { attribute.value.forEach((chunk: Node) => { if (chunk.type !== 'Text') { @@ -70,11 +76,11 @@ export default class Element extends Node { // // if (this.name === 'option' && !valueAttribute) { - this.attributes.push({ + this.attributes.push(new Attribute({ type: 'Attribute', name: 'value', value: this.children - }); + })); } // special case — in a case like this... @@ -88,7 +94,7 @@ export default class Element extends Node { // so that if `foo.qux` changes, we know that we need to // mark `bar` and `baz` as dirty too if (this.name === 'select') { - const binding = this.attributes.find((node: Node) => node.type === 'Binding' && node.name === 'value'); + const binding = this.attributes.find(node => node.type === 'Binding' && node.name === 'value'); if (binding) { // TODO does this also apply to e.g. ``? const dependencies = binding.metadata.dependencies; @@ -132,6 +138,294 @@ export default class Element extends Node { } } + build( + block: Block, + state: State, + node: Node, + elementStack: Node[], + componentStack: Node[] + ) { + if (this.name in meta) { + return meta[this.name](generator, block, this); + } + + if (this.name === 'slot') { // TODO deal with in walkTemplate + if (this.generator.customElement) { + const slotName = getStaticAttributeValue(this, 'name') || 'default'; + this.generator.slots.add(slotName); + } else { + return visitSlot(this.generator, block, state, this, elementStack, componentStack); + } + } + + const childState = this._state; + const name = childState.parentNode; + + const slot = this.attributes.find((attribute: Node) => attribute.name === 'slot'); + const parentNode = this.slotted ? + `${componentStack[componentStack.length - 1].var}._slotted.${slot.value[0].data}` : // TODO this looks bonkers + state.parentNode; + + block.addVariable(name); + block.builders.create.addLine( + `${name} = ${getRenderStatement( + this.generator, + childState.namespace, + this.name + )};` + ); + + if (this.generator.hydratable) { + block.builders.claim.addBlock(deindent` + ${name} = ${getClaimStatement(generator, childState.namespace, state.parentNodes, node)}; + var ${childState.parentNodes} = @children(${name}); + `); + } + + if (parentNode) { + block.builders.mount.addLine( + `@appendNode(${name}, ${parentNode});` + ); + } else { + block.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`); + + // TODO we eventually need to consider what happens to elements + // that belong to the same outgroup as an outroing element... + block.builders.unmount.addLine(`@detachNode(${name});`); + } + + // add CSS encapsulation attribute + if (this._needsCssAttribute && !generator.customElement) { + generator.needsEncapsulateHelper = true; + block.builders.hydrate.addLine( + `@encapsulateStyles(${name});` + ); + + if (this._cssRefAttribute) { + block.builders.hydrate.addLine( + `@setAttribute(${name}, "svelte-ref-${this._cssRefAttribute}", "");` + ) + } + } + + if (this.name === 'textarea') { + // this is an egregious hack, but it's the easiest way to get