Skip to content

Commit 3a7f7e2

Browse files
authored
Merge pull request #559 from sveltejs/simpler-codegen
Simpler codegen
2 parents c71cb29 + d8364f6 commit 3a7f7e2

File tree

19 files changed

+173
-301
lines changed

19 files changed

+173
-301
lines changed

src/generators/dom/index.js

+61-127
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ class DomGenerator extends Generator {
1919
this.readonly = new Set();
2020

2121
// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
22-
this.builders = {
23-
metaBindings: new CodeBuilder()
24-
};
22+
this.metaBindings = [];
2523
}
2624

2725
helper ( name ) {
@@ -57,21 +55,9 @@ export default function dom ( parsed, source, options ) {
5755

5856
const builders = {
5957
main: new CodeBuilder(),
60-
init: new CodeBuilder(),
6158
_set: new CodeBuilder()
6259
};
6360

64-
if ( options.dev ) {
65-
builders._set.addBlock( deindent`
66-
if ( typeof newState !== 'object' ) {
67-
throw new Error( 'Component .set was called without an object of data key-values to update.' );
68-
}
69-
`);
70-
}
71-
72-
builders._set.addLine( 'var oldState = this._state;' );
73-
builders._set.addLine( `this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );` );
74-
7561
if ( computations.length ) {
7662
const builder = new CodeBuilder();
7763
const differs = generator.helper( 'differs' );
@@ -97,19 +83,26 @@ export default function dom ( parsed, source, options ) {
9783
` );
9884
}
9985

100-
if ( options.dev ) {
101-
Array.from( generator.readonly ).forEach( prop => {
102-
builders._set.addLine( `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` );
103-
});
104-
}
105-
106-
if ( computations.length ) {
107-
builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` );
108-
}
86+
builders._set.addBlock( deindent`
87+
${options.dev && deindent`
88+
if ( typeof newState !== 'object' ) {
89+
throw new Error( 'Component .set was called without an object of data key-values to update.' );
90+
}
10991
110-
builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );` );
111-
if ( block.hasUpdateMethod ) builders._set.addLine( `if ( this._fragment ) this._fragment.update( newState, this._state );` ); // TODO is the condition necessary?
112-
builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );` );
92+
${Array.from( generator.readonly ).map( prop =>
93+
`if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );`
94+
)}
95+
`}
96+
97+
var oldState = this._state;
98+
this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );
99+
${computations.length && `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )`}
100+
${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );
101+
${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`}
102+
${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
103+
${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
104+
${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
105+
` );
113106

114107
if ( hasJs ) {
115108
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
@@ -130,104 +123,6 @@ export default function dom ( parsed, source, options ) {
130123
builders.main.addBlock( block.render() );
131124
});
132125

133-
builders.init.addLine( `this._torndown = false;` );
134-
135-
if ( parsed.css && options.css !== false ) {
136-
builders.init.addLine( `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();` );
137-
}
138-
139-
if ( generator.hasComponents || generator.hasIntroTransitions ) {
140-
builders.init.addLine( `this._renderHooks = [];` );
141-
}
142-
143-
if ( generator.hasComplexBindings ) {
144-
builders.init.addBlock( deindent`
145-
this._bindings = [];
146-
this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
147-
if ( options.target ) this._fragment.mount( options.target, null );
148-
while ( this._bindings.length ) this._bindings.pop()();
149-
` );
150-
151-
builders._set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` );
152-
} else {
153-
builders.init.addBlock( deindent`
154-
this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
155-
if ( options.target ) this._fragment.mount( options.target, null );
156-
` );
157-
}
158-
159-
if ( generator.hasComponents || generator.hasIntroTransitions ) {
160-
const statement = `this._flush();`;
161-
162-
builders.init.addBlock( statement );
163-
builders._set.addBlock( statement );
164-
}
165-
166-
if ( templateProperties.oncreate ) {
167-
builders.init.addBlock( deindent`
168-
if ( options._root ) {
169-
options._root._renderHooks.push( ${generator.alias( 'template' )}.oncreate.bind( this ) );
170-
} else {
171-
${generator.alias( 'template' )}.oncreate.call( this );
172-
}
173-
` );
174-
}
175-
176-
const constructorBlock = new CodeBuilder();
177-
178-
constructorBlock.addLine( `options = options || {};` );
179-
if ( generator.usesRefs ) constructorBlock.addLine( `this.refs = {};` );
180-
181-
constructorBlock.addLine(
182-
`this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};`
183-
);
184-
185-
if ( !generator.builders.metaBindings.isEmpty() ) {
186-
constructorBlock.addBlock( generator.builders.metaBindings );
187-
}
188-
189-
if ( computations.length ) {
190-
constructorBlock.addLine(
191-
`${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`
192-
);
193-
}
194-
195-
if ( options.dev ) {
196-
generator.expectedProperties.forEach( prop => {
197-
constructorBlock.addLine(
198-
`if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`
199-
);
200-
});
201-
202-
constructorBlock.addBlock(
203-
`if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`
204-
);
205-
}
206-
207-
if ( generator.bindingGroups.length ) {
208-
constructorBlock.addLine( `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];` );
209-
}
210-
211-
constructorBlock.addBlock( deindent`
212-
this._observers = {
213-
pre: Object.create( null ),
214-
post: Object.create( null )
215-
};
216-
217-
this._handlers = Object.create( null );
218-
219-
this._root = options._root || this;
220-
this._yield = options._yield;
221-
222-
${builders.init}
223-
` );
224-
225-
builders.main.addBlock( deindent`
226-
function ${name} ( options ) {
227-
${constructorBlock}
228-
}
229-
` );
230-
231126
const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared;
232127

233128
const prototypeBase = `${name}.prototype` + ( templateProperties.methods ? `, ${generator.alias( 'template' )}.methods` : '' );
@@ -240,10 +135,49 @@ export default function dom ( parsed, source, options ) {
240135
}
241136
}`;
242137

243-
builders.main.addBlock( `${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});` );
244-
245138
// TODO deprecate component.teardown()
246139
builders.main.addBlock( deindent`
140+
function ${name} ( options ) {
141+
options = options || {};
142+
${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`}
143+
${generator.usesRefs && `this.refs = {};`}
144+
this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};
145+
${generator.metaBindings}
146+
${computations.length && `${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`}
147+
${options.dev && Array.from( generator.expectedProperties ).map( prop => `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`)}
148+
${generator.bindingGroups.length && `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];`}
149+
150+
this._observers = {
151+
pre: Object.create( null ),
152+
post: Object.create( null )
153+
};
154+
155+
this._handlers = Object.create( null );
156+
157+
this._root = options._root || this;
158+
this._yield = options._yield;
159+
160+
this._torndown = false;
161+
${parsed.css && options.css !== false && `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();`}
162+
${( generator.hasComponents || generator.hasIntroTransitions ) && `this._renderHooks = [];`}
163+
${generator.hasComplexBindings && `this._bindings = [];`}
164+
165+
this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
166+
if ( options.target ) this._fragment.mount( options.target, null );
167+
${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
168+
${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
169+
170+
${templateProperties.oncreate && deindent`
171+
if ( options._root ) {
172+
options._root._renderHooks.push( ${generator.alias( 'template' )}.oncreate.bind( this ) );
173+
} else {
174+
${generator.alias( 'template' )}.oncreate.call( this );
175+
}
176+
`}
177+
}
178+
179+
${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});
180+
247181
${name}.prototype._set = function _set ( newState ) {
248182
${builders._set}
249183
};

src/generators/dom/visitors/EachBlock.js

+7-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import CodeBuilder from '../../../utils/CodeBuilder.js';
21
import deindent from '../../../utils/deindent.js';
32
import visit from '../visit.js';
43

@@ -119,24 +118,13 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
119118
const iteration = block.getUniqueName( `${each_block}_iteration` );
120119
const _iterations = block.getUniqueName( `_${each_block}_iterations` );
121120

122-
block.builders.create.addLine( `var ${lookup} = Object.create( null );` );
123-
124-
const create = new CodeBuilder();
125-
126-
create.addBlock( deindent`
127-
var ${key} = ${each_block_value}[${i}].${node.key};
128-
${iterations}[${i}] = ${lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} );
129-
` );
130-
131-
if ( state.parentNode ) {
132-
create.addLine(
133-
`${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`
134-
);
135-
}
136-
137121
block.builders.create.addBlock( deindent`
122+
var ${lookup} = Object.create( null );
123+
138124
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) {
139-
${create}
125+
var ${key} = ${each_block_value}[${i}].${node.key};
126+
${iterations}[${i}] = ${lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} );
127+
${state.parentNode && `${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`}
140128
}
141129
` );
142130

@@ -206,21 +194,10 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
206194
}
207195

208196
function unkeyed ( generator, block, state, node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) {
209-
const create = new CodeBuilder();
210-
211-
create.addLine(
212-
`${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );`
213-
);
214-
215-
if ( state.parentNode ) {
216-
create.addLine(
217-
`${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`
218-
);
219-
}
220-
221197
block.builders.create.addBlock( deindent`
222198
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) {
223-
${create}
199+
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
200+
${state.parentNode && `${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`}
224201
}
225202
` );
226203

src/generators/dom/visitors/Element/EventHandler.js

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import deindent from '../../../../utils/deindent.js';
2-
import CodeBuilder from '../../../../utils/CodeBuilder.js';
32
import flattenReference from '../../../../utils/flattenReference.js';
43

54
export default function visitEventHandler ( generator, block, state, node, attribute ) {
@@ -49,18 +48,11 @@ export default function visitEventHandler ( generator, block, state, node, attri
4948
block.getUniqueName( `${name}_handler` );
5049

5150
// create the handler body
52-
const handlerBody = new CodeBuilder();
53-
54-
if ( state.usesComponent ) {
55-
// TODO the element needs to know to create `thing._svelte = { component: component }`
56-
handlerBody.addLine( `var ${block.component} = this._svelte.component;` );
57-
}
58-
59-
declarations.forEach( declaration => {
60-
handlerBody.addLine( declaration );
61-
});
62-
63-
handlerBody.addLine( `[✂${attribute.expression.start}-${attribute.expression.end}✂];` );
51+
const handlerBody = deindent`
52+
${state.usesComponent && `var ${block.component} = this._svelte.component;`}
53+
${declarations}
54+
[✂${attribute.expression.start}-${attribute.expression.end}✂];
55+
`;
6456

6557
const handler = isCustomEvent ?
6658
deindent`

src/generators/dom/visitors/Element/meta/Window.js

+12-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import flattenReference from '../../../../../utils/flattenReference.js';
22
import deindent from '../../../../../utils/deindent.js';
3-
import CodeBuilder from '../../../../../utils/CodeBuilder.js';
43

54
const associatedEvents = {
65
innerWidth: 'resize',
@@ -43,8 +42,10 @@ export default function visitWindow ( generator, block, node ) {
4342
}
4443

4544
const handlerName = block.getUniqueName( `onwindow${attribute.name}` );
46-
const handlerBody = ( usesState ? `var state = ${block.component}.get();\n` : '' ) +
47-
`[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
45+
const handlerBody = deindent`
46+
${usesState && `var state = ${block.component}.get();`}
47+
[✂${attribute.expression.start}-${attribute.expression.end}✂];
48+
`;
4849

4950
block.builders.create.addBlock( deindent`
5051
function ${handlerName} ( event ) {
@@ -84,7 +85,7 @@ export default function visitWindow ( generator, block, node ) {
8485
events[ associatedEvent ].push( `${attribute.value.name}: this.${attribute.name}` );
8586

8687
// add initial value
87-
generator.builders.metaBindings.addLine(
88+
generator.metaBindings.push(
8889
`this._state.${attribute.value.name} = window.${attribute.name};`
8990
);
9091
}
@@ -96,25 +97,21 @@ export default function visitWindow ( generator, block, node ) {
9697
const handlerName = block.getUniqueName( `onwindow${event}` );
9798
const props = events[ event ].join( ',\n' );
9899

99-
const handlerBody = new CodeBuilder();
100100
if ( event === 'scroll' ) { // TODO other bidirectional bindings...
101101
block.addVariable( lock, 'false' );
102-
handlerBody.addLine( `${lock} = true;` );
103102
}
104103

105-
if ( generator.options.dev ) handlerBody.addLine( `component._updatingReadonlyProperty = true;` );
104+
const handlerBody = deindent`
105+
${event === 'scroll' && `${lock} = true;`}
106+
${generator.options.dev && `component._updatingReadonlyProperty = true;`}
106107
107-
handlerBody.addBlock( deindent`
108108
${block.component}.set({
109109
${props}
110110
});
111-
` );
112-
113-
if ( generator.options.dev ) handlerBody.addLine( `component._updatingReadonlyProperty = false;` );
114111
115-
if ( event === 'scroll' ) {
116-
handlerBody.addLine( `${lock} = false;` );
117-
}
112+
${generator.options.dev && `component._updatingReadonlyProperty = false;`}
113+
${event === 'scroll' && `${lock} = false;`}
114+
`;
118115

119116
block.builders.create.addBlock( deindent`
120117
function ${handlerName} ( event ) {
@@ -166,7 +163,7 @@ export default function visitWindow ( generator, block, node ) {
166163
` );
167164

168165
// add initial value
169-
generator.builders.metaBindings.addLine(
166+
generator.metaBindings.push(
170167
`this._state.${bindings.online} = navigator.onLine;`
171168
);
172169

0 commit comments

Comments
 (0)