From 9ff58cdb4139a79f1cb797511c3394852c29c12b Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 19 Nov 2016 11:52:08 -0500 Subject: [PATCH] computed values --- .eslintrc | 3 +- compiler/generate/index.js | 54 +++++++++++++++++++++--- test/compiler/computed-values/_config.js | 4 +- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.eslintrc b/.eslintrc index 1520f88d5b..b6e21eac07 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,7 +20,8 @@ "one-var": [ 2, "never" ], "prefer-arrow-callback": 2, "prefer-const": [ 2, { "destructuring": "all" } ], - "arrow-spacing": 2 + "arrow-spacing": 2, + "no-inner-declarations": 0 }, "env": { "es6": true, diff --git a/compiler/generate/index.js b/compiler/generate/index.js index 0acf6d47da..1e84d0c400 100644 --- a/compiler/generate/index.js +++ b/compiler/generate/index.js @@ -47,7 +47,7 @@ export default function generate ( parsed, template ) { code.overwrite( defaultExport.start, defaultExport.declaration.start, `const template = ` ); defaultExport.declaration.properties.forEach( prop => { - templateProperties[ prop.key.name ] = true; + templateProperties[ prop.key.name ] = prop.value; }); } } @@ -353,6 +353,49 @@ export default function generate ( parsed, template ) { renderers.push( createRenderer( current ) ); + const setStatements = [ deindent` + const oldState = state; + state = Object.assign( {}, oldState, newState ); + ` ]; + + if ( templateProperties.computed ) { + const dependencies = new Map(); + + templateProperties.computed.properties.forEach( prop => { + const key = prop.key.name; + const value = prop.value; + + const deps = value.params.map( param => param.name ); + dependencies.set( key, deps ); + }); + + const visited = new Set(); + + function visit ( key ) { + if ( !dependencies.has( key ) ) return; // not a computation + + if ( visited.has( key ) ) return; + visited.add( key ); + + const deps = dependencies.get( key ); + deps.forEach( visit ); + + setStatements.push( deindent` + if ( ${deps.map( dep => `( '${dep}' in newState && typeof state.${dep} === 'object' || state.${dep} !== oldState.${dep} )` ).join( ' || ' )} ) { + state.${key} = newState.${key} = template.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} ); + } + ` ); + } + + templateProperties.computed.properties.forEach( prop => visit( prop.key.name ) ); + } + + setStatements.push( deindent` + dispatchObservers( observers.immediate, newState, oldState ); + mainFragment.update( state ); + dispatchObservers( observers.deferred, newState, oldState ); + ` ); + const result = deindent` ${parsed.js ? `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` : ``} @@ -367,9 +410,11 @@ export default function generate ( parsed, template ) { deferred: Object.create( null ) }; - function dispatchObservers ( group, state, oldState ) { + function dispatchObservers ( group, newState, oldState ) { for ( const key in group ) { - const newValue = state[ key ]; + if ( !( key in newState ) ) continue; + + const newValue = newState[ key ]; const oldValue = oldState[ key ]; if ( newValue === oldValue && typeof newValue !== 'object' ) continue; @@ -388,8 +433,7 @@ export default function generate ( parsed, template ) { }; component.set = function set ( newState ) { - Object.assign( state, newState ); - mainFragment.update( state ); + ${setStatements.join( '\n\n' )} }; component.observe = function ( key, callback, options = {} ) { diff --git a/test/compiler/computed-values/_config.js b/test/compiler/computed-values/_config.js index 3d55fa3154..d63d3c0da1 100644 --- a/test/compiler/computed-values/_config.js +++ b/test/compiler/computed-values/_config.js @@ -1,11 +1,11 @@ import * as assert from 'assert'; export default { - html: '

1 + 2 = 3

3 * 3 = 9

', + html: '

1 + 2 = 3

\n

3 * 3 = 9

', test ( component, target ) { component.set({ a: 3 }); assert.equal( component.get( 'c' ), 5 ); assert.equal( component.get( 'cSquared' ), 25 ); - assert.equal( target.innerHTML, '

3 + 2 = 5

5 * 5 = 25

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

3 + 2 = 5

\n

5 * 5 = 25

' ); } };