Skip to content
Closed
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
68 changes: 42 additions & 26 deletions src/compiler/compile/render_dom/wrappers/Element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,37 +665,51 @@ export default class ElementWrapper extends Wrapper {
const initial_props = [];
const updates = [];

const all_dependencies = new Set();
this.attributes.forEach(attr => {
add_to_set(all_dependencies, attr.node.get_dependencies());
});

this.attributes
.forEach(attr => {
const condition = attr.node.dependencies.size > 0
? block.renderer.dirty(Array.from(attr.node.dependencies))
const dependencies = attr.node.get_dependencies();

const condition = dependencies.length > 0 && (dependencies.length !== all_dependencies.size)
? block.renderer.dirty(dependencies)
: null;
const unchanged = dependencies.length === 0;

let snippet;
if (attr.node.is_spread) {
const snippet = attr.node.expression.manipulate(block);
snippet = attr.node.expression.manipulate(block);

initial_props.push(snippet);

updates.push(condition ? x`${condition} && ${snippet}` : snippet);
if (attr.node.expression.node.type !== 'ObjectExpression') {
snippet = x`@get_spread_object(${snippet})`;
}
} else {
const metadata = attr.get_metadata();
const name = attr.is_indirectly_bound_value()
? '__value'
: (metadata && metadata.property_name) || fix_attribute_casing(attr.node.name);
const snippet = x`{ ${name}: ${attr.get_value(block)} }`;
snippet = x`{ ${name}: ${attr.get_value(block)} }`;
initial_props.push(snippet);

updates.push(condition ? x`${condition} && ${snippet}` : snippet);
}

updates.push(
unchanged
? x`false`
: condition
? x`${condition} && ${snippet}`
: snippet
);
});

block.chunks.init.push(b`
let ${levels} = [${initial_props}];

let ${data} = {};
for (let #i = 0; #i < ${levels}.length; #i += 1) {
${data} = @assign(${data}, ${levels}[#i]);
}
let ${data} = @get_attributes_for_spread(${levels});
`);

const fn = this.node.namespace === namespaces.svg ? x`@set_svg_attributes` : x`@set_attributes`;
Expand All @@ -704,27 +718,29 @@ export default class ElementWrapper extends Wrapper {
b`${fn}(${this.var}, ${data});`
);

block.chunks.update.push(b`
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [
${updates}
]));
`);
const need_update_data = this.node.name === 'select';

// handle edge cases for elements
if (this.node.name === 'select') {
const dependencies = new Set();
for (const attr of this.attributes) {
for (const dep of attr.node.dependencies) {
dependencies.add(dep);
if (all_dependencies.size > 0) {
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(all_dependencies))}) {
${fn}(${this.var}, @get_spread_update(${levels}, [
${updates}
]));
${need_update_data && b`${data} = @get_attributes_for_spread(${levels});`}
}
}
`);
}

// handle edge cases for elements
if (this.node.name === 'select') {
block.chunks.mount.push(b`
if (${data}.multiple) @select_options(${this.var}, ${data}.value);
`);
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(dependencies))} && ${data}.multiple) @select_options(${this.var}, ${data}.value);
`);
if (all_dependencies.size > 0) {
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(all_dependencies))} && ${data}.multiple) @select_options(${this.var}, ${data}.value);
`);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,17 @@ export default class InlineComponentWrapper extends Wrapper {
const all_dependencies: Set<string> = new Set();

this.node.attributes.forEach(attr => {
add_to_set(all_dependencies, attr.dependencies);
add_to_set(all_dependencies, attr.get_dependencies());
});

this.node.attributes.forEach((attr, i) => {
const { name, dependencies } = attr;
this.node.attributes.forEach((attr) => {
const { name } = attr;
const dependencies = attr.get_dependencies();

const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size)
const condition = dependencies.length > 0 && (dependencies.length !== all_dependencies.size)
? renderer.dirty(Array.from(dependencies))
: null;
const unchanged = dependencies.size === 0;
const unchanged = dependencies.length === 0;

let change_object;
if (attr.is_spread) {
Expand All @@ -252,7 +253,7 @@ export default class InlineComponentWrapper extends Wrapper {

changes.push(
unchanged
? x`${levels}[${i}]`
? x`false`
: condition
? x`${condition} && ${change_object}`
: change_object
Expand All @@ -266,9 +267,7 @@ export default class InlineComponentWrapper extends Wrapper {
`);

statements.push(b`
for (let #i = 0; #i < ${levels}.length; #i += 1) {
${props} = @assign(${props}, ${levels}[#i]);
}
${props} = @assign(${props}, @get_attributes_for_spread(${levels}))
`);

if (all_dependencies.size) {
Expand Down
17 changes: 16 additions & 1 deletion src/runtime/internal/spread.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { assign } from "./utils";

export function get_spread_update(levels, updates) {
const update = {};

Expand All @@ -11,7 +13,7 @@ export function get_spread_update(levels, updates) {

if (n) {
for (const key in o) {
if (!(key in n)) to_null_out[key] = 1;
if (!(key in n) && !accounted_for[key]) to_null_out[key] = 1;
}

for (const key in n) {
Expand All @@ -24,6 +26,11 @@ export function get_spread_update(levels, updates) {
levels[i] = n;
} else {
for (const key in o) {
// if spreads decides to null out the key
// should reset it back for static attribute
if (to_null_out[key]) {
update[key] = o[key];
}
accounted_for[key] = 1;
}
}
Expand All @@ -36,6 +43,14 @@ export function get_spread_update(levels, updates) {
return update;
}

export function get_attributes_for_spread(levels) {
const attrs = {};
for (let i = 0; i < levels.length; i++) {
assign(attrs, levels[i++]);
}
return attrs;
}

export function get_spread_object(spread_props) {
return typeof spread_props === 'object' && spread_props !== null ? spread_props : {};
}
47 changes: 47 additions & 0 deletions test/runtime/samples/spread-element-dynamic-non-object/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export default {
props: {
props: {
foo: 'lol',
baz: 40 + 2,
}
},

html: `
<div baz="42" foo="lol" qux="named"></div>
`,

test({ assert, component, target }) {
const html = `
<div qux="named"></div>
`;

// test undefined
component.props = undefined;
assert.htmlEqual(target.innerHTML, html);

// set object props
component.props = this.props.props;
assert.htmlEqual(target.innerHTML, this.html);

// test null
component.props = null;
assert.htmlEqual(target.innerHTML, html);

// set object props
component.props = this.props.props;
assert.htmlEqual(target.innerHTML, this.html);

// test boolean
component.props = true;
assert.htmlEqual(target.innerHTML, html);

// set object props
component.props = this.props.props;
assert.htmlEqual(target.innerHTML, this.html);

// test number
component.props = 123;
assert.htmlEqual(target.innerHTML, html);

}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
export let props;
</script>

<div {...props} qux="named"/>