Skip to content

Commit def48e2

Browse files
authored
Merge pull request #111 from sveltejs/component-codegen
move component codegen into its own file
2 parents d94209b + f699b61 commit def48e2

File tree

2 files changed

+185
-149
lines changed

2 files changed

+185
-149
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import deindent from '../utils/deindent.js';
2+
import addComponentAttributes from './attributes/addComponentAttributes.js';
3+
import counter from '../utils/counter.js';
4+
5+
export default {
6+
enter ( generator, node ) {
7+
const hasChildren = node.children.length > 0;
8+
const name = generator.current.counter( `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` );
9+
10+
const local = {
11+
name,
12+
namespace: generator.current.namespace,
13+
isComponent: true,
14+
15+
allUsedContexts: new Set(),
16+
17+
init: [],
18+
mount: [],
19+
update: [],
20+
teardown: []
21+
};
22+
23+
const isToplevel = generator.current.localElementDepth === 0;
24+
25+
generator.hasComponents = true;
26+
27+
addComponentAttributes( generator, node, local );
28+
29+
const componentInitProperties = [
30+
`target: ${!isToplevel ? generator.current.target: 'null'}`,
31+
'root: component.root || component'
32+
];
33+
// Component has children
34+
if ( hasChildren ) {
35+
const yieldName = `render${name}YieldFragment`;
36+
37+
// {{YIELD STUFF}}
38+
generator.push({
39+
useAnchor: true,
40+
name: generator.current.counter(yieldName),
41+
target: 'target',
42+
localElementDepth: 0,
43+
44+
initStatements: [],
45+
mountStatements: [],
46+
updateStatements: [],
47+
teardownStatements: [],
48+
49+
counter: counter()
50+
});
51+
52+
node.children.forEach( generator.visit );
53+
generator.addRenderer( generator.current );
54+
generator.pop();
55+
56+
// Don't render children twice
57+
node.children = [];
58+
59+
generator.current.initStatements.push(`var ${name}_yieldFragment = ${yieldName}( root, component );`);
60+
generator.current.updateStatements.push(`${name}_yieldFragment.update ( changed, root );`);
61+
62+
componentInitProperties.push(`yield: ${name}_yieldFragment`);
63+
}
64+
65+
const statements = [];
66+
67+
if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) {
68+
const initialProps = local.staticAttributes
69+
.concat( local.dynamicAttributes )
70+
.map( attribute => `${attribute.name}: ${attribute.value}` );
71+
72+
if ( initialProps.length ) {
73+
statements.push( deindent`
74+
var ${name}_initialData = {
75+
${initialProps.join( ',\n' )}
76+
};
77+
` );
78+
} else {
79+
statements.push( `var ${name}_initialData = {};` );
80+
}
81+
82+
if ( local.bindings.length ) {
83+
const bindings = local.bindings.map( binding => {
84+
const parts = binding.value.split( '.' );
85+
const tail = parts.pop();
86+
return `if ( '${tail}' in ${parts.join( '.' )} ) ${name}_initialData.${binding.name} = ${binding.value};`;
87+
});
88+
89+
statements.push( bindings.join( '\n' ) );
90+
}
91+
componentInitProperties.push(`data: ${name}_initialData`);
92+
}
93+
94+
local.init.unshift( deindent`
95+
${statements.join( '\n\n' )}
96+
var ${name} = new template.components.${node.name}({
97+
${componentInitProperties.join(',\n')}
98+
});
99+
` );
100+
101+
if ( isToplevel ) {
102+
local.mount.unshift( `${name}.mount( target, anchor );` );
103+
}
104+
105+
if ( local.dynamicAttributes.length ) {
106+
const updates = local.dynamicAttributes.map( attribute => {
107+
return deindent`
108+
if ( ${attribute.dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) ${name}_changes.${attribute.name} = ${attribute.value};
109+
`;
110+
});
111+
112+
local.update.push( deindent`
113+
var ${name}_changes = {};
114+
115+
${updates.join( '\n' )}
116+
117+
if ( Object.keys( ${name}_changes ).length ) ${name}.set( ${name}_changes );
118+
` );
119+
}
120+
121+
local.teardown.push( `${name}.teardown( ${isToplevel ? 'detach' : 'false'} );` );
122+
123+
generator.current.initStatements.push( local.init.join( '\n' ) );
124+
if ( local.update.length ) generator.current.updateStatements.push( local.update.join( '\n' ) );
125+
if ( local.mount.length ) generator.current.mountStatements.push( local.mount.join( '\n' ) );
126+
generator.current.teardownStatements.push( local.teardown.join( '\n' ) );
127+
128+
generator.push({
129+
namespace: local.namespace,
130+
target: name,
131+
parent: generator.current,
132+
elementDepth: generator.current.elementDepth + 1,
133+
localElementDepth: generator.current.localElementDepth + 1
134+
});
135+
},
136+
137+
leave ( generator ) {
138+
generator.pop();
139+
}
140+
};

compiler/generate/visitors/Element.js

Lines changed: 45 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import deindent from '../utils/deindent.js';
2-
import addComponentAttributes from './attributes/addComponentAttributes.js';
32
import addElementAttributes from './attributes/addElementAttributes.js';
4-
import counter from '../utils/counter.js';
3+
import Component from './Component.js';
54

65
export default {
76
enter ( generator, node ) {
8-
const hasChildren = node.children.length > 0;
97
const isComponent = node.name in generator.components;
10-
const name = generator.current.counter( isComponent ? `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` : node.name );
8+
if ( isComponent ) {
9+
return Component.enter( generator, node );
10+
}
11+
12+
const name = generator.current.counter( node.name );
1113

1214
const local = {
1315
name,
1416
namespace: name === 'svg' ? 'http://www.w3.org/2000/svg' : generator.current.namespace,
15-
isComponent,
17+
isComponent: false,
1618

1719
allUsedContexts: new Set(),
1820

@@ -25,154 +27,49 @@ export default {
2527

2628
const isToplevel = generator.current.localElementDepth === 0;
2729

28-
if ( isComponent ) {
29-
generator.hasComponents = true;
30-
31-
addComponentAttributes( generator, node, local );
32-
33-
const componentInitProperties = [
34-
`target: ${!isToplevel ? generator.current.target: 'null'}`,
35-
'root: component.root || component'
36-
];
37-
// Component has children
38-
if ( hasChildren ) {
39-
const yieldName = `render${name}YieldFragment`;
40-
41-
// {{YIELD STUFF}}
42-
generator.push({
43-
useAnchor: true,
44-
name: generator.current.counter(yieldName),
45-
target: 'target',
46-
localElementDepth: 0,
47-
48-
initStatements: [],
49-
mountStatements: [],
50-
updateStatements: [],
51-
detachStatements: [],
52-
teardownStatements: [],
53-
54-
counter: counter()
55-
});
56-
57-
node.children.forEach( generator.visit );
58-
generator.addRenderer( generator.current );
59-
generator.pop();
60-
61-
// Don't render children twice
62-
node.children = [];
63-
64-
generator.current.initStatements.push(`var ${name}_yieldFragment = ${yieldName}( root, component );`);
65-
generator.current.updateStatements.push(`${name}_yieldFragment.update ( changed, root );`);
66-
67-
componentInitProperties.push(`yield: ${name}_yieldFragment`);
68-
}
69-
70-
const statements = [];
71-
72-
if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) {
73-
const initialProps = local.staticAttributes
74-
.concat( local.dynamicAttributes )
75-
.map( attribute => `${attribute.name}: ${attribute.value}` );
76-
77-
if ( initialProps.length ) {
78-
statements.push( deindent`
79-
var ${name}_initialData = {
80-
${initialProps.join( ',\n' )}
81-
};
82-
` );
83-
} else {
84-
statements.push( `var ${name}_initialData = {};` );
85-
}
86-
87-
if ( local.bindings.length ) {
88-
const bindings = local.bindings.map( binding => {
89-
const parts = binding.value.split( '.' );
90-
const tail = parts.pop();
91-
return `if ( '${tail}' in ${parts.join( '.' )} ) ${name}_initialData.${binding.name} = ${binding.value};`;
92-
});
93-
94-
statements.push( bindings.join( '\n' ) );
95-
}
96-
componentInitProperties.push(`data: ${name}_initialData`);
97-
}
98-
99-
local.init.unshift( deindent`
100-
${statements.join( '\n\n' )}
101-
102-
var ${name} = new template.components.${node.name}({
103-
${componentInitProperties.join(',\n')}
104-
});
105-
` );
106-
107-
108-
if ( isToplevel ) {
109-
local.mount.unshift( `${name}.mount( target, anchor );` );
110-
}
111-
112-
if ( local.dynamicAttributes.length ) {
113-
const updates = local.dynamicAttributes.map( attribute => {
114-
return deindent`
115-
if ( ${attribute.dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) ${name}_changes.${attribute.name} = ${attribute.value};
116-
`;
117-
});
118-
119-
local.update.push( deindent`
120-
var ${name}_changes = {};
121-
122-
${updates.join( '\n' )}
123-
124-
if ( Object.keys( ${name}_changes ).length ) ${name}.set( ${name}_changes );
125-
` );
126-
}
127-
128-
local.teardown.push( `${name}.teardown( ${isToplevel ? 'detach' : 'false'} );` );
129-
}
30+
addElementAttributes( generator, node, local );
13031

131-
else {
132-
addElementAttributes( generator, node, local );
32+
if ( local.allUsedContexts.size ) {
33+
const contextNames = [...local.allUsedContexts];
13334

134-
if ( local.allUsedContexts.size ) {
135-
const contextNames = [...local.allUsedContexts];
35+
const initialProps = contextNames.map( contextName => {
36+
if ( contextName === 'root' ) return `root: root`;
13637

137-
const initialProps = contextNames.map( contextName => {
138-
if ( contextName === 'root' ) return `root: root`;
38+
const listName = generator.current.listNames[ contextName ];
39+
const indexName = generator.current.indexNames[ contextName ];
13940

140-
const listName = generator.current.listNames[ contextName ];
141-
const indexName = generator.current.indexNames[ contextName ];
41+
return `${listName}: ${listName},\n${indexName}: ${indexName}`;
42+
}).join( ',\n' );
14243

143-
return `${listName}: ${listName},\n${indexName}: ${indexName}`;
144-
}).join( ',\n' );
44+
const updates = contextNames.map( contextName => {
45+
if ( contextName === 'root' ) return `${name}.__svelte.root = root;`;
14546

146-
const updates = contextNames.map( contextName => {
147-
if ( contextName === 'root' ) return `${name}.__svelte.root = root;`;
47+
const listName = generator.current.listNames[ contextName ];
48+
const indexName = generator.current.indexNames[ contextName ];
14849

149-
const listName = generator.current.listNames[ contextName ];
150-
const indexName = generator.current.indexNames[ contextName ];
50+
return `${name}.__svelte.${listName} = ${listName};\n${name}.__svelte.${indexName} = ${indexName};`;
51+
}).join( '\n' );
15152

152-
return `${name}.__svelte.${listName} = ${listName};\n${name}.__svelte.${indexName} = ${indexName};`;
153-
}).join( '\n' );
154-
155-
local.init.push( deindent`
156-
${name}.__svelte = {
157-
${initialProps}
158-
};
159-
` );
53+
local.init.push( deindent`
54+
${name}.__svelte = {
55+
${initialProps}
56+
};
57+
` );
16058

161-
local.update.push( updates );
162-
}
59+
local.update.push( updates );
60+
}
16361

164-
let render = local.namespace ?
165-
`var ${name} = document.createElementNS( '${local.namespace}', '${node.name}' );` :
166-
`var ${name} = document.createElement( '${node.name}' );`;
62+
let render = local.namespace ?
63+
`var ${name} = document.createElementNS( '${local.namespace}', '${node.name}' );` :
64+
`var ${name} = document.createElement( '${node.name}' );`;
16765

168-
if ( generator.cssId && !generator.current.elementDepth ) {
169-
render += `\n${name}.setAttribute( '${generator.cssId}', '' );`;
170-
}
66+
if ( generator.cssId && !generator.current.elementDepth ) {
67+
render += `\n${name}.setAttribute( '${generator.cssId}', '' );`;
68+
}
17169

172-
local.init.unshift( render );
173-
if ( isToplevel ) {
174-
local.detach.push( `${name}.parentNode.removeChild( ${name} );` );
175-
}
70+
local.init.unshift( render );
71+
if ( isToplevel ) {
72+
local.teardown.push( `if ( detach ) ${name}.parentNode.removeChild( ${name} );` );
17673
}
17774

17875
// special case – bound <option> without a value attribute
@@ -188,8 +85,9 @@ export default {
18885
generator.current.detachStatements.push( local.detach.join( '\n' ) );
18986
generator.current.teardownStatements.push( local.teardown.join( '\n' ) );
19087

88+
generator.createMountStatement( name );
89+
19190
generator.push({
192-
isComponent,
19391
namespace: local.namespace,
19492
target: name,
19593
parent: generator.current,
@@ -198,14 +96,12 @@ export default {
19896
});
19997
},
20098

201-
leave ( generator ) {
202-
const name = generator.current.target;
203-
const isComponent = generator.current.isComponent;
99+
leave ( generator, node ) {
100+
const isComponent = node.name in generator.components;
101+
if ( isComponent ) {
102+
return Component.leave( generator, node );
103+
}
204104

205105
generator.pop();
206-
207-
if ( isComponent ) return;
208-
209-
generator.createMountStatement( name );
210106
}
211107
};

0 commit comments

Comments
 (0)