Skip to content

$$slots to check whether slots is provided #4602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* Expose object of which slots have received content in `$$slots` ([#2106](https://github.com/sveltejs/svelte/issues/2106))
* Add types to `createEventDispatcher` ([#5211](https://github.com/sveltejs/svelte/issues/5211))
* In SSR mode, do not automatically declare variables for reactive assignments to member expressions ([#5247](https://github.com/sveltejs/svelte/issues/5247))
* Include selector in message of `unused-css-selector` warning ([#5252](https://github.com/sveltejs/svelte/issues/5252))
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default class Renderer {

if (component.slots.size > 0) {
this.add_to_context('$$scope');
this.add_to_context('$$slots');
this.add_to_context('#slots');
}

if (this.binding_groups.size > 0) {
Expand Down
16 changes: 13 additions & 3 deletions src/compiler/compile/render_dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ export default function dom(
);
}

const uses_slots = component.var_lookup.has('$$slots');
let compute_slots;
if (uses_slots) {
compute_slots = b`
const $$slots = @compute_slots(#slots);
`;
}


const uses_props = component.var_lookup.has('$$props');
const uses_rest = component.var_lookup.has('$$restProps');
const $$props = uses_props || uses_rest ? `$$new_props` : `$$props`;
Expand Down Expand Up @@ -409,13 +418,14 @@ export default function dom(

${resubscribable_reactive_store_unsubscribers}

${component.slots.size || component.compile_options.dev || uses_slots ? b`let { $$slots: #slots = {}, $$scope } = $$props;` : null}
${component.compile_options.dev && b`@validate_slots('${component.tag}', #slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`}
${compute_slots}

${instance_javascript}

${unknown_props_check}

${component.slots.size || component.compile_options.dev ? b`let { $$slots = {}, $$scope } = $$props;` : null}
${component.compile_options.dev && b`@validate_slots('${component.tag}', $$slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`}

${renderer.binding_groups.size > 0 && b`const $$binding_groups = [${[...renderer.binding_groups.keys()].map(_ => x`[]`)}];`}

${component.partly_hoisted}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/wrappers/Slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export default class SlotWrapper extends Wrapper {
const slot_or_fallback = has_fallback ? block.get_unique_name(`${sanitize(slot_name)}_slot_or_fallback`) : slot;

block.chunks.init.push(b`
const ${slot_definition} = ${renderer.reference('$$slots')}.${slot_name};
const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name};
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn});
${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null}
`);
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/compile/render_ssr/handlers/Slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions)
const result = renderer.pop();

renderer.add_expression(x`
$$slots.${node.slot_name}
? $$slots.${node.slot_name}(${slot_data})
#slots.${node.slot_name}
? #slots.${node.slot_name}(${slot_data})
: ${result}
`);
}
6 changes: 5 additions & 1 deletion src/compiler/compile/render_ssr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export default function ssr(
const props = component.vars.filter(variable => !variable.module && variable.export_name);
const rest = uses_rest ? b`let $$restProps = @compute_rest_props($$props, [${props.map(prop => `"${prop.export_name}"`).join(',')}]);` : null;

const uses_slots = component.var_lookup.has('$$slots');
const slots = uses_slots ? b`let $$slots = @compute_slots(#slots);` : null;

const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$');
const reactive_store_values = reactive_stores
.map(({ name }) => {
Expand Down Expand Up @@ -118,6 +121,7 @@ export default function ssr(

const blocks = [
rest,
slots,
...reactive_stores.map(({ name }) => {
const store_name = name.slice(1);
const store = component.var_lookup.get(store_name);
Expand All @@ -144,7 +148,7 @@ export default function ssr(

${component.fully_hoisted}

const ${name} = @create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
const ${name} = @create_ssr_component(($$result, $$props, $$bindings, #slots) => {
${blocks}
});
`;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/utils/reserved_keywords.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const reserved_keywords = new Set(["$$props", "$$restProps"]);
export const reserved_keywords = new Set(["$$props", "$$restProps", "$$slots"]);

export function is_reserved_keyword(name) {
return reserved_keywords.has(name);
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/internal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ export function compute_rest_props(props, keys) {
return rest;
}

export function compute_slots(slots) {
const result = {};
for (const key in slots) {
result[key] = true;
}
return result;
}

export function once(fn) {
let ran = false;
return function(this: any, ...args) {
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/capture-inject-state/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ function instance($$self, $$props, $$invalidate) {
$$subscribe_prop = () => ($$unsubscribe_prop(), $$unsubscribe_prop = subscribe(prop, $$value => $$invalidate(2, $prop = $$value)), prop);

$$self.$$.on_destroy.push(() => $$unsubscribe_prop());
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let { prop } = $$props;
validate_store(prop, "prop");
$$subscribe_prop();
Expand All @@ -115,9 +117,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

$$self.$$set = $$props => {
if ("prop" in $$props) $$subscribe_prop($$invalidate(0, prop = $$props.prop));
if ("alias" in $$props) $$invalidate(1, realName = $$props.alias);
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/debug-empty/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,15 @@ function create_fragment(ctx) {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let { name } = $$props;
const writable_props = ["name"];

Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

$$self.$$set = $$props => {
if ("name" in $$props) $$invalidate(0, name = $$props.name);
};
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/debug-foo-bar-baz-things/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ function create_fragment(ctx) {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let { things } = $$props;
let { foo } = $$props;
let { bar } = $$props;
Expand All @@ -180,9 +182,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/debug-foo/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ function create_fragment(ctx) {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let { things } = $$props;
let { foo } = $$props;
const writable_props = ["things", "foo"];
Expand All @@ -172,9 +174,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
Expand Down
4 changes: 2 additions & 2 deletions test/js/samples/debug-hoisted/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ function create_fragment(ctx) {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let obj = { x: 5 };
let kobzol = 5;
const writable_props = [];
Expand All @@ -57,8 +59,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$capture_state = () => ({ obj, kobzol });

$$self.$inject_state = $$props => {
Expand Down
4 changes: 2 additions & 2 deletions test/js/samples/debug-no-dependencies/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ function create_fragment(ctx) {
}

function instance($$self, $$props) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
const writable_props = [];

Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
return [];
}

Expand Down
2 changes: 1 addition & 1 deletion test/js/samples/debug-ssr-foo/expected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { create_ssr_component, debug, each, escape } from "svelte/internal";

const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
const Component = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { things } = $$props;
let { foo } = $$props;
if ($$props.things === void 0 && $$bindings.things && things !== void 0) $$bindings.things(things);
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/dev-warning-missing-data-computed/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ function create_fragment(ctx) {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let { foo } = $$props;
let bar;
const writable_props = ["foo"];
Expand All @@ -73,9 +75,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};
Expand Down
5 changes: 2 additions & 3 deletions test/js/samples/loop-protect/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ function foo() {
}

function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots("Component", slots, []);
let node;

{
Expand Down Expand Up @@ -111,9 +113,6 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console_1.warn(`<Component> was created with unknown prop '${key}'`);
});

let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);

function div_binding($$value) {
binding_callbacks[$$value ? "unshift" : "push"](() => {
node = $$value;
Expand Down
2 changes: 1 addition & 1 deletion test/js/samples/ssr-no-oncreate-etc/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function swipe(node, callback) {

} // TODO implement

const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
const Component = create_ssr_component(($$result, $$props, $$bindings, slots) => {
onMount(() => {
console.log("onMount");
});
Expand Down
2 changes: 1 addition & 1 deletion test/js/samples/ssr-preserve-comments/expected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { create_ssr_component } from "svelte/internal";

const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
const Component = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<div>content</div>

<div>more content</div>`;
Expand Down
31 changes: 31 additions & 0 deletions test/runtime/samples/$$slot/A.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script>
let data = '';

if ($$slots.b) {
data = 'foo';
}

export function getData() {
return data;
}

function toString(data) {
const result = {};
const sortedKeys = Object.keys(data).sort();
sortedKeys.forEach(key => result[key] = data[key]);
return JSON.stringify(result);
}
</script>

<slot></slot>
<slot name="a"></slot>

$$slots: {toString($$slots)}

{#if $$slots.b}
<div>
<slot name="b"></slot>
</div>
{:else}
Slot b is not available
{/if}
18 changes: 18 additions & 0 deletions test/runtime/samples/$$slot/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default {
html: `
<span>bye</span><span>world</span>
<span slot="a">hello world</span>
$$slots: {"a":true,"default":true}
Slot b is not available

<span>bye world</span>
<span slot="a">hello world</span>
$$slots: {"a":true,"b":true,"default":true}
<div><span slot="b">hello world</span></div>
`,

async test({ assert, target, component }) {
assert.equal(component.getA(), '');
assert.equal(component.getB(), 'foo');
}
};
23 changes: 23 additions & 0 deletions test/runtime/samples/$$slot/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script>
import A from "./A.svelte";
let a, b;

export function getA() {
return a.getData();
}
export function getB() {
return b.getData();
}
</script>

<A bind:this={a}>
<span slot="a">hello world</span>
<span>bye</span>
<span>world</span>
</A>

<A bind:this={b}>
<span slot="a">hello world</span>
<span slot="b">hello world</span>
<span>bye world</span>
</A>