Convert src/compiler/compile/render_dom/invalidate.ts to JavaScript

pull/8569/head
Simon Holthausen 3 years ago
parent 79a87770d4
commit 0812f0cde7

@ -4,7 +4,25 @@ import { x } from 'code-red';
import flatten_reference from '../utils/flatten_reference.js'; import flatten_reference from '../utils/flatten_reference.js';
import { reserved_keywords } from '../utils/reserved_keywords.js'; import { reserved_keywords } from '../utils/reserved_keywords.js';
import { renderer_invalidate } from './invalidate.js'; import { renderer_invalidate } from './invalidate.js';
export default class Renderer { export default class Renderer {
/**
* @typedef {Object} ContextMember
* @property {string} name
* @property {import('estree').Literal} index
* @property {boolean} is_contextual
* @property {boolean} is_non_contextual
* @property {import('../../interfaces.js').Var} variable
* @property {number} priority
*/
/**
* @typedef {Array<{
* n: number;
* names: string[];
* }>} BitMasks
*/
/** @type {import('../Component.js').default} */ /** @type {import('../Component.js').default} */
component; // TODO Maybe Renderer shouldn't know about Component? component; // TODO Maybe Renderer shouldn't know about Component?
@ -181,7 +199,7 @@ export default class Renderer {
/** /**
* @param {string[]} names * @param {string[]} names
* @param {any} is_reactive_declaration * @param {any} is_reactive_declaration
* @returns {import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').Expression} * @returns {import('estree').Expression}
*/ */
dirty(names, is_reactive_declaration = false) { dirty(names, is_reactive_declaration = false) {
const renderer = this; const renderer = this;
@ -237,7 +255,7 @@ export default class Renderer {
// NOTE: this method may be called before this.context_overflow / this.context is fully defined // NOTE: this method may be called before this.context_overflow / this.context is fully defined
// therefore, they can only be evaluated later in a getter function // therefore, they can only be evaluated later in a getter function
/** @returns {import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').UnaryExpression | import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').ArrayExpression} */ /** @returns {import('estree').UnaryExpression | import('estree').ArrayExpression} */
get_initial_dirty() { get_initial_dirty() {
const _this = this; const _this = this;
// TODO: context-overflow make it less gross // TODO: context-overflow make it less gross
@ -295,25 +313,11 @@ export default class Renderer {
} }
/** /**
* @typedef {Array<{ * @typedef {Object} BindingGroup
* n: number; * @property {(to_reference?:boolean)=>import('estree').Node} binding_group
* names: string[];
* }>} BitMasks
*/
/** @typedef {Object} ContextMember
* @property {string} name
* @property {Literal} index
* @property {boolean} is_contextual
* @property {boolean} is_non_contextual
* @property {Var} variable
* @property {number} priority
*/
/** @typedef {Object} BindingGroup
* @property {(to_reference?:boolean)=>Node} binding_group
* @property {string[]} contexts * @property {string[]} contexts
* @property {Set<string>} list_dependencies * @property {Set<string>} list_dependencies
* @property {string} keypath * @property {string} keypath
* @property {(block:Block,element:Identifier)=>void} add_element * @property {(block:Block,element:import('estree').PrivateIdentifier) => void} add_element
* @property {(block:Block)=>void} render * @property {(block:Block)=>void} render
*/ */

@ -1,158 +1,140 @@
import { nodes_match } from '../../utils/nodes_match'; import { nodes_match } from '../../utils/nodes_match.js';
import { Scope } from 'periscopic';
import { x } from 'code-red'; import { x } from 'code-red';
import { Node, Expression } from 'estree';
import Renderer from './Renderer';
import { Var } from '../../interfaces';
export function invalidate( /**
renderer: Renderer, * @param {import('./Renderer.js').default} renderer
scope: Scope, * @param {import('periscopic').Scope} scope
node: Node, * @param {import('estree').Node} node
names: Set<string>, * @param {Set<string>} names
main_execution_context: boolean = false * @param {boolean} main_execution_context
) { */
const { component } = renderer; export function invalidate(renderer, scope, node, names, main_execution_context = false) {
const { component } = renderer;
const [head, ...tail] = Array.from(names) const [head, ...tail] = /** @type {import('../../interfaces.js').Var[]} */ (Array.from(names)
.filter((name) => { .filter((name) => {
const owner = scope.find_owner(name); const owner = scope.find_owner(name);
return !owner || owner === component.instance_scope; return !owner || owner === component.instance_scope;
}) })
.map((name) => component.var_lookup.get(name)) .map((name) => component.var_lookup.get(name))
.filter((variable) => { .filter((variable) => {
return ( return (variable &&
variable && !variable.hoistable &&
!variable.hoistable && !variable.global &&
!variable.global && !variable.module &&
!variable.module && (variable.referenced ||
(variable.referenced || variable.subscribable ||
variable.subscribable || variable.is_reactive_dependency ||
variable.is_reactive_dependency || variable.export_name ||
variable.export_name || variable.name[0] === '$'));
variable.name[0] === '$') }));
);
}) as Var[]; /**
* @param {import('../../interfaces.js').Var} variable
function get_invalidated(variable: Var, node?: Expression) { * @param {import('estree').Expression} [node]
if (main_execution_context && !variable.subscribable && variable.name[0] !== '$') { */
return node; function get_invalidated(variable, node) {
} if (main_execution_context && !variable.subscribable && variable.name[0] !== '$') {
return renderer_invalidate(renderer, variable.name, undefined, main_execution_context); return node;
} }
return renderer_invalidate(renderer, variable.name, undefined, main_execution_context);
if (!head) { }
return node; if (!head) {
} return node;
}
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (node.type === 'AssignmentExpression' &&
if ( node.operator === '=' &&
node.type === 'AssignmentExpression' && nodes_match(node.left, node.right) &&
node.operator === '=' && tail.length === 0) {
nodes_match(node.left, node.right) && return get_invalidated(head, node);
tail.length === 0 }
) { const is_store_value = head.name[0] === '$' && head.name[1] !== '$';
return get_invalidated(head, node); const extra_args = tail.map((variable) => get_invalidated(variable)).filter(Boolean);
} if (is_store_value) {
return x `@set_store_value(${head.name.slice(1)}, ${node}, ${head.name}, ${extra_args})`;
const is_store_value = head.name[0] === '$' && head.name[1] !== '$'; }
const extra_args = tail.map((variable) => get_invalidated(variable)).filter(Boolean); let invalidate;
if (!main_execution_context) {
if (is_store_value) { const pass_value = extra_args.length > 0 ||
return x`@set_store_value(${head.name.slice(1)}, ${node}, ${head.name}, ${extra_args})`; (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
} (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier'));
if (pass_value) {
let invalidate; extra_args.unshift({
if (!main_execution_context) { type: 'Identifier',
const pass_value = name: head.name
extra_args.length > 0 || });
(node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || }
(node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')); invalidate = x `$$invalidate(${renderer.context_lookup.get(head.name).index}, ${node}, ${extra_args})`;
if (pass_value) { }
extra_args.unshift({ else {
type: 'Identifier', // skip `$$invalidate` if it is in the main execution context
name: head.name invalidate = extra_args.length ? [node, ...extra_args] : node;
}); }
} if (head.subscribable && head.reassigned) {
invalidate = x`$$invalidate(${ const subscribe = `$$subscribe_${head.name}`;
renderer.context_lookup.get(head.name).index invalidate = x `${subscribe}(${invalidate})`;
}, ${node}, ${extra_args})`; }
} else { return invalidate;
// skip `$$invalidate` if it is in the main execution context
invalidate = extra_args.length ? [node, ...extra_args] : node;
}
if (head.subscribable && head.reassigned) {
const subscribe = `$$subscribe_${head.name}`;
invalidate = x`${subscribe}(${invalidate})`;
}
return invalidate;
} }
export function renderer_invalidate( /**
renderer: Renderer, * @param {import('./Renderer.js').default} renderer
name: string, * @param {string} name
value?: unknown, * @param {unknown} [value]
main_execution_context: boolean = false * @param {boolean} main_execution_context
) { */
const variable = renderer.component.var_lookup.get(name); export function renderer_invalidate(renderer, name, value, main_execution_context = false) {
const variable = renderer.component.var_lookup.get(name);
if (variable && variable.subscribable && (variable.reassigned || variable.export_name)) { if (variable && variable.subscribable && (variable.reassigned || variable.export_name)) {
if (main_execution_context) { if (main_execution_context) {
return x`${`$$subscribe_${name}`}(${value || name})`; return x `${`$$subscribe_${name}`}(${value || name})`;
} else { }
const member = renderer.context_lookup.get(name); else {
return x`${`$$subscribe_${name}`}($$invalidate(${member.index}, ${value || name}))`; const member = renderer.context_lookup.get(name);
} return x `${`$$subscribe_${name}`}($$invalidate(${member.index}, ${value || name}))`;
} }
}
if (name[0] === '$' && name[1] !== '$') { if (name[0] === '$' && name[1] !== '$') {
return x`${name.slice(1)}.set(${value || name})`; return x `${name.slice(1)}.set(${value || name})`;
} }
if (variable &&
if ( (variable.module ||
variable && (!variable.referenced &&
(variable.module || !variable.is_reactive_dependency &&
(!variable.referenced && !variable.export_name &&
!variable.is_reactive_dependency && !name.startsWith('$$')))) {
!variable.export_name && return value || name;
!name.startsWith('$$'))) }
) { if (value) {
return value || name; if (main_execution_context) {
} return x `${value}`;
}
if (value) { else {
if (main_execution_context) { const member = renderer.context_lookup.get(name);
return x`${value}`; return x `$$invalidate(${member.index}, ${value})`;
} else { }
const member = renderer.context_lookup.get(name); }
return x`$$invalidate(${member.index}, ${value})`; if (main_execution_context)
} return;
} // if this is a reactive declaration, invalidate dependencies recursively
const deps = new Set([name]);
if (main_execution_context) return; deps.forEach((name) => {
const reactive_declarations = renderer.component.reactive_declarations.filter((x) => x.assignees.has(name));
reactive_declarations.forEach((declaration) => {
declaration.dependencies.forEach((name) => {
deps.add(name);
});
});
});
// TODO ideally globals etc wouldn't be here in the first place
const filtered = Array.from(deps).filter((n) => renderer.context_lookup.has(n));
if (!filtered.length)
return null;
return filtered
.map((n) => x `$$invalidate(${renderer.context_lookup.get(n).index}, ${n})`)
.reduce((lhs, rhs) => x `${lhs}, ${rhs}`);
}
// if this is a reactive declaration, invalidate dependencies recursively
const deps = new Set([name]);
deps.forEach((name) => {
const reactive_declarations = renderer.component.reactive_declarations.filter((x) =>
x.assignees.has(name)
);
reactive_declarations.forEach((declaration) => {
declaration.dependencies.forEach((name) => {
deps.add(name);
});
});
});
// TODO ideally globals etc wouldn't be here in the first place
const filtered = Array.from(deps).filter((n) => renderer.context_lookup.has(n));
if (!filtered.length) return null;
return filtered
.map((n) => x`$$invalidate(${renderer.context_lookup.get(n).index}, ${n})`)
.reduce((lhs, rhs) => x`${lhs}, ${rhs}`);
}

Loading…
Cancel
Save