diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 002e52bbf9..7798cf3c86 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -125,7 +125,8 @@ export default class Generator { ) { this.addSourcemapLocations(expression); - const usedContexts: string[] = []; + const usedContexts = new Set(); + const usedIndexes = new Set(); const { code, helpers } = this; const { contexts, indexes } = block; @@ -168,12 +169,13 @@ export default class Generator { ); } - if (!~usedContexts.indexOf(name)) usedContexts.push(name); + usedContexts.add(name); } else if (helpers.has(name)) { code.prependRight(node.start, `${self.alias('template')}.helpers.`); } else if (indexes.has(name)) { const context = indexes.get(name); - if (!~usedContexts.indexOf(context)) usedContexts.push(context); + usedContexts.add(context); // TODO is this right? + usedIndexes.add(name); } else { // handle shorthand properties if (parent && parent.type === 'Property' && parent.shorthand) { @@ -193,7 +195,7 @@ export default class Generator { code.prependRight(node.start, `state.`); } - if (!~usedContexts.indexOf('state')) usedContexts.push('state'); + usedContexts.add('state'); } this.skip(); @@ -221,6 +223,7 @@ export default class Generator { return { dependencies: Array.from(dependencies), contexts: usedContexts, + indexes: usedIndexes, snippet: `[✂${expression.start}-${expression.end}✂]`, }; } diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index f851727276..0cfce0924a 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -12,6 +12,7 @@ export interface BlockOptions { key?: string; contexts?: Map; indexes?: Map; + changeableIndexes?: Map; contextDependencies?: Map; params?: string[]; indexNames?: Map; @@ -32,6 +33,7 @@ export default class Block { contexts: Map; indexes: Map; + changeableIndexes: Map; contextDependencies: Map; dependencies: Set; params: string[]; @@ -77,6 +79,7 @@ export default class Block { this.contexts = options.contexts; this.indexes = options.indexes; + this.changeableIndexes = options.changeableIndexes; this.contextDependencies = options.contextDependencies; this.dependencies = new Set(); @@ -121,8 +124,7 @@ export default class Block { name: string, renderStatement: string, claimStatement: string, - parentNode: string, - needsIdentifier = false + parentNode: string ) { const isToplevel = !parentNode; @@ -195,7 +197,7 @@ export default class Block { let outroing; const hasOutros = !this.builders.outro.isEmpty(); if (hasOutros) { - outroing = this.getUniqueName('outroing'); + outroing = this.alias('outroing'); this.addVariable(outroing); } diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index 2ffae2ee2f..2513623c49 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -74,10 +74,9 @@ export default function dom( }); const builder = new CodeBuilder(); + const computationBuilder = new CodeBuilder(); if (computations.length) { - const computationBuilder = new CodeBuilder(); - computations.forEach(({ key, deps }) => { if (generator.readonly.has(key)) { // <:Window> bindings @@ -88,48 +87,16 @@ export default function dom( generator.readonly.add(key); - const condition = `isInitial || ${deps - .map( - dep => - `( '${dep}' in newState && @differs( state.${dep}, oldState.${dep} ) )` - ) - .join(' || ')}`; - const statement = `state.${key} = newState.${key} = @template.computed.${key}( ${deps + const condition = `isInitial || ${deps.map(dep => `changed.${dep}`).join(' || ')}`; + + const statement = `if ( @differs( ( state.${key} = @template.computed.${key}( ${deps .map(dep => `state.${dep}`) - .join(', ')} );`; + .join(', ')} ) ), oldState.${key} ) ) changed.${key} = true;`; - computationBuilder.addConditionalLine(condition, statement); + computationBuilder.addConditional(condition, statement); }); - - builder.addBlock(deindent` - function @recompute ( state, newState, oldState, isInitial ) { - ${computationBuilder} - } - `); } - const _set = deindent` - ${options.dev && - deindent` - if ( typeof newState !== 'object' ) { - throw new Error( 'Component .set was called without an object of data key-values to update.' ); - } - - ${Array.from(generator.readonly).map( - prop => - `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` - )} - `} - - var oldState = this._state; - this._state = @assign( {}, oldState, newState ); - ${computations.length && - `@recompute( this._state, newState, oldState, false )`} - @dispatchObservers( this, this._observers.pre, newState, oldState ); - ${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`} - @dispatchObservers( this, this._observers.post, newState, oldState ); - `; - if (hasJs) { builder.addBlock(`[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`); } @@ -174,7 +141,7 @@ export default function dom( ? `@proto ` : deindent` { - ${['destroy', 'get', 'fire', 'observe', 'on', 'set', 'teardown'] + ${['destroy', 'get', 'fire', 'observe', 'on', 'set', '_set', 'teardown'] .map(n => `${n}: @${n === 'teardown' ? 'destroy' : n}`) .join(',\n')} }`; @@ -190,7 +157,7 @@ export default function dom( ? `@assign( @template.data(), options.data )` : `options.data || {}`}; ${generator.metaBindings} - ${computations.length && `@recompute( this._state, this._state, {}, true );`} + ${computations.length && `this._recompute( {}, this._state, {}, true );`} ${options.dev && Array.from(generator.expectedProperties).map( prop => @@ -259,9 +226,20 @@ export default function dom( @assign( ${prototypeBase}, ${proto}); - ${name}.prototype._set = function _set ( newState ) { - ${_set} - }; + ${options.dev && deindent` + ${name}.prototype._checkReadOnly = function _checkReadOnly ( newState ) { + ${Array.from(generator.readonly).map( + prop => + `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` + )} + }; + `} + + ${computations.length ? deindent` + ${name}.prototype._recompute = function _recompute ( changed, state, oldState, isInitial ) { + ${computationBuilder} + } + ` : (!sharedPath && `${name}.prototype._recompute = @noop;`)} ${templateProperties.setup && `@template.setup( ${name} );`} `); diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 1811bc762f..9b784bc558 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -189,7 +189,10 @@ const preprocessors = { contexts.set(node.context, context); const indexes = new Map(block.indexes); - if (node.index) indexes.set(indexName, node.context); + if (node.index) indexes.set(node.index, node.context); + + const changeableIndexes = new Map(block.changeableIndexes); + if (node.index) changeableIndexes.set(node.index, node.key); const contextDependencies = new Map(block.contextDependencies); contextDependencies.set(node.context, dependencies); @@ -203,6 +206,7 @@ const preprocessors = { contextDependencies, contexts, indexes, + changeableIndexes, listName, indexName, @@ -274,6 +278,11 @@ const preprocessors = { } } }); + } else if (attribute.type === 'EventHandler' && attribute.expression) { + attribute.expression.arguments.forEach((arg: Node) => { + const dependencies = block.findDependencies(arg); + block.addDependencies(dependencies); + }); } else if (attribute.type === 'Binding') { const dependencies = block.findDependencies(attribute.value); block.addDependencies(dependencies); @@ -444,6 +453,7 @@ export default function preprocess( contexts: new Map(), indexes: new Map(), + changeableIndexes: new Map(), contextDependencies: new Map(), params: ['state'], @@ -462,7 +472,7 @@ export default function preprocess( generator.blocks.push(block); preprocessChildren(generator, block, state, node, false, [], true, null); - block.hasUpdateMethod = block.dependencies.size > 0; + block.hasUpdateMethod = true; return { block, state }; } diff --git a/src/generators/dom/visitors/Component/Binding.ts b/src/generators/dom/visitors/Component/Binding.ts index e87ab0bff1..75cdd637e3 100644 --- a/src/generators/dom/visitors/Component/Binding.ts +++ b/src/generators/dom/visitors/Component/Binding.ts @@ -89,7 +89,7 @@ export default function visitBinding( local.update.addBlock(deindent` if ( !${updating} && ${dependencies - .map(dependency => `'${dependency}' in changed`) + .map(dependency => `changed.${dependency}`) .join(' || ')} ) { ${updating} = true; ${local.name}._set({ ${attribute.name}: ${snippet} }); diff --git a/src/generators/dom/visitors/Component/Component.ts b/src/generators/dom/visitors/Component/Component.ts index a9fff4ee39..0e966cdc0f 100644 --- a/src/generators/dom/visitors/Component/Component.ts +++ b/src/generators/dom/visitors/Component/Component.ts @@ -195,10 +195,8 @@ export default function visitComponent( if (attribute.dependencies.length) { return deindent` if ( ${attribute.dependencies - .map(dependency => `'${dependency}' in changed`) - .join( - '||' - )} ) ${name}_changes.${attribute.name} = ${attribute.value}; + .map(dependency => `changed.${dependency}`) + .join(' || ')} ) ${name}_changes.${attribute.name} = ${attribute.value}; `; } @@ -212,7 +210,7 @@ export default function visitComponent( ${updates.join('\n')} - if ( Object.keys( ${name}_changes ).length ) ${name}._set( ${name}_changes ); + ${name}._set( ${name}_changes ); `); } diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 2ad8117c36..1c8a9c9346 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -57,8 +57,7 @@ export default function visitEachBlock( anchor, `@createComment()`, `@createComment()`, - state.parentNode, - true + state.parentNode ); } else if (node.next) { node.next.usedAsAnchor = true; @@ -172,8 +171,7 @@ function keyed( node._block.first, `@createComment()`, `@createComment()`, - null, - true + null ); } @@ -411,7 +409,7 @@ function unkeyed( // TODO do this for keyed blocks as well const condition = Array.from(allDependencies) - .map(dependency => `'${dependency}' in changed`) + .map(dependency => `changed.${dependency}`) .join(' || '); const parentNode = state.parentNode || `${anchor}.parentNode`; diff --git a/src/generators/dom/visitors/Element/Attribute.ts b/src/generators/dom/visitors/Element/Attribute.ts index b03c379482..5ee1afdea7 100644 --- a/src/generators/dom/visitors/Element/Attribute.ts +++ b/src/generators/dom/visitors/Element/Attribute.ts @@ -47,10 +47,28 @@ export default function visitAttribute( if (isDynamic) { let value; + const allDependencies = new Set(); + let shouldCache; + let hasChangeableIndex; + + // TODO some of this code is repeated in Tag.ts — would be good to + // DRY it out if that's possible without introducing crazy indirection if (attribute.value.length === 1) { // single {{tag}} — may be a non-string - const { snippet } = block.contextualise(attribute.value[0].expression); + const { expression } = attribute.value[0]; + const { snippet, dependencies, indexes } = block.contextualise(expression); value = snippet; + dependencies.forEach(d => { + allDependencies.add(d); + }); + + hasChangeableIndex = Array.from(indexes).some(index => block.changeableIndexes.get(index)); + + shouldCache = ( + expression.type !== 'Identifier' || + block.contexts.has(expression.name) || + hasChangeableIndex + ); } else { // '{{foo}} {{bar}}' — treat as string concatenation value = @@ -60,22 +78,35 @@ export default function visitAttribute( if (chunk.type === 'Text') { return stringify(chunk.data); } else { - const { snippet } = block.contextualise(chunk.expression); + const { snippet, dependencies, indexes } = block.contextualise(chunk.expression); + + if (Array.from(indexes).some(index => block.changeableIndexes.get(index))) { + hasChangeableIndex = true; + } + + dependencies.forEach(d => { + allDependencies.add(d); + }); + return `( ${snippet} )`; } }) .join(' + '); + + shouldCache = true; } - const last = block.getUniqueName( + const isSelectValueAttribute = + name === 'value' && state.parentNodeName === 'select'; + + const last = (shouldCache || isSelectValueAttribute) && block.getUniqueName( `${state.parentNode}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` ); - block.addVariable(last); - const isSelectValueAttribute = - name === 'value' && state.parentNodeName === 'select'; + if (shouldCache || isSelectValueAttribute) block.addVariable(last); let updater; + const init = shouldCache ? `${last} = ${value}` : value; if (isSelectValueAttribute) { // annoying special case @@ -104,27 +135,42 @@ export default function visitAttribute( } `; - block.builders.hydrate.addLine(deindent` - ${last} = ${value} + block.builders.hydrate.addBlock(deindent` + ${last} = ${value}; ${updater} `); + + block.builders.update.addLine(`${last} = ${value};`); } else if (propertyName) { block.builders.hydrate.addLine( - `${state.parentNode}.${propertyName} = ${last} = ${value};` + `${state.parentNode}.${propertyName} = ${init};` ); - updater = `${state.parentNode}.${propertyName} = ${last};`; + updater = `${state.parentNode}.${propertyName} = ${shouldCache || isSelectValueAttribute ? last : value};`; } else { block.builders.hydrate.addLine( - `${method}( ${state.parentNode}, '${name}', ${last} = ${value} );` + `${method}( ${state.parentNode}, '${name}', ${init} );` ); - updater = `${method}( ${state.parentNode}, '${name}', ${last} );`; + updater = `${method}( ${state.parentNode}, '${name}', ${shouldCache || isSelectValueAttribute ? last : value} );`; } - block.builders.update.addBlock(deindent` - if ( ${last} !== ( ${last} = ${value} ) ) { - ${updater} - } - `); + if (allDependencies.size || hasChangeableIndex || isSelectValueAttribute) { + const dependencies = Array.from(allDependencies); + const changedCheck = ( + ( block.hasOutroMethod ? `#outroing || ` : '' ) + + dependencies.map(dependency => `changed.${dependency}`).join(' || ') + ); + + const updateCachedValue = `${last} !== ( ${last} = ${value} )`; + + const condition = shouldCache ? + ( dependencies.length ? `( ${changedCheck} ) && ${updateCachedValue}` : updateCachedValue ) : + changedCheck; + + block.builders.update.addConditional( + condition, + updater + ); + } } else { const value = attribute.value === true ? 'true' diff --git a/src/generators/dom/visitors/Element/meta/Window.ts b/src/generators/dom/visitors/Element/meta/Window.ts index d6c848c342..8e15ee2512 100644 --- a/src/generators/dom/visitors/Element/meta/Window.ts +++ b/src/generators/dom/visitors/Element/meta/Window.ts @@ -38,8 +38,8 @@ export default function visitWindow( let usesState = false; attribute.expression.arguments.forEach((arg: Node) => { - const { contexts } = block.contextualise(arg, null, true); - if (contexts.length) usesState = true; + const { dependencies } = block.contextualise(arg, null, true); + if (dependencies.length) usesState = true; }); const flattened = flattenReference(attribute.expression.callee); diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index dfb6b3bfcd..193edd872b 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -119,8 +119,7 @@ export default function visitIfBlock( anchor, `@createComment()`, `@createComment()`, - state.parentNode, - true + state.parentNode ); } else if (node.next) { node.next.usedAsAnchor = true; diff --git a/src/generators/dom/visitors/MustacheTag.ts b/src/generators/dom/visitors/MustacheTag.ts index 3ef015a9fd..3c03c51167 100644 --- a/src/generators/dom/visitors/MustacheTag.ts +++ b/src/generators/dom/visitors/MustacheTag.ts @@ -1,4 +1,5 @@ import deindent from '../../../utils/deindent'; +import visitTag from './shared/Tag'; import { DomGenerator } from '../index'; import Block from '../Block'; import { Node } from '../../../interfaces'; @@ -10,25 +11,21 @@ export default function visitMustacheTag( state: State, node: Node ) { - const name = node._state.name; - const value = block.getUniqueName(`${name}_value`); + const { name } = node._state; - const { snippet } = block.contextualise(node.expression); + const { init } = visitTag( + generator, + block, + state, + node, + name, + value => `${name}.data = ${value};` + ); - block.addVariable(value); block.addElement( name, - `@createText( ${value} = ${snippet} )`, - generator.hydratable - ? `@claimText( ${state.parentNodes}, ${value} = ${snippet} )` - : '', - state.parentNode, - true + `@createText( ${init} )`, + `@claimText( ${state.parentNodes}, ${init} )`, + state.parentNode ); - - block.builders.update.addBlock(deindent` - if ( ${value} !== ( ${value} = ${snippet} ) ) { - ${name}.data = ${value}; - } - `); } diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index 41c3af9aa4..02facfcd24 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -1,4 +1,6 @@ import deindent from '../../../utils/deindent'; +import addUpdateBlock from './shared/addUpdateBlock'; +import visitTag from './shared/Tag'; import { DomGenerator } from '../index'; import Block from '../Block'; import { Node } from '../../../interfaces'; @@ -12,12 +14,19 @@ export default function visitRawMustacheTag( ) { const name = node._state.basename; const before = node._state.name; - const value = block.getUniqueName(`${name}_value`); const after = block.getUniqueName(`${name}_after`); - const { snippet } = block.contextualise(node.expression); - - block.addVariable(value); + const { init } = visitTag( + generator, + block, + state, + node, + name, + value => deindent` + @detachBetween( ${before}, ${after} ); + ${before}.insertAdjacentHTML( 'afterend', ${value} ); + ` + ); // we would have used comments here, but the `insertAdjacentHTML` api only // exists for `Element`s. @@ -25,30 +34,16 @@ export default function visitRawMustacheTag( before, `@createElement( 'noscript' )`, `@createElement( 'noscript' )`, - state.parentNode, - true + state.parentNode ); + block.addElement( after, `@createElement( 'noscript' )`, `@createElement( 'noscript' )`, - state.parentNode, - true + state.parentNode ); - const isToplevel = !state.parentNode; - - const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} = ${snippet} );`; - const detachStatement = `@detachBetween( ${before}, ${after} );`; - - block.builders.mount.addLine(mountStatement); - - block.builders.update.addBlock(deindent` - if ( ${value} !== ( ${value} = ${snippet} ) ) { - ${detachStatement} - ${mountStatement} - } - `); - - block.builders.detachRaw.addBlock(detachStatement); -} + block.builders.mount.addLine(`${before}.insertAdjacentHTML( 'afterend', ${init} );`); + block.builders.detachRaw.addBlock(`@detachBetween( ${before}, ${after} );`); +} \ No newline at end of file diff --git a/src/generators/dom/visitors/Text.ts b/src/generators/dom/visitors/Text.ts index 49135a129e..796bf9e309 100644 --- a/src/generators/dom/visitors/Text.ts +++ b/src/generators/dom/visitors/Text.ts @@ -14,10 +14,7 @@ export default function visitText( block.addElement( node._state.name, `@createText( ${stringify(node.data)} )`, - generator.hydratable - ? `@claimText( ${state.parentNodes}, ${stringify(node.data)} )` - : '', - state.parentNode, - node.usedAsAnchor + `@claimText( ${state.parentNodes}, ${stringify(node.data)} )`, + state.parentNode ); } diff --git a/src/generators/dom/visitors/shared/Tag.ts b/src/generators/dom/visitors/shared/Tag.ts new file mode 100644 index 0000000000..f15d72a5ba --- /dev/null +++ b/src/generators/dom/visitors/shared/Tag.ts @@ -0,0 +1,49 @@ +import deindent from '../../../../utils/deindent'; +import { DomGenerator } from '../../index'; +import Block from '../../Block'; +import { Node } from '../../../../interfaces'; +import { State } from '../../interfaces'; + +export default function visitTag( + generator: DomGenerator, + block: Block, + state: State, + node: Node, + name: string, + update: (value: string) => string +) { + const { dependencies, indexes, snippet } = block.contextualise(node.expression); + + const hasChangeableIndex = Array.from(indexes).some(index => block.changeableIndexes.get(index)); + + const shouldCache = ( + node.expression.type !== 'Identifier' || + block.contexts.has(node.expression.name) || + hasChangeableIndex + ); + + const value = shouldCache && block.getUniqueName(`${name}_value`); + const init = shouldCache ? value : snippet; + + if (shouldCache) block.addVariable(value, snippet); + + if (dependencies.length || hasChangeableIndex) { + const changedCheck = ( + ( block.hasOutroMethod ? `#outroing || ` : '' ) + + dependencies.map(dependency => `changed.${dependency}`).join(' || ') + ); + + const updateCachedValue = `${value} !== ( ${value} = ${snippet} )`; + + const condition = shouldCache ? + ( dependencies.length ? `( ${changedCheck} ) && ${updateCachedValue}` : updateCachedValue ) : + changedCheck; + + block.builders.update.addConditional( + condition, + update(shouldCache ? value : snippet) + ); + } + + return { init }; +} diff --git a/src/shared/index.js b/src/shared/index.js index f198a4ef95..901fbfdb3b 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -26,25 +26,23 @@ export function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -export function dispatchObservers(component, group, newState, oldState) { +export function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -133,6 +131,34 @@ export function set(newState) { this._root._lock = false; } +export function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + +export function _setDev(newState) { + if (typeof newState !== 'object') { + throw new Error( + 'Component .set was called without an object of data key-values to update.' + ); + } + + this._checkReadOnly(newState); + _set.call(this, newState); +} + export function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -144,7 +170,9 @@ export var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; export var protoDev = { @@ -154,5 +182,7 @@ export var protoDev = { observe: observeDev, on: onDev, set: set, - teardown: destroyDev + teardown: destroyDev, + _recompute: noop, + _set: _setDev }; diff --git a/src/utils/CodeBuilder.ts b/src/utils/CodeBuilder.ts index 92c88ea483..bdbcc3fcb6 100644 --- a/src/utils/CodeBuilder.ts +++ b/src/utils/CodeBuilder.ts @@ -21,15 +21,17 @@ export default class CodeBuilder { this.lastCondition = null; } - addConditionalLine(condition: string, line: string) { + addConditional(condition: string, body: string) { + body = body.replace(/^/gm, '\t'); + if (condition === this.lastCondition) { - this.result += `\n\t${line}`; + this.result += `\n${body}`; } else { if (this.lastCondition) { - this.result += `\n}\n\n`; + this.result += `\n}`; } - this.result += `if ( ${condition} ) {\n\t${line}`; + this.result += `${this.last === ChunkType.Block ? '\n\n' : '\n'}if ( ${condition} ) {\n${body}`; this.lastCondition = condition; } diff --git a/test/helpers.js b/test/helpers.js index f4be292ef4..e3343d4aa8 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -125,7 +125,7 @@ export function normalizeHtml(window, html) { .replace(/>[\s\r\n]+<') .trim(); cleanChildren(node, ''); - return node.innerHTML; + return node.innerHTML.replace(/<\/?noscript\/?>/g, ''); } export function setupHtmlEqual() { diff --git a/test/js/index.js b/test/js/index.js index d6497b6b75..737a76e700 100644 --- a/test/js/index.js +++ b/test/js/index.js @@ -52,7 +52,7 @@ describe("js", () => { } ] }).then(bundle => { - return bundle.generate({ format: "es" }) + return bundle.generate({ format: "es" }); }).then(({ code }) => { fs.writeFileSync(`${dir}/_actual-bundle.js`, code); @@ -65,6 +65,9 @@ describe("js", () => { code.trim().replace(/^\s+$/gm, ""), expectedBundle.trim().replace(/^\s+$/gm, "") ); + }).catch(err => { + console.error(err.loc); + throw err; }); }); }); diff --git a/test/js/samples/collapses-text-around-comments/expected-bundle.js b/test/js/samples/collapses-text-around-comments/expected-bundle.js index e52f943862..10a7a08930 100644 --- a/test/js/samples/collapses-text-around-comments/expected-bundle.js +++ b/test/js/samples/collapses-text-around-comments/expected-bundle.js @@ -52,25 +52,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -134,6 +132,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -145,7 +160,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; var template = (function () { @@ -168,12 +185,12 @@ function add_css () { } function create_main_fragment ( state, component ) { - var p, text_value, text; + var p, text; return { create: function () { p = createElement( 'p' ); - text = createText( text_value = state.foo ); + text = createText( state.foo ); this.hydrate(); }, @@ -187,8 +204,8 @@ function create_main_fragment ( state, component ) { }, update: function ( changed, state ) { - if ( text_value !== ( text_value = state.foo ) ) { - text.data = text_value; + if ( changed.foo ) { + text.data = state.foo; } }, @@ -226,12 +243,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index 5a3e3d52f6..60a7180f1d 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; +import { appendNode, assign, createElement, createText, detachNode, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; var template = (function () { return { @@ -20,12 +20,12 @@ function add_css () { } function create_main_fragment ( state, component ) { - var p, text_value, text; + var p, text; return { create: function () { p = createElement( 'p' ); - text = createText( text_value = state.foo ); + text = createText( state.foo ); this.hydrate(); }, @@ -39,8 +39,8 @@ function create_main_fragment ( state, component ) { }, update: function ( changed, state ) { - if ( text_value !== ( text_value = state.foo ) ) { - text.data = text_value; + if ( changed.foo ) { + text.data = state.foo; } }, @@ -78,12 +78,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/computed-collapsed-if/expected-bundle.js b/test/js/samples/computed-collapsed-if/expected-bundle.js index 0da5cdf32c..20fe6ef0fa 100644 --- a/test/js/samples/computed-collapsed-if/expected-bundle.js +++ b/test/js/samples/computed-collapsed-if/expected-bundle.js @@ -28,25 +28,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -110,6 +108,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -121,16 +136,11 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; -function recompute ( state, newState, oldState, isInitial ) { - if ( isInitial || ( 'x' in newState && differs( state.x, oldState.x ) ) ) { - state.a = newState.a = template.computed.a( state.x ); - state.b = newState.b = template.computed.b( state.x ); - } -} - var template = (function () { return { computed: { @@ -147,6 +157,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -156,7 +168,7 @@ function create_main_fragment ( state, component ) { function SvelteComponent ( options ) { options = options || {}; this._state = options.data || {}; - recompute( this._state, this._state, {}, true ); + this._recompute( {}, this._state, {}, true ); this._observers = { pre: Object.create( null ), @@ -178,12 +190,11 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - recompute( this._state, newState, oldState, false ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); +SvelteComponent.prototype._recompute = function _recompute ( changed, state, oldState, isInitial ) { + if ( isInitial || changed.x ) { + if ( differs( ( state.a = template.computed.a( state.x ) ), oldState.a ) ) changed.a = true; + if ( differs( ( state.b = template.computed.b( state.x ) ), oldState.b ) ) changed.b = true; + } }; export default SvelteComponent; diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js index a17e730122..6318b3b5cf 100644 --- a/test/js/samples/computed-collapsed-if/expected.js +++ b/test/js/samples/computed-collapsed-if/expected.js @@ -1,11 +1,4 @@ -import { assign, differs, dispatchObservers, noop, proto } from "svelte/shared.js"; - -function recompute ( state, newState, oldState, isInitial ) { - if ( isInitial || ( 'x' in newState && differs( state.x, oldState.x ) ) ) { - state.a = newState.a = template.computed.a( state.x ); - state.b = newState.b = template.computed.b( state.x ); - } -} +import { assign, differs, noop, proto } from "svelte/shared.js"; var template = (function () { return { @@ -23,6 +16,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -32,7 +27,7 @@ function create_main_fragment ( state, component ) { function SvelteComponent ( options ) { options = options || {}; this._state = options.data || {}; - recompute( this._state, this._state, {}, true ); + this._recompute( {}, this._state, {}, true ); this._observers = { pre: Object.create( null ), @@ -54,12 +49,11 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - recompute( this._state, newState, oldState, false ) - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; +SvelteComponent.prototype._recompute = function _recompute ( changed, state, oldState, isInitial ) { + if ( isInitial || changed.x ) { + if ( differs( ( state.a = template.computed.a( state.x ) ), oldState.a ) ) changed.a = true; + if ( differs( ( state.b = template.computed.b( state.x ) ), oldState.b ) ) changed.b = true; + } +} export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/css-media-query/expected-bundle.js b/test/js/samples/css-media-query/expected-bundle.js index 3324ebf9be..868ca03e71 100644 --- a/test/js/samples/css-media-query/expected-bundle.js +++ b/test/js/samples/css-media-query/expected-bundle.js @@ -48,25 +48,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -130,6 +128,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -141,7 +156,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; function encapsulateStyles ( node ) { @@ -172,6 +189,8 @@ function create_main_fragment ( state, component ) { insertNode( div, target, anchor ); }, + update: noop, + unmount: function () { detachNode( div ); }, @@ -206,11 +225,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/css-media-query/expected.js b/test/js/samples/css-media-query/expected.js index 72338bb55b..8fb4bde21a 100644 --- a/test/js/samples/css-media-query/expected.js +++ b/test/js/samples/css-media-query/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createElement, detachNode, dispatchObservers, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; +import { appendNode, assign, createElement, detachNode, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; function encapsulateStyles ( node ) { setAttribute( node, 'svelte-2363328337', '' ); @@ -28,6 +28,8 @@ function create_main_fragment ( state, component ) { insertNode( div, target, anchor ); }, + update: noop, + unmount: function () { detachNode( div ); }, @@ -62,11 +64,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/each-block-changed-check/expected-bundle.js b/test/js/samples/each-block-changed-check/expected-bundle.js index aac78b9adb..2d14d12151 100644 --- a/test/js/samples/each-block-changed-check/expected-bundle.js +++ b/test/js/samples/each-block-changed-check/expected-bundle.js @@ -61,25 +61,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -143,6 +141,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -154,11 +169,13 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; function create_main_fragment ( state, component ) { - var text, p, text_1_value, text_1; + var text, p, text_1; var each_block_value = state.comments; @@ -176,7 +193,7 @@ function create_main_fragment ( state, component ) { text = createText( "\n\n" ); p = createElement( 'p' ); - text_1 = createText( text_1_value = state.foo ); + text_1 = createText( state.foo ); }, mount: function ( target, anchor ) { @@ -192,7 +209,7 @@ function create_main_fragment ( state, component ) { update: function ( changed, state ) { var each_block_value = state.comments; - if ( 'comments' in changed || 'elapsed' in changed || 'time' in changed ) { + if ( changed.comments || changed.elapsed || changed.time ) { for ( var i = 0; i < each_block_value.length; i += 1 ) { if ( each_block_iterations[i] ) { each_block_iterations[i].update( changed, state, each_block_value, each_block_value[i], i ); @@ -210,8 +227,8 @@ function create_main_fragment ( state, component ) { each_block_iterations.length = each_block_value.length; } - if ( text_1_value !== ( text_1_value = state.foo ) ) { - text_1.data = text_1_value; + if ( changed.foo ) { + text_1.data = state.foo; } }, @@ -231,18 +248,18 @@ function create_main_fragment ( state, component ) { } function create_each_block ( state, each_block_value, comment, i, component ) { - var div, strong, text_value, text, text_1, span, text_2_value, text_2, text_3, text_4_value, text_4, text_5, text_6, raw_value, raw_before, raw_after; + var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before, raw_after; return { create: function () { div = createElement( 'div' ); strong = createElement( 'strong' ); - text = createText( text_value = i ); + text = createText( i ); text_1 = createText( "\n\n\t\t" ); span = createElement( 'span' ); - text_2 = createText( text_2_value = comment.author ); + text_2 = createText( text_2_value ); text_3 = createText( " wrote " ); - text_4 = createText( text_4_value = state.elapsed(comment.time, state.time) ); + text_4 = createText( text_4_value ); text_5 = createText( " ago:" ); text_6 = createText( "\n\n\t\t" ); raw_before = createElement( 'noscript' ); @@ -268,25 +285,21 @@ function create_each_block ( state, each_block_value, comment, i, component ) { appendNode( text_6, div ); appendNode( raw_before, div ); appendNode( raw_after, div ); - raw_before.insertAdjacentHTML( 'afterend', raw_value = comment.html ); + raw_before.insertAdjacentHTML( 'afterend', raw_value ); }, update: function ( changed, state, each_block_value, comment, i ) { - if ( text_value !== ( text_value = i ) ) { - text.data = text_value; - } - - if ( text_2_value !== ( text_2_value = comment.author ) ) { + if ( ( changed.comments ) && text_2_value !== ( text_2_value = comment.author ) ) { text_2.data = text_2_value; } - if ( text_4_value !== ( text_4_value = state.elapsed(comment.time, state.time) ) ) { + if ( ( changed.elapsed || changed.comments || changed.time ) && text_4_value !== ( text_4_value = state.elapsed(comment.time, state.time) ) ) { text_4.data = text_4_value; } - if ( raw_value !== ( raw_value = comment.html ) ) { + if ( ( changed.comments ) && raw_value !== ( raw_value = comment.html ) ) { detachBetween( raw_before, raw_after ); - raw_before.insertAdjacentHTML( 'afterend', raw_value = comment.html ); + raw_before.insertAdjacentHTML( 'afterend', raw_value ); } }, @@ -324,12 +337,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index e7683e0743..9bfc875e78 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -1,7 +1,7 @@ -import { appendNode, assign, createElement, createText, destroyEach, detachBetween, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; +import { appendNode, assign, createElement, createText, destroyEach, detachBetween, detachNode, insertNode, noop, proto } from "svelte/shared.js"; function create_main_fragment ( state, component ) { - var text, p, text_1_value, text_1; + var text, p, text_1; var each_block_value = state.comments; @@ -19,7 +19,7 @@ function create_main_fragment ( state, component ) { text = createText( "\n\n" ); p = createElement( 'p' ); - text_1 = createText( text_1_value = state.foo ); + text_1 = createText( state.foo ); }, mount: function ( target, anchor ) { @@ -35,7 +35,7 @@ function create_main_fragment ( state, component ) { update: function ( changed, state ) { var each_block_value = state.comments; - if ( 'comments' in changed || 'elapsed' in changed || 'time' in changed ) { + if ( changed.comments || changed.elapsed || changed.time ) { for ( var i = 0; i < each_block_value.length; i += 1 ) { if ( each_block_iterations[i] ) { each_block_iterations[i].update( changed, state, each_block_value, each_block_value[i], i ); @@ -53,8 +53,8 @@ function create_main_fragment ( state, component ) { each_block_iterations.length = each_block_value.length; } - if ( text_1_value !== ( text_1_value = state.foo ) ) { - text_1.data = text_1_value; + if ( changed.foo ) { + text_1.data = state.foo; } }, @@ -74,18 +74,18 @@ function create_main_fragment ( state, component ) { } function create_each_block ( state, each_block_value, comment, i, component ) { - var div, strong, text_value, text, text_1, span, text_2_value, text_2, text_3, text_4_value, text_4, text_5, text_6, raw_value, raw_before, raw_after; + var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before, raw_after; return { create: function () { div = createElement( 'div' ); strong = createElement( 'strong' ); - text = createText( text_value = i ); + text = createText( i ); text_1 = createText( "\n\n\t\t" ); span = createElement( 'span' ); - text_2 = createText( text_2_value = comment.author ); + text_2 = createText( text_2_value ); text_3 = createText( " wrote " ); - text_4 = createText( text_4_value = state.elapsed(comment.time, state.time) ); + text_4 = createText( text_4_value ); text_5 = createText( " ago:" ); text_6 = createText( "\n\n\t\t" ); raw_before = createElement( 'noscript' ); @@ -111,25 +111,21 @@ function create_each_block ( state, each_block_value, comment, i, component ) { appendNode( text_6, div ); appendNode( raw_before, div ); appendNode( raw_after, div ); - raw_before.insertAdjacentHTML( 'afterend', raw_value = comment.html ); + raw_before.insertAdjacentHTML( 'afterend', raw_value ); }, update: function ( changed, state, each_block_value, comment, i ) { - if ( text_value !== ( text_value = i ) ) { - text.data = text_value; - } - - if ( text_2_value !== ( text_2_value = comment.author ) ) { + if ( ( changed.comments ) && text_2_value !== ( text_2_value = comment.author ) ) { text_2.data = text_2_value; } - if ( text_4_value !== ( text_4_value = state.elapsed(comment.time, state.time) ) ) { + if ( ( changed.elapsed || changed.comments || changed.time ) && text_4_value !== ( text_4_value = state.elapsed(comment.time, state.time) ) ) { text_4.data = text_4_value; } - if ( raw_value !== ( raw_value = comment.html ) ) { + if ( ( changed.comments ) && raw_value !== ( raw_value = comment.html ) ) { detachBetween( raw_before, raw_after ); - raw_before.insertAdjacentHTML( 'afterend', raw_value = comment.html ); + raw_before.insertAdjacentHTML( 'afterend', raw_value ); } }, @@ -167,12 +163,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/event-handlers-custom/expected-bundle.js b/test/js/samples/event-handlers-custom/expected-bundle.js index ed200a7b4c..97090f092d 100644 --- a/test/js/samples/event-handlers-custom/expected-bundle.js +++ b/test/js/samples/event-handlers-custom/expected-bundle.js @@ -48,25 +48,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -130,6 +128,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -141,7 +156,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; var template = (function () { @@ -181,6 +198,8 @@ function create_main_fragment ( state, component ) { appendNode( text, button ); }, + update: noop, + unmount: function () { detachNode( button ); }, @@ -215,11 +234,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, template.methods, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/event-handlers-custom/expected.js b/test/js/samples/event-handlers-custom/expected.js index 9d5e771bdd..54e9111aff 100644 --- a/test/js/samples/event-handlers-custom/expected.js +++ b/test/js/samples/event-handlers-custom/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createElement, createText, detachNode, dispatchObservers, insertNode, proto } from "svelte/shared.js"; +import { appendNode, assign, createElement, createText, detachNode, insertNode, noop, proto } from "svelte/shared.js"; var template = (function () { return { @@ -37,6 +37,8 @@ function create_main_fragment ( state, component ) { appendNode( text, button ); }, + update: noop, + unmount: function () { detachNode( button ); }, @@ -71,11 +73,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, template.methods, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/if-block-no-update/expected-bundle.js b/test/js/samples/if-block-no-update/expected-bundle.js index 273ce81fc1..a36be53f93 100644 --- a/test/js/samples/if-block-no-update/expected-bundle.js +++ b/test/js/samples/if-block-no-update/expected-bundle.js @@ -52,25 +52,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -134,6 +132,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -145,7 +160,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; function create_main_fragment ( state, component ) { @@ -259,12 +276,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/if-block-no-update/expected.js b/test/js/samples/if-block-no-update/expected.js index 8d30b66a03..025e0a0e43 100644 --- a/test/js/samples/if-block-no-update/expected.js +++ b/test/js/samples/if-block-no-update/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; +import { appendNode, assign, createComment, createElement, createText, detachNode, insertNode, noop, proto } from "svelte/shared.js"; function create_main_fragment ( state, component ) { var if_block_anchor; @@ -111,12 +111,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/if-block-simple/expected-bundle.js b/test/js/samples/if-block-simple/expected-bundle.js index 3b57117283..c16dbcaaa3 100644 --- a/test/js/samples/if-block-simple/expected-bundle.js +++ b/test/js/samples/if-block-simple/expected-bundle.js @@ -52,25 +52,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -134,6 +132,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -145,7 +160,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; function create_main_fragment ( state, component ) { @@ -235,12 +252,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/if-block-simple/expected.js b/test/js/samples/if-block-simple/expected.js index db04589093..2cc3be36f9 100644 --- a/test/js/samples/if-block-simple/expected.js +++ b/test/js/samples/if-block-simple/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; +import { appendNode, assign, createComment, createElement, createText, detachNode, insertNode, noop, proto } from "svelte/shared.js"; function create_main_fragment ( state, component ) { var if_block_anchor; @@ -87,12 +87,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/non-imported-component/expected-bundle.js b/test/js/samples/non-imported-component/expected-bundle.js index b40f56d109..09576689bc 100644 --- a/test/js/samples/non-imported-component/expected-bundle.js +++ b/test/js/samples/non-imported-component/expected-bundle.js @@ -42,25 +42,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -124,6 +122,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -135,7 +150,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; var template = (function () { @@ -170,6 +187,8 @@ function create_main_fragment ( state, component ) { nonimported._fragment.mount( target, anchor ); }, + update: noop, + unmount: function () { imported._fragment.unmount(); detachNode( text ); @@ -221,11 +240,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js index 2382e04ab3..ce86538e00 100644 --- a/test/js/samples/non-imported-component/expected.js +++ b/test/js/samples/non-imported-component/expected.js @@ -1,6 +1,6 @@ import Imported from 'Imported.html'; -import { assign, callAll, createText, detachNode, dispatchObservers, insertNode, proto } from "svelte/shared.js"; +import { assign, callAll, createText, detachNode, insertNode, noop, proto } from "svelte/shared.js"; var template = (function () { return { @@ -34,6 +34,8 @@ function create_main_fragment ( state, component ) { nonimported._fragment.mount( target, anchor ); }, + update: noop, + unmount: function () { imported._fragment.unmount(); detachNode( text ); @@ -85,11 +87,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js index a754118158..227ac71e7c 100644 --- a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js +++ b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js @@ -28,25 +28,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -110,6 +108,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -121,7 +136,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; var template = (function () { @@ -139,6 +156,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -182,11 +201,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/onrender-onteardown-rewritten/expected.js b/test/js/samples/onrender-onteardown-rewritten/expected.js index bd473fc2d7..a5125745c6 100644 --- a/test/js/samples/onrender-onteardown-rewritten/expected.js +++ b/test/js/samples/onrender-onteardown-rewritten/expected.js @@ -1,4 +1,4 @@ -import { assign, callAll, dispatchObservers, noop, proto } from "svelte/shared.js"; +import { assign, callAll, noop, proto } from "svelte/shared.js"; var template = (function () { return { @@ -15,6 +15,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -58,11 +60,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/setup-method/expected-bundle.js b/test/js/samples/setup-method/expected-bundle.js index 006152cea5..0ecdbafa5b 100644 --- a/test/js/samples/setup-method/expected-bundle.js +++ b/test/js/samples/setup-method/expected-bundle.js @@ -28,25 +28,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -110,6 +108,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -121,7 +136,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; var template = (function () { @@ -150,6 +167,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -180,13 +199,6 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, template.methods, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - template.setup( SvelteComponent ); export default SvelteComponent; diff --git a/test/js/samples/setup-method/expected.js b/test/js/samples/setup-method/expected.js index 85f0d5c393..bf9da033ad 100644 --- a/test/js/samples/setup-method/expected.js +++ b/test/js/samples/setup-method/expected.js @@ -1,4 +1,4 @@ -import { assign, dispatchObservers, noop, proto } from "svelte/shared.js"; +import { assign, noop, proto } from "svelte/shared.js"; var template = (function () { return { @@ -26,6 +26,8 @@ function create_main_fragment ( state, component ) { mount: noop, + update: noop, + unmount: noop, destroy: noop @@ -56,13 +58,6 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, template.methods, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - template.setup( SvelteComponent ); export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/use-elements-as-anchors/expected-bundle.js b/test/js/samples/use-elements-as-anchors/expected-bundle.js index 502e08b22e..33f1b49df3 100644 --- a/test/js/samples/use-elements-as-anchors/expected-bundle.js +++ b/test/js/samples/use-elements-as-anchors/expected-bundle.js @@ -52,25 +52,23 @@ function differs(a, b) { return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -function dispatchObservers(component, group, newState, oldState) { +function dispatchObservers(component, group, changed, newState, oldState) { for (var key in group) { - if (!(key in newState)) continue; + if (!changed[key]) continue; var newValue = newState[key]; var oldValue = oldState[key]; - if (differs(newValue, oldValue)) { - var callbacks = group[key]; - if (!callbacks) continue; + var callbacks = group[key]; + if (!callbacks) continue; - for (var i = 0; i < callbacks.length; i += 1) { - var callback = callbacks[i]; - if (callback.__calling) continue; + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; - callback.__calling = true; - callback.call(component, newValue, oldValue); - callback.__calling = false; - } + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; } } } @@ -134,6 +132,23 @@ function set(newState) { this._root._lock = false; } +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state, oldState, false); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.update(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + function callAll(fns) { while (fns && fns.length) fns.pop()(); } @@ -145,7 +160,9 @@ var proto = { observe: observe, on: on, set: set, - teardown: destroy + teardown: destroy, + _recompute: noop, + _set: _set }; function create_main_fragment ( state, component ) { @@ -419,12 +436,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; diff --git a/test/js/samples/use-elements-as-anchors/expected.js b/test/js/samples/use-elements-as-anchors/expected.js index 3ed3f1e7db..28f0a660d4 100644 --- a/test/js/samples/use-elements-as-anchors/expected.js +++ b/test/js/samples/use-elements-as-anchors/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; +import { appendNode, assign, createComment, createElement, createText, detachNode, insertNode, noop, proto } from "svelte/shared.js"; function create_main_fragment ( state, component ) { var div, text, p, text_1, text_2, text_3, text_4, p_1, text_5, text_6, text_8, if_block_4_anchor; @@ -271,12 +271,4 @@ function SvelteComponent ( options ) { assign( SvelteComponent.prototype, proto ); -SvelteComponent.prototype._set = function _set ( newState ) { - var oldState = this._state; - this._state = assign( {}, oldState, newState ); - dispatchObservers( this, this._observers.pre, newState, oldState ); - this._fragment.update( newState, this._state ); - dispatchObservers( this, this._observers.post, newState, oldState ); -}; - export default SvelteComponent; \ No newline at end of file diff --git a/test/runtime/samples/each-block-keyed/_config.js b/test/runtime/samples/each-block-keyed/_config.js index 35b47f5be8..29cf422fbf 100644 --- a/test/runtime/samples/each-block-keyed/_config.js +++ b/test/runtime/samples/each-block-keyed/_config.js @@ -6,7 +6,10 @@ export default { ] }, - html: '

implement keyed each blocks

implement client-side hydration

', + html: ` +

1: implement keyed each blocks

+

2: implement client-side hydration

+ `, test ( assert, component, target ) { const [ p1, p2 ] = target.querySelectorAll( 'p' ); @@ -16,7 +19,7 @@ export default { { id: 234, description: 'implement client-side hydration' } ] }); - assert.htmlEqual( target.innerHTML, '

implement client-side hydration

' ); + assert.htmlEqual( target.innerHTML, '

1: implement client-side hydration

' ); const [ p3 ] = target.querySelectorAll( 'p' ); diff --git a/test/runtime/samples/each-block-keyed/main.html b/test/runtime/samples/each-block-keyed/main.html index 7d5b90a9f8..da74eaa701 100644 --- a/test/runtime/samples/each-block-keyed/main.html +++ b/test/runtime/samples/each-block-keyed/main.html @@ -1,3 +1,3 @@ -{{#each todos as todo @id}} -

{{todo.description}}

+{{#each todos as todo, i @id}} +

{{i+1}}: {{todo.description}}

{{/each}} diff --git a/test/runtime/samples/flush-before-bindings/Nested.html b/test/runtime/samples/flush-before-bindings/Nested.html index 9b5a838cb4..d4071beee9 100644 --- a/test/runtime/samples/flush-before-bindings/Nested.html +++ b/test/runtime/samples/flush-before-bindings/Nested.html @@ -1,7 +1,7 @@ {{#each things as thing}}

{{thing}} ({{visibilityMap[thing]}})

-
+ {{/each}} diff --git a/test/runtime/samples/ignore-unchanged-attribute/_config.js b/test/runtime/samples/ignore-unchanged-attribute/_config.js new file mode 100644 index 0000000000..eb62f5dd8e --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-attribute/_config.js @@ -0,0 +1,26 @@ +import counter from './counter.js'; + +export default { + data: { + x: 1, + y: 2 + }, + + html: ` +

1

+

+ `, + + test(assert, component) { + counter.count = 0; + + component.set({ x: 3 }); + assert.equal(counter.count, 0); + + component.set({ x: 4, y: 5 }); + assert.equal(counter.count, 1); + + component.set({ x: 5, y: 5 }); + assert.equal(counter.count, 1); + } +}; diff --git a/test/runtime/samples/ignore-unchanged-attribute/counter.js b/test/runtime/samples/ignore-unchanged-attribute/counter.js new file mode 100644 index 0000000000..63872cd6a2 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-attribute/counter.js @@ -0,0 +1,3 @@ +export default { + count: 0 +}; \ No newline at end of file diff --git a/test/runtime/samples/ignore-unchanged-attribute/main.html b/test/runtime/samples/ignore-unchanged-attribute/main.html new file mode 100644 index 0000000000..afaa83c85f --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-attribute/main.html @@ -0,0 +1,15 @@ +

{{x}}

+

+ + diff --git a/test/runtime/samples/ignore-unchanged-raw/_config.js b/test/runtime/samples/ignore-unchanged-raw/_config.js new file mode 100644 index 0000000000..b8c58367b3 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-raw/_config.js @@ -0,0 +1,26 @@ +import counter from './counter.js'; + +export default { + data: { + x: 1, + y: 2 + }, + + html: ` +

1

+

2

+ `, + + test(assert, component) { + counter.count = 0; + + component.set({ x: 3 }); + assert.equal(counter.count, 0); + + component.set({ x: 4, y: 5 }); + assert.equal(counter.count, 1); + + component.set({ x: 5, y: 5 }); + assert.equal(counter.count, 1); + } +}; diff --git a/test/runtime/samples/ignore-unchanged-raw/counter.js b/test/runtime/samples/ignore-unchanged-raw/counter.js new file mode 100644 index 0000000000..63872cd6a2 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-raw/counter.js @@ -0,0 +1,3 @@ +export default { + count: 0 +}; \ No newline at end of file diff --git a/test/runtime/samples/ignore-unchanged-raw/main.html b/test/runtime/samples/ignore-unchanged-raw/main.html new file mode 100644 index 0000000000..375985c0d2 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-raw/main.html @@ -0,0 +1,15 @@ +

{{x}}

+

{{{myHelper(y)}}}

+ + diff --git a/test/runtime/samples/ignore-unchanged-tag/_config.js b/test/runtime/samples/ignore-unchanged-tag/_config.js new file mode 100644 index 0000000000..b8c58367b3 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-tag/_config.js @@ -0,0 +1,26 @@ +import counter from './counter.js'; + +export default { + data: { + x: 1, + y: 2 + }, + + html: ` +

1

+

2

+ `, + + test(assert, component) { + counter.count = 0; + + component.set({ x: 3 }); + assert.equal(counter.count, 0); + + component.set({ x: 4, y: 5 }); + assert.equal(counter.count, 1); + + component.set({ x: 5, y: 5 }); + assert.equal(counter.count, 1); + } +}; diff --git a/test/runtime/samples/ignore-unchanged-tag/counter.js b/test/runtime/samples/ignore-unchanged-tag/counter.js new file mode 100644 index 0000000000..63872cd6a2 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-tag/counter.js @@ -0,0 +1,3 @@ +export default { + count: 0 +}; \ No newline at end of file diff --git a/test/runtime/samples/ignore-unchanged-tag/main.html b/test/runtime/samples/ignore-unchanged-tag/main.html new file mode 100644 index 0000000000..f50864df94 --- /dev/null +++ b/test/runtime/samples/ignore-unchanged-tag/main.html @@ -0,0 +1,15 @@ +

{{x}}

+

{{myHelper(y)}}

+ +