Merge pull request #332 from sveltejs/gh-51

self-references
pull/339/head
Rich Harris 8 years ago committed by GitHub
commit 319b3b6765

@ -9,9 +9,10 @@ import getOutro from './shared/utils/getOutro.js';
import annotateWithScopes from './annotateWithScopes.js'; import annotateWithScopes from './annotateWithScopes.js';
export default class Generator { export default class Generator {
constructor ( parsed, source, names, visitors ) { constructor ( parsed, source, name, names, visitors ) {
this.parsed = parsed; this.parsed = parsed;
this.source = source; this.source = source;
this.name = name;
this.names = names; this.names = names;
this.visitors = visitors; this.visitors = visitors;

@ -8,8 +8,8 @@ import Generator from '../Generator.js';
import * as shared from '../../shared/index.js'; import * as shared from '../../shared/index.js';
class DomGenerator extends Generator { class DomGenerator extends Generator {
constructor ( parsed, source, names, visitors ) { constructor ( parsed, source, name, names, visitors ) {
super( parsed, source, names, visitors ); super( parsed, source, name, names, visitors );
this.renderers = []; this.renderers = [];
this.uses = {}; this.uses = {};
@ -152,7 +152,7 @@ export default function dom ( parsed, source, options, names ) {
const format = options.format || 'es'; const format = options.format || 'es';
const name = options.name || 'SvelteComponent'; const name = options.name || 'SvelteComponent';
const generator = new DomGenerator( parsed, source, names, visitors ); const generator = new DomGenerator( parsed, source, name, names, visitors );
const { computations, templateProperties } = generator.parseJs(); const { computations, templateProperties } = generator.parseJs();

@ -2,10 +2,14 @@ import deindent from '../../../utils/deindent.js';
import CodeBuilder from '../../../utils/CodeBuilder.js'; import CodeBuilder from '../../../utils/CodeBuilder.js';
import addComponentAttributes from './attributes/addComponentAttributes.js'; import addComponentAttributes from './attributes/addComponentAttributes.js';
function capDown ( name ) {
return `${name[0].toLowerCase()}${name.slice( 1 )}`;
}
export default { export default {
enter ( generator, node ) { enter ( generator, node ) {
const hasChildren = node.children.length > 0; const hasChildren = node.children.length > 0;
const name = generator.current.getUniqueName( `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` ); const name = generator.current.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
const local = { const local = {
name, name,
@ -104,9 +108,11 @@ export default {
componentInitProperties.push(`data: ${name}_initialData`); componentInitProperties.push(`data: ${name}_initialData`);
} }
const expression = node.name === ':Self' ? generator.name : `template.components.${node.name}`;
local.init.addBlockAtStart( deindent` local.init.addBlockAtStart( deindent`
${statements.join( '\n\n' )} ${statements.join( '\n\n' )}
var ${name} = new template.components.${node.name}({ var ${name} = new ${expression}({
${componentInitProperties.join(',\n')} ${componentInitProperties.join(',\n')}
}); });
` ); ` );

@ -5,7 +5,7 @@ import Component from './Component.js';
export default { export default {
enter ( generator, node ) { enter ( generator, node ) {
const isComponent = node.name in generator.components; const isComponent = node.name in generator.components || node.name === ':Self';
if ( isComponent ) { if ( isComponent ) {
return Component.enter( generator, node ); return Component.enter( generator, node );
} }

@ -5,8 +5,8 @@ import visitors from './visitors/index.js';
import Generator from '../Generator.js'; import Generator from '../Generator.js';
class SsrGenerator extends Generator { class SsrGenerator extends Generator {
constructor ( parsed, source, names, visitors ) { constructor ( parsed, source, name, names, visitors ) {
super( parsed, source, names, visitors ); super( parsed, source, name, names, visitors );
this.bindings = []; this.bindings = [];
this.renderCode = ''; this.renderCode = '';
} }
@ -18,7 +18,7 @@ class SsrGenerator extends Generator {
this.bindings.push( deindent` this.bindings.push( deindent`
if ( ${conditions.join( '&&' )} ) { if ( ${conditions.join( '&&' )} ) {
tmp = template.components.${name}.data(); tmp = ${name}.data();
if ( '${binding.value}' in tmp ) { if ( '${binding.value}' in tmp ) {
root.${binding.name} = tmp.${binding.value}; root.${binding.name} = tmp.${binding.value};
settled = false; settled = false;
@ -36,7 +36,7 @@ export default function ssr ( parsed, source, options, names ) {
const format = options.format || 'cjs'; const format = options.format || 'cjs';
const name = options.name || 'SvelteComponent'; const name = options.name || 'SvelteComponent';
const generator = new SsrGenerator( parsed, source, names, visitors ); const generator = new SsrGenerator( parsed, source, name, names, visitors );
const { computations, templateProperties } = generator.parseJs(); const { computations, templateProperties } = generator.parseJs();

@ -43,11 +43,13 @@ export default {
}) })
.join( ', ' ); .join( ', ' );
const expression = node.name === ':Self' ? generator.name : `template.components.${node.name}`;
bindings.forEach( binding => { bindings.forEach( binding => {
generator.addBinding( binding, node.name ); generator.addBinding( binding, expression );
}); });
let open = `\${template.components.${node.name}.render({${props}}`; let open = `\${${expression}.render({${props}}`;
if ( node.children.length ) { if ( node.children.length ) {
open += `, { yield: () => \``; open += `, { yield: () => \``;

@ -3,7 +3,7 @@ import voidElementNames from '../../../utils/voidElementNames.js';
export default { export default {
enter ( generator, node ) { enter ( generator, node ) {
if ( node.name in generator.components ) { if ( node.name in generator.components || node.name === ':Self' ) {
Component.enter( generator, node ); Component.enter( generator, node );
return; return;
} }
@ -39,7 +39,7 @@ export default {
}, },
leave ( generator, node ) { leave ( generator, node ) {
if ( node.name in generator.components ) { if ( node.name in generator.components || node.name === ':Self' ) {
Component.leave( generator, node ); Component.leave( generator, node );
return; return;
} }

@ -9,6 +9,8 @@ import voidElementNames from '../../utils/voidElementNames.js';
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/; const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/;
const SELF = ':Self';
const specials = { const specials = {
script: { script: {
read: readScript, read: readScript,
@ -170,6 +172,27 @@ export default function tag ( parser ) {
function readTagName ( parser ) { function readTagName ( parser ) {
const start = parser.index; const start = parser.index;
if ( parser.eat( SELF ) ) {
// check we're inside a block, otherwise this
// will cause infinite recursion
let i = parser.stack.length;
let legal = false;
while ( i-- ) {
const fragment = parser.stack[i];
if ( fragment.type === 'IfBlock' || fragment.type === 'ElseBlock' ) {
legal = true;
break;
}
}
if ( !legal ) {
parser.error( `<${SELF}> components can only exist inside if-blocks or each-blocks`, start );
}
return SELF;
}
const name = parser.readUntil( /(\s|\/|>)/ ); const name = parser.readUntil( /(\s|\/|>)/ );
if ( !validTagName.test( name ) ) { if ( !validTagName.test( name ) ) {
parser.error( `Expected valid tag name`, start ); parser.error( `Expected valid tag name`, start );

@ -0,0 +1,14 @@
export default {
data: {
depth: 5
},
html: `
<span>5</span>
<span>4</span>
<span>3</span>
<span>2</span>
<span>1</span>
<span>0</span>
`
};

@ -0,0 +1,4 @@
<span>{{depth}}</span>
{{#if depth > 0}}
<:Self depth='{{depth - 1}}'/>
{{/if}}

@ -0,0 +1,8 @@
{
"message": "<:Self> components can only exist inside if-blocks or each-blocks",
"loc": {
"line": 1,
"column": 1
},
"pos": 1
}

@ -0,0 +1,3 @@
{{#if depth > 1}}
<:Self depth='{{depth - 1}}'/>
{{/if}}

@ -0,0 +1,79 @@
{
"hash": 1792372370,
"html": {
"start": 0,
"end": 57,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 57,
"type": "IfBlock",
"expression": {
"type": "BinaryExpression",
"start": 6,
"end": 15,
"left": {
"type": "Identifier",
"start": 6,
"end": 11,
"name": "depth"
},
"operator": ">",
"right": {
"type": "Literal",
"start": 14,
"end": 15,
"value": 1,
"raw": "1"
}
},
"children": [
{
"start": 19,
"end": 49,
"type": "Element",
"name": ":Self",
"attributes": [
{
"start": 26,
"end": 47,
"type": "Attribute",
"name": "depth",
"value": [
{
"start": 33,
"end": 46,
"type": "MustacheTag",
"expression": {
"type": "BinaryExpression",
"start": 35,
"end": 44,
"left": {
"type": "Identifier",
"start": 35,
"end": 40,
"name": "depth"
},
"operator": "-",
"right": {
"type": "Literal",
"start": 43,
"end": 44,
"value": 1,
"raw": "1"
}
}
}
]
}
],
"children": []
}
]
}
]
},
"css": null,
"js": null
}
Loading…
Cancel
Save