diff --git a/src/compile/Component.ts b/src/compile/Component.ts
index 36730ed09e..fba0bb0a86 100644
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -882,7 +882,12 @@ export default class Component {
}
warn_if_undefined(node, template_scope: TemplateScope, allow_implicit?: boolean) {
- const { name } = node;
+ let { name } = node;
+
+ if (allow_implicit && name[0] === '$') {
+ name = name.slice(1);
+ this.has_reactive_assignments = true;
+ }
if (allow_implicit && !this.instance_script) return;
if (this.instance_scope && this.instance_scope.declarations.has(name)) return;
diff --git a/src/compile/nodes/shared/Expression.ts b/src/compile/nodes/shared/Expression.ts
index c7a3d5de91..e3e2fb975e 100644
--- a/src/compile/nodes/shared/Expression.ts
+++ b/src/compile/nodes/shared/Expression.ts
@@ -118,7 +118,7 @@ export default class Expression {
// conditions — it doesn't apply if the dependency is inside a
// function, and it only applies if the dependency is writable
if (component.instance_script) {
- if (component.writable_declarations.has(name)) {
+ if (component.writable_declarations.has(name) || name[0] === '$') {
dynamic_dependencies.add(name);
}
} else {
diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts
index ffc0634e02..8bab9ae994 100644
--- a/src/compile/render-dom/index.ts
+++ b/src/compile/render-dom/index.ts
@@ -257,11 +257,15 @@ export default function dom(
return true;
});
+ const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$');
+ filtered_declarations.push(...reactive_stores);
+
const has_definition = (
component.javascript ||
filtered_props.length > 0 ||
component.partly_hoisted.length > 0 ||
filtered_declarations.length > 0 ||
+ reactive_stores.length > 0 ||
component.reactive_declarations.length > 0
);
@@ -280,6 +284,13 @@ export default function dom(
: null
);
+ const reactive_store_subscriptions = reactive_stores
+ .map(name => deindent`
+ let ${name};
+ $$self.$$.on_destroy.push(${name.slice(1)}.subscribe($$value => { ${name} = $$value; $$make_dirty('${name}'); }));
+ `)
+ .join('\n\n');
+
if (has_definition) {
builder.addBlock(deindent`
function ${definition}(${args.join(', ')}) {
@@ -287,6 +298,8 @@ export default function dom(
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
+ ${reactive_store_subscriptions}
+
${filtered_declarations.length > 0 && `$$self.$$.get = () => (${stringifyProps(filtered_declarations)});`}
${set && `$$self.$$.set = ${set};`}
diff --git a/src/compile/render-ssr/index.ts b/src/compile/render-ssr/index.ts
index 28d47475a5..47b69bb746 100644
--- a/src/compile/render-ssr/index.ts
+++ b/src/compile/render-ssr/index.ts
@@ -36,6 +36,9 @@ export default function ssr(
user_code = `let { ${props.join(', ')} } = $$props;`
}
+ const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$');
+ const reactive_store_values = reactive_stores.map(name => `const ${name} = @get_store_value(${name.slice(1)});`)
+
// TODO only do this for props with a default value
const parent_bindings = component.javascript
? component.props.map(prop => {
@@ -51,6 +54,8 @@ export default function ssr(
do {
$$settled = true;
+ ${reactive_store_values}
+
${component.reactive_declarations.map(d => d.snippet)}
$$rendered = \`${renderer.code}\`;
@@ -59,6 +64,8 @@ export default function ssr(
return $$rendered;
`
: deindent`
+ ${reactive_store_values}
+
${component.reactive_declarations.map(d => d.snippet)}
return \`${renderer.code}\`;`;
diff --git a/src/internal/ssr.js b/src/internal/ssr.js
index a6eb13107b..170f841ec6 100644
--- a/src/internal/ssr.js
+++ b/src/internal/ssr.js
@@ -97,4 +97,10 @@ export function create_ssr_component($$render) {
$$render
};
+}
+
+export function get_store_value(store) {
+ let value;
+ store.subscribe(_ => value = _)();
+ return value;
}
\ No newline at end of file
diff --git a/test/runtime/samples/store-auto-subscribe/_config.js b/test/runtime/samples/store-auto-subscribe/_config.js
new file mode 100644
index 0000000000..b8203f2a68
--- /dev/null
+++ b/test/runtime/samples/store-auto-subscribe/_config.js
@@ -0,0 +1,28 @@
+import { writable } from '../../../../store.js';
+
+export default {
+ props: {
+ count: writable(0)
+ },
+
+ html: `
+
+ `,
+
+ async test({ assert, component, target, window }) {
+ const button = target.querySelector('button');
+ const click = new window.MouseEvent('click');
+
+ await button.dispatchEvent(click);
+
+ assert.htmlEqual(target.innerHTML, `
+
+ `);
+
+ await component.count.set(42);
+
+ assert.htmlEqual(target.innerHTML, `
+
+ `);
+ }
+};
\ No newline at end of file
diff --git a/test/runtime/samples/store-auto-subscribe/main.html b/test/runtime/samples/store-auto-subscribe/main.html
new file mode 100644
index 0000000000..9dfad52e31
--- /dev/null
+++ b/test/runtime/samples/store-auto-subscribe/main.html
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file