Skip to content

Instantly share code, notes, and snippets.

@mrkishi
Created February 7, 2018 22:02
Show Gist options
  • Save mrkishi/6f2e5946dd430eed08a9a951c63b8295 to your computer and use it in GitHub Desktop.
Save mrkishi/6f2e5946dd430eed08a9a951c63b8295 to your computer and use it in GitHub Desktop.
Experiments on spread attributes for Svelte
// App.html
<Child a="{{a}}" ...{{b_props}} c="{{c}}" d="{{d}}" ...{{e_props}} />
// app.output.js
function create_main_fragment(state, component) {
var child = new Child({
root: component.root,
// Even though not allowed by the standard, browsers
// simply ignore duplicate attributes
// So let's also use that precedence: a > a_props > b > b_props > c
data: assign(
state.e_props,
{ c: state.b, d: state.d },
state.b_props,
{ a: state.a }
)
});
// Note: disregard unmangled names -- assume they'd have
// unique prefixes and suffixes
// Cache all attributes by precedence level
// We know the number of spread objects used, so we can calculate
// the number of levels at compile-time and pre-instantiate these maps
// Naively: 1 precedence per source attribute (a + b + c + d + e; 5)
// Small optimization:
// 1 + 1 precedence per spread boundary (1 + a-b + b-c + d-e; 4)
var attr_by_precedence = [{}, {}, {}, {}, {}];
// Given an attribute, get active precedence level
var current_precedence_for_attr = {};
// Cache the attribute names for each spread object
var active_b_props = {};
var active_e_props = {};
return {
c: function create() {
child._fragment.c();
},
m: function mount(target, anchor) {
child._mount(target, anchor);
},
p: function update(changed, state) {
var child_changes = {};
// We start with the max precedence = 4
if (changed.a) {
attr_by_precedence[4].a = state.a;
current_precedence_for_attr.a = 4;
child_changes.a = state.a;
}
// Precedence = 3
if (changed.b_props) {
var leftover_attributes = active_b_props;
active_b_props = {};
for (var key in state.b_props) {
active_b_props[key] = true;
delete leftover_attributes[key];
// Always save value in appropriate cache...
attr_by_precedence[3][key] = state.b_props[key];
// ...and only propagate changes if we have
// a higher precedence than what's already there
if ((current_precedence_for_attr[key] || 0) <= 3) {
current_precedence_for_attr[key] = 3;
child_changes[key] = state.b_props[key];
}
}
for (var key in leftover_attributes) {
delete attr_by_precedence[3][key];
// Bail if we weren't active in the first place...
if (current_precedence_for_attr[key] !== 3) continue;
// ...otherwise, find a lower precedence value
var next_in_line = undefined;
for (var i = 2; i >= 0; ++i) {
if (key in attr_by_precedence[i]) {
current_precedence_for_attr[key] = i;
next_in_line = attr_by_precedence[i][key];
break;
}
}
child_changes[key] = next_in_line;
}
}
// Precedence = 2
if (changed.c) {
attr_by_precedence[2].c = state.c;
// Only propagate changes if we have
// a higher precedence than what's there
if (current_precedence_for_attr.c >= 2) {
current_precedence_for_attr.c = 2;
child_changes.c = state.c;
}
}
// Precedence = 1
if (changed.d) {
// ...
}
// Precedence = 0
if (changed.e_props) {
// ...
}
child._set(nested_changes);
},
u: function unmount() {
nested._unmount();
},
d: function destroy() {
child.destroy(false);
}
};
}
// App.html
<Child ...{{props_b}} a="{{a}}" c="{{c}}" d="{{d}}" />
// app.output.js
function create_main_fragment(state, component) {
var child = new Child({
root: component.root,
// Always give preference to statically set attributes
data: assign(
state.b_props,
{ a: state.a, c: state.c, d: state.d }
)
});
// Note: disregard unmangled names -- assume they'd have
// unique prefixes and suffixes
// Attributes set statically on the component
var child_attributes = ['a', 'c', 'd'];
// Cache the attribute names for the spread object
var b_props = {};
return {
c: function create() {
child._fragment.c();
},
m: function mount(target, anchor) {
child._mount(target, anchor);
},
p: function update(changed, state) {
var child_changes = {};
if (changed.a) child_changes.a = state.a;
if (changed.b_props) {
var leftover_attributes = b_props;
b_props = {};
for (var key in state.b_props) {
if (child_attributes.indexOf(key) !== -1) continue;
b_props[key] = true;
delete leftover_attributes[key];
child_changes[key] = state.b_props[key];
}
for (var key in leftover_attributes) {
child_changes[key] = undefined;
}
}
if (changed.c) child_changes.c = state.c;
if (changed.d) child_changes.d = state.d;
child._set(nested_changes);
},
u: function unmount() {
nested._unmount();
},
d: function destroy() {
child.destroy(false);
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment