From 0bbd917442bcb5518e3b50493c38d50b5432842b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 17 Jun 2017 16:28:43 -0400 Subject: [PATCH] more hydration --- src/generators/dom/Block.ts | 20 +++- src/generators/dom/index.ts | 10 +- .../dom/visitors/Component/Component.ts | 8 +- src/generators/dom/visitors/EachBlock.ts | 94 ++++++++++++------- .../dom/visitors/Element/Attribute.ts | 22 +++-- .../dom/visitors/Element/Element.ts | 39 ++++---- src/generators/dom/visitors/IfBlock.ts | 4 +- src/generators/dom/visitors/RawMustacheTag.ts | 13 ++- src/shared/dom.js | 10 +- test/hydration/index.js | 2 +- test/hydration/samples/component/Nested.html | 1 + test/hydration/samples/component/_after.html | 1 + test/hydration/samples/component/_before.html | 1 + test/hydration/samples/component/_config.js | 17 ++++ test/hydration/samples/component/main.html | 11 +++ test/hydration/samples/each-block/_after.html | 5 + .../hydration/samples/each-block/_before.html | 5 + test/hydration/samples/each-block/_config.js | 29 ++++++ test/hydration/samples/each-block/main.html | 5 + .../element-attribute-added/_after.html | 1 + .../element-attribute-added/_before.html | 1 + .../element-attribute-added/_config.js | 19 ++++ .../samples/element-attribute-added/main.html | 1 + .../element-attribute-changed/_after.html | 1 + .../element-attribute-changed/_before.html | 1 + .../element-attribute-changed/_config.js | 19 ++++ .../element-attribute-changed/main.html | 1 + .../element-attribute-removed/_after.html | 1 + .../element-attribute-removed/_before.html | 1 + .../element-attribute-removed/_config.js | 19 ++++ .../element-attribute-removed/main.html | 1 + .../element-attribute-unchanged/_after.html | 1 + .../element-attribute-unchanged/_before.html | 1 + .../element-attribute-unchanged/_config.js | 15 +++ .../element-attribute-unchanged/main.html | 1 + .../hydration/samples/element-ref/_after.html | 1 + .../samples/element-ref/_before.html | 1 + test/hydration/samples/element-ref/_config.js | 16 ++++ test/hydration/samples/element-ref/main.html | 1 + test/hydration/samples/raw/_after.html | 4 + test/hydration/samples/raw/_before.html | 2 + test/hydration/samples/raw/_config.js | 27 ++++++ test/hydration/samples/raw/main.html | 1 + 43 files changed, 350 insertions(+), 84 deletions(-) create mode 100644 test/hydration/samples/component/Nested.html create mode 100644 test/hydration/samples/component/_after.html create mode 100644 test/hydration/samples/component/_before.html create mode 100644 test/hydration/samples/component/_config.js create mode 100644 test/hydration/samples/component/main.html create mode 100644 test/hydration/samples/each-block/_after.html create mode 100644 test/hydration/samples/each-block/_before.html create mode 100644 test/hydration/samples/each-block/_config.js create mode 100644 test/hydration/samples/each-block/main.html create mode 100644 test/hydration/samples/element-attribute-added/_after.html create mode 100644 test/hydration/samples/element-attribute-added/_before.html create mode 100644 test/hydration/samples/element-attribute-added/_config.js create mode 100644 test/hydration/samples/element-attribute-added/main.html create mode 100644 test/hydration/samples/element-attribute-changed/_after.html create mode 100644 test/hydration/samples/element-attribute-changed/_before.html create mode 100644 test/hydration/samples/element-attribute-changed/_config.js create mode 100644 test/hydration/samples/element-attribute-changed/main.html create mode 100644 test/hydration/samples/element-attribute-removed/_after.html create mode 100644 test/hydration/samples/element-attribute-removed/_before.html create mode 100644 test/hydration/samples/element-attribute-removed/_config.js create mode 100644 test/hydration/samples/element-attribute-removed/main.html create mode 100644 test/hydration/samples/element-attribute-unchanged/_after.html create mode 100644 test/hydration/samples/element-attribute-unchanged/_before.html create mode 100644 test/hydration/samples/element-attribute-unchanged/_config.js create mode 100644 test/hydration/samples/element-attribute-unchanged/main.html create mode 100644 test/hydration/samples/element-ref/_after.html create mode 100644 test/hydration/samples/element-ref/_before.html create mode 100644 test/hydration/samples/element-ref/_config.js create mode 100644 test/hydration/samples/element-ref/main.html create mode 100644 test/hydration/samples/raw/_after.html create mode 100644 test/hydration/samples/raw/_before.html create mode 100644 test/hydration/samples/raw/_config.js create mode 100644 test/hydration/samples/raw/main.html diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 06a6320cc2..f9cd8e8513 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -42,6 +42,7 @@ export default class Block { builders: { init: CodeBuilder; create: CodeBuilder; + claim: CodeBuilder; hydrate: CodeBuilder; mount: CodeBuilder; intro: CodeBuilder; @@ -90,6 +91,7 @@ export default class Block { this.builders = { init: new CodeBuilder(), create: new CodeBuilder(), + claim: new CodeBuilder(), hydrate: new CodeBuilder(), mount: new CodeBuilder(), intro: new CodeBuilder(), @@ -124,7 +126,7 @@ export default class Block { addElement( name: string, renderStatement: string, - hydrateStatement: string, + claimStatement: string, parentNode: string, needsIdentifier = false ) { @@ -132,7 +134,7 @@ export default class Block { this.addVariable(name); this.builders.create.addLine(`${name} = ${renderStatement};`); - this.builders.hydrate.addLine(`${name} = ${hydrateStatement};`) + this.builders.claim.addLine(`${name} = ${claimStatement};`) this.mount(name, parentNode); @@ -235,13 +237,23 @@ export default class Block { properties.addBlock(deindent` create: function () { ${this.builders.create} + ${!this.builders.hydrate.isEmpty() && `this.hydrate();`} }, `); } - if (this.builders.hydrate.isEmpty()) { - properties.addBlock(`hydrate: ${this.generator.helper('noop')},`); + if (this.builders.claim.isEmpty()) { + properties.addBlock(`claim: ${this.generator.helper('noop')},`); } else { + properties.addBlock(deindent` + claim: function ( nodes ) { + ${this.builders.claim} + ${!this.builders.hydrate.isEmpty() && `this.hydrate();`} + }, + `); + } + + if (!this.builders.hydrate.isEmpty()) { properties.addBlock(deindent` hydrate: function ( nodes ) { ${this.builders.hydrate} diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index cc54419cbe..5ba3d8ea3a 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -234,8 +234,14 @@ export default function dom( this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this ); - this._fragment.hydrate( ${generator.helper('children')}( options.target ) ); - this._fragment.mount( options.target, null ); + + if ( options.target ) { + var nodes = ${generator.helper('children')}( options.target ); + this._fragment.claim( nodes ); + nodes.forEach( ${generator.helper('detachNode')} ); + this._fragment.mount( options.target, null ); + } + ${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`} ${(generator.hasComponents || generator.hasIntroTransitions) && diff --git a/src/generators/dom/visitors/Component/Component.ts b/src/generators/dom/visitors/Component/Component.ts index 60c58602fe..8bcd52e6ca 100644 --- a/src/generators/dom/visitors/Component/Component.ts +++ b/src/generators/dom/visitors/Component/Component.ts @@ -129,7 +129,7 @@ export default function visitComponent( const yieldFragment = block.getUniqueName(`${name}_yield_fragment`); - block.builders.create.addLine( + block.builders.init.addLine( `var ${yieldFragment} = ${childBlock.name}( ${params}, ${block.component} );` ); @@ -222,6 +222,10 @@ export default function visitComponent( block.builders.unmount.addLine(`${name}._fragment.unmount();`); block.builders.destroy.addLine(`${name}.destroy( false );`); - block.builders.create.addBlock(local.create); + block.builders.init.addBlock(local.create); + + block.builders.create.addLine(`${name}._fragment.create();`); + block.builders.claim.addLine(`${name}._fragment.claim( ${state.parentNodes} );`); + if (!local.update.isEmpty()) block.builders.update.addBlock(local.update); } diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 25642b1865..1648534438 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -35,7 +35,7 @@ export default function visitEachBlock( const { snippet } = block.contextualise(node.expression); - block.builders.create.addLine(`var ${each_block_value} = ${snippet};`); + block.builders.init.addLine(`var ${each_block_value} = ${snippet};`); if (node.key) { keyed(generator, block, state, node, snippet, vars); @@ -49,6 +49,7 @@ export default function visitEachBlock( block.addElement( anchor, `${generator.helper('createComment')}()`, + `${generator.helper('createComment')}()`, state.parentNode, true ); @@ -59,23 +60,18 @@ export default function visitEachBlock( if (node.else) { const each_block_else = generator.getUniqueName(`${each_block}_else`); - block.builders.create.addLine(`var ${each_block_else} = null;`); + block.builders.init.addLine(`var ${each_block_else} = null;`); // TODO neaten this up... will end up with an empty line in the block - block.builders.create.addBlock(deindent` + block.builders.init.addBlock(deindent` if ( !${each_block_value}.length ) { - ${each_block_else} = ${node.else._block - .name}( ${params}, ${block.component} ); - ${!isToplevel - ? `${each_block_else}.${mountOrIntro}( ${state.parentNode}, null );` - : ''} + ${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); } `); block.builders.mount.addBlock(deindent` if ( ${each_block_else} ) { - ${each_block_else}.${mountOrIntro}( ${state.parentNode || - block.target}, null ); + ${each_block_else}.${mountOrIntro}( ${state.parentNode || block.target}, null ); } `); @@ -86,8 +82,7 @@ export default function visitEachBlock( if ( !${each_block_value}.length && ${each_block_else} ) { ${each_block_else}.update( changed, ${params} ); } else if ( !${each_block_value}.length ) { - ${each_block_else} = ${node.else._block - .name}( ${params}, ${block.component} ); + ${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); ${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} ); } else if ( ${each_block_else} ) { ${each_block_else}.unmount(); @@ -162,6 +157,7 @@ function keyed( node._block.addElement( node._block.first, `${generator.helper('createComment')}()`, + `${generator.helper('createComment')}()`, null, true ); @@ -176,8 +172,6 @@ function keyed( for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { var ${key} = ${each_block_value}[${i}].${node.key}; var ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} ); - ${state.parentNode && - `${iteration}.${mountOrIntro}( ${state.parentNode}, null );`} if ( ${last} ) ${last}.next = ${iteration}; ${iteration}.last = ${last}; @@ -187,15 +181,32 @@ function keyed( } `); - if (!state.parentNode) { - block.builders.mount.addBlock(deindent` - var ${iteration} = ${head}; - while ( ${iteration} ) { - ${iteration}.${mountOrIntro}( ${block.target}, anchor ); - ${iteration} = ${iteration}.next; - } - `); - } + const targetNode = state.parentNode || block.target; + const anchorNode = state.parentNode ? 'null' : 'anchor'; + + block.builders.create.addBlock(deindent` + var ${iteration} = ${head}; + while ( ${iteration} ) { + ${iteration}.create(); + ${iteration} = ${iteration}.next; + } + `); + + block.builders.claim.addBlock(deindent` + var ${iteration} = ${head}; + while ( ${iteration} ) { + ${iteration}.claim( ${state.parentNodes} ); + ${iteration} = ${iteration}.next; + } + `); + + block.builders.mount.addBlock(deindent` + var ${iteration} = ${head}; + while ( ${iteration} ) { + ${iteration}.${mountOrIntro}( ${targetNode}, ${anchorNode} ); + ${iteration} = ${iteration}.next; + } + `); const dynamic = node._block.hasUpdateMethod; const parentNode = state.parentNode || `${anchor}.parentNode`; @@ -203,7 +214,7 @@ function keyed( let destroy; if (node._block.hasOutroMethod) { const fn = block.getUniqueName(`${each_block}_outro`); - block.builders.create.addBlock(deindent` + block.builders.init.addBlock(deindent` function ${fn} ( iteration ) { iteration.outro( function () { iteration.unmount(); @@ -227,7 +238,7 @@ function keyed( `; } else { const fn = block.getUniqueName(`${each_block}_destroy`); - block.builders.create.addBlock(deindent` + block.builders.init.addBlock(deindent` function ${fn} ( iteration ) { iteration.unmount(); iteration.destroy(); @@ -353,27 +364,38 @@ function unkeyed( mountOrIntro, } ) { - block.builders.create.addBlock(deindent` + block.builders.init.addBlock(deindent` var ${iterations} = []; for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { ${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} ); - ${state.parentNode && - `${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`} } `); - if (!state.parentNode) { - block.builders.mount.addBlock(deindent` - for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { - ${iterations}[${i}].${mountOrIntro}( ${block.target}, anchor ); - } - `); - } + const targetNode = state.parentNode || block.target; + const anchorNode = state.parentNode ? 'null' : 'anchor'; + + block.builders.create.addBlock(deindent` + for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { + ${iterations}[${i}].create(); + } + `); + + block.builders.claim.addBlock(deindent` + for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { + ${iterations}[${i}].claim( ${state.parentNodes} ); + } + `); + + block.builders.mount.addBlock(deindent` + for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { + ${iterations}[${i}].${mountOrIntro}( ${targetNode}, ${anchorNode} ); + } + `); const dependencies = block.findDependencies(node.expression); const allDependencies = new Set(node._block.dependencies); - dependencies.forEach(dependency => { + dependencies.forEach((dependency: string) => { allDependencies.add(dependency); }); diff --git a/src/generators/dom/visitors/Element/Attribute.ts b/src/generators/dom/visitors/Element/Attribute.ts index 14f68b5a5e..97d2bfe8be 100644 --- a/src/generators/dom/visitors/Element/Attribute.ts +++ b/src/generators/dom/visitors/Element/Attribute.ts @@ -22,7 +22,11 @@ export default function visitAttribute( const isIndirectlyBoundValue = name === 'value' && (node.name === 'option' || // TODO check it's actually bound - (node.name === 'input' && node.attributes.find((attribute: Node) => attribute.type === 'Binding' && /checked|group/.test(attribute.name)))); + (node.name === 'input' && + node.attributes.find( + (attribute: Node) => + attribute.type === 'Binding' && /checked|group/.test(attribute.name) + ))); const propertyName = isIndirectlyBoundValue ? '__value' @@ -76,7 +80,7 @@ export default function visitAttribute( // annoying special case const isMultipleSelect = node.name === 'select' && - node.attributes.find(attr => attr.name.toLowerCase() === 'multiple'); // TODO use getStaticAttributeValue + node.attributes.find((attr: Node) => attr.name.toLowerCase() === 'multiple'); // TODO use getStaticAttributeValue const i = block.getUniqueName('i'); const option = block.getUniqueName('option'); @@ -97,17 +101,17 @@ export default function visitAttribute( } `; - block.builders.create.addLine(deindent` + block.builders.hydrate.addLine(deindent` ${last} = ${value} ${updater} `); } else if (propertyName) { - block.builders.create.addLine( + block.builders.hydrate.addLine( `${state.parentNode}.${propertyName} = ${last} = ${value};` ); updater = `${state.parentNode}.${propertyName} = ${last};`; } else { - block.builders.create.addLine( + block.builders.hydrate.addLine( `${generator.helper( method )}( ${state.parentNode}, '${name}', ${last} = ${value} );` @@ -132,10 +136,10 @@ export default function visitAttribute( const statement = propertyName ? `${state.parentNode}.${propertyName} = ${value};` : `${generator.helper( - method - )}( ${state.parentNode}, '${name}', ${value} );`; + method + )}( ${state.parentNode}, '${name}', ${value} );`; - block.builders.create.addLine(statement); + block.builders.hydrate.addLine(statement); // special case – autofocus. has to be handled in a bit of a weird way if (attribute.value === true && name === 'autofocus') { @@ -152,7 +156,7 @@ export default function visitAttribute( if (isIndirectlyBoundValue) { const updateValue = `${state.parentNode}.value = ${state.parentNode}.__value;`; - block.builders.create.addLine(updateValue); + block.builders.hydrate.addLine(updateValue); if (isDynamic) block.builders.update.addLine(updateValue); } } diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 7d7694733a..14b92e5bae 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -51,9 +51,9 @@ export default function visitElement( block.addVariable(name); block.builders.create.addLine(`${name} = ${getRenderStatement(generator, childState.namespace, node.name)};`); - block.builders.hydrate.addBlock(deindent` - ${name} = ${getHydrateStatement(generator, childState.namespace, state.parentNodes, node.name)}; - var ${childState.parentNodes} = ${generator.helper('children')}( ${name} ) + block.builders.claim.addBlock(deindent` + ${name} = ${getClaimStatement(generator, childState.namespace, state.parentNodes, node)}; + var ${childState.parentNodes} = ${generator.helper('children')}( ${name} ); `); if (state.parentNode) { @@ -62,12 +62,6 @@ export default function visitElement( block.builders.mount.addLine(`${block.generator.helper('insertNode')}( ${name}, ${block.target}, anchor );`); } - if (isToplevel) { - block.builders.unmount.addLine( - `${block.generator.helper('detachNode')}( ${name} );` - ); - } - // block.addVariable(name); // block.builders.create.addLine( @@ -78,8 +72,8 @@ export default function visitElement( // )};` // ); - // block.builders.hydrate.addLine( - // `${name} = ${getHydrateStatement( + // block.builders.claim.addLine( + // `${name} = ${getClaimStatement( // generator, // childState.namespace, // node.name @@ -88,7 +82,7 @@ export default function visitElement( // add CSS encapsulation attribute if (generator.cssId && (!generator.cascade || state.isTopLevel)) { - block.builders.create.addLine( + block.builders.hydrate.addLine( `${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );` @@ -150,7 +144,7 @@ export default function visitElement( } } - if (!state.parentNode) { + if (isToplevel) { // TODO we eventually need to consider what happens to elements // that belong to the same outgroup as an outroing element... block.builders.unmount.addLine( @@ -206,6 +200,10 @@ export default function visitElement( if (node.initialUpdate) { block.builders.create.addBlock(node.initialUpdate); } + + block.builders.claim.addLine( + `${childState.parentNodes}.forEach( ${generator.helper('detachNode')} );` + ); } function getRenderStatement( @@ -224,19 +222,24 @@ function getRenderStatement( return `${generator.helper('createElement')}( '${name}' )`; } -function getHydrateStatement( +function getClaimStatement( generator: DomGenerator, namespace: string, nodes: string, - name: string + node: Node ) { if (namespace === 'http://www.w3.org/2000/svg') { - return `${generator.helper('claimSvgElement')}( '${name}' )`; + return `${generator.helper('claimSvgElement')}( '${node.name}' )`; } if (namespace) { - throw new Error('TODO hydrate exotic namespaces'); + throw new Error('TODO claim exotic namespaces'); } - return `${generator.helper('claimElement')}( ${nodes}, '${name.toUpperCase()}' )`; + const attributes = node.attributes + .filter((attr: Node) => attr.type === 'Attribute') + .map((attr: Node) => `${attr.name}: true`) + .join(', '); + + return `${generator.helper('claimElement')}( ${nodes}, '${node.name.toUpperCase()}', ${attributes ? `{ ${attributes} }` : `{}`} )`; } \ No newline at end of file diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index 5a40541031..8678537ed6 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -109,8 +109,8 @@ export default function visitIfBlock( `${name}.create();` ); - block.builders.hydrate.addLine( - `${name}.hydrate( ${state.parentNodes} );` + block.builders.claim.addLine( + `${name}.claim( ${state.parentNodes} );` ); if (node.needsAnchor) { diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index 93c4aabace..ff487302b3 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -17,34 +17,33 @@ export default function visitRawMustacheTag( const { snippet } = block.contextualise(node.expression); + block.addVariable(value); + // we would have used comments here, but the `insertAdjacentHTML` api only // exists for `Element`s. block.addElement( before, `${generator.helper('createElement')}( 'noscript' )`, + `${generator.helper('createElement')}( 'noscript' )`, state.parentNode, true ); block.addElement( after, `${generator.helper('createElement')}( 'noscript' )`, + `${generator.helper('createElement')}( 'noscript' )`, state.parentNode, true ); const isToplevel = !state.parentNode; - block.builders.create.addLine(`var ${value} = ${snippet};`); - const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} );`; + const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} = ${snippet} );`; const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`; - if (isToplevel) { - block.builders.mount.addLine(mountStatement); - } else { - block.builders.create.addLine(mountStatement); - } + block.builders.mount.addLine(mountStatement); block.builders.update.addBlock(deindent` if ( ${value} !== ( ${value} = ${snippet} ) ) { diff --git a/src/shared/dom.js b/src/shared/dom.js index 11d5395b89..f87bdd8bdd 100644 --- a/src/shared/dom.js +++ b/src/shared/dom.js @@ -67,14 +67,18 @@ export function toNumber(value) { return value === '' ? undefined : +value; } -export function children ( element ) { +export function children (element) { return Array.from(element.childNodes); } -export function claimElement ( nodes, name ) { +export function claimElement (nodes, name, attributes) { for (var i = 0; i < nodes.length; i += 1) { var node = nodes[i]; if (node.nodeName === name) { + for (var j = 0; j < node.attributes.length; j += 1) { + var attribute = node.attributes[j]; + if (!attributes[attribute.name]) node.removeAttribute(attribute.name); + } return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes } } @@ -83,7 +87,7 @@ export function claimElement ( nodes, name ) { return createElement(name); } -export function claimText ( nodes, data ) { +export function claimText (nodes, data) { for (var i = 0; i < nodes.length; i += 1) { var node = nodes[i]; if (node.nodeType === 3) { diff --git a/test/hydration/index.js b/test/hydration/index.js index 86df95e1c8..3f8ece97e8 100644 --- a/test/hydration/index.js +++ b/test/hydration/index.js @@ -82,7 +82,7 @@ describe.only('hydration', () => { assert.htmlEqual(target.innerHTML, fs.readFileSync(`${cwd}/_after.html`, 'utf-8')); if (config.test) { - config.test(assert, target, snapshot); + config.test(assert, target, snapshot, component); } else { component.destroy(); assert.equal(target.innerHTML, ''); diff --git a/test/hydration/samples/component/Nested.html b/test/hydration/samples/component/Nested.html new file mode 100644 index 0000000000..70bf63ad9d --- /dev/null +++ b/test/hydration/samples/component/Nested.html @@ -0,0 +1 @@ +

nested

\ No newline at end of file diff --git a/test/hydration/samples/component/_after.html b/test/hydration/samples/component/_after.html new file mode 100644 index 0000000000..70bf63ad9d --- /dev/null +++ b/test/hydration/samples/component/_after.html @@ -0,0 +1 @@ +

nested

\ No newline at end of file diff --git a/test/hydration/samples/component/_before.html b/test/hydration/samples/component/_before.html new file mode 100644 index 0000000000..70bf63ad9d --- /dev/null +++ b/test/hydration/samples/component/_before.html @@ -0,0 +1 @@ +

nested

\ No newline at end of file diff --git a/test/hydration/samples/component/_config.js b/test/hydration/samples/component/_config.js new file mode 100644 index 0000000000..c26b3c7ad0 --- /dev/null +++ b/test/hydration/samples/component/_config.js @@ -0,0 +1,17 @@ +export default { + snapshot(target) { + const p = target.querySelector('p'); + + return { + p, + text: p.childNodes[0] + }; + }, + + test(assert, target, snapshot) { + const p = target.querySelector('p'); + + assert.equal(p, snapshot.p); + assert.equal(p.childNodes[0], snapshot.text); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/component/main.html b/test/hydration/samples/component/main.html new file mode 100644 index 0000000000..c669ca09a8 --- /dev/null +++ b/test/hydration/samples/component/main.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/hydration/samples/each-block/_after.html b/test/hydration/samples/each-block/_after.html new file mode 100644 index 0000000000..36cd79642a --- /dev/null +++ b/test/hydration/samples/each-block/_after.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/hydration/samples/each-block/_before.html b/test/hydration/samples/each-block/_before.html new file mode 100644 index 0000000000..36cd79642a --- /dev/null +++ b/test/hydration/samples/each-block/_before.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/hydration/samples/each-block/_config.js b/test/hydration/samples/each-block/_config.js new file mode 100644 index 0000000000..065ae25cb9 --- /dev/null +++ b/test/hydration/samples/each-block/_config.js @@ -0,0 +1,29 @@ +export default { + data: { + things: [ + 'animal', + 'vegetable', + 'mineral' + ] + }, + + snapshot(target) { + const ul = target.querySelector('ul'); + const lis = ul.querySelectorAll('li'); + + return { + ul, + lis + }; + }, + + test(assert, target, snapshot) { + const ul = target.querySelector('ul'); + const lis = ul.querySelectorAll('li'); + + assert.equal(ul, snapshot.ul); + assert.equal(lis[0], snapshot.lis[0]); + assert.equal(lis[1], snapshot.lis[1]); + assert.equal(lis[2], snapshot.lis[2]); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/each-block/main.html b/test/hydration/samples/each-block/main.html new file mode 100644 index 0000000000..821f6c53e2 --- /dev/null +++ b/test/hydration/samples/each-block/main.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/hydration/samples/element-attribute-added/_after.html b/test/hydration/samples/element-attribute-added/_after.html new file mode 100644 index 0000000000..8d93bba36d --- /dev/null +++ b/test/hydration/samples/element-attribute-added/_after.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-added/_before.html b/test/hydration/samples/element-attribute-added/_before.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-added/_before.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-added/_config.js b/test/hydration/samples/element-attribute-added/_config.js new file mode 100644 index 0000000000..a1cfe933c2 --- /dev/null +++ b/test/hydration/samples/element-attribute-added/_config.js @@ -0,0 +1,19 @@ +export default { + data: { + class: 'bar' + }, + + snapshot(target) { + const div = target.querySelector('div'); + + return { + div + }; + }, + + test(assert, target, snapshot) { + const div = target.querySelector('div'); + + assert.equal(div, snapshot.div); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/element-attribute-added/main.html b/test/hydration/samples/element-attribute-added/main.html new file mode 100644 index 0000000000..6b149d165f --- /dev/null +++ b/test/hydration/samples/element-attribute-added/main.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-changed/_after.html b/test/hydration/samples/element-attribute-changed/_after.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-changed/_after.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-changed/_before.html b/test/hydration/samples/element-attribute-changed/_before.html new file mode 100644 index 0000000000..281c6866c3 --- /dev/null +++ b/test/hydration/samples/element-attribute-changed/_before.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-changed/_config.js b/test/hydration/samples/element-attribute-changed/_config.js new file mode 100644 index 0000000000..a1cfe933c2 --- /dev/null +++ b/test/hydration/samples/element-attribute-changed/_config.js @@ -0,0 +1,19 @@ +export default { + data: { + class: 'bar' + }, + + snapshot(target) { + const div = target.querySelector('div'); + + return { + div + }; + }, + + test(assert, target, snapshot) { + const div = target.querySelector('div'); + + assert.equal(div, snapshot.div); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/element-attribute-changed/main.html b/test/hydration/samples/element-attribute-changed/main.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-changed/main.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-removed/_after.html b/test/hydration/samples/element-attribute-removed/_after.html new file mode 100644 index 0000000000..281c6866c3 --- /dev/null +++ b/test/hydration/samples/element-attribute-removed/_after.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-removed/_before.html b/test/hydration/samples/element-attribute-removed/_before.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-removed/_before.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-removed/_config.js b/test/hydration/samples/element-attribute-removed/_config.js new file mode 100644 index 0000000000..a1cfe933c2 --- /dev/null +++ b/test/hydration/samples/element-attribute-removed/_config.js @@ -0,0 +1,19 @@ +export default { + data: { + class: 'bar' + }, + + snapshot(target) { + const div = target.querySelector('div'); + + return { + div + }; + }, + + test(assert, target, snapshot) { + const div = target.querySelector('div'); + + assert.equal(div, snapshot.div); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/element-attribute-removed/main.html b/test/hydration/samples/element-attribute-removed/main.html new file mode 100644 index 0000000000..281c6866c3 --- /dev/null +++ b/test/hydration/samples/element-attribute-removed/main.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-unchanged/_after.html b/test/hydration/samples/element-attribute-unchanged/_after.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-unchanged/_after.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-unchanged/_before.html b/test/hydration/samples/element-attribute-unchanged/_before.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-unchanged/_before.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-attribute-unchanged/_config.js b/test/hydration/samples/element-attribute-unchanged/_config.js new file mode 100644 index 0000000000..d41f5db0fb --- /dev/null +++ b/test/hydration/samples/element-attribute-unchanged/_config.js @@ -0,0 +1,15 @@ +export default { + snapshot(target) { + const div = target.querySelector('div'); + + return { + div + }; + }, + + test(assert, target, snapshot) { + const div = target.querySelector('div'); + + assert.equal(div, snapshot.div); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/element-attribute-unchanged/main.html b/test/hydration/samples/element-attribute-unchanged/main.html new file mode 100644 index 0000000000..c6a8a8c95d --- /dev/null +++ b/test/hydration/samples/element-attribute-unchanged/main.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/hydration/samples/element-ref/_after.html b/test/hydration/samples/element-ref/_after.html new file mode 100644 index 0000000000..efe5048cbc --- /dev/null +++ b/test/hydration/samples/element-ref/_after.html @@ -0,0 +1 @@ +

Hello world!

\ No newline at end of file diff --git a/test/hydration/samples/element-ref/_before.html b/test/hydration/samples/element-ref/_before.html new file mode 100644 index 0000000000..efe5048cbc --- /dev/null +++ b/test/hydration/samples/element-ref/_before.html @@ -0,0 +1 @@ +

Hello world!

\ No newline at end of file diff --git a/test/hydration/samples/element-ref/_config.js b/test/hydration/samples/element-ref/_config.js new file mode 100644 index 0000000000..21743569bd --- /dev/null +++ b/test/hydration/samples/element-ref/_config.js @@ -0,0 +1,16 @@ +export default { + snapshot(target) { + const h1 = target.querySelector('h1'); + + return { + h1, + }; + }, + + test(assert, target, snapshot, component) { + const h1 = target.querySelector('h1'); + + assert.equal(h1, snapshot.h1); + assert.equal(component.refs.h1, h1); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/element-ref/main.html b/test/hydration/samples/element-ref/main.html new file mode 100644 index 0000000000..95c0299762 --- /dev/null +++ b/test/hydration/samples/element-ref/main.html @@ -0,0 +1 @@ +

Hello world!

\ No newline at end of file diff --git a/test/hydration/samples/raw/_after.html b/test/hydration/samples/raw/_after.html new file mode 100644 index 0000000000..47214db3c9 --- /dev/null +++ b/test/hydration/samples/raw/_after.html @@ -0,0 +1,4 @@ + +

this is some html

+

and so is this

+ \ No newline at end of file diff --git a/test/hydration/samples/raw/_before.html b/test/hydration/samples/raw/_before.html new file mode 100644 index 0000000000..b68c0e317e --- /dev/null +++ b/test/hydration/samples/raw/_before.html @@ -0,0 +1,2 @@ +

this is some html

+

and so is this

\ No newline at end of file diff --git a/test/hydration/samples/raw/_config.js b/test/hydration/samples/raw/_config.js new file mode 100644 index 0000000000..e1108714ee --- /dev/null +++ b/test/hydration/samples/raw/_config.js @@ -0,0 +1,27 @@ +export default { + skip: true, // existing nodes are blown away + + data: { + raw: `

this is some html

and so is this

` + }, + + snapshot(target) { + const ps = target.querySelectorAll('p'); + + return { + p0: ps[0], + text0: ps[0].firstChild, + p1: ps[1], + text1: ps[1].firstChild + }; + }, + + test(assert, target, snapshot) { + const ps = target.querySelectorAll('p'); + + assert.equal(ps[0], snapshot.p0); + assert.equal(ps[0].firstChild, snapshot.text0); + assert.equal(ps[1], snapshot.p1); + assert.equal(ps[1].firstChild, snapshot.text1); + } +}; \ No newline at end of file diff --git a/test/hydration/samples/raw/main.html b/test/hydration/samples/raw/main.html new file mode 100644 index 0000000000..7523dfd4de --- /dev/null +++ b/test/hydration/samples/raw/main.html @@ -0,0 +1 @@ +{{{raw}}} \ No newline at end of file