From 4f991536d436cbdf817a62cbc7ca9c04350c69cc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 14:30:25 -0500 Subject: [PATCH 01/10] parse :Switch tags --- src/parse/state/tag.ts | 35 +++++++++------ .../samples/component-dynamic/input.html | 1 + .../samples/component-dynamic/output.json | 43 +++++++++++++++++++ 3 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 test/parser/samples/component-dynamic/input.html create mode 100644 test/parser/samples/component-dynamic/output.json diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index ffe9a4021866..d2c17e30fa1b 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -15,9 +15,10 @@ import { Node } from '../../interfaces'; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const SELF = ':Self'; +const SWITCH = ':Switch'; const metaTags = { - ':Window': true, + ':Window': true }; const specials = new Map([ @@ -104,6 +105,15 @@ export default function tag(parser: Parser) { } } + const element: Node = { + start, + end: null, // filled in later + type: 'Element', + name, + attributes: [], + children: [], + }; + parser.allowWhitespace(); if (isClosingTag) { @@ -156,12 +166,18 @@ export default function tag(parser: Parser) { } } - const attributes = []; + if (name === SWITCH) { + parser.eat('{', true); + element.expression = readExpression(parser); + parser.allowWhitespace(); + parser.eat('}', true); + } + const uniqueNames = new Set(); let attribute; while ((attribute = readAttribute(parser, uniqueNames))) { - attributes.push(attribute); + element.attributes.push(attribute); parser.allowWhitespace(); } @@ -179,19 +195,10 @@ export default function tag(parser: Parser) { } parser.eat('>', true); - parser[special.property] = special.read(parser, start, attributes); + parser[special.property] = special.read(parser, start, element.attributes); return; } - const element: Node = { - start, - end: null, // filled in later - type: 'Element', - name, - attributes, - children: [], - }; - parser.current().children.push(element); const selfClosing = parser.eat('/') || isVoidElementName(name); @@ -242,6 +249,8 @@ function readTagName(parser: Parser) { return SELF; } + if (parser.eat(SWITCH)) return SWITCH; + const name = parser.readUntil(/(\s|\/|>)/); if (name in metaTags) return name; diff --git a/test/parser/samples/component-dynamic/input.html b/test/parser/samples/component-dynamic/input.html new file mode 100644 index 000000000000..1c4e872902b6 --- /dev/null +++ b/test/parser/samples/component-dynamic/input.html @@ -0,0 +1 @@ +<:Switch {foo ? Foo : Bar}> \ No newline at end of file diff --git a/test/parser/samples/component-dynamic/output.json b/test/parser/samples/component-dynamic/output.json new file mode 100644 index 000000000000..22b32bb932c6 --- /dev/null +++ b/test/parser/samples/component-dynamic/output.json @@ -0,0 +1,43 @@ +{ + "hash": 755548012, + "html": { + "start": 0, + "end": 37, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 37, + "type": "Element", + "name": ":Switch", + "attributes": [], + "children": [], + "expression": { + "type": "ConditionalExpression", + "start": 10, + "end": 25, + "test": { + "type": "Identifier", + "start": 10, + "end": 13, + "name": "foo" + }, + "consequent": { + "type": "Identifier", + "start": 16, + "end": 19, + "name": "Foo" + }, + "alternate": { + "type": "Identifier", + "start": 22, + "end": 25, + "name": "Bar" + } + } + } + ] + }, + "css": null, + "js": null +} \ No newline at end of file From dba32df84e7ee41c8a3699ce347a7b272a2a2ee1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 16:25:01 -0500 Subject: [PATCH 02/10] client-side dynamic components mostly working (#640) --- src/generators/Generator.ts | 4 + src/generators/dom/preprocess.ts | 8 +- src/generators/dom/visitors/Component.ts | 141 ++++++++++++++---- .../dom/visitors/Element/Element.ts | 2 +- src/parse/state/tag.ts | 3 +- test/runtime/index.js | 4 + test/runtime/samples/switch/Bar.html | 1 + test/runtime/samples/switch/Foo.html | 1 + test/runtime/samples/switch/_config.js | 19 +++ test/runtime/samples/switch/main.html | 12 ++ 10 files changed, 162 insertions(+), 33 deletions(-) create mode 100644 test/runtime/samples/switch/Bar.html create mode 100644 test/runtime/samples/switch/Foo.html create mode 100644 test/runtime/samples/switch/_config.js create mode 100644 test/runtime/samples/switch/main.html diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 323af454c7fa..2dcc3f379730 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -763,6 +763,10 @@ export default class Generator { node.metadata = contextualise(node.expression, contextDependencies, indexes); this.skip(); } + + if (node.type === 'Element' && node.name === ':Switch') { + node.metadata = contextualise(node.expression, contextDependencies, indexes); + } }, leave(node: Node, parent: Node) { diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 62768184a065..a6333c286cd3 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -436,13 +436,17 @@ const preprocessors = { } const isComponent = - generator.components.has(node.name) || node.name === ':Self'; + generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch'; if (isComponent) { cannotUseInnerHTML(node); node.var = block.getUniqueName( - (node.name === ':Self' ? generator.name : node.name).toLowerCase() + ( + node.name === ':Self' ? generator.name : + node.name === ':Switch' ? 'switch_instance' : + node.name + ).toLowerCase() ); node._state = getChildState(state, { diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index bfd52559d679..a9dbd4dbe247 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -3,6 +3,7 @@ import CodeBuilder from '../../../utils/CodeBuilder'; import visit from '../visit'; import { DomGenerator } from '../index'; import Block from '../Block'; +import isDomNode from './shared/isDomNode'; import getTailSnippet from '../../../utils/getTailSnippet'; import getObject from '../../../utils/getObject'; import getExpressionPrecedence from '../../../utils/getExpressionPrecedence'; @@ -67,6 +68,9 @@ export default function visitComponent( .filter((a: Node) => a.type === 'Binding') .map((a: Node) => mungeBinding(a, block)); + const ref = node.attributes.find((a: Node) => a.type === 'Ref'); + if (ref) generator.usesRefs = true; + if (attributes.length || bindings.length) { const initialProps = attributes .map((attribute: Attribute) => `${attribute.name}: ${attribute.value}`); @@ -205,30 +209,122 @@ export default function visitComponent( } } - const expression = node.name === ':Self' ? generator.name : `%components-${node.name}`; + const isSwitch = node.name === ':Switch'; - block.builders.init.addBlock(deindent` - ${statements.join('\n')} - var ${name} = new ${expression}({ - ${componentInitProperties.join(',\n')} - }); + const switch_vars = isSwitch && { + value: block.getUniqueName('switch_value'), + props: block.getUniqueName('switch_props') + }; - ${beforecreate} - `); + const expression = ( + node.name === ':Self' ? generator.name : + isSwitch ? switch_vars.value : + `%components-${node.name}` + ); - block.builders.create.addLine(`${name}._fragment.c();`); + if (isSwitch) { + block.contextualise(node.expression); + const { dependencies, snippet } = node.metadata; + + const needsAnchor = node.next ? !isDomNode(node.next, generator) : !state.parentNode || !isDomNode(node.parent, generator); + const anchor = needsAnchor + ? block.getUniqueName(`${name}_anchor`) + : (node.next && node.next.var) || 'null'; + + if (needsAnchor) { + block.addElement( + anchor, + `@createComment()`, + `@createComment()`, + state.parentNode + ); + } - block.builders.claim.addLine( - `${name}._fragment.l(${state.parentNodes});` - ); + const params = block.params.join(', '); - block.builders.mount.addLine( - `${name}._mount(${state.parentNode || '#target'}, ${state.parentNode ? 'null' : 'anchor'});` - ); + block.builders.init.addBlock(deindent` + var ${switch_vars.value} = ${snippet}; + `); - if (!state.parentNode) block.builders.unmount.addLine(`${name}._unmount();`); + block.builders.init.addBlock(deindent` + function ${switch_vars.props}(${params}) { + return { + ${componentInitProperties.join(',\n')} + }; + } + + if (${switch_vars.value}) { + ${statements.length > 0 && statements.join('\n')} + var ${name} = new ${expression}(${switch_vars.props}(${params})); + + ${beforecreate} + } + `); + + block.builders.create.addLine( + `if (${name}) ${name}._fragment.c();` + ); + + block.builders.claim.addLine( + `if (${name}) ${name}._fragment.l(${state.parentNodes});` + ); + + block.builders.mount.addLine( + `if (${name}) ${name}._mount(${state.parentNode || '#target'}, ${state.parentNode ? 'null' : 'anchor'});` + ); + + block.builders.update.addBlock(deindent` + if (${switch_vars.value} !== (${switch_vars.value} = ${snippet})) { + if (${name}) ${name}.destroy(); - block.builders.destroy.addLine(`${name}.destroy(false);`); + if (${switch_vars.value}) { + ${name} = new ${switch_vars.value}(${switch_vars.props}(${params})); + ${name}._fragment.c(); + ${name}._mount(${anchor}.parentNode, ${anchor}); + ${ref && `#component.refs.${ref.name} = ${name};`} + } + + ${ref && deindent` + else if (#component.refs.${ref.name} === ${name}) { + #component.refs.${ref.name} = null; + }`} + } else { + // normal update + } + `); + + if (!state.parentNode) block.builders.unmount.addLine(`if (${name}) ${name}._unmount();`); + + block.builders.destroy.addLine(`if (${name}) ${name}.destroy(false);`); + } else { + block.builders.init.addBlock(deindent` + ${statements.join('\n')} + var ${name} = new ${expression}({ + ${componentInitProperties.join(',\n')} + }); + + ${beforecreate} + + ${ref && `#component.refs.${ref.name} = ${name};`} + `); + + block.builders.create.addLine(`${name}._fragment.c();`); + + block.builders.claim.addLine( + `${name}._fragment.l(${state.parentNodes});` + ); + + block.builders.mount.addLine( + `${name}._mount(${state.parentNode || '#target'}, ${state.parentNode ? 'null' : 'anchor'});` + ); + + if (!state.parentNode) block.builders.unmount.addLine(`${name}._unmount();`); + + block.builders.destroy.addLine(deindent` + ${name}.destroy(false); + ${ref && `if (#component.refs.${ref.name} === ${name}) #component.refs.${ref.name} = null;`} + `); + } // event handlers node.attributes.filter((a: Node) => a.type === 'EventHandler').forEach((handler: Node) => { @@ -274,17 +370,6 @@ export default function visitComponent( `); }); - // refs - node.attributes.filter((a: Node) => a.type === 'Ref').forEach((ref: Node) => { - generator.usesRefs = true; - - block.builders.init.addLine(`#component.refs.${ref.name} = ${name};`); - - block.builders.destroy.addLine(deindent` - if (#component.refs.${ref.name} === ${name}) #component.refs.${ref.name} = null; - `); - }); - // maintain component context if (allContexts.size) { const contexts = Array.from(allContexts); diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 8de38c766d01..7dc6eb72626a 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -43,7 +43,7 @@ export default function visitElement( } } - if (generator.components.has(node.name) || node.name === ':Self') { + if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch') { return visitComponent(generator, block, state, node, elementStack, componentStack); } diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index d2c17e30fa1b..c62053ffb55b 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -171,6 +171,7 @@ export default function tag(parser: Parser) { element.expression = readExpression(parser); parser.allowWhitespace(); parser.eat('}', true); + parser.allowWhitespace(); } const uniqueNames = new Set(); @@ -181,8 +182,6 @@ export default function tag(parser: Parser) { parser.allowWhitespace(); } - parser.allowWhitespace(); - // special cases – top-level \ No newline at end of file From 57af1539d9c1b58728a4fe1b695f2b54a8f2d227 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 16:56:05 -0500 Subject: [PATCH 03/10] SSR :Switch components --- src/generators/server-side-rendering/index.ts | 8 ++++++++ .../server-side-rendering/visitors/Component.ts | 9 ++++++++- src/generators/server-side-rendering/visitors/Element.ts | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts index f9c40ddb085d..13ad9f026dc6 100644 --- a/src/generators/server-side-rendering/index.ts +++ b/src/generators/server-side-rendering/index.ts @@ -189,6 +189,14 @@ export default function ssr( } ` } + + ${ + /__missingComponent/.test(generator.renderCode) && deindent` + var __missingComponent = { + render: () => '' + }; + ` + } `.replace(/(@+|#+|%+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => { if (sigil === '@') return generator.alias(name); if (sigil === '%') return generator.templateVars.get(name); diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts index 74c7e239e458..1689647ba1f5 100644 --- a/src/generators/server-side-rendering/visitors/Component.ts +++ b/src/generators/server-side-rendering/visitors/Component.ts @@ -71,7 +71,14 @@ export default function visitComponent( ) .join(', '); - const expression = node.name === ':Self' ? generator.name : `%components-${node.name}`; + const isSwitch = node.name === ':Switch'; + if (isSwitch) block.contextualise(node.expression); + + const expression = ( + node.name === ':Self' ? generator.name : + isSwitch ? `((${node.metadata.snippet}) || __missingComponent)` : + `%components-${node.name}` + ); bindings.forEach(binding => { block.addBinding(binding, expression); diff --git a/src/generators/server-side-rendering/visitors/Element.ts b/src/generators/server-side-rendering/visitors/Element.ts index e1d7e197d040..8834a2b90b8a 100644 --- a/src/generators/server-side-rendering/visitors/Element.ts +++ b/src/generators/server-side-rendering/visitors/Element.ts @@ -40,7 +40,7 @@ export default function visitElement( return; } - if (generator.components.has(node.name) || node.name === ':Self') { + if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch') { visitComponent(generator, block, node); return; } From e1777b6037e1ead1b203abbdc777c472184d1be0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 17:47:21 -0500 Subject: [PATCH 04/10] dynamic component event handlers --- src/generators/dom/visitors/Component.ts | 126 +++++++++++------- test/runtime/samples/switch-events/Bar.html | 1 + test/runtime/samples/switch-events/Foo.html | 1 + test/runtime/samples/switch-events/_config.js | 27 ++++ test/runtime/samples/switch-events/main.html | 12 ++ 5 files changed, 117 insertions(+), 50 deletions(-) create mode 100644 test/runtime/samples/switch-events/Bar.html create mode 100644 test/runtime/samples/switch-events/Foo.html create mode 100644 test/runtime/samples/switch-events/_config.js create mode 100644 test/runtime/samples/switch-events/main.html diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index a9dbd4dbe247..a725953ffa0c 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -61,12 +61,16 @@ export default function visitComponent( let beforecreate: string = null; const attributes = node.attributes - .filter((a: Node) => a.type === 'Attribute') - .map((a: Node) => mungeAttribute(a, block)); + .filter(a => a.type === 'Attribute') + .map(a => mungeAttribute(a, block)); const bindings = node.attributes - .filter((a: Node) => a.type === 'Binding') - .map((a: Node) => mungeBinding(a, block)); + .filter(a => a.type === 'Binding') + .map(a => mungeBinding(a, block)); + + const eventHandlers = node.attributes + .filter((a: Node) => a.type === 'EventHandler') + .map(a => mungeEventHandler(generator, node, a, block, name_context, allContexts)); const ref = node.attributes.find((a: Node) => a.type === 'Ref'); if (ref) generator.usesRefs = true; @@ -244,9 +248,7 @@ export default function visitComponent( block.builders.init.addBlock(deindent` var ${switch_vars.value} = ${snippet}; - `); - block.builders.init.addBlock(deindent` function ${switch_vars.props}(${params}) { return { ${componentInitProperties.join(',\n')} @@ -259,6 +261,14 @@ export default function visitComponent( ${beforecreate} } + + ${eventHandlers.map(handler => deindent` + function ${handler.var}(event) { + ${handler.body} + } + + if (${name}) ${name}.on("${handler.name}", ${handler.var}); + `)} `); block.builders.create.addLine( @@ -281,6 +291,11 @@ export default function visitComponent( ${name} = new ${switch_vars.value}(${switch_vars.props}(${params})); ${name}._fragment.c(); ${name}._mount(${anchor}.parentNode, ${anchor}); + + ${eventHandlers.map(handler => deindent` + ${name}.on("${handler.name}", ${handler.var}); + `)} + ${ref && `#component.refs.${ref.name} = ${name};`} } @@ -305,6 +320,12 @@ export default function visitComponent( ${beforecreate} + ${eventHandlers.map(handler => deindent` + ${name}.on("${handler.name}", function(event) { + ${handler.body} + }); + `)} + ${ref && `#component.refs.${ref.name} = ${name};`} `); @@ -326,50 +347,6 @@ export default function visitComponent( `); } - // event handlers - node.attributes.filter((a: Node) => a.type === 'EventHandler').forEach((handler: Node) => { - const usedContexts: string[] = []; - - if (handler.expression) { - generator.addSourcemapLocations(handler.expression); - generator.code.prependRight( - handler.expression.start, - `${block.alias('component')}.` - ); - - handler.expression.arguments.forEach((arg: Node) => { - const { contexts } = block.contextualise(arg, null, true); - - contexts.forEach(context => { - if (!~usedContexts.indexOf(context)) usedContexts.push(context); - allContexts.add(context); - }); - }); - } - - // TODO hoist event handlers? can do `this.__component.method(...)` - const declarations = usedContexts.map(name => { - if (name === 'state') return `var state = ${name_context}.state;`; - - const listName = block.listNames.get(name); - const indexName = block.indexNames.get(name); - - return `var ${listName} = ${name_context}.${listName}, ${indexName} = ${name_context}.${indexName}, ${name} = ${listName}[${indexName}]`; - }); - - const handlerBody = - (declarations.length ? declarations.join('\n') + '\n\n' : '') + - (handler.expression ? - `[✂${handler.expression.start}-${handler.expression.end}✂];` : - `${block.alias('component')}.fire('${handler.name}', event);`); - - block.builders.init.addBlock(deindent` - ${name}.on("${handler.name}", function(event) { - ${handlerBody} - }); - `); - }); - // maintain component context if (allContexts.size) { const contexts = Array.from(allContexts); @@ -512,6 +489,55 @@ function mungeBinding(binding: Node, block: Block): Binding { }; } +function mungeEventHandler(generator: DomGenerator, node: Node, handler: Node, block: Block, name_context: string, allContexts: Set) { + let body; + + if (handler.expression) { + generator.addSourcemapLocations(handler.expression); + generator.code.prependRight( + handler.expression.start, + `${block.alias('component')}.` + ); + + const usedContexts: string[] = []; + + handler.expression.arguments.forEach((arg: Node) => { + const { contexts } = block.contextualise(arg, null, true); + + contexts.forEach(context => { + if (!~usedContexts.indexOf(context)) usedContexts.push(context); + allContexts.add(context); + }); + }); + + // TODO hoist event handlers? can do `this.__component.method(...)` + const declarations = usedContexts.map(name => { + if (name === 'state') return `var state = ${name_context}.state;`; + + const listName = block.listNames.get(name); + const indexName = block.indexNames.get(name); + + return `var ${listName} = ${name_context}.${listName}, ${indexName} = ${name_context}.${indexName}, ${name} = ${listName}[${indexName}]`; + }); + + body = deindent` + ${declarations} + + [✂${handler.expression.start}-${handler.expression.end}✂]; + `; + } else { + body = deindent` + ${block.alias('component')}.fire('${handler.name}', event); + `; + } + + return { + name: handler.name, + var: block.getUniqueName(`${node.var}_${handler.name}`), + body + }; +} + function isComputed(node: Node) { while (node.type === 'MemberExpression') { if (node.computed) return true; diff --git a/test/runtime/samples/switch-events/Bar.html b/test/runtime/samples/switch-events/Bar.html new file mode 100644 index 000000000000..e6f79984c4cd --- /dev/null +++ b/test/runtime/samples/switch-events/Bar.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/switch-events/Foo.html b/test/runtime/samples/switch-events/Foo.html new file mode 100644 index 000000000000..3601714a5586 --- /dev/null +++ b/test/runtime/samples/switch-events/Foo.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/switch-events/_config.js b/test/runtime/samples/switch-events/_config.js new file mode 100644 index 000000000000..3315382e9166 --- /dev/null +++ b/test/runtime/samples/switch-events/_config.js @@ -0,0 +1,27 @@ +export default { + data: { + x: true + }, + + html: ` + + `, + + test(assert, component, target, window) { + const click = new window.MouseEvent('click'); + + target.querySelector('button').dispatchEvent(click); + assert.equal(component.get('selected'), 'foo'); + + component.set({ + x: false + }); + + assert.htmlEqual(target.innerHTML, ` + + `); + + target.querySelector('button').dispatchEvent(click); + assert.equal(component.get('selected'), 'bar'); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/switch-events/main.html b/test/runtime/samples/switch-events/main.html new file mode 100644 index 000000000000..2cfd9d3ce673 --- /dev/null +++ b/test/runtime/samples/switch-events/main.html @@ -0,0 +1,12 @@ +<:Switch { x ? Foo : Bar } on:select='set({ selected: event.id })'/> + + \ No newline at end of file From caa4d7d76dee8990b621ba019987256aeb9967a2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 17:49:18 -0500 Subject: [PATCH 05/10] rename tests --- .../samples/{switch-events => dynamic-component-events}/Bar.html | 0 .../samples/{switch-events => dynamic-component-events}/Foo.html | 0 .../{switch-events => dynamic-component-events}/_config.js | 0 .../samples/{switch-events => dynamic-component-events}/main.html | 0 test/runtime/samples/{switch => dynamic-component}/Bar.html | 0 test/runtime/samples/{switch => dynamic-component}/Foo.html | 0 test/runtime/samples/{switch => dynamic-component}/_config.js | 0 test/runtime/samples/{switch => dynamic-component}/main.html | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename test/runtime/samples/{switch-events => dynamic-component-events}/Bar.html (100%) rename test/runtime/samples/{switch-events => dynamic-component-events}/Foo.html (100%) rename test/runtime/samples/{switch-events => dynamic-component-events}/_config.js (100%) rename test/runtime/samples/{switch-events => dynamic-component-events}/main.html (100%) rename test/runtime/samples/{switch => dynamic-component}/Bar.html (100%) rename test/runtime/samples/{switch => dynamic-component}/Foo.html (100%) rename test/runtime/samples/{switch => dynamic-component}/_config.js (100%) rename test/runtime/samples/{switch => dynamic-component}/main.html (100%) diff --git a/test/runtime/samples/switch-events/Bar.html b/test/runtime/samples/dynamic-component-events/Bar.html similarity index 100% rename from test/runtime/samples/switch-events/Bar.html rename to test/runtime/samples/dynamic-component-events/Bar.html diff --git a/test/runtime/samples/switch-events/Foo.html b/test/runtime/samples/dynamic-component-events/Foo.html similarity index 100% rename from test/runtime/samples/switch-events/Foo.html rename to test/runtime/samples/dynamic-component-events/Foo.html diff --git a/test/runtime/samples/switch-events/_config.js b/test/runtime/samples/dynamic-component-events/_config.js similarity index 100% rename from test/runtime/samples/switch-events/_config.js rename to test/runtime/samples/dynamic-component-events/_config.js diff --git a/test/runtime/samples/switch-events/main.html b/test/runtime/samples/dynamic-component-events/main.html similarity index 100% rename from test/runtime/samples/switch-events/main.html rename to test/runtime/samples/dynamic-component-events/main.html diff --git a/test/runtime/samples/switch/Bar.html b/test/runtime/samples/dynamic-component/Bar.html similarity index 100% rename from test/runtime/samples/switch/Bar.html rename to test/runtime/samples/dynamic-component/Bar.html diff --git a/test/runtime/samples/switch/Foo.html b/test/runtime/samples/dynamic-component/Foo.html similarity index 100% rename from test/runtime/samples/switch/Foo.html rename to test/runtime/samples/dynamic-component/Foo.html diff --git a/test/runtime/samples/switch/_config.js b/test/runtime/samples/dynamic-component/_config.js similarity index 100% rename from test/runtime/samples/switch/_config.js rename to test/runtime/samples/dynamic-component/_config.js diff --git a/test/runtime/samples/switch/main.html b/test/runtime/samples/dynamic-component/main.html similarity index 100% rename from test/runtime/samples/switch/main.html rename to test/runtime/samples/dynamic-component/main.html From dd9ecb8064e0b046893ee7b8ed887688c2393179 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 18:40:53 -0500 Subject: [PATCH 06/10] dynamic component bindings --- src/validate/html/validateElement.ts | 2 +- .../dynamic-component-bindings/Bar.html | 2 ++ .../dynamic-component-bindings/Foo.html | 2 ++ .../dynamic-component-bindings/_config.js | 33 +++++++++++++++++++ .../dynamic-component-bindings/main.html | 12 +++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/dynamic-component-bindings/Bar.html create mode 100644 test/runtime/samples/dynamic-component-bindings/Foo.html create mode 100644 test/runtime/samples/dynamic-component-bindings/_config.js create mode 100644 test/runtime/samples/dynamic-component-bindings/main.html diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 0d67b8002d8f..721a91cb6c48 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -14,7 +14,7 @@ export default function validateElement( elementStack: Node[] ) { const isComponent = - node.name === ':Self' || validator.components.has(node.name); + node.name === ':Self' || node.name === ':Switch' || validator.components.has(node.name); if (!isComponent && /^[A-Z]/.test(node.name[0])) { // TODO upgrade to validator.error in v2 diff --git a/test/runtime/samples/dynamic-component-bindings/Bar.html b/test/runtime/samples/dynamic-component-bindings/Bar.html new file mode 100644 index 000000000000..f609d76ebe9e --- /dev/null +++ b/test/runtime/samples/dynamic-component-bindings/Bar.html @@ -0,0 +1,2 @@ +

bar

+ \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-bindings/Foo.html b/test/runtime/samples/dynamic-component-bindings/Foo.html new file mode 100644 index 000000000000..cae76b057361 --- /dev/null +++ b/test/runtime/samples/dynamic-component-bindings/Foo.html @@ -0,0 +1,2 @@ +

foo

+ \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-bindings/_config.js b/test/runtime/samples/dynamic-component-bindings/_config.js new file mode 100644 index 000000000000..b10e1d720b2f --- /dev/null +++ b/test/runtime/samples/dynamic-component-bindings/_config.js @@ -0,0 +1,33 @@ +export default { + data: { + x: true + }, + + html: ` +

foo

+ + `, + + test(assert, component, target, window) { + let input = target.querySelector('input'); + input.value = 'abc'; + input.dispatchEvent(new window.Event('input')); + + assert.equal(component.get('y'), 'abc'); + + component.set({ + x: false + }); + + assert.htmlEqual(target.innerHTML, ` +

bar

+ + `); + + input = target.querySelector('input'); + input.checked = true; + input.dispatchEvent(new window.Event('change')); + + assert.equal(component.get('z'), true); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-bindings/main.html b/test/runtime/samples/dynamic-component-bindings/main.html new file mode 100644 index 000000000000..179677a19afe --- /dev/null +++ b/test/runtime/samples/dynamic-component-bindings/main.html @@ -0,0 +1,12 @@ +<:Switch { x ? Foo : Bar } bind:y bind:z/> + + \ No newline at end of file From f4e66c0e726022bb646c13b71c38cef25ced361f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 3 Dec 2017 19:00:19 -0500 Subject: [PATCH 07/10] update props of existing dynamic component --- src/generators/dom/visitors/Component.ts | 35 ++++++++++++------- .../Bar.html | 1 + .../Foo.html | 1 + .../_config.js | 19 ++++++++++ .../main.html | 12 +++++++ 5 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 test/runtime/samples/dynamic-component-update-existing-instance/Bar.html create mode 100644 test/runtime/samples/dynamic-component-update-existing-instance/Foo.html create mode 100644 test/runtime/samples/dynamic-component-update-existing-instance/_config.js create mode 100644 test/runtime/samples/dynamic-component-update-existing-instance/main.html diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index a725953ffa0c..eac4fa30aae8 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -75,14 +75,14 @@ export default function visitComponent( const ref = node.attributes.find((a: Node) => a.type === 'Ref'); if (ref) generator.usesRefs = true; + const updates: string[] = []; + if (attributes.length || bindings.length) { const initialProps = attributes .map((attribute: Attribute) => `${attribute.name}: ${attribute.value}`); const initialPropString = stringifyProps(initialProps); - const updates: string[] = []; - attributes .filter((attribute: Attribute) => attribute.dynamic) .forEach((attribute: Attribute) => { @@ -202,15 +202,6 @@ export default function visitComponent( } else if (initialProps.length) { componentInitProperties.push(`data: ${initialPropString}`); } - - if (updates.length) { - block.builders.update.addBlock(deindent` - var ${name}_changes = {}; - ${updates.join('\n')} - ${name}._set(${name}_changes); - ${bindings.length && `${name_updating} = {};`} - `); - } } const isSwitch = node.name === ':Switch'; @@ -303,11 +294,20 @@ export default function visitComponent( else if (#component.refs.${ref.name} === ${name}) { #component.refs.${ref.name} = null; }`} - } else { - // normal update } `); + if (updates.length) { + block.builders.update.addBlock(deindent` + else { + var ${name}_changes = {}; + ${updates.join('\n')} + ${name}._set(${name}_changes); + ${bindings.length && `${name_updating} = {};`} + } + `); + } + if (!state.parentNode) block.builders.unmount.addLine(`if (${name}) ${name}._unmount();`); block.builders.destroy.addLine(`if (${name}) ${name}.destroy(false);`); @@ -339,6 +339,15 @@ export default function visitComponent( `${name}._mount(${state.parentNode || '#target'}, ${state.parentNode ? 'null' : 'anchor'});` ); + if (updates.length) { + block.builders.update.addBlock(deindent` + var ${name}_changes = {}; + ${updates.join('\n')} + ${name}._set(${name}_changes); + ${bindings.length && `${name_updating} = {};`} + `); + } + if (!state.parentNode) block.builders.unmount.addLine(`${name}._unmount();`); block.builders.destroy.addLine(deindent` diff --git a/test/runtime/samples/dynamic-component-update-existing-instance/Bar.html b/test/runtime/samples/dynamic-component-update-existing-instance/Bar.html new file mode 100644 index 000000000000..80328a2e8ad4 --- /dev/null +++ b/test/runtime/samples/dynamic-component-update-existing-instance/Bar.html @@ -0,0 +1 @@ +

Bar {{x}}

\ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-update-existing-instance/Foo.html b/test/runtime/samples/dynamic-component-update-existing-instance/Foo.html new file mode 100644 index 000000000000..fcb10c0f99c5 --- /dev/null +++ b/test/runtime/samples/dynamic-component-update-existing-instance/Foo.html @@ -0,0 +1 @@ +

Foo {{x}}

\ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-update-existing-instance/_config.js b/test/runtime/samples/dynamic-component-update-existing-instance/_config.js new file mode 100644 index 000000000000..d614cf82c5db --- /dev/null +++ b/test/runtime/samples/dynamic-component-update-existing-instance/_config.js @@ -0,0 +1,19 @@ +export default { + data: { + x: 1 + }, + + html: ` +

Foo 1

+ `, + + test(assert, component, target) { + component.set({ + x: 2 + }); + + assert.htmlEqual(target.innerHTML, ` +

Foo 2

+ `); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-update-existing-instance/main.html b/test/runtime/samples/dynamic-component-update-existing-instance/main.html new file mode 100644 index 000000000000..eb39498a5c36 --- /dev/null +++ b/test/runtime/samples/dynamic-component-update-existing-instance/main.html @@ -0,0 +1,12 @@ +<:Switch { x ? Foo : Bar } x='{{x}}'/> + + \ No newline at end of file From e6ef5af6baf352e0d6d6c7fedee923e235132639 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Dec 2017 21:21:56 -0500 Subject: [PATCH 08/10] slotted contents of dynamic components --- src/generators/dom/preprocess.ts | 1 + src/generators/dom/visitors/Component.ts | 31 +++++++++++++ src/generators/dom/visitors/EachBlock.ts | 2 +- .../shared/utils/isChildOfComponent.ts | 2 +- src/validate/html/validateElement.ts | 2 +- .../samples/dynamic-component-slot/Bar.html | 3 ++ .../samples/dynamic-component-slot/Baz.html | 1 + .../samples/dynamic-component-slot/Foo.html | 3 ++ .../samples/dynamic-component-slot/_config.js | 37 +++++++++++++++ .../samples/dynamic-component-slot/main.html | 45 +++++++++++++++++++ 10 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/dynamic-component-slot/Bar.html create mode 100644 test/runtime/samples/dynamic-component-slot/Baz.html create mode 100644 test/runtime/samples/dynamic-component-slot/Foo.html create mode 100644 test/runtime/samples/dynamic-component-slot/_config.js create mode 100644 test/runtime/samples/dynamic-component-slot/main.html diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index a6333c286cd3..1bdedd67954e 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -256,6 +256,7 @@ const preprocessors = { ) => { cannotUseInnerHTML(node); node.var = block.getUniqueName(`each`); + node.iterations = block.getUniqueName(`${node.var}_blocks`); const { dependencies } = node.metadata; block.addDependencies(dependencies); diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index eac4fa30aae8..0e8cb3d69a8d 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -7,6 +7,7 @@ import isDomNode from './shared/isDomNode'; import getTailSnippet from '../../../utils/getTailSnippet'; import getObject from '../../../utils/getObject'; import getExpressionPrecedence from '../../../utils/getExpressionPrecedence'; +import getStaticAttributeValue from '../../../utils/getStaticAttributeValue'; import { stringify } from '../../../utils/stringify'; import stringifyProps from '../../../utils/stringifyProps'; import { Node } from '../../../interfaces'; @@ -281,6 +282,8 @@ export default function visitComponent( if (${switch_vars.value}) { ${name} = new ${switch_vars.value}(${switch_vars.props}(${params})); ${name}._fragment.c(); + + ${node.children.map(child => remount(generator, child, name))} ${name}._mount(${anchor}.parentNode, ${anchor}); ${eventHandlers.map(handler => deindent` @@ -554,4 +557,32 @@ function isComputed(node: Node) { } return false; +} + +function remount(generator: DomGenerator, node: Node, name: string) { + // TODO make this a method of the nodes + + if (node.type === 'Element') { + if (node.name === ':Self' || node.name === ':Switch' || generator.components.has(node.name)) { + return `${node.var}._mount(${name}._slotted.default, null);`; + } + + const slot = node.attributes.find(attribute => attribute.name === 'slot'); + if (slot) { + return `@appendNode(${node.var}, ${name}._slotted.${getStaticAttributeValue(node, 'slot')});`; + } + + return `@appendNode(${node.var}, ${name}._slotted.default);`; + } + + if (node.type === 'Text' || node.type === 'MustacheTag' || node.type === 'RawMustacheTag') { + return `@appendNode(${node.var}, ${name}._slotted.default);`; + } + + if (node.type === 'EachBlock') { + // TODO consider keyed blocks + return `for (var #i = 0; #i < ${node.iterations}.length; #i += 1) ${node.iterations}[#i].m(${name}._slotted.default, null);`; + } + + return `${node.var}.m(${name}._slotted.default, null);`; } \ No newline at end of file diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index fd36e6533d3a..6b659ccf068e 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -18,7 +18,7 @@ export default function visitEachBlock( const create_each_block = node._block.name; const each_block_value = node._block.listName; - const iterations = block.getUniqueName(`${each}_blocks`); + const iterations = node.iterations; const params = block.params.join(', '); const needsAnchor = node.next ? !isDomNode(node.next, generator) : !state.parentNode || !isDomNode(node.parent, generator); diff --git a/src/generators/shared/utils/isChildOfComponent.ts b/src/generators/shared/utils/isChildOfComponent.ts index 2de64c486014..e74b6a658bfc 100644 --- a/src/generators/shared/utils/isChildOfComponent.ts +++ b/src/generators/shared/utils/isChildOfComponent.ts @@ -4,7 +4,7 @@ import Generator from '../../Generator'; export default function isChildOfComponent(node: Node, generator: Generator) { while (node = node.parent) { if (node.type !== 'Element') continue; - if (generator.components.has(node.name)) return true; + if (node.name === ':Self' || node.name === ':Switch' || generator.components.has(node.name)) return true; // TODO extract this out into a helper if (/-/.test(node.name)) return false; } } \ No newline at end of file diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 721a91cb6c48..46e6bb94bf57 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -230,7 +230,7 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s const parent = stack[i]; if (parent.type === 'Element') { // if we're inside a component or a custom element, gravy - if (validator.components.has(parent.name)) return; + if (parent.name === ':Self' || parent.name === ':Switch' || validator.components.has(parent.name)) return; if (/-/.test(parent.name)) return; } diff --git a/test/runtime/samples/dynamic-component-slot/Bar.html b/test/runtime/samples/dynamic-component-slot/Bar.html new file mode 100644 index 000000000000..76cea7cb67e1 --- /dev/null +++ b/test/runtime/samples/dynamic-component-slot/Bar.html @@ -0,0 +1,3 @@ +

Bar

+ + \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-slot/Baz.html b/test/runtime/samples/dynamic-component-slot/Baz.html new file mode 100644 index 000000000000..9c7f92262c33 --- /dev/null +++ b/test/runtime/samples/dynamic-component-slot/Baz.html @@ -0,0 +1 @@ +
baz
\ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-slot/Foo.html b/test/runtime/samples/dynamic-component-slot/Foo.html new file mode 100644 index 000000000000..45e4d0001fa5 --- /dev/null +++ b/test/runtime/samples/dynamic-component-slot/Foo.html @@ -0,0 +1,3 @@ +

Foo

+ + \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-slot/_config.js b/test/runtime/samples/dynamic-component-slot/_config.js new file mode 100644 index 000000000000..1b47436cb1de --- /dev/null +++ b/test/runtime/samples/dynamic-component-slot/_config.js @@ -0,0 +1,37 @@ +export default { + data: { + x: true + }, + + html: ` +

Foo

+
what goes up must come down
+

element

+ you're it +

neither foo nor bar

+ text + a + b + c +
baz
+ `, + + test(assert, component, target) { + component.set({ + x: false + }); + + assert.htmlEqual(target.innerHTML, ` +

Bar

+

element

+ you're it +

neither foo nor bar

+ text + a + b + c +
baz
+
what goes up must come down
+ `); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/dynamic-component-slot/main.html b/test/runtime/samples/dynamic-component-slot/main.html new file mode 100644 index 000000000000..19bd6c6c4496 --- /dev/null +++ b/test/runtime/samples/dynamic-component-slot/main.html @@ -0,0 +1,45 @@ +<:Switch { x ? Foo : Bar } x='{{x}}'> +

element

+ + {{tag}} + + {{#if foo}} +

foo

+ {{elseif bar}} +

bar

+ {{else}} +

neither foo nor bar

+ {{/if}} + + text + + {{#each things as thing}} + {{thing}} + {{/each}} + + + +
what goes up must come down
+ + + \ No newline at end of file From e238c65a153d85980fd4885d24fcb8f5884c75ff Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Dec 2017 21:25:09 -0500 Subject: [PATCH 09/10] rename :Switch -> :Component --- src/generators/Generator.ts | 2 +- src/generators/dom/preprocess.ts | 4 ++-- src/generators/dom/visitors/Component.ts | 4 ++-- src/generators/dom/visitors/Element/Element.ts | 2 +- src/generators/server-side-rendering/visitors/Component.ts | 2 +- src/generators/server-side-rendering/visitors/Element.ts | 2 +- src/generators/shared/utils/isChildOfComponent.ts | 2 +- src/parse/state/tag.ts | 2 +- src/validate/html/validateElement.ts | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 2dcc3f379730..65574ee30372 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -764,7 +764,7 @@ export default class Generator { this.skip(); } - if (node.type === 'Element' && node.name === ':Switch') { + if (node.type === 'Element' && node.name === ':Component') { node.metadata = contextualise(node.expression, contextDependencies, indexes); } }, diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 1bdedd67954e..89af90a36eac 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -437,7 +437,7 @@ const preprocessors = { } const isComponent = - generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch'; + generator.components.has(node.name) || node.name === ':Self' || node.name === ':Component'; if (isComponent) { cannotUseInnerHTML(node); @@ -445,7 +445,7 @@ const preprocessors = { node.var = block.getUniqueName( ( node.name === ':Self' ? generator.name : - node.name === ':Switch' ? 'switch_instance' : + node.name === ':Component' ? 'switch_instance' : node.name ).toLowerCase() ); diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index 0e8cb3d69a8d..7f9cd1661008 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -205,7 +205,7 @@ export default function visitComponent( } } - const isSwitch = node.name === ':Switch'; + const isSwitch = node.name === ':Component'; const switch_vars = isSwitch && { value: block.getUniqueName('switch_value'), @@ -563,7 +563,7 @@ function remount(generator: DomGenerator, node: Node, name: string) { // TODO make this a method of the nodes if (node.type === 'Element') { - if (node.name === ':Self' || node.name === ':Switch' || generator.components.has(node.name)) { + if (node.name === ':Self' || node.name === ':Component' || generator.components.has(node.name)) { return `${node.var}._mount(${name}._slotted.default, null);`; } diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 7dc6eb72626a..3121e24a013e 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -43,7 +43,7 @@ export default function visitElement( } } - if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch') { + if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Component') { return visitComponent(generator, block, state, node, elementStack, componentStack); } diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts index 1689647ba1f5..0c800e1c8b01 100644 --- a/src/generators/server-side-rendering/visitors/Component.ts +++ b/src/generators/server-side-rendering/visitors/Component.ts @@ -71,7 +71,7 @@ export default function visitComponent( ) .join(', '); - const isSwitch = node.name === ':Switch'; + const isSwitch = node.name === ':Component'; if (isSwitch) block.contextualise(node.expression); const expression = ( diff --git a/src/generators/server-side-rendering/visitors/Element.ts b/src/generators/server-side-rendering/visitors/Element.ts index 8834a2b90b8a..25da587a1f9f 100644 --- a/src/generators/server-side-rendering/visitors/Element.ts +++ b/src/generators/server-side-rendering/visitors/Element.ts @@ -40,7 +40,7 @@ export default function visitElement( return; } - if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Switch') { + if (generator.components.has(node.name) || node.name === ':Self' || node.name === ':Component') { visitComponent(generator, block, node); return; } diff --git a/src/generators/shared/utils/isChildOfComponent.ts b/src/generators/shared/utils/isChildOfComponent.ts index e74b6a658bfc..77275f3d8cb6 100644 --- a/src/generators/shared/utils/isChildOfComponent.ts +++ b/src/generators/shared/utils/isChildOfComponent.ts @@ -4,7 +4,7 @@ import Generator from '../../Generator'; export default function isChildOfComponent(node: Node, generator: Generator) { while (node = node.parent) { if (node.type !== 'Element') continue; - if (node.name === ':Self' || node.name === ':Switch' || generator.components.has(node.name)) return true; // TODO extract this out into a helper + if (node.name === ':Self' || node.name === ':Component' || generator.components.has(node.name)) return true; // TODO extract this out into a helper if (/-/.test(node.name)) return false; } } \ No newline at end of file diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index c62053ffb55b..d946656a90ca 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -15,7 +15,7 @@ import { Node } from '../../interfaces'; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const SELF = ':Self'; -const SWITCH = ':Switch'; +const SWITCH = ':Component'; const metaTags = { ':Window': true diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 46e6bb94bf57..e29066be0187 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -14,7 +14,7 @@ export default function validateElement( elementStack: Node[] ) { const isComponent = - node.name === ':Self' || node.name === ':Switch' || validator.components.has(node.name); + node.name === ':Self' || node.name === ':Component' || validator.components.has(node.name); if (!isComponent && /^[A-Z]/.test(node.name[0])) { // TODO upgrade to validator.error in v2 @@ -230,7 +230,7 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s const parent = stack[i]; if (parent.type === 'Element') { // if we're inside a component or a custom element, gravy - if (parent.name === ':Self' || parent.name === ':Switch' || validator.components.has(parent.name)) return; + if (parent.name === ':Self' || parent.name === ':Component' || validator.components.has(parent.name)) return; if (/-/.test(parent.name)) return; } From 0d42ff84db2c55b13e14cf2b04735470e784b7fb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Dec 2017 21:32:22 -0500 Subject: [PATCH 10/10] fix tests, finish renaming stuff --- src/generators/dom/visitors/Component.ts | 8 +++---- .../visitors/Component.ts | 6 ++--- src/parse/state/tag.ts | 6 ++--- .../samples/component-dynamic/input.html | 2 +- .../samples/component-dynamic/output.json | 24 +++++++++---------- .../dynamic-component-bindings/main.html | 2 +- .../dynamic-component-events/main.html | 2 +- .../samples/dynamic-component-slot/main.html | 4 ++-- .../main.html | 2 +- .../samples/dynamic-component/main.html | 2 +- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index 7f9cd1661008..f3487b5a5c8c 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -205,20 +205,20 @@ export default function visitComponent( } } - const isSwitch = node.name === ':Component'; + const isDynamicComponent = node.name === ':Component'; - const switch_vars = isSwitch && { + const switch_vars = isDynamicComponent && { value: block.getUniqueName('switch_value'), props: block.getUniqueName('switch_props') }; const expression = ( node.name === ':Self' ? generator.name : - isSwitch ? switch_vars.value : + isDynamicComponent ? switch_vars.value : `%components-${node.name}` ); - if (isSwitch) { + if (isDynamicComponent) { block.contextualise(node.expression); const { dependencies, snippet } = node.metadata; diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts index 0c800e1c8b01..d6f44abb23c7 100644 --- a/src/generators/server-side-rendering/visitors/Component.ts +++ b/src/generators/server-side-rendering/visitors/Component.ts @@ -71,12 +71,12 @@ export default function visitComponent( ) .join(', '); - const isSwitch = node.name === ':Component'; - if (isSwitch) block.contextualise(node.expression); + const isDynamicComponent = node.name === ':Component'; + if (isDynamicComponent) block.contextualise(node.expression); const expression = ( node.name === ':Self' ? generator.name : - isSwitch ? `((${node.metadata.snippet}) || __missingComponent)` : + isDynamicComponent ? `((${node.metadata.snippet}) || __missingComponent)` : `%components-${node.name}` ); diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index d946656a90ca..90d9f5cd2c67 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -15,7 +15,7 @@ import { Node } from '../../interfaces'; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const SELF = ':Self'; -const SWITCH = ':Component'; +const COMPONENT = ':Component'; const metaTags = { ':Window': true @@ -166,7 +166,7 @@ export default function tag(parser: Parser) { } } - if (name === SWITCH) { + if (name === COMPONENT) { parser.eat('{', true); element.expression = readExpression(parser); parser.allowWhitespace(); @@ -248,7 +248,7 @@ function readTagName(parser: Parser) { return SELF; } - if (parser.eat(SWITCH)) return SWITCH; + if (parser.eat(COMPONENT)) return COMPONENT; const name = parser.readUntil(/(\s|\/|>)/); diff --git a/test/parser/samples/component-dynamic/input.html b/test/parser/samples/component-dynamic/input.html index 1c4e872902b6..0d5be48552ff 100644 --- a/test/parser/samples/component-dynamic/input.html +++ b/test/parser/samples/component-dynamic/input.html @@ -1 +1 @@ -<:Switch {foo ? Foo : Bar}> \ No newline at end of file +<:Component {foo ? Foo : Bar}> \ No newline at end of file diff --git a/test/parser/samples/component-dynamic/output.json b/test/parser/samples/component-dynamic/output.json index 22b32bb932c6..614c28c0a9f7 100644 --- a/test/parser/samples/component-dynamic/output.json +++ b/test/parser/samples/component-dynamic/output.json @@ -1,37 +1,37 @@ { - "hash": 755548012, + "hash": 410218696, "html": { "start": 0, - "end": 37, + "end": 43, "type": "Fragment", "children": [ { "start": 0, - "end": 37, + "end": 43, "type": "Element", - "name": ":Switch", + "name": ":Component", "attributes": [], "children": [], "expression": { "type": "ConditionalExpression", - "start": 10, - "end": 25, + "start": 13, + "end": 28, "test": { "type": "Identifier", - "start": 10, - "end": 13, + "start": 13, + "end": 16, "name": "foo" }, "consequent": { "type": "Identifier", - "start": 16, - "end": 19, + "start": 19, + "end": 22, "name": "Foo" }, "alternate": { "type": "Identifier", - "start": 22, - "end": 25, + "start": 25, + "end": 28, "name": "Bar" } } diff --git a/test/runtime/samples/dynamic-component-bindings/main.html b/test/runtime/samples/dynamic-component-bindings/main.html index 179677a19afe..cb260da71fe2 100644 --- a/test/runtime/samples/dynamic-component-bindings/main.html +++ b/test/runtime/samples/dynamic-component-bindings/main.html @@ -1,4 +1,4 @@ -<:Switch { x ? Foo : Bar } bind:y bind:z/> +<:Component { x ? Foo : Bar } bind:y bind:z/>