From 0812f0cde78911c041b6eeee66c50a7e0ae5bbaa Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 12 May 2023 15:18:48 +0200 Subject: [PATCH] Convert src/compiler/compile/render_dom/invalidate.ts to JavaScript --- src/compiler/compile/render_dom/Renderer.js | 42 +-- src/compiler/compile/render_dom/invalidate.ts | 282 ++++++++---------- 2 files changed, 155 insertions(+), 169 deletions(-) diff --git a/src/compiler/compile/render_dom/Renderer.js b/src/compiler/compile/render_dom/Renderer.js index 2ca22a0e58..23f9ab5fba 100644 --- a/src/compiler/compile/render_dom/Renderer.js +++ b/src/compiler/compile/render_dom/Renderer.js @@ -4,7 +4,25 @@ import { x } from 'code-red'; import flatten_reference from '../utils/flatten_reference.js'; import { reserved_keywords } from '../utils/reserved_keywords.js'; import { renderer_invalidate } from './invalidate.js'; + 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} */ component; // TODO Maybe Renderer shouldn't know about Component? @@ -181,7 +199,7 @@ export default class Renderer { /** * @param {string[]} names * @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) { 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 // 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() { const _this = this; // TODO: context-overflow make it less gross @@ -295,25 +313,11 @@ export default class Renderer { } /** - * @typedef {Array<{ - * n: number; - * 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 + * @typedef {Object} BindingGroup + * @property {(to_reference?:boolean)=>import('estree').Node} binding_group * @property {string[]} contexts * @property {Set} list_dependencies * @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 */ diff --git a/src/compiler/compile/render_dom/invalidate.ts b/src/compiler/compile/render_dom/invalidate.ts index d3e63298df..5c56b26855 100644 --- a/src/compiler/compile/render_dom/invalidate.ts +++ b/src/compiler/compile/render_dom/invalidate.ts @@ -1,158 +1,140 @@ -import { nodes_match } from '../../utils/nodes_match'; -import { Scope } from 'periscopic'; +import { nodes_match } from '../../utils/nodes_match.js'; import { x } from 'code-red'; -import { Node, Expression } from 'estree'; -import Renderer from './Renderer'; -import { Var } from '../../interfaces'; -export function invalidate( - renderer: Renderer, - scope: Scope, - node: Node, - names: Set, - main_execution_context: boolean = false -) { - const { component } = renderer; - - const [head, ...tail] = Array.from(names) - .filter((name) => { - const owner = scope.find_owner(name); - return !owner || owner === component.instance_scope; - }) - .map((name) => component.var_lookup.get(name)) - .filter((variable) => { - return ( - variable && - !variable.hoistable && - !variable.global && - !variable.module && - (variable.referenced || - variable.subscribable || - variable.is_reactive_dependency || - variable.export_name || - variable.name[0] === '$') - ); - }) as Var[]; - - function get_invalidated(variable: Var, node?: Expression) { - if (main_execution_context && !variable.subscribable && variable.name[0] !== '$') { - return node; - } - return renderer_invalidate(renderer, variable.name, undefined, main_execution_context); - } - - if (!head) { - return node; - } - - component.has_reactive_assignments = true; - - if ( - node.type === 'AssignmentExpression' && - node.operator === '=' && - nodes_match(node.left, node.right) && - tail.length === 0 - ) { - return get_invalidated(head, node); - } - - const is_store_value = head.name[0] === '$' && head.name[1] !== '$'; - 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})`; - } - - let invalidate; - if (!main_execution_context) { - const pass_value = - extra_args.length > 0 || - (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || - (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')); - if (pass_value) { - extra_args.unshift({ - type: 'Identifier', - name: head.name - }); - } - invalidate = x`$$invalidate(${ - renderer.context_lookup.get(head.name).index - }, ${node}, ${extra_args})`; - } else { - // 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; +/** + * @param {import('./Renderer.js').default} renderer + * @param {import('periscopic').Scope} scope + * @param {import('estree').Node} node + * @param {Set} names + * @param {boolean} main_execution_context + */ +export function invalidate(renderer, scope, node, names, main_execution_context = false) { + const { component } = renderer; + const [head, ...tail] = /** @type {import('../../interfaces.js').Var[]} */ (Array.from(names) + .filter((name) => { + const owner = scope.find_owner(name); + return !owner || owner === component.instance_scope; + }) + .map((name) => component.var_lookup.get(name)) + .filter((variable) => { + return (variable && + !variable.hoistable && + !variable.global && + !variable.module && + (variable.referenced || + variable.subscribable || + variable.is_reactive_dependency || + variable.export_name || + variable.name[0] === '$')); + })); + + /** + * @param {import('../../interfaces.js').Var} variable + * @param {import('estree').Expression} [node] + */ + function get_invalidated(variable, node) { + if (main_execution_context && !variable.subscribable && variable.name[0] !== '$') { + return node; + } + return renderer_invalidate(renderer, variable.name, undefined, main_execution_context); + } + if (!head) { + return node; + } + component.has_reactive_assignments = true; + if (node.type === 'AssignmentExpression' && + node.operator === '=' && + nodes_match(node.left, node.right) && + tail.length === 0) { + return get_invalidated(head, node); + } + const is_store_value = head.name[0] === '$' && head.name[1] !== '$'; + 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})`; + } + let invalidate; + if (!main_execution_context) { + const pass_value = extra_args.length > 0 || + (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || + (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')); + if (pass_value) { + extra_args.unshift({ + type: 'Identifier', + name: head.name + }); + } + invalidate = x `$$invalidate(${renderer.context_lookup.get(head.name).index}, ${node}, ${extra_args})`; + } + else { + // 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, - name: string, - value?: unknown, - main_execution_context: boolean = false -) { - const variable = renderer.component.var_lookup.get(name); - - if (variable && variable.subscribable && (variable.reassigned || variable.export_name)) { - if (main_execution_context) { - return x`${`$$subscribe_${name}`}(${value || name})`; - } else { - const member = renderer.context_lookup.get(name); - return x`${`$$subscribe_${name}`}($$invalidate(${member.index}, ${value || name}))`; - } - } - - if (name[0] === '$' && name[1] !== '$') { - return x`${name.slice(1)}.set(${value || name})`; - } - - if ( - variable && - (variable.module || - (!variable.referenced && - !variable.is_reactive_dependency && - !variable.export_name && - !name.startsWith('$$'))) - ) { - return value || name; - } - - if (value) { - if (main_execution_context) { - return x`${value}`; - } else { - const member = renderer.context_lookup.get(name); - return x`$$invalidate(${member.index}, ${value})`; - } - } - - if (main_execution_context) return; +/** + * @param {import('./Renderer.js').default} renderer + * @param {string} name + * @param {unknown} [value] + * @param {boolean} main_execution_context + */ +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 (main_execution_context) { + return x `${`$$subscribe_${name}`}(${value || name})`; + } + else { + const member = renderer.context_lookup.get(name); + return x `${`$$subscribe_${name}`}($$invalidate(${member.index}, ${value || name}))`; + } + } + if (name[0] === '$' && name[1] !== '$') { + return x `${name.slice(1)}.set(${value || name})`; + } + if (variable && + (variable.module || + (!variable.referenced && + !variable.is_reactive_dependency && + !variable.export_name && + !name.startsWith('$$')))) { + return value || name; + } + if (value) { + if (main_execution_context) { + return x `${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]); + 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}`); -}