From 53d0d64971a29f982def0dfd3e53461ff416abb8 Mon Sep 17 00:00:00 2001
From: Emil Ajdyna <2088208+emilos@users.noreply.github.com>
Date: Wed, 7 Feb 2018 21:05:18 +0100
Subject: [PATCH] Rename default slot to base to support older browsers
---
src/generators/nodes/Component.ts | 14 +-
src/generators/nodes/Element.ts | 2 +-
src/generators/nodes/Slot.ts | 2 +-
.../visitors/Component.ts | 4 +-
.../server-side-rendering/visitors/Slot.ts | 2 +-
src/validate/html/validateElement.ts | 4 +-
test/js/samples/slot-base/Footer.html | 7 +
test/js/samples/slot-base/expected-bundle.js | 262 ++++++++++++++++++
test/js/samples/slot-base/expected.js | 69 +++++
test/js/samples/slot-base/input.html | 7 +
.../component-slot-base-reserved/errors.json | 8 +
.../component-slot-base-reserved/input.html | 1 +
12 files changed, 368 insertions(+), 14 deletions(-)
create mode 100644 test/js/samples/slot-base/Footer.html
create mode 100644 test/js/samples/slot-base/expected-bundle.js
create mode 100644 test/js/samples/slot-base/expected.js
create mode 100644 test/js/samples/slot-base/input.html
create mode 100644 test/validator/samples/component-slot-base-reserved/errors.json
create mode 100644 test/validator/samples/component-slot-base-reserved/input.html
diff --git a/src/generators/nodes/Component.ts b/src/generators/nodes/Component.ts
index 1b9b1bb8b6..cbdfae85ca 100644
--- a/src/generators/nodes/Component.ts
+++ b/src/generators/nodes/Component.ts
@@ -50,7 +50,7 @@ export default class Component extends Node {
);
if (this.children.length) {
- this._slots = new Set(['default']);
+ this._slots = new Set(['base']);
this.children.forEach(child => {
child.init(block, stripWhitespace, nextSibling);
@@ -75,7 +75,7 @@ export default class Component extends Node {
componentInitProperties.push(`slots: { ${slots.join(', ')} }`);
this.children.forEach((child: Node) => {
- child.build(block, `${this.var}._slotted.default`, 'nodes');
+ child.build(block, `${this.var}._slotted.base`, 'nodes');
});
}
@@ -584,7 +584,7 @@ function remount(generator: DomGenerator, node: Node, name: string) {
// TODO make this a method of the nodes
if (node.type === 'Component') {
- return `${node.var}._mount(${name}._slotted.default, null);`;
+ return `${node.var}._mount(${name}._slotted.base, null);`;
}
if (node.type === 'Element') {
@@ -593,17 +593,17 @@ function remount(generator: DomGenerator, node: Node, name: string) {
return `@appendNode(${node.var}, ${name}._slotted.${node.getStaticAttributeValue('slot')});`;
}
- return `@appendNode(${node.var}, ${name}._slotted.default);`;
+ return `@appendNode(${node.var}, ${name}._slotted.base);`;
}
if (node.type === 'Text' || node.type === 'MustacheTag' || node.type === 'RawMustacheTag') {
- return `@appendNode(${node.var}, ${name}._slotted.default);`;
+ return `@appendNode(${node.var}, ${name}._slotted.base);`;
}
if (node.type === 'EachBlock') {
// TODO consider keyed blocks
- return `for (var #i = 0; #i < ${node.iterations}.length; #i += 1) ${node.iterations}[#i].m(${name}._slotted.default, null);`;
+ return `for (var #i = 0; #i < ${node.iterations}.length; #i += 1) ${node.iterations}[#i].m(${name}._slotted.base, null);`;
}
- return `${node.var}.m(${name}._slotted.default, null);`;
+ return `${node.var}.m(${name}._slotted.base, null);`;
}
\ No newline at end of file
diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts
index 48852c27ea..f5ab31759a 100644
--- a/src/generators/nodes/Element.ts
+++ b/src/generators/nodes/Element.ts
@@ -158,7 +158,7 @@ export default class Element extends Node {
const { generator } = this;
if (this.name === 'slot') {
- const slotName = this.getStaticAttributeValue('name') || 'default';
+ const slotName = this.getStaticAttributeValue('name') || 'base';
this.generator.slots.add(slotName);
}
diff --git a/src/generators/nodes/Slot.ts b/src/generators/nodes/Slot.ts
index 4413eea916..63bc0ac1b8 100644
--- a/src/generators/nodes/Slot.ts
+++ b/src/generators/nodes/Slot.ts
@@ -31,7 +31,7 @@ export default class Slot extends Element {
) {
const { generator } = this;
- const slotName = this.getStaticAttributeValue('name') || 'default';
+ const slotName = this.getStaticAttributeValue('name') || 'base';
generator.slots.add(slotName);
const content_name = block.getUniqueName(`slot_content_${slotName}`);
diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts
index 4843010fcb..791e4385e9 100644
--- a/src/generators/server-side-rendering/visitors/Component.ts
+++ b/src/generators/server-side-rendering/visitors/Component.ts
@@ -93,8 +93,8 @@ export default function visitComponent(
if (node.children.length) {
const appendTarget: AppendTarget = {
- slots: { default: '' },
- slotStack: ['default']
+ slots: { base: '' },
+ slotStack: ['base']
};
generator.appendTargets.push(appendTarget);
diff --git a/src/generators/server-side-rendering/visitors/Slot.ts b/src/generators/server-side-rendering/visitors/Slot.ts
index 7af3b05588..209b3d63cc 100644
--- a/src/generators/server-side-rendering/visitors/Slot.ts
+++ b/src/generators/server-side-rendering/visitors/Slot.ts
@@ -9,7 +9,7 @@ export default function visitSlot(
node: Node
) {
const name = node.attributes.find((attribute: Node) => attribute.name);
- const slotName = name && name.value[0].data || 'default';
+ const slotName = name && name.value[0].data || 'base';
generator.append(`\${options && options.slotted && options.slotted.${slotName} ? options.slotted.${slotName}() : \``);
diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts
index 550f8d2ae7..aadbb0a8f7 100644
--- a/src/validate/html/validateElement.ts
+++ b/src/validate/html/validateElement.ts
@@ -38,8 +38,8 @@ export default function validateElement(
}
const slotName = nameAttribute.value[0].data;
- if (slotName === 'default') {
- validator.error(`default is a reserved word — it cannot be used as a slot name`, nameAttribute.start);
+ if (slotName === 'default' || slotName === 'base') {
+ validator.error(`${slotName} is a reserved word — it cannot be used as a slot name`, nameAttribute.start);
}
// TODO should duplicate slots be disallowed? Feels like it's more likely to be a
diff --git a/test/js/samples/slot-base/Footer.html b/test/js/samples/slot-base/Footer.html
new file mode 100644
index 0000000000..06d5b87690
--- /dev/null
+++ b/test/js/samples/slot-base/Footer.html
@@ -0,0 +1,7 @@
+
+
diff --git a/test/js/samples/slot-base/expected-bundle.js b/test/js/samples/slot-base/expected-bundle.js
new file mode 100644
index 0000000000..10d55d2371
--- /dev/null
+++ b/test/js/samples/slot-base/expected-bundle.js
@@ -0,0 +1,262 @@
+function noop() {}
+
+function assign(target) {
+ var k,
+ source,
+ i = 1,
+ len = arguments.length;
+ for (; i < len; i++) {
+ source = arguments[i];
+ for (k in source) target[k] = source[k];
+ }
+
+ return target;
+}
+
+function appendNode(node, target) {
+ target.appendChild(node);
+}
+
+function insertNode(node, target, anchor) {
+ target.insertBefore(node, anchor);
+}
+
+function detachNode(node) {
+ node.parentNode.removeChild(node);
+}
+
+function reinsertChildren(parent, target) {
+ while (parent.firstChild) target.appendChild(parent.firstChild);
+}
+
+function createElement(name) {
+ return document.createElement(name);
+}
+
+function setAttribute(node, attribute, value) {
+ node.setAttribute(attribute, value);
+}
+
+function blankObject() {
+ return Object.create(null);
+}
+
+function destroy(detach) {
+ this.destroy = noop;
+ this.fire('destroy');
+ this.set = this.get = noop;
+
+ if (detach !== false) this._fragment.u();
+ this._fragment.d();
+ this._fragment = this._state = null;
+}
+
+function differs(a, b) {
+ return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
+}
+
+function dispatchObservers(component, group, changed, newState, oldState) {
+ for (var key in group) {
+ if (!changed[key]) continue;
+
+ var newValue = newState[key];
+ var oldValue = oldState[key];
+
+ var callbacks = group[key];
+ if (!callbacks) continue;
+
+ for (var i = 0; i < callbacks.length; i += 1) {
+ var callback = callbacks[i];
+ if (callback.__calling) continue;
+
+ callback.__calling = true;
+ callback.call(component, newValue, oldValue);
+ callback.__calling = false;
+ }
+ }
+}
+
+function fire(eventName, data) {
+ var handlers =
+ eventName in this._handlers && this._handlers[eventName].slice();
+ if (!handlers) return;
+
+ for (var i = 0; i < handlers.length; i += 1) {
+ handlers[i].call(this, data);
+ }
+}
+
+function get(key) {
+ return key ? this._state[key] : this._state;
+}
+
+function init(component, options) {
+ component._observers = { pre: blankObject(), post: blankObject() };
+ component._handlers = blankObject();
+ component._bind = options._bind;
+
+ component.options = options;
+ component.root = options.root || component;
+ component.store = component.root.store || options.store;
+}
+
+function observe(key, callback, options) {
+ var group = options && options.defer
+ ? this._observers.post
+ : this._observers.pre;
+
+ (group[key] || (group[key] = [])).push(callback);
+
+ if (!options || options.init !== false) {
+ callback.__calling = true;
+ callback.call(this, this._state[key]);
+ callback.__calling = false;
+ }
+
+ return {
+ cancel: function() {
+ var index = group[key].indexOf(callback);
+ if (~index) group[key].splice(index, 1);
+ }
+ };
+}
+
+function on(eventName, handler) {
+ if (eventName === 'teardown') return this.on('destroy', handler);
+
+ var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
+ handlers.push(handler);
+
+ return {
+ cancel: function() {
+ var index = handlers.indexOf(handler);
+ if (~index) handlers.splice(index, 1);
+ }
+ };
+}
+
+function set(newState) {
+ this._set(assign({}, newState));
+ if (this.root._lock) return;
+ this.root._lock = true;
+ callAll(this.root._beforecreate);
+ callAll(this.root._oncreate);
+ callAll(this.root._aftercreate);
+ this.root._lock = false;
+}
+
+function _set(newState) {
+ var oldState = this._state,
+ changed = {},
+ dirty = false;
+
+ for (var key in newState) {
+ if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
+ }
+ if (!dirty) return;
+
+ this._state = assign({}, oldState, newState);
+ this._recompute(changed, this._state);
+ if (this._bind) this._bind(changed, this._state);
+
+ if (this._fragment) {
+ dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
+ this._fragment.p(changed, this._state);
+ dispatchObservers(this, this._observers.post, changed, this._state, oldState);
+ }
+}
+
+function callAll(fns) {
+ while (fns && fns.length) fns.shift()();
+}
+
+function _mount(target, anchor) {
+ this._fragment.m(target, anchor);
+}
+
+function _unmount() {
+ if (this._fragment) this._fragment.u();
+}
+
+var proto = {
+ destroy: destroy,
+ get: get,
+ fire: fire,
+ observe: observe,
+ on: on,
+ set: set,
+ teardown: destroy,
+ _recompute: noop,
+ _set: _set,
+ _mount: _mount,
+ _unmount: _unmount
+};
+
+/* generated by Svelte vX.Y.Z */
+function encapsulateStyles(node) {
+ setAttribute(node, "svelte-2614221383", "");
+}
+
+function add_css() {
+ var style = createElement("style");
+ style.id = 'svelte-2614221383-style';
+ style.textContent = "footer[svelte-2614221383],[svelte-2614221383] footer{background-color:black;color:white}";
+ appendNode(style, document.head);
+}
+
+function create_main_fragment(state, component) {
+ var footer, slot_content_base = component._slotted.base;
+
+ return {
+ c: function create() {
+ footer = createElement("footer");
+ this.h();
+ },
+
+ h: function hydrate() {
+ encapsulateStyles(footer);
+ },
+
+ m: function mount(target, anchor) {
+ insertNode(footer, target, anchor);
+
+ if (slot_content_base) {
+ appendNode(slot_content_base, footer);
+ }
+ },
+
+ p: noop,
+
+ u: function unmount() {
+ detachNode(footer);
+
+ if (slot_content_base) {
+ reinsertChildren(footer, slot_content_base);
+ }
+ },
+
+ d: noop
+ };
+}
+
+function SvelteComponent(options) {
+ init(this, options);
+ this._state = assign({}, options.data);
+
+ this._slotted = options.slots || {};
+
+ if (!document.getElementById("svelte-2614221383-style")) add_css();
+
+ this.slots = {};
+
+ this._fragment = create_main_fragment(this._state, this);
+
+ if (options.target) {
+ this._fragment.c();
+ this._fragment.m(options.target, options.anchor || null);
+ }
+}
+
+assign(SvelteComponent.prototype, proto);
+
+export default SvelteComponent;
diff --git a/test/js/samples/slot-base/expected.js b/test/js/samples/slot-base/expected.js
new file mode 100644
index 0000000000..01ff409b63
--- /dev/null
+++ b/test/js/samples/slot-base/expected.js
@@ -0,0 +1,69 @@
+/* generated by Svelte vX.Y.Z */
+import { appendNode, assign, createElement, detachNode, init, insertNode, noop, proto, reinsertChildren, setAttribute } from "svelte/shared.js";
+
+function encapsulateStyles(node) {
+ setAttribute(node, "svelte-2614221383", "");
+}
+
+function add_css() {
+ var style = createElement("style");
+ style.id = 'svelte-2614221383-style';
+ style.textContent = "footer[svelte-2614221383],[svelte-2614221383] footer{background-color:black;color:white}";
+ appendNode(style, document.head);
+}
+
+function create_main_fragment(state, component) {
+ var footer, slot_content_base = component._slotted.base;
+
+ return {
+ c: function create() {
+ footer = createElement("footer");
+ this.h();
+ },
+
+ h: function hydrate() {
+ encapsulateStyles(footer);
+ },
+
+ m: function mount(target, anchor) {
+ insertNode(footer, target, anchor);
+
+ if (slot_content_base) {
+ appendNode(slot_content_base, footer);
+ }
+ },
+
+ p: noop,
+
+ u: function unmount() {
+ detachNode(footer);
+
+ if (slot_content_base) {
+ reinsertChildren(footer, slot_content_base);
+ }
+ },
+
+ d: noop
+ };
+}
+
+function SvelteComponent(options) {
+ init(this, options);
+ this._state = assign({}, options.data);
+
+ this._slotted = options.slots || {};
+
+ if (!document.getElementById("svelte-2614221383-style")) add_css();
+
+ this.slots = {};
+
+ this._fragment = create_main_fragment(this._state, this);
+
+ if (options.target) {
+ this._fragment.c();
+ this._fragment.m(options.target, options.anchor || null);
+ }
+}
+
+assign(SvelteComponent.prototype, proto);
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/slot-base/input.html b/test/js/samples/slot-base/input.html
new file mode 100644
index 0000000000..bb7352bf2d
--- /dev/null
+++ b/test/js/samples/slot-base/input.html
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/test/validator/samples/component-slot-base-reserved/errors.json b/test/validator/samples/component-slot-base-reserved/errors.json
new file mode 100644
index 0000000000..031ca667ff
--- /dev/null
+++ b/test/validator/samples/component-slot-base-reserved/errors.json
@@ -0,0 +1,8 @@
+[{
+ "message": "base is a reserved word — it cannot be used as a slot name",
+ "loc": {
+ "line": 1,
+ "column": 6
+ },
+ "pos": 6
+}]
\ No newline at end of file
diff --git a/test/validator/samples/component-slot-base-reserved/input.html b/test/validator/samples/component-slot-base-reserved/input.html
new file mode 100644
index 0000000000..09cbb18be3
--- /dev/null
+++ b/test/validator/samples/component-slot-base-reserved/input.html
@@ -0,0 +1 @@
+
\ No newline at end of file