Skip to content

Commit 1868334

Browse files
authored
Merge pull request #212 from sveltejs/helpers
helpers
2 parents dfcbd01 + 7da9a23 commit 1868334

File tree

12 files changed

+229
-147
lines changed

12 files changed

+229
-147
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/shared

src/generators/dom/index.js

+100-132
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import namespaces from '../../utils/namespaces.js';
55
import processCss from '../shared/css/process.js';
66
import visitors from './visitors/index.js';
77
import Generator from '../Generator.js';
8+
import * as shared from '../../shared/index.js';
89

910
class DomGenerator extends Generator {
1011
constructor ( parsed, source, names, visitors ) {
1112
super( parsed, source, names, visitors );
1213
this.renderers = [];
14+
this.uses = {};
1315
}
1416

1517
addElement ( name, renderStatement, needsIdentifier = false ) {
@@ -21,15 +23,13 @@ class DomGenerator extends Generator {
2123

2224
this.createMountStatement( name );
2325
} else {
24-
this.current.builders.init.addLine(
25-
`${this.current.target}.appendChild( ${renderStatement} );`
26-
);
26+
this.uses.appendNode = true;
27+
this.current.builders.init.addLine( `appendNode( ${renderStatement}, ${this.current.target} );` );
2728
}
2829

2930
if ( isToplevel ) {
30-
this.current.builders.detach.addLine(
31-
`${name}.parentNode.removeChild( ${name} );`
32-
);
31+
this.uses.detachNode = true;
32+
this.current.builders.detach.addLine( `detachNode( ${name} );` );
3333
}
3434
}
3535

@@ -54,19 +54,38 @@ class DomGenerator extends Generator {
5454

5555
if ( fragment.key ) properties.addBlock( `key: key,` );
5656

57-
properties.addBlock( deindent`
58-
mount: function ( target, anchor ) {
59-
${fragment.builders.mount}
60-
},
57+
if ( fragment.builders.mount.isEmpty() ) {
58+
this.uses.noop = true;
59+
properties.addBlock( `mount: noop,` );
60+
} else {
61+
properties.addBlock( deindent`
62+
mount: function ( target, anchor ) {
63+
${fragment.builders.mount}
64+
},
65+
` );
66+
}
6167

62-
update: function ( changed, ${fragment.params} ) {
63-
${fragment.builders.update}
64-
},
68+
if ( fragment.builders.update.isEmpty() ) {
69+
this.uses.noop = true;
70+
properties.addBlock( `update: noop,` );
71+
} else {
72+
properties.addBlock( deindent`
73+
update: function ( changed, ${fragment.params} ) {
74+
${fragment.builders.update}
75+
},
76+
` );
77+
}
6578

66-
teardown: function ( detach ) {
67-
${fragment.builders.teardown}
68-
}
69-
` );
79+
if ( fragment.builders.teardown.isEmpty() ) {
80+
this.uses.noop = true;
81+
properties.addBlock( `teardown: noop,` );
82+
} else {
83+
properties.addBlock( deindent`
84+
teardown: function ( detach ) {
85+
${fragment.builders.teardown}
86+
},
87+
` );
88+
}
7089

7190
this.renderers.push( deindent`
7291
function ${fragment.name} ( ${fragment.params}, component${fragment.key ? `, key` : ''} ) {
@@ -80,18 +99,18 @@ class DomGenerator extends Generator {
8099
}
81100

82101
createAnchor ( name, description = '' ) {
83-
const renderStatement = `document.createComment( ${JSON.stringify( description )} )`;
102+
this.uses.createComment = true;
103+
const renderStatement = `createComment( ${JSON.stringify( description )} )`;
84104
this.addElement( name, renderStatement, true );
85105
}
86106

87107
createMountStatement ( name ) {
88108
if ( this.current.target === 'target' ) {
89-
this.current.builders.mount.addLine(
90-
`target.insertBefore( ${name}, anchor );`
91-
);
109+
this.uses.insertNode = true;
110+
this.current.builders.mount.addLine( `insertNode( ${name}, target, anchor );` );
92111
} else {
93-
this.current.builders.init.addLine(
94-
`${this.current.target}.appendChild( ${name} );` );
112+
this.uses.appendNode = true;
113+
this.current.builders.init.addLine( `appendNode( ${name}, ${this.current.target} );` );
95114
}
96115
}
97116

@@ -158,8 +177,8 @@ export default function dom ( parsed, source, options, names ) {
158177
set: new CodeBuilder()
159178
};
160179

161-
builders.set.addLine( 'var oldState = state;' );
162-
builders.set.addLine( 'state = Object.assign( {}, oldState, newState );' );
180+
builders.set.addLine( 'var oldState = this._state;' );
181+
builders.set.addLine( 'this._state = Object.assign( {}, oldState, newState );' );
163182

164183
if ( computations.length ) {
165184
const builder = new CodeBuilder();
@@ -178,26 +197,30 @@ export default function dom ( parsed, source, options, names ) {
178197
}
179198
` );
180199

181-
builders.set.addLine( `applyComputations( state, newState, oldState )` );
200+
builders.set.addLine( `applyComputations( this._state, newState, oldState )` );
182201
}
183202

203+
// TODO is the `if` necessary?
184204
builders.set.addBlock( deindent`
185-
dispatchObservers( observers.immediate, newState, oldState );
186-
if ( mainFragment ) mainFragment.update( newState, state );
187-
dispatchObservers( observers.deferred, newState, oldState );
205+
dispatchObservers( this, this._observers.pre, newState, oldState );
206+
if ( this._fragment ) this._fragment.update( newState, this._state );
207+
dispatchObservers( this, this._observers.post, newState, oldState );
188208
` );
189209

190210
if ( parsed.js ) {
191211
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
192212
}
193213

194214
if ( parsed.css && options.css !== false ) {
215+
generator.uses.appendNode = true;
216+
generator.uses.createElement = true;
217+
195218
builders.main.addBlock( deindent`
196219
let addedCss = false;
197220
function addCss () {
198-
var style = document.createElement( 'style' );
221+
var style = createElement( 'style' );
199222
style.textContent = ${JSON.stringify( processCss( parsed ) )};
200-
document.head.appendChild( style );
223+
appendNode( style, document.head );
201224
202225
addedCss = true;
203226
}
@@ -212,29 +235,29 @@ export default function dom ( parsed, source, options, names ) {
212235
}
213236

214237
if ( generator.hasComponents ) {
215-
builders.init.addLine( `this.__renderHooks = [];` );
238+
builders.init.addLine( `this._renderHooks = [];` );
216239
}
217240

218241
if ( generator.hasComplexBindings ) {
219242
builders.init.addBlock( deindent`
220-
this.__bindings = [];
221-
var mainFragment = renderMainFragment( state, this );
222-
if ( options.target ) this._mount( options.target );
223-
while ( this.__bindings.length ) this.__bindings.pop()();
243+
this._bindings = [];
244+
this._fragment = renderMainFragment( this._state, this );
245+
if ( options.target ) this._fragment.mount( options.target, null );
246+
while ( this._bindings.length ) this._bindings.pop()();
224247
` );
225248

226-
builders.set.addLine( `while ( this.__bindings.length ) this.__bindings.pop()();` );
249+
builders.set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` );
227250
} else {
228251
builders.init.addBlock( deindent`
229-
var mainFragment = renderMainFragment( state, this );
230-
if ( options.target ) this._mount( options.target );
252+
this._fragment = renderMainFragment( this._state, this );
253+
if ( options.target ) this._fragment.mount( options.target, null );
231254
` );
232255
}
233256

234257
if ( generator.hasComponents ) {
235258
const statement = deindent`
236-
while ( this.__renderHooks.length ) {
237-
var hook = this.__renderHooks.pop();
259+
while ( this._renderHooks.length ) {
260+
var hook = this._renderHooks.pop();
238261
hook.fn.call( hook.context );
239262
}
240263
`;
@@ -245,8 +268,8 @@ export default function dom ( parsed, source, options, names ) {
245268

246269
if ( templateProperties.onrender ) {
247270
builders.init.addBlock( deindent`
248-
if ( options.root ) {
249-
options.root.__renderHooks.push({ fn: template.onrender, context: this });
271+
if ( options._root ) {
272+
options._root._renderHooks.push({ fn: template.onrender, context: this });
250273
} else {
251274
template.onrender.call( this );
252275
}
@@ -258,111 +281,56 @@ export default function dom ( parsed, source, options, names ) {
258281
builders.main.addBlock( deindent`
259282
function ${name} ( options ) {
260283
options = options || {};
284+
${generator.usesRefs ? `\nthis.refs = {}` : ``}
285+
this._state = ${initialState};${templateProperties.computed ? `\napplyComputations( this._state, this._state, {} );` : ``}
261286
262-
var component = this;${generator.usesRefs ? `\nthis.refs = {}` : ``}
263-
var state = ${initialState};${templateProperties.computed ? `\napplyComputations( state, state, {} );` : ``}
264-
265-
var observers = {
266-
immediate: Object.create( null ),
267-
deferred: Object.create( null )
268-
};
269-
270-
var callbacks = Object.create( null );
271-
272-
function dispatchObservers ( group, newState, oldState ) {
273-
for ( var key in group ) {
274-
if ( !( key in newState ) ) continue;
275-
276-
var newValue = newState[ key ];
277-
var oldValue = oldState[ key ];
278-
279-
if ( newValue === oldValue && typeof newValue !== 'object' ) continue;
280-
281-
var callbacks = group[ key ];
282-
if ( !callbacks ) continue;
283-
284-
for ( var i = 0; i < callbacks.length; i += 1 ) {
285-
var callback = callbacks[i];
286-
if ( callback.__calling ) continue;
287-
288-
callback.__calling = true;
289-
callback.call( component, newValue, oldValue );
290-
callback.__calling = false;
291-
}
292-
}
293-
}
294-
295-
this.fire = function fire ( eventName, data ) {
296-
var handlers = eventName in callbacks && callbacks[ eventName ].slice();
297-
if ( !handlers ) return;
298-
299-
for ( var i = 0; i < handlers.length; i += 1 ) {
300-
handlers[i].call( this, data );
301-
}
302-
};
303-
304-
this.get = function get ( key ) {
305-
return key ? state[ key ] : state;
287+
this._observers = {
288+
pre: Object.create( null ),
289+
post: Object.create( null )
306290
};
307291
308-
this.set = function set ( newState ) {
309-
${builders.set}
310-
};
311-
312-
this._mount = function mount ( target, anchor ) {
313-
mainFragment.mount( target, anchor );
314-
}
292+
this._handlers = Object.create( null );
315293
316-
this.observe = function ( key, callback, options ) {
317-
var group = ( options && options.defer ) ? observers.deferred : observers.immediate;
294+
this._root = options._root;
295+
this._yield = options._yield;
318296
319-
( group[ key ] || ( group[ key ] = [] ) ).push( callback );
297+
${builders.init}
298+
}
299+
` );
320300

321-
if ( !options || options.init !== false ) {
322-
callback.__calling = true;
323-
callback.call( component, state[ key ] );
324-
callback.__calling = false;
325-
}
301+
if ( templateProperties.methods ) {
302+
builders.main.addBlock( `${name}.prototype = template.methods;` );
303+
}
326304

327-
return {
328-
cancel: function () {
329-
var index = group[ key ].indexOf( callback );
330-
if ( ~index ) group[ key ].splice( index, 1 );
331-
}
332-
};
333-
};
305+
builders.main.addBlock( deindent`
306+
${name}.prototype.get = ${shared.get};
334307
335-
this.on = function on ( eventName, handler ) {
336-
var handlers = callbacks[ eventName ] || ( callbacks[ eventName ] = [] );
337-
handlers.push( handler );
308+
${name}.prototype.fire = ${shared.fire};
338309
339-
return {
340-
cancel: function () {
341-
var index = handlers.indexOf( handler );
342-
if ( ~index ) handlers.splice( index, 1 );
343-
}
344-
};
345-
};
310+
${name}.prototype.observe = ${shared.observe};
346311
347-
this.teardown = function teardown ( detach ) {
348-
this.fire( 'teardown' );${templateProperties.onteardown ? `\ntemplate.onteardown.call( this );` : ``}
312+
${name}.prototype.on = ${shared.on};
349313
350-
mainFragment.teardown( detach !== false );
351-
mainFragment = null;
314+
${name}.prototype.set = function set ( newState ) {
315+
${builders.set}
316+
};
352317
353-
state = {};
354-
};
318+
${name}.prototype.teardown = function teardown ( detach ) {
319+
this.fire( 'teardown' );${templateProperties.onteardown ? `\ntemplate.onteardown.call( this );` : ``}
355320
356-
this.root = options.root;
357-
this.yield = options.yield;
321+
this._fragment.teardown( detach !== false );
322+
this._fragment = null;
358323
359-
${builders.init}
360-
}
324+
this._state = {};
325+
};
361326
` );
362327

363-
if ( templateProperties.methods ) {
364-
builders.main.addBlock( `${name}.prototype = template.methods;` );
365-
}
328+
builders.main.addBlock( shared.dispatchObservers.toString() );
329+
330+
Object.keys( generator.uses ).forEach( key => {
331+
const fn = shared[ key ]; // eslint-disable-line import/namespace
332+
builders.main.addBlock( fn.toString() );
333+
});
366334

367335
return generator.generate( builders.main.toString(), options, { name, format } );
368336
}

src/generators/dom/visitors/Component.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default {
2626

2727
const componentInitProperties = [
2828
`target: ${!isToplevel ? generator.current.target: 'null'}`,
29-
'root: component.root || component'
29+
'_root: component._root || component'
3030
];
3131

3232
// Component has children, put them in a separate {{yield}} block
@@ -43,7 +43,7 @@ export default {
4343
`${name}_yieldFragment.update( changed, root );`
4444
);
4545

46-
componentInitProperties.push(`yield: ${name}_yieldFragment`);
46+
componentInitProperties.push( `_yield: ${name}_yieldFragment`);
4747
}
4848

4949
const statements = [];
@@ -83,7 +83,7 @@ export default {
8383
` );
8484

8585
if ( isToplevel ) {
86-
generator.current.builders.mount.addLine( `${name}._mount( target, anchor );` );
86+
generator.current.builders.mount.addLine( `${name}._fragment.mount( target, anchor );` );
8787
}
8888

8989
if ( local.dynamicAttributes.length ) {

0 commit comments

Comments
 (0)