move store subscriptions into a helper. broke a bunch of stuff, bear with me

pull/2099/head
Richard Harris 6 years ago
parent 55295a0e33
commit 9757fbfdb8

@ -183,7 +183,11 @@ export default class Component {
writable: true
});
this.add_reference(name.slice(1));
const subscribable_name = name.slice(1);
this.add_reference(subscribable_name);
const variable = this.var_lookup.get(subscribable_name);
variable.subscribable = true;
} else if (!this.ast.instance) {
this.add_var({
name,
@ -213,6 +217,11 @@ export default class Component {
return this.aliases.get(name);
}
helper(name: string) {
this.helpers.add(name);
return this.alias(name);
}
generate(result: string) {
const { compileOptions, name } = this;
const { format = 'esm' } = compileOptions;
@ -641,6 +650,9 @@ export default class Component {
});
this.add_reference(name.slice(1));
const variable = this.var_lookup.get(name.slice(1));
variable.subscribable = true;
} else {
this.add_var({
name,
@ -783,8 +795,7 @@ export default class Component {
// can't use the @ trick here, because we're
// manipulating the underlying magic string
component.helpers.add('exclude_internal_props');
const exclude_internal_props = component.alias('exclude_internal_props');
const exclude_internal_props = component.helper('exclude_internal_props');
const suffix = code.original[declarator.end] === ';'
? ` = ${exclude_internal_props}($$props)`
@ -799,7 +810,9 @@ export default class Component {
if (variable.export_name) {
has_exports = true;
} else {
}
if (!variable.export_name || variable.subscribable) {
has_only_exports = false;
}
});

@ -202,6 +202,38 @@ export default function dom(
component.has_reactive_assignments = true;
}
else if (node.type === 'VariableDeclarator') {
const names = extractNames(node.id);
names.forEach(name => {
const owner = scope.findOwner(name);
if (owner && owner !== component.instance_scope) return;
const variable = component.var_lookup.get(name);
if (variable && variable.subscribable) {
const value = `$${name}`;
const subscribe = component.helper('subscribe');
const index = parent.declarations.indexOf(node);
const next = parent.declarations[index + 1];
let insert = `${subscribe}($$self, ${name}, $$value => { ${value} = $$value; $$invalidate('${value}', ${value}) })`;
if (component.compileOptions.dev) {
const validate_store = component.helper('validate_store');
insert = `${validate_store}(${name}, '${name}'); ${insert}`;
}
// initialise store value here
if (next) {
code.overwrite(node.end, next.start, `; ${insert}; ${parent.kind} `);
} else {
// final (or only) declarator
code.appendLeft(node.end, `; ${insert}`);
}
}
});
}
if (pending_assignments.size > 0) {
if (node.type === 'ArrowFunctionExpression') {
const insert = Array.from(pending_assignments).map(name => `$$invalidate('${name}', ${name})`).join(';');
@ -310,12 +342,12 @@ export default function dom(
: null
);
const reactive_store_subscriptions = reactive_stores.length > 0 && reactive_stores
const reactive_store_subscriptions = reactive_stores
.filter(store => component.var_lookup.get(store.name).hoistable)
.map(({ name }) => deindent`
${component.compileOptions.dev && `@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
$$self.$$.on_destroy.push(${name.slice(1)}.subscribe($$value => { ${name} = $$value; $$invalidate('${name}', ${name}); }));
`)
.join('\n\n');
@subscribe($$self, ${name.slice(1)}, $$value => { ${name} = $$value; $$invalidate('${name}', ${name}); });
`);
if (has_definition) {
const reactive_declarations = component.reactive_declarations.map(d => {
@ -346,6 +378,8 @@ export default function dom(
function ${definition}(${args.join(', ')}) {
${reactive_stores.length > 0 && `let ${reactive_stores.map(store => store.name).join(', ')};`}
${reactive_store_subscriptions}
${user_code}
${renderer.slots.size && `let { ${[...renderer.slots].map(name => `$$slot_${sanitize(name)}`).join(', ')}, $$scope } = $$props;`}
@ -354,8 +388,6 @@ export default function dom(
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
${reactive_store_subscriptions}
${set && `$$self.$set = ${set};`}
${reactive_declarations.length > 0 && deindent`

@ -3,6 +3,8 @@ import Component from '../Component';
import { CompileOptions } from '../../interfaces';
import { stringify } from '../../utils/stringify';
import Renderer from './Renderer';
import { walk } from 'estree-walker';
import { extractNames } from '../../utils/annotateWithScopes';
export default function ssr(
component: Component,
@ -22,11 +24,63 @@ export default function ssr(
{ code: null, map: null } :
component.stylesheet.render(options.filename, true);
let user_code;
// insert store values
if (component.ast.instance) {
let scope = component.instance_scope;
let map = component.instance_scope_map;
walk(component.ast.instance.content, {
enter: (node, parent) => {
if (map.has(node)) {
scope = map.get(node);
}
},
leave(node, parent) {
if (map.has(node)) {
scope = scope.parent;
}
if (node.type === 'VariableDeclarator') {
const names = extractNames(node.id);
names.forEach(name => {
const owner = scope.findOwner(name);
if (owner && owner !== component.instance_scope) return;
const variable = component.var_lookup.get(name);
if (variable && variable.subscribable) {
const value = `$${name}`;
const get_store_value = component.helper('get_store_value');
const index = parent.declarations.indexOf(node);
const next = parent.declarations[index + 1];
let insert = `const ${value} = ${get_store_value}(${name});`;
if (component.compileOptions.dev) {
const validate_store = component.helper('validate_store');
insert = `${validate_store}(${name}, '${name}'); ${insert}`;
}
// initialise store value here
if (next) {
component.code.overwrite(node.end, next.start, `; ${insert}; ${parent.kind} `);
} else {
// final (or only) declarator
component.code.appendLeft(node.end, `; ${insert}`);
}
}
});
}
}
});
}
// TODO remove this, just use component.vars everywhere
const props = component.vars.filter(variable => !variable.module && variable.export_name && variable.export_name !== component.componentOptions.props_object);
let user_code;
if (component.javascript) {
component.rewrite_props();
user_code = component.javascript;
@ -38,13 +92,15 @@ export default function ssr(
}
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$');
const reactive_store_values = reactive_stores.map(({ name }) => {
const assignment = `const ${name} = @get_store_value(${name.slice(1)});`;
return component.compileOptions.dev
? `@validate_store(${name.slice(1)}, '${name.slice(1)}'); ${assignment}`
: assignment;
});
const reactive_store_values = reactive_stores
.filter(store => component.var_lookup.get(store.name).hoistable)
.map(({ name }) => {
const assignment = `const ${name} = @get_store_value(${name.slice(1)});`;
return component.compileOptions.dev
? `@validate_store(${name.slice(1)}, '${name.slice(1)}'); ${assignment}`
: assignment;
});
// TODO only do this for props with a default value
const parent_bindings = component.javascript

@ -89,4 +89,5 @@ export interface Var {
internal?: boolean; // event handlers, bindings
initialised?: boolean;
hoistable?: boolean;
subscribable?: boolean;
}

@ -47,6 +47,10 @@ export function validate_store(store, name) {
}
}
export function subscribe(component, store, callback) {
component.$$.on_destroy.push(store.subscribe(callback));
}
export function create_slot(definition, ctx, fn) {
if (definition) {
const slot_ctx = get_slot_context(definition, ctx, fn);

@ -0,0 +1,9 @@
export default {
html: `
<p>42</p>
`,
async test({ assert, component }) {
assert.equal(component.initial_foo, 42);
}
};

@ -0,0 +1,7 @@
<script>
import { writable } from 'svelte/store';
const foo = writable(42), bar = 'something else';
export let initial_foo = $foo;
</script>
<p>{initial_foo}</p>
Loading…
Cancel
Save