inherit default slot values in named slots in SSR mode

pull/2419/head
Richard Harris 5 years ago
parent 45f28fbf30
commit 1d6ac5ea34

@ -20,6 +20,7 @@ import fuzzymatch from '../utils/fuzzymatch';
import { remove_indentation, add_indentation } from '../utils/indentation';
import get_object from './utils/get_object';
import unwrap_parens from './utils/unwrap_parens';
import Slot from './nodes/Slot';
type ComponentOptions = {
namespace?: string;
@ -117,6 +118,8 @@ export default class Component {
used_names: Set<string> = new Set();
globally_used_names: Set<string> = new Set();
slots: Map<string, Slot> = new Map();
constructor(
ast: Ast,
source: string,

@ -1,15 +1,17 @@
import Node from './shared/Node';
import Element from './Element';
import Attribute from './Attribute';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
export default class Slot extends Element {
type: 'Element';
name: string;
slot_name: string;
attributes: Attribute[];
children: Node[];
slot_name: string;
values: Map<string, Attribute> = new Map();
constructor(component, parent, scope, info) {
constructor(component: Component, parent: Node, scope: TemplateScope, info: any) {
super(component, parent, scope, info);
info.attributes.forEach(attr => {
@ -37,6 +39,8 @@ export default class Slot extends Element {
}
}
this.values.set(attr.name, new Attribute(component, this, scope, attr));
// TODO should duplicate slots be disallowed? Feels like it's more likely to be a
// bug than anything. Perhaps it should be a warning
@ -49,6 +53,30 @@ export default class Slot extends Element {
if (!this.slot_name) this.slot_name = 'default';
if (this.slot_name === 'default') {
// if this is the default slot, add our dependencies to any
// other slots (which inherit our slot values) that were
// previously encountered
component.slots.forEach((slot) => {
this.values.forEach((attribute, name) => {
if (!slot.values.has(name)) {
slot.values.set(name, attribute);
}
});
});
} else if (component.slots.has('default')) {
// otherwise, go the other way — inherit values from
// a previously encountered default slot
const default_slot = component.slots.get('default');
default_slot.values.forEach((attribute, name) => {
if (!this.values.has(name)) {
this.values.set(name, attribute);
}
});
}
component.slots.set(this.slot_name, this);
// if (node.attributes.length === 0) && validator.slots.has('default')) {
// validator.error(node, {
// code: `duplicate-slot`,

@ -14,7 +14,6 @@ import Attribute from '../../nodes/Attribute';
export default class SlotWrapper extends Wrapper {
node: Slot;
fragment: FragmentWrapper;
slot_values: Map<string, Attribute> = new Map();
var = 'slot';
dependencies: Set<string> = new Set(['$$scope']);
@ -39,36 +38,7 @@ export default class SlotWrapper extends Wrapper {
next_sibling
);
this.node.attributes.forEach(attribute => {
if (attribute.name !== 'name') this.slot_values.set(attribute.name, attribute);
});
if (this.node.slot_name === 'default') {
// if this is the default slot, add our dependencies to any
// other slots (which inherit our slot values) that were
// previously encountered
renderer.slots.forEach(({ slot, block }) => {
this.slot_values.forEach((attribute, name) => {
if (!slot.slot_values.has(name)) {
slot.slot_values.set(name, attribute);
add_to_set(slot.dependencies, attribute.dependencies);
block.add_dependencies(attribute.dependencies);
}
});
});
} else if (renderer.slots.has('default')) {
// otherwise, go the other way — inherit values from
// a previously encountered default slot
const { slot: default_slot } = renderer.slots.get('default');
default_slot.slot_values.forEach((attribute, name) => {
if (!this.slot_values.has(name)) {
this.slot_values.set(name, attribute);
}
});
}
this.slot_values.forEach(attribute => {
this.node.values.forEach(attribute => {
add_to_set(this.dependencies, attribute.dependencies);
block.add_dependencies(attribute.dependencies);
});
@ -92,18 +62,18 @@ export default class SlotWrapper extends Wrapper {
let get_slot_changes;
let get_slot_context;
if (this.slot_values.size > 0) {
const attributes = Array.from(this.slot_values.values());
if (this.node.values.size > 0) {
const attributes = Array.from(this.node.values.values());
get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`);
get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`);
const context_props = get_slot_data(attributes, false);
const context_props = get_slot_data(this.node.values, false);
const changes_props = [];
const dependencies = new Set();
attributes.forEach(attribute => {
this.node.values.forEach(attribute => {
attribute.chunks.forEach(chunk => {
if ((chunk as Expression).dependencies) {
add_to_set(dependencies, (chunk as Expression).dependencies);

@ -51,13 +51,21 @@ export default function(node, renderer, options) {
let textarea_contents; // awkward special case
const slot = node.get_static_attribute_value('slot');
if (slot && node.has_ancestor('InlineComponent')) {
const component = node.find_nearest(/InlineComponent/);
if (slot && component) {
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
const slot_name = slot.chunks[0].data;
const target = renderer.targets[renderer.targets.length - 1];
target.slot_stack.push(slot_name);
target.slots[slot_name] = '';
const lets = node.lets;
const seen = new Set(lets.map(l => l.name));
component.lets.forEach(l => {
if (!seen.has(l.name)) lets.push(l);
});
options.slot_scopes.set(slot_name, get_slot_scope(node.lets));
}

@ -4,7 +4,7 @@ import get_slot_data from '../../utils/get_slot_data';
export default function(node, renderer, options) {
const prop = quote_prop_if_necessary(node.slot_name);
const slot_data = get_slot_data(node.attributes, true);
const slot_data = get_slot_data(node.values, true);
const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : '';

@ -1,8 +1,9 @@
import { snip } from './snip';
import { stringify_attribute } from './stringify_attribute';
import Attribute from '../nodes/Attribute';
export default function get_slot_data(attributes, is_ssr: boolean) {
return attributes
export default function get_slot_data(values: Map<string, Attribute>, is_ssr: boolean) {
return Array.from(values.values())
.filter(attribute => attribute.name !== 'name')
.map(attribute => {
const value = attribute.is_true

Loading…
Cancel
Save