Skip to content

Commit cb64fb2

Browse files
authoredFeb 23, 2020
make $capture_state/$inject_state act on entire state (#3822)
Previously, these methods only applied to exported props. Also, add $$inject option to constructor, which injects state before running the update loop.
1 parent a53da7e commit cb64fb2

File tree

11 files changed

+298
-43
lines changed

11 files changed

+298
-43
lines changed
 

‎src/compiler/compile/render_dom/index.ts

+38-23
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ export default function dom(
9191
const accessors = [];
9292

9393
const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
94-
let dev_props_check; let inject_state; let capture_state;
94+
let dev_props_check: Node[] | Node;
95+
let inject_state: Expression;
96+
let capture_state: Expression;
97+
let props_inject: Node[] | Node;
9598

9699
props.forEach(prop => {
97100
const variable = component.var_lookup.get(prop.name);
@@ -164,27 +167,30 @@ export default function dom(
164167
`;
165168
}
166169

167-
capture_state = (uses_props || writable_props.length > 0) ? x`
168-
() => {
169-
return { ${component.vars.filter(prop => prop.writable).map(prop => p`${prop.name}`)} };
170-
}
171-
` : x`
172-
() => {
173-
return {};
174-
}
175-
`;
170+
const capturable_vars = component.vars.filter(v => !v.internal && !v.name.startsWith('$$'));
176171

177-
const writable_vars = component.vars.filter(variable => !variable.module && variable.writable);
178-
inject_state = (uses_props || writable_vars.length > 0) ? x`
179-
${$$props} => {
180-
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
181-
${writable_vars.map(prop => b`
182-
if ('${prop.name}' in $$props) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.name}`)};
183-
`)}
184-
}
185-
` : x`
186-
${$$props} => {}
187-
`;
172+
if (capturable_vars.length > 0) {
173+
capture_state = x`() => ({ ${capturable_vars.map(prop => p`${prop.name}`)} })`;
174+
}
175+
176+
const injectable_vars = capturable_vars.filter(v => !v.module && v.writable && v.name[0] !== '$');
177+
178+
if (uses_props || injectable_vars.length > 0) {
179+
inject_state = x`
180+
${$$props} => {
181+
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
182+
${injectable_vars.map(
183+
v => b`if ('${v.name}' in $$props) ${renderer.invalidate(v.name, x`${v.name} = ${$$props}.${v.name}`)};`
184+
)}
185+
}
186+
`;
187+
188+
props_inject = b`
189+
if ($$props && "$$inject" in $$props) {
190+
$$self.$inject_state($$props.$$inject);
191+
}
192+
`;
193+
}
188194
}
189195

190196
// instrument assignments
@@ -246,7 +252,12 @@ export default function dom(
246252
}
247253

248254
const args = [x`$$self`];
249-
if (props.length > 0 || component.has_reactive_assignments || component.slots.size > 0) {
255+
const has_invalidate = props.length > 0 ||
256+
component.has_reactive_assignments ||
257+
component.slots.size > 0 ||
258+
capture_state ||
259+
inject_state;
260+
if (has_invalidate) {
250261
args.push(x`$$props`, x`$$invalidate`);
251262
}
252263

@@ -294,7 +305,9 @@ export default function dom(
294305
uses_props ||
295306
component.partly_hoisted.length > 0 ||
296307
initial_context.length > 0 ||
297-
component.reactive_declarations.length > 0
308+
component.reactive_declarations.length > 0 ||
309+
capture_state ||
310+
inject_state
298311
);
299312

300313
const definition = has_definition
@@ -409,6 +422,8 @@ export default function dom(
409422
410423
${injected.map(name => b`let ${name};`)}
411424
425+
${/* before reactive declarations */ props_inject}
426+
412427
${reactive_declarations.length > 0 && b`
413428
$$self.$$.update = () => {
414429
${reactive_declarations}

‎src/runtime/internal/dev.ts

+4
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ export class SvelteComponentDev extends SvelteComponent {
120120
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
121121
};
122122
}
123+
124+
$capture_state() {}
125+
126+
$inject_state() {}
123127
}
124128

125129
export function loop_guard(timeout) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default {
2+
options: {
3+
dev: true
4+
}
5+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/* generated by Svelte vX.Y.Z */
2+
import {
3+
SvelteComponentDev,
4+
add_location,
5+
append_dev,
6+
detach_dev,
7+
dispatch_dev,
8+
element,
9+
init,
10+
insert_dev,
11+
noop,
12+
safe_not_equal,
13+
set_data_dev,
14+
space,
15+
subscribe,
16+
text,
17+
validate_store
18+
} from "svelte/internal";
19+
20+
const file = undefined;
21+
22+
function create_fragment(ctx) {
23+
let p;
24+
let t0;
25+
let t1;
26+
let t2;
27+
let t3;
28+
let t4;
29+
let t5;
30+
let t6;
31+
let t7;
32+
let t8;
33+
let t9;
34+
let t10;
35+
36+
const block = {
37+
c: function create() {
38+
p = element("p");
39+
t0 = text(/*prop*/ ctx[0]);
40+
t1 = space();
41+
t2 = text(/*realName*/ ctx[1]);
42+
t3 = space();
43+
t4 = text(/*local*/ ctx[3]);
44+
t5 = space();
45+
t6 = text(priv);
46+
t7 = space();
47+
t8 = text(/*$prop*/ ctx[2]);
48+
t9 = space();
49+
t10 = text(/*shadowedByModule*/ ctx[4]);
50+
add_location(p, file, 22, 0, 430);
51+
},
52+
l: function claim(nodes) {
53+
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
54+
},
55+
m: function mount(target, anchor) {
56+
insert_dev(target, p, anchor);
57+
append_dev(p, t0);
58+
append_dev(p, t1);
59+
append_dev(p, t2);
60+
append_dev(p, t3);
61+
append_dev(p, t4);
62+
append_dev(p, t5);
63+
append_dev(p, t6);
64+
append_dev(p, t7);
65+
append_dev(p, t8);
66+
append_dev(p, t9);
67+
append_dev(p, t10);
68+
},
69+
p: function update(ctx, [dirty]) {
70+
if (dirty & /*prop*/ 1) set_data_dev(t0, /*prop*/ ctx[0]);
71+
if (dirty & /*realName*/ 2) set_data_dev(t2, /*realName*/ ctx[1]);
72+
if (dirty & /*$prop*/ 4) set_data_dev(t8, /*$prop*/ ctx[2]);
73+
},
74+
i: noop,
75+
o: noop,
76+
d: function destroy(detaching) {
77+
if (detaching) detach_dev(p);
78+
}
79+
};
80+
81+
dispatch_dev("SvelteRegisterBlock", {
82+
block,
83+
id: create_fragment.name,
84+
type: "component",
85+
source: "",
86+
ctx
87+
});
88+
89+
return block;
90+
}
91+
92+
let moduleLiveBinding;
93+
const moduleContantProps = 4;
94+
let moduleLet;
95+
const moduleConst = 2;
96+
let shadowedByModule;
97+
const priv = "priv";
98+
99+
function instance($$self, $$props, $$invalidate) {
100+
let $prop,
101+
$$unsubscribe_prop = noop,
102+
$$subscribe_prop = () => ($$unsubscribe_prop(), $$unsubscribe_prop = subscribe(prop, $$value => $$invalidate(2, $prop = $$value)), prop);
103+
104+
$$self.$$.on_destroy.push(() => $$unsubscribe_prop());
105+
let { prop } = $$props;
106+
validate_store(prop, "prop");
107+
$$subscribe_prop();
108+
let { alias: realName } = $$props;
109+
let local;
110+
let shadowedByModule;
111+
const writable_props = ["prop", "alias"];
112+
113+
Object.keys($$props).forEach(key => {
114+
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
115+
});
116+
117+
$$self.$set = $$props => {
118+
if ("prop" in $$props) $$subscribe_prop($$invalidate(0, prop = $$props.prop));
119+
if ("alias" in $$props) $$invalidate(1, realName = $$props.alias);
120+
};
121+
122+
$$self.$capture_state = () => ({
123+
moduleLiveBinding,
124+
moduleContantProps,
125+
moduleLet,
126+
moduleConst,
127+
shadowedByModule,
128+
prop,
129+
realName,
130+
local,
131+
priv,
132+
shadowedByModule,
133+
computed,
134+
$prop
135+
});
136+
137+
$$self.$inject_state = $$props => {
138+
if ("prop" in $$props) $$subscribe_prop($$invalidate(0, prop = $$props.prop));
139+
if ("realName" in $$props) $$invalidate(1, realName = $$props.realName);
140+
if ("local" in $$props) $$invalidate(3, local = $$props.local);
141+
if ("shadowedByModule" in $$props) $$invalidate(4, shadowedByModule = $$props.shadowedByModule);
142+
if ("computed" in $$props) computed = $$props.computed;
143+
};
144+
145+
let computed;
146+
147+
if ($$props && "$$inject" in $$props) {
148+
$$self.$inject_state($$props.$$inject);
149+
}
150+
151+
$: computed = local * 2;
152+
return [prop, realName, $prop, local, shadowedByModule];
153+
}
154+
155+
class Component extends SvelteComponentDev {
156+
constructor(options) {
157+
super(options);
158+
init(this, options, instance, create_fragment, safe_not_equal, { prop: 0, alias: 1 });
159+
160+
dispatch_dev("SvelteRegisterComponent", {
161+
component: this,
162+
tagName: "Component",
163+
options,
164+
id: create_fragment.name
165+
});
166+
167+
const { ctx } = this.$$;
168+
const props = options.props || {};
169+
170+
if (/*prop*/ ctx[0] === undefined && !("prop" in props)) {
171+
console.warn("<Component> was created without expected prop 'prop'");
172+
}
173+
174+
if (/*realName*/ ctx[1] === undefined && !("alias" in props)) {
175+
console.warn("<Component> was created without expected prop 'alias'");
176+
}
177+
}
178+
179+
get prop() {
180+
throw new Error("<Component>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
181+
}
182+
183+
set prop(value) {
184+
throw new Error("<Component>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
185+
}
186+
187+
get alias() {
188+
throw new Error("<Component>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
189+
}
190+
191+
set alias(value) {
192+
throw new Error("<Component>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
193+
}
194+
}
195+
196+
export default Component;
197+
export { moduleLiveBinding, moduleContantProps };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script context="module">
2+
export let moduleLiveBinding;
3+
export const moduleContantProps = 4;
4+
let moduleLet;
5+
const moduleConst = 2;
6+
let shadowedByModule;
7+
</script>
8+
<script>
9+
export let prop;
10+
11+
let realName;
12+
export { realName as alias };
13+
14+
let local;
15+
16+
const priv = 'priv';
17+
18+
$: computed = local * 2;
19+
20+
let shadowedByModule;
21+
</script>
22+
<!-- NOTE $prop ensures store subscriptions are not part of captured state -->
23+
<p>{prop} {realName} {local} {priv} {$prop} {shadowedByModule}</p>

‎test/js/samples/debug-empty/expected.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,16 @@ function instance($$self, $$props, $$invalidate) {
7979
if ("name" in $$props) $$invalidate(0, name = $$props.name);
8080
};
8181

82-
$$self.$capture_state = () => {
83-
return { name };
84-
};
82+
$$self.$capture_state = () => ({ name });
8583

8684
$$self.$inject_state = $$props => {
8785
if ("name" in $$props) $$invalidate(0, name = $$props.name);
8886
};
8987

88+
if ($$props && "$$inject" in $$props) {
89+
$$self.$inject_state($$props.$$inject);
90+
}
91+
9092
return [name];
9193
}
9294

‎test/js/samples/debug-foo-bar-baz-things/expected.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,7 @@ function instance($$self, $$props, $$invalidate) {
186186
if ("baz" in $$props) $$invalidate(3, baz = $$props.baz);
187187
};
188188

189-
$$self.$capture_state = () => {
190-
return { things, foo, bar, baz };
191-
};
189+
$$self.$capture_state = () => ({ things, foo, bar, baz });
192190

193191
$$self.$inject_state = $$props => {
194192
if ("things" in $$props) $$invalidate(0, things = $$props.things);
@@ -197,6 +195,10 @@ function instance($$self, $$props, $$invalidate) {
197195
if ("baz" in $$props) $$invalidate(3, baz = $$props.baz);
198196
};
199197

198+
if ($$props && "$$inject" in $$props) {
199+
$$self.$inject_state($$props.$$inject);
200+
}
201+
200202
return [things, foo, bar, baz];
201203
}
202204

‎test/js/samples/debug-foo/expected.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,17 @@ function instance($$self, $$props, $$invalidate) {
176176
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
177177
};
178178

179-
$$self.$capture_state = () => {
180-
return { things, foo };
181-
};
179+
$$self.$capture_state = () => ({ things, foo });
182180

183181
$$self.$inject_state = $$props => {
184182
if ("things" in $$props) $$invalidate(0, things = $$props.things);
185183
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
186184
};
187185

186+
if ($$props && "$$inject" in $$props) {
187+
$$self.$inject_state($$props.$$inject);
188+
}
189+
188190
return [things, foo];
189191
}
190192

‎test/js/samples/debug-hoisted/expected.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,20 @@ function create_fragment(ctx) {
4747
return block;
4848
}
4949

50-
function instance($$self) {
50+
function instance($$self, $$props, $$invalidate) {
5151
let obj = { x: 5 };
5252
let kobzol = 5;
53-
54-
$$self.$capture_state = () => {
55-
return {};
56-
};
53+
$$self.$capture_state = () => ({ obj, kobzol });
5754

5855
$$self.$inject_state = $$props => {
5956
if ("obj" in $$props) $$invalidate(0, obj = $$props.obj);
6057
if ("kobzol" in $$props) $$invalidate(1, kobzol = $$props.kobzol);
6158
};
6259

60+
if ($$props && "$$inject" in $$props) {
61+
$$self.$inject_state($$props.$$inject);
62+
}
63+
6364
return [obj, kobzol];
6465
}
6566

‎test/js/samples/dev-warning-missing-data-computed/expected.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,17 @@ function instance($$self, $$props, $$invalidate) {
7676
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
7777
};
7878

79-
$$self.$capture_state = () => {
80-
return { foo, bar };
81-
};
79+
$$self.$capture_state = () => ({ foo, bar });
8280

8381
$$self.$inject_state = $$props => {
8482
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
8583
if ("bar" in $$props) $$invalidate(1, bar = $$props.bar);
8684
};
8785

86+
if ($$props && "$$inject" in $$props) {
87+
$$self.$inject_state($$props.$$inject);
88+
}
89+
8890
$$self.$$.update = () => {
8991
if ($$self.$$.dirty & /*foo*/ 1) {
9092
$: $$invalidate(1, bar = foo * 2);

‎test/js/samples/loop-protect/expected.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,16 @@ function instance($$self, $$props, $$invalidate) {
108108
});
109109
}
110110

111-
$$self.$capture_state = () => {
112-
return {};
113-
};
111+
$$self.$capture_state = () => ({ node, foo, console });
114112

115113
$$self.$inject_state = $$props => {
116114
if ("node" in $$props) $$invalidate(0, node = $$props.node);
117115
};
118116

117+
if ($$props && "$$inject" in $$props) {
118+
$$self.$inject_state($$props.$$inject);
119+
}
120+
119121
$: {
120122
const guard_4 = loop_guard(100);
121123

0 commit comments

Comments
 (0)
Please sign in to comment.