From bbec1f1d030dd754a89553645caabe51694b65fd Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 12 May 2023 15:02:29 +0200 Subject: [PATCH] Convert src/compiler/compile/render_dom/wrappers/AwaitBlock.ts to JavaScript --- .../compile/render_dom/wrappers/AwaitBlock.ts | 503 +++++++++--------- .../render_dom/wrappers/shared/Wrapper.js | 3 +- 2 files changed, 250 insertions(+), 256 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts index 633d2cf61e..41d89304ec 100644 --- a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts @@ -1,217 +1,221 @@ -import Wrapper from './shared/Wrapper'; -import Renderer from '../Renderer'; -import Block from '../Block'; -import AwaitBlock from '../../nodes/AwaitBlock'; -import create_debugging_comment from './shared/create_debugging_comment'; +import Wrapper from './shared/Wrapper.js'; +import create_debugging_comment from './shared/create_debugging_comment.js'; import { b, x } from 'code-red'; -import FragmentWrapper from './Fragment'; -import PendingBlock from '../../nodes/PendingBlock'; -import ThenBlock from '../../nodes/ThenBlock'; -import CatchBlock from '../../nodes/CatchBlock'; -import { Context } from '../../nodes/shared/Context'; -import { Identifier, Literal, Node } from 'estree'; -import { add_const_tags, add_const_tags_context } from './shared/add_const_tags'; -import Expression from '../../nodes/shared/Expression'; - -type Status = 'pending' | 'then' | 'catch'; +import FragmentWrapper from './Fragment.js'; +import ThenBlock from '../../nodes/ThenBlock.js'; +import CatchBlock from '../../nodes/CatchBlock.js'; +import { add_const_tags, add_const_tags_context } from './shared/add_const_tags.js'; +import Expression from '../../nodes/shared/Expression.js'; +/** @extends Wrapper */ class AwaitBlockBranch extends Wrapper { - parent: AwaitBlockWrapper; - node: PendingBlock | ThenBlock | CatchBlock; - block: Block; - fragment: FragmentWrapper; - is_dynamic: boolean; - - var = null; - status: Status; - - value: string; - value_index: Literal; - value_contexts: Context[]; - is_destructured: boolean; - - constructor( - status: Status, - renderer: Renderer, - block: Block, - parent: AwaitBlockWrapper, - node: PendingBlock | ThenBlock | CatchBlock, - strip_whitespace: boolean, - next_sibling: Wrapper - ) { - super(renderer, block, parent, node); - this.status = status; - - this.block = block.child({ - comment: create_debugging_comment(node, this.renderer.component), - name: this.renderer.component.get_unique_name(`create_${status}_block`), - type: status - }); - - this.add_context(parent.node[status + '_node'], parent.node[status + '_contexts']); - - this.fragment = new FragmentWrapper( - renderer, - this.block, - this.node.children, - parent, - strip_whitespace, - next_sibling - ); - - this.is_dynamic = this.block.dependencies.size > 0; - } - add_context(node: Node | null, contexts: Context[]) { - if (!node) return; - - if (node.type === 'Identifier') { - this.value = node.name; - this.renderer.add_to_context(this.value, true); - } else { - contexts.forEach((context) => { - if (context.type !== 'DestructuredVariable') return; - this.renderer.add_to_context(context.key.name, true); - }); - this.value = this.block.parent.get_unique_name('value').name; - this.value_contexts = contexts; - this.renderer.add_to_context(this.value, true); - this.is_destructured = true; - } - this.value_index = this.renderer.context_lookup.get(this.value).index; - - if (this.has_consts(this.node)) { - add_const_tags_context(this.renderer, this.node.const_tags); - } - } - - has_consts(node: PendingBlock | ThenBlock | CatchBlock): node is ThenBlock | CatchBlock { - return node instanceof ThenBlock || node instanceof CatchBlock; - } - - render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { - this.fragment.render(block, parent_node, parent_nodes); - - if (this.is_destructured || (this.has_consts(this.node) && this.node.const_tags.length > 0)) { - this.render_get_context(); - } - } - - render_get_context() { - const props = this.is_destructured - ? this.value_contexts.map((prop) => { - if (prop.type === 'ComputedProperty') { - const expression = new Expression( - this.renderer.component, - this.node, - this.has_consts(this.node) ? this.node.scope : null, - prop.key - ); - return b`const ${prop.property_name} = ${expression.manipulate(this.block, '#ctx')};`; - } else { - const to_ctx = (name) => this.renderer.reference(name); - return b`#ctx[${ - this.block.renderer.context_lookup.get(prop.key.name).index - }] = ${prop.default_modifier(prop.modifier(x`#ctx[${this.value_index}]`), to_ctx)};`; - } - }) - : null; - - const const_tags_props = this.has_consts(this.node) - ? add_const_tags(this.block, this.node.const_tags, '#ctx') - : null; - - const get_context = this.block.renderer.component.get_unique_name(`get_${this.status}_context`); - this.block.renderer.blocks.push(b` + /** @type {AwaitBlockWrapper} */ + parent; + + /** @type {import('../../nodes/PendingBlock.js').default | import('../../nodes/ThenBlock.js').default | import('../../nodes/CatchBlock.js').default} */ + node; + + /** @type {import('../Block.js').default} */ + block; + + /** @type {import('./Fragment.js').default} */ + fragment; + + /** @type {boolean} */ + is_dynamic; + /** */ + var = null; + + /** @type {Status} */ + status; + + /** @type {string} */ + value; + + /** @type {import('estree').Literal} */ + value_index; + + /** @type {import('../../nodes/shared/Context.js').Context[]} */ + value_contexts; + + /** @type {boolean} */ + is_destructured; + + /** + * @param {Status} status + * @param {import('../Renderer.js').default} renderer + * @param {import('../Block.js').default} block + * @param {AwaitBlockWrapper} parent + * @param {import('../../nodes/PendingBlock.js').default | import('../../nodes/ThenBlock.js').default | import('../../nodes/CatchBlock.js').default} node + * @param {boolean} strip_whitespace + * @param {import('./shared/Wrapper.js').default} next_sibling + */ + constructor(status, renderer, block, parent, node, strip_whitespace, next_sibling) { + super(renderer, block, parent, node); + this.status = status; + this.block = block.child({ + comment: create_debugging_comment(node, this.renderer.component), + name: this.renderer.component.get_unique_name(`create_${status}_block`), + type: status + }); + this.add_context(parent.node[status + '_node'], parent.node[status + '_contexts']); + this.fragment = new FragmentWrapper(renderer, this.block, this.node.children, parent, strip_whitespace, next_sibling); + this.is_dynamic = this.block.dependencies.size > 0; + } + + /** + * @param {import('estree').Node | null} node + * @param {import('../../nodes/shared/Context.js').Context[]} contexts + */ + add_context(node, contexts) { + if (!node) + return; + if (node.type === 'Identifier') { + this.value = node.name; + this.renderer.add_to_context(this.value, true); + } + else { + contexts.forEach((context) => { + if (context.type !== 'DestructuredVariable') + return; + this.renderer.add_to_context(context.key.name, true); + }); + this.value = this.block.parent.get_unique_name('value').name; + this.value_contexts = contexts; + this.renderer.add_to_context(this.value, true); + this.is_destructured = true; + } + this.value_index = this.renderer.context_lookup.get(this.value).index; + if (this.has_consts(this.node)) { + add_const_tags_context(this.renderer, this.node.const_tags); + } + } + + /** + * @param {import('../../nodes/PendingBlock.js').default | import('../../nodes/ThenBlock.js').default | import('../../nodes/CatchBlock.js').default} node + * @returns {boolean} + */ + has_consts(node) { + return node instanceof ThenBlock || node instanceof CatchBlock; + } + + /** + * @param {import('../Block.js').default} block + * @param {import('estree').Identifier} parent_node + * @param {import('estree').Identifier} parent_nodes + */ + render(block, parent_node, parent_nodes) { + this.fragment.render(block, parent_node, parent_nodes); + if (this.is_destructured || (this.has_consts(this.node) && this.node.const_tags.length > 0)) { + this.render_get_context(); + } + } + render_get_context() { + const props = this.is_destructured + ? this.value_contexts.map((prop) => { + if (prop.type === 'ComputedProperty') { + const expression = new Expression(this.renderer.component, this.node, this.has_consts(this.node) ? this.node.scope : null, prop.key); + return b `const ${prop.property_name} = ${expression.manipulate(this.block, '#ctx')};`; + } + else { + + /** @param {any} name */ + const to_ctx = (name) => this.renderer.reference(name); + return b `#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x `#ctx[${this.value_index}]`), to_ctx)};`; + } + }) + : null; + const const_tags_props = this.has_consts(this.node) + ? add_const_tags(this.block, this.node.const_tags, '#ctx') + : null; + const get_context = this.block.renderer.component.get_unique_name(`get_${this.status}_context`); + this.block.renderer.blocks.push(b ` function ${get_context}(#ctx) { ${props} ${const_tags_props} } `); - this.block.chunks.declarations.push(b`${get_context}(#ctx)`); - if (this.block.has_update_method) { - this.block.chunks.update.unshift(b`${get_context}(#ctx)`); - } - } + this.block.chunks.declarations.push(b `${get_context}(#ctx)`); + if (this.block.has_update_method) { + this.block.chunks.update.unshift(b `${get_context}(#ctx)`); + } + } } +/** @extends Wrapper */ export default class AwaitBlockWrapper extends Wrapper { - node: AwaitBlock; - - pending: AwaitBlockBranch; - then: AwaitBlockBranch; - catch: AwaitBlockBranch; - - var: Identifier = { type: 'Identifier', name: 'await_block' }; - - constructor( - renderer: Renderer, - block: Block, - parent: Wrapper, - node: AwaitBlock, - strip_whitespace: boolean, - next_sibling: Wrapper - ) { - super(renderer, block, parent, node); - - block.add_dependencies(this.node.expression.dependencies); - - let is_dynamic = false; - let has_intros = false; - let has_outros = false; - - ['pending', 'then', 'catch'].forEach((status: Status) => { - const child = this.node[status]; - - const branch = new AwaitBlockBranch( - status, - renderer, - block, - this, - child, - strip_whitespace, - next_sibling - ); - - renderer.blocks.push(branch.block); - - if (branch.is_dynamic) { - is_dynamic = true; - // TODO should blocks update their own parents? - block.add_dependencies(branch.block.dependencies); - } - - if (branch.block.has_intros) has_intros = true; - if (branch.block.has_outros) has_outros = true; - this[status] = branch; - }); - - ['pending', 'then', 'catch'].forEach((status) => { - this[status].block.has_update_method = is_dynamic; - this[status].block.has_intro_method = has_intros; - this[status].block.has_outro_method = has_outros; - }); - - if (has_outros) { - block.add_outro(); - } - } - - render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { - const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes); - const update_mount_node = this.get_update_mount_node(anchor); - - const snippet = this.node.expression.manipulate(block); - - const info = block.get_unique_name('info'); - const promise = block.get_unique_name('promise'); - - block.add_variable(promise); - - block.maintain_context = true; - - const info_props: any = x`{ + /** @type {import('../../nodes/AwaitBlock.js').default} */ + node; + + /** @type {AwaitBlockBranch} */ + pending; + + /** @type {AwaitBlockBranch} */ + then; + + /** @type {AwaitBlockBranch} */ + catch; + + /** @type {import('estree').Identifier} */ + var = { type: 'Identifier', name: 'await_block' }; + + /** + * @param {import('../Renderer.js').default} renderer + * @param {import('../Block.js').default} block + * @param {import('./shared/Wrapper.js').default} parent + * @param {import('../../nodes/AwaitBlock.js').default} node + * @param {boolean} strip_whitespace + * @param {import('./shared/Wrapper.js').default} next_sibling + */ + constructor(renderer, block, parent, node, strip_whitespace, next_sibling) { + super(renderer, block, parent, node); + block.add_dependencies(this.node.expression.dependencies); + let is_dynamic = false; + let has_intros = false; + let has_outros = false; + ['pending', 'then', 'catch'].forEach((status) => { + const child = this.node[status]; + const branch = new AwaitBlockBranch(status, renderer, block, this, child, strip_whitespace, next_sibling); + renderer.blocks.push(branch.block); + if (branch.is_dynamic) { + is_dynamic = true; + // TODO should blocks update their own parents? + block.add_dependencies(branch.block.dependencies); + } + if (branch.block.has_intros) + has_intros = true; + if (branch.block.has_outros) + has_outros = true; + this[status] = branch; + }); + ['pending', 'then', 'catch'].forEach((status) => { + this[status].block.has_update_method = is_dynamic; + this[status].block.has_intro_method = has_intros; + this[status].block.has_outro_method = has_outros; + }); + if (has_outros) { + block.add_outro(); + } + } + + /** + * @param {import('../Block.js').default} block + * @param {import('estree').Identifier} parent_node + * @param {import('estree').Identifier} parent_nodes + */ + render(block, parent_node, parent_nodes) { + const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes); + const update_mount_node = this.get_update_mount_node(anchor); + const snippet = this.node.expression.manipulate(block); + const info = block.get_unique_name('info'); + const promise = block.get_unique_name('promise'); + block.add_variable(promise); + block.maintain_context = true; + + /** @type {any} */ + const info_props = x `{ ctx: #ctx, current: null, token: null, @@ -221,93 +225,82 @@ export default class AwaitBlockWrapper extends Wrapper { catch: ${this.catch.block.name}, value: ${this.then.value_index}, error: ${this.catch.value_index}, - blocks: ${this.pending.block.has_outro_method && x`[,,,]`} + blocks: ${this.pending.block.has_outro_method && x `[,,,]`} }`; - - block.chunks.init.push(b` + block.chunks.init.push(b ` let ${info} = ${info_props}; `); - - block.chunks.init.push(b` + block.chunks.init.push(b ` @handle_promise(${promise} = ${snippet}, ${info}); `); - - block.chunks.create.push(b` + block.chunks.create.push(b ` ${info}.block.c(); `); - - if (parent_nodes && this.renderer.options.hydratable) { - block.chunks.claim.push(b` + if (parent_nodes && this.renderer.options.hydratable) { + block.chunks.claim.push(b ` ${info}.block.l(${parent_nodes}); `); - } - - const initial_mount_node = parent_node || '#target'; - const anchor_node = parent_node ? 'null' : '#anchor'; - - const has_transitions = - this.pending.block.has_intro_method || this.pending.block.has_outro_method; - - block.chunks.mount.push(b` + } + const initial_mount_node = parent_node || '#target'; + const anchor_node = parent_node ? 'null' : '#anchor'; + const has_transitions = this.pending.block.has_intro_method || this.pending.block.has_outro_method; + block.chunks.mount.push(b ` ${info}.block.m(${initial_mount_node}, ${info}.anchor = ${anchor_node}); ${info}.mount = () => ${update_mount_node}; ${info}.anchor = ${anchor}; `); - - if (has_transitions) { - block.chunks.intro.push(b`@transition_in(${info}.block);`); - } - - const dependencies = this.node.expression.dynamic_dependencies(); - - const update_await_block_branch = b`@update_await_block_branch(${info}, #ctx, #dirty)`; - - if (dependencies.length > 0) { - const condition = x` + if (has_transitions) { + block.chunks.intro.push(b `@transition_in(${info}.block);`); + } + const dependencies = this.node.expression.dynamic_dependencies(); + const update_await_block_branch = b `@update_await_block_branch(${info}, #ctx, #dirty)`; + if (dependencies.length > 0) { + const condition = x ` ${block.renderer.dirty(dependencies)} && ${promise} !== (${promise} = ${snippet}) && @handle_promise(${promise}, ${info})`; - - block.chunks.update.push(b`${info}.ctx = #ctx;`); - - if (this.pending.block.has_update_method) { - block.chunks.update.push(b` + block.chunks.update.push(b `${info}.ctx = #ctx;`); + if (this.pending.block.has_update_method) { + block.chunks.update.push(b ` if (${condition}) { } else { ${update_await_block_branch} } `); - } else { - block.chunks.update.push(b` + } + else { + block.chunks.update.push(b ` ${condition} `); - } - } else { - if (this.pending.block.has_update_method) { - block.chunks.update.push(b` + } + } + else { + if (this.pending.block.has_update_method) { + block.chunks.update.push(b ` ${update_await_block_branch} `); - } - } - - if (this.pending.block.has_outro_method) { - block.chunks.outro.push(b` + } + } + if (this.pending.block.has_outro_method) { + block.chunks.outro.push(b ` for (let #i = 0; #i < 3; #i += 1) { const block = ${info}.blocks[#i]; @transition_out(block); } `); - } - - block.chunks.destroy.push(b` + } + block.chunks.destroy.push(b ` ${info}.block.d(${parent_node ? null : 'detaching'}); ${info}.token = null; ${info} = null; `); - - [this.pending, this.then, this.catch].forEach((branch) => { - branch.render(branch.block, null, x`#nodes` as Identifier); - }); - } + [this.pending, this.then, this.catch].forEach((branch) => { + branch.render(branch.block, null, /** @type {import('estree').Identifier} */ (x `#nodes`)); + }); + } } + + +/** @typedef {'pending' | 'then' | 'catch'} Status */ + diff --git a/src/compiler/compile/render_dom/wrappers/shared/Wrapper.js b/src/compiler/compile/render_dom/wrappers/shared/Wrapper.js index 5aed5c53e2..89db77b49e 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/Wrapper.js +++ b/src/compiler/compile/render_dom/wrappers/shared/Wrapper.js @@ -1,4 +1,5 @@ import { x } from 'code-red'; + export default class Wrapper { /** @type {import('../../Renderer.js').default} */ renderer; @@ -66,7 +67,7 @@ export default class Wrapper { /** * @param {import('estree').Identifier} anchor - * @returns {import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').Identifier} + * @returns {import('estree').Identifier} */ get_update_mount_node(anchor) { return /** @type {import('estree').Identifier} */ (