Skip to content

Commit 319b3b6

Browse files
authored
Merge pull request #332 from sveltejs/gh-51
self-references
2 parents f799cae + 0f60242 commit 319b3b6

File tree

14 files changed

+156
-15
lines changed

14 files changed

+156
-15
lines changed

src/generators/Generator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import getOutro from './shared/utils/getOutro.js';
99
import annotateWithScopes from './annotateWithScopes.js';
1010

1111
export default class Generator {
12-
constructor ( parsed, source, names, visitors ) {
12+
constructor ( parsed, source, name, names, visitors ) {
1313
this.parsed = parsed;
1414
this.source = source;
15+
this.name = name;
1516
this.names = names;
1617
this.visitors = visitors;
1718

src/generators/dom/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import Generator from '../Generator.js';
88
import * as shared from '../../shared/index.js';
99

1010
class DomGenerator extends Generator {
11-
constructor ( parsed, source, names, visitors ) {
12-
super( parsed, source, names, visitors );
11+
constructor ( parsed, source, name, names, visitors ) {
12+
super( parsed, source, name, names, visitors );
1313
this.renderers = [];
1414
this.uses = {};
1515

@@ -152,7 +152,7 @@ export default function dom ( parsed, source, options, names ) {
152152
const format = options.format || 'es';
153153
const name = options.name || 'SvelteComponent';
154154

155-
const generator = new DomGenerator( parsed, source, names, visitors );
155+
const generator = new DomGenerator( parsed, source, name, names, visitors );
156156

157157
const { computations, templateProperties } = generator.parseJs();
158158

src/generators/dom/visitors/Component.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import deindent from '../../../utils/deindent.js';
22
import CodeBuilder from '../../../utils/CodeBuilder.js';
33
import addComponentAttributes from './attributes/addComponentAttributes.js';
44

5+
function capDown ( name ) {
6+
return `${name[0].toLowerCase()}${name.slice( 1 )}`;
7+
}
8+
59
export default {
610
enter ( generator, node ) {
711
const hasChildren = node.children.length > 0;
8-
const name = generator.current.getUniqueName( `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` );
12+
const name = generator.current.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
913

1014
const local = {
1115
name,
@@ -104,9 +108,11 @@ export default {
104108
componentInitProperties.push(`data: ${name}_initialData`);
105109
}
106110

111+
const expression = node.name === ':Self' ? generator.name : `template.components.${node.name}`;
112+
107113
local.init.addBlockAtStart( deindent`
108114
${statements.join( '\n\n' )}
109-
var ${name} = new template.components.${node.name}({
115+
var ${name} = new ${expression}({
110116
${componentInitProperties.join(',\n')}
111117
});
112118
` );

src/generators/dom/visitors/Element.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Component from './Component.js';
55

66
export default {
77
enter ( generator, node ) {
8-
const isComponent = node.name in generator.components;
8+
const isComponent = node.name in generator.components || node.name === ':Self';
99
if ( isComponent ) {
1010
return Component.enter( generator, node );
1111
}

src/generators/server-side-rendering/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import visitors from './visitors/index.js';
55
import Generator from '../Generator.js';
66

77
class SsrGenerator extends Generator {
8-
constructor ( parsed, source, names, visitors ) {
9-
super( parsed, source, names, visitors );
8+
constructor ( parsed, source, name, names, visitors ) {
9+
super( parsed, source, name, names, visitors );
1010
this.bindings = [];
1111
this.renderCode = '';
1212
}
@@ -18,7 +18,7 @@ class SsrGenerator extends Generator {
1818

1919
this.bindings.push( deindent`
2020
if ( ${conditions.join( '&&' )} ) {
21-
tmp = template.components.${name}.data();
21+
tmp = ${name}.data();
2222
if ( '${binding.value}' in tmp ) {
2323
root.${binding.name} = tmp.${binding.value};
2424
settled = false;
@@ -36,7 +36,7 @@ export default function ssr ( parsed, source, options, names ) {
3636
const format = options.format || 'cjs';
3737
const name = options.name || 'SvelteComponent';
3838

39-
const generator = new SsrGenerator( parsed, source, names, visitors );
39+
const generator = new SsrGenerator( parsed, source, name, names, visitors );
4040

4141
const { computations, templateProperties } = generator.parseJs();
4242

src/generators/server-side-rendering/visitors/Component.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ export default {
4343
})
4444
.join( ', ' );
4545

46+
const expression = node.name === ':Self' ? generator.name : `template.components.${node.name}`;
47+
4648
bindings.forEach( binding => {
47-
generator.addBinding( binding, node.name );
49+
generator.addBinding( binding, expression );
4850
});
4951

50-
let open = `\${template.components.${node.name}.render({${props}}`;
52+
let open = `\${${expression}.render({${props}}`;
5153

5254
if ( node.children.length ) {
5355
open += `, { yield: () => \``;

src/generators/server-side-rendering/visitors/Element.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import voidElementNames from '../../../utils/voidElementNames.js';
33

44
export default {
55
enter ( generator, node ) {
6-
if ( node.name in generator.components ) {
6+
if ( node.name in generator.components || node.name === ':Self' ) {
77
Component.enter( generator, node );
88
return;
99
}
@@ -39,7 +39,7 @@ export default {
3939
},
4040

4141
leave ( generator, node ) {
42-
if ( node.name in generator.components ) {
42+
if ( node.name in generator.components || node.name === ':Self' ) {
4343
Component.leave( generator, node );
4444
return;
4545
}

src/parse/state/tag.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import voidElementNames from '../../utils/voidElementNames.js';
99
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
1010
const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/;
1111

12+
const SELF = ':Self';
13+
1214
const specials = {
1315
script: {
1416
read: readScript,
@@ -170,6 +172,27 @@ export default function tag ( parser ) {
170172
function readTagName ( parser ) {
171173
const start = parser.index;
172174

175+
if ( parser.eat( SELF ) ) {
176+
// check we're inside a block, otherwise this
177+
// will cause infinite recursion
178+
let i = parser.stack.length;
179+
let legal = false;
180+
181+
while ( i-- ) {
182+
const fragment = parser.stack[i];
183+
if ( fragment.type === 'IfBlock' || fragment.type === 'ElseBlock' ) {
184+
legal = true;
185+
break;
186+
}
187+
}
188+
189+
if ( !legal ) {
190+
parser.error( `<${SELF}> components can only exist inside if-blocks or each-blocks`, start );
191+
}
192+
193+
return SELF;
194+
}
195+
173196
const name = parser.readUntil( /(\s|\/|>)/ );
174197
if ( !validTagName.test( name ) ) {
175198
parser.error( `Expected valid tag name`, start );
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
data: {
3+
depth: 5
4+
},
5+
6+
html: `
7+
<span>5</span>
8+
<span>4</span>
9+
<span>3</span>
10+
<span>2</span>
11+
<span>1</span>
12+
<span>0</span>
13+
`
14+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<span>{{depth}}</span>
2+
{{#if depth > 0}}
3+
<:Self depth='{{depth - 1}}'/>
4+
{{/if}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"message": "<:Self> components can only exist inside if-blocks or each-blocks",
3+
"loc": {
4+
"line": 1,
5+
"column": 1
6+
},
7+
"pos": 1
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<:Self/>

test/parser/self-reference/input.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#if depth > 1}}
2+
<:Self depth='{{depth - 1}}'/>
3+
{{/if}}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"hash": 1792372370,
3+
"html": {
4+
"start": 0,
5+
"end": 57,
6+
"type": "Fragment",
7+
"children": [
8+
{
9+
"start": 0,
10+
"end": 57,
11+
"type": "IfBlock",
12+
"expression": {
13+
"type": "BinaryExpression",
14+
"start": 6,
15+
"end": 15,
16+
"left": {
17+
"type": "Identifier",
18+
"start": 6,
19+
"end": 11,
20+
"name": "depth"
21+
},
22+
"operator": ">",
23+
"right": {
24+
"type": "Literal",
25+
"start": 14,
26+
"end": 15,
27+
"value": 1,
28+
"raw": "1"
29+
}
30+
},
31+
"children": [
32+
{
33+
"start": 19,
34+
"end": 49,
35+
"type": "Element",
36+
"name": ":Self",
37+
"attributes": [
38+
{
39+
"start": 26,
40+
"end": 47,
41+
"type": "Attribute",
42+
"name": "depth",
43+
"value": [
44+
{
45+
"start": 33,
46+
"end": 46,
47+
"type": "MustacheTag",
48+
"expression": {
49+
"type": "BinaryExpression",
50+
"start": 35,
51+
"end": 44,
52+
"left": {
53+
"type": "Identifier",
54+
"start": 35,
55+
"end": 40,
56+
"name": "depth"
57+
},
58+
"operator": "-",
59+
"right": {
60+
"type": "Literal",
61+
"start": 43,
62+
"end": 44,
63+
"value": 1,
64+
"raw": "1"
65+
}
66+
}
67+
}
68+
]
69+
}
70+
],
71+
"children": []
72+
}
73+
]
74+
}
75+
]
76+
},
77+
"css": null,
78+
"js": null
79+
}

0 commit comments

Comments
 (0)