mirror of https://github.com/sveltejs/svelte
parent
f9108d01d7
commit
07d4350a6f
@ -0,0 +1,107 @@
|
|||||||
|
import { x } from 'code-red';
|
||||||
|
import { TemplateNode } from '../../interfaces';
|
||||||
|
import { regex_only_whitespaces } from '../../utils/patterns';
|
||||||
|
import compiler_errors from '../compiler_errors';
|
||||||
|
import Component from '../Component';
|
||||||
|
|
||||||
|
export function extract_children_to_slot_templates(component: Component, node: TemplateNode, extract_default_slot: boolean) {
|
||||||
|
const result = [];
|
||||||
|
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||||
|
const child = node.children[i];
|
||||||
|
if (child.type === 'SlotTemplate') {
|
||||||
|
result.push(child);
|
||||||
|
node.children.splice(i, 1);
|
||||||
|
} else if ((child.type === 'Element' || child.type === 'InlineComponent' || child.type === 'Slot') && child.attributes.find(attribute => attribute.name === 'slot')) {
|
||||||
|
let slot_template = {
|
||||||
|
start: child.start,
|
||||||
|
end: child.end,
|
||||||
|
type: 'SlotTemplate',
|
||||||
|
name: 'svelte:fragment',
|
||||||
|
attributes: [],
|
||||||
|
children: [child]
|
||||||
|
};
|
||||||
|
|
||||||
|
// transfer attributes
|
||||||
|
for (let i = child.attributes.length - 1; i >= 0; i--) {
|
||||||
|
const attribute = child.attributes[i];
|
||||||
|
if (attribute.type === 'Let') {
|
||||||
|
slot_template.attributes.push(attribute);
|
||||||
|
child.attributes.splice(i, 1);
|
||||||
|
} else if (attribute.type === 'Attribute' && attribute.name === 'slot') {
|
||||||
|
slot_template.attributes.push(attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// transfer const
|
||||||
|
for (let i = child.children.length - 1; i >= 0; i--) {
|
||||||
|
const child_child = child.children[i];
|
||||||
|
if (child_child.type === 'ConstTag') {
|
||||||
|
slot_template.children.push(child_child);
|
||||||
|
child.children.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the <slot slot="x"> does not have any fallback
|
||||||
|
// then we make <slot slot="x" name="b" />
|
||||||
|
// into {#if $$slots.x}<slot slot="x" name="b" />{/if}
|
||||||
|
// this makes the slots forwarding to passthrough
|
||||||
|
if (child.type === 'Slot' && child.children.length === 0) {
|
||||||
|
const slot_template_name = child.attributes.find(attribute => attribute.name === 'name')?.value[0].data ?? 'default';
|
||||||
|
slot_template = {
|
||||||
|
start: slot_template.start,
|
||||||
|
end: slot_template.end,
|
||||||
|
type: 'SlotTemplateIfBlock',
|
||||||
|
expression: x`$$slots.${slot_template_name}`,
|
||||||
|
children: [slot_template]
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(slot_template);
|
||||||
|
node.children.splice(i, 1);
|
||||||
|
} else if (child.type === 'Comment' && result.length > 0) {
|
||||||
|
result[result.length - 1].children.unshift(child);
|
||||||
|
node.children.splice(i, 1);
|
||||||
|
} else if (child.type === 'Text' && regex_only_whitespaces.test(child.data)) {
|
||||||
|
// ignore
|
||||||
|
} else if (child.type === 'ConstTag') {
|
||||||
|
if (!extract_default_slot) {
|
||||||
|
result.push(child);
|
||||||
|
}
|
||||||
|
} else if (child.type === 'IfBlock' && if_block_contains_slot_template(child)) {
|
||||||
|
result.push({
|
||||||
|
...child,
|
||||||
|
type: 'SlotTemplateIfBlock'
|
||||||
|
});
|
||||||
|
node.children.splice(i, 1);
|
||||||
|
} else if (!extract_default_slot) {
|
||||||
|
component.error(child, compiler_errors.invalid_mix_element_and_conditional_slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_default_slot) {
|
||||||
|
if (node.children.some(node => not_whitespace_text(node))) {
|
||||||
|
result.push({
|
||||||
|
start: node.start,
|
||||||
|
end: node.end,
|
||||||
|
type: 'SlotTemplate',
|
||||||
|
name: 'svelte:fragment',
|
||||||
|
attributes: [],
|
||||||
|
children: node.children
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function not_whitespace_text(node) {
|
||||||
|
return !(node.type === 'Text' && regex_only_whitespaces.test(node.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
function if_block_contains_slot_template(node: TemplateNode) {
|
||||||
|
for (const child of node.children) {
|
||||||
|
if (child.type === 'SlotTemplate') return true;
|
||||||
|
if (child.type === 'IfBlock' && if_block_contains_slot_template(child)) return true;
|
||||||
|
if ((child.type === 'Element' || child.type === 'InlineComponent' || child.type === 'Slot') && child.attributes.find(attribute => attribute.name === 'slot')) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<slot name="a">default a</slot>
|
||||||
|
<hr/>
|
||||||
|
<slot name="b">default b</slot>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
default a
|
||||||
|
<hr>
|
||||||
|
<div slot="b">hello b</div>
|
||||||
|
`,
|
||||||
|
test({ assert, component, target }) {
|
||||||
|
component.condition = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div slot="a">hello a</div>
|
||||||
|
<hr>
|
||||||
|
<div slot="b">hello b</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<script>
|
||||||
|
import Foo from "./Foo.svelte";
|
||||||
|
export let condition = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Foo>
|
||||||
|
{#if condition}
|
||||||
|
<div slot="a">hello a</div>
|
||||||
|
{/if}
|
||||||
|
<div slot="b">hello b</div>
|
||||||
|
</Foo>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
x: <slot name="x">fallback x</slot>
|
||||||
|
y: <slot name="y">fallback y</slot>
|
||||||
|
z: <slot name="z">fallback z</slot>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
import Bar from './Bar.svelte';
|
||||||
|
export let condition1 = false;
|
||||||
|
export let condition2 = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Bar>
|
||||||
|
<slot name="a" slot="x">Fallback a</slot>
|
||||||
|
{#if condition1}
|
||||||
|
<slot name="b" slot="y">Fallback b</slot>
|
||||||
|
{/if}
|
||||||
|
{#if condition2}
|
||||||
|
<slot name="c" slot="z">Fallback c</slot>
|
||||||
|
{/if}
|
||||||
|
</Bar>
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
x: Fallback a
|
||||||
|
y: fallback y
|
||||||
|
z: <div slot="c">hello c</div>
|
||||||
|
`,
|
||||||
|
test({ assert, component, target }) {
|
||||||
|
component.condition1 = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
x: Fallback a
|
||||||
|
y: <div slot="b">hello b</div>
|
||||||
|
z: <div slot="c">hello c</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
component.condition3 = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
x: <div slot="a">hello a</div>
|
||||||
|
y: <div slot="b">hello b</div>
|
||||||
|
z: <div slot="c">hello c</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
component.condition4 = false;
|
||||||
|
component.condition1 = false;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
x: <div slot="a">hello a</div>
|
||||||
|
y: fallback y
|
||||||
|
z: <div slot="c">hello c</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
component.condition2 = false;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
x: <div slot="a">hello a</div>
|
||||||
|
y: fallback y
|
||||||
|
z: fallback z
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import Foo from "./Foo.svelte";
|
||||||
|
export let condition1;
|
||||||
|
export let condition2;
|
||||||
|
export let condition3 = false;
|
||||||
|
export let condition4 = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Foo {condition1} {condition2}>
|
||||||
|
{#if condition3}
|
||||||
|
<div slot="a">hello a</div>
|
||||||
|
{/if}
|
||||||
|
{#if condition4}
|
||||||
|
<div slot="b">hello b</div>
|
||||||
|
{/if}
|
||||||
|
<div slot="c">hello c</div>
|
||||||
|
</Foo>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
x: <slot name="x">fallback x</slot>
|
||||||
|
y: <slot name="y">fallback y</slot>
|
||||||
|
z: <slot name="z">fallback z</slot>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<script>
|
||||||
|
import Bar from './Bar.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Bar>
|
||||||
|
<slot name="a" slot="x" />
|
||||||
|
<slot name="b" slot="y" />
|
||||||
|
<slot name="c" slot="z" />
|
||||||
|
</Bar>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
x: fallback x
|
||||||
|
y: <div slot="b">hello b</div>
|
||||||
|
z: fallback z
|
||||||
|
`,
|
||||||
|
test({ assert, component, target }) {
|
||||||
|
component.condition = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
x: <div slot="a">hello a</div>
|
||||||
|
y: <div slot="b">hello b</div>
|
||||||
|
z: fallback z
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<script>
|
||||||
|
import Foo from "./Foo.svelte";
|
||||||
|
export let condition = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Foo>
|
||||||
|
{#if condition}
|
||||||
|
<div slot="a">hello a</div>
|
||||||
|
{/if}
|
||||||
|
<div slot="b">hello b</div>
|
||||||
|
</Foo>
|
||||||
@ -1,12 +0,0 @@
|
|||||||
[{
|
|
||||||
"code": "invalid-slotted-content",
|
|
||||||
"message": "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element",
|
|
||||||
"start": {
|
|
||||||
"line": 7,
|
|
||||||
"column": 7
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 7,
|
|
||||||
"column": 17
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<script>
|
|
||||||
import Nested from './Nested.svelte';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Nested>
|
|
||||||
{#if thing}
|
|
||||||
<div slot='foo'>{thing}</div>
|
|
||||||
{/if}
|
|
||||||
</Nested>
|
|
||||||
Loading…
Reference in new issue