From 4cb02a5a021d7b6435ce636b816c9c548ac35652 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 12 May 2023 15:33:41 +0200 Subject: [PATCH] Convert src/compiler/compile/css/Selector.ts to JavaScript --- src/compiler/compile/css/Selector.ts | 1536 +++++++++-------- src/compiler/compile/css/Stylesheet.ts | 2 +- ...le_values.ts => gather_possible_values.js} | 8 +- .../css/{interfaces.ts => private.d.ts} | 0 src/compiler/compile/render_dom/Block.js | 2 +- src/compiler/compile/render_dom/index.js | 7 +- src/compiler/compile/render_dom/invalidate.js | 5 +- .../compile/render_dom/wrappers/EachBlock.js | 2 +- .../compile/render_dom/wrappers/IfBlock.js | 4 +- 9 files changed, 797 insertions(+), 769 deletions(-) rename src/compiler/compile/css/{gather_possible_values.ts => gather_possible_values.js} (66%) rename src/compiler/compile/css/{interfaces.ts => private.d.ts} (100%) diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index a61cf5e2da..050a89c6a2 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -1,540 +1,520 @@ -import MagicString from 'magic-string'; -import Stylesheet from './Stylesheet'; -import { gather_possible_values, UNKNOWN } from './gather_possible_values'; -import { CssNode } from './interfaces'; -import Component from '../Component'; -import Element from '../nodes/Element'; -import { INode } from '../nodes/interfaces'; -import EachBlock from '../nodes/EachBlock'; -import IfBlock from '../nodes/IfBlock'; -import AwaitBlock from '../nodes/AwaitBlock'; -import compiler_errors from '../compiler_errors'; -import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns'; - -enum BlockAppliesToNode { - NotPossible, - Possible, - UnknownSelectorType -} -enum NodeExist { - Probably = 1, - Definitely = 2 -} - +import { gather_possible_values, UNKNOWN } from './gather_possible_values.js'; +import compiler_errors from '../compiler_errors.js'; +import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns.js'; +var BlockAppliesToNode; +(function (BlockAppliesToNode) { + BlockAppliesToNode[BlockAppliesToNode["NotPossible"] = 0] = "NotPossible"; + BlockAppliesToNode[BlockAppliesToNode["Possible"] = 1] = "Possible"; + BlockAppliesToNode[BlockAppliesToNode["UnknownSelectorType"] = 2] = "UnknownSelectorType"; +})(BlockAppliesToNode || (BlockAppliesToNode = {})); +var NodeExist; +(function (NodeExist) { + NodeExist[NodeExist["Probably"] = 1] = "Probably"; + NodeExist[NodeExist["Definitely"] = 2] = "Definitely"; +})(NodeExist || (NodeExist = {})); const whitelist_attribute_selector = new Map([ - ['details', new Set(['open'])], - ['dialog', new Set(['open'])] + ['details', new Set(['open'])], + ['dialog', new Set(['open'])] ]); - const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/; - export default class Selector { - node: CssNode; - stylesheet: Stylesheet; - blocks: Block[]; - local_blocks: Block[]; - used: boolean; - - constructor(node: CssNode, stylesheet: Stylesheet) { - this.node = node; - this.stylesheet = stylesheet; - - this.blocks = group_selectors(node); - - // take trailing :global(...) selectors out of consideration - let i = this.blocks.length; - while (i > 0) { - if (!this.blocks[i - 1].global) break; - i -= 1; - } - - this.local_blocks = this.blocks.slice(0, i); - - const host_only = this.blocks.length === 1 && this.blocks[0].host; - const root_only = this.blocks.length === 1 && this.blocks[0].root; - - this.used = this.local_blocks.length === 0 || host_only || root_only; - } - - apply(node: Element) { - const to_encapsulate: Array<{ node: Element; block: Block }> = []; - - apply_selector(this.local_blocks.slice(), node, to_encapsulate); - - if (to_encapsulate.length > 0) { - to_encapsulate.forEach(({ node, block }) => { - this.stylesheet.nodes_with_css_class.add(node); - block.should_encapsulate = true; - }); - - this.used = true; - } - } - - minify(code: MagicString) { - let c: number = null; - this.blocks.forEach((block, i) => { - if (i > 0) { - if (block.start - c > 1) { - code.update(c, block.start, block.combinator.name || ' '); - } - } - - c = block.end; - }); - } - - transform(code: MagicString, attr: string, max_amount_class_specificity_increased: number) { - const amount_class_specificity_to_increase = - max_amount_class_specificity_increased - - this.blocks.filter((block) => block.should_encapsulate).length; - - function remove_global_pseudo_class(selector: CssNode) { - const first = selector.children[0]; - const last = selector.children[selector.children.length - 1]; - code.remove(selector.start, first.start).remove(last.end, selector.end); - } - - function encapsulate_block(block: Block, attr: string) { - for (const selector of block.selectors) { - if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - remove_global_pseudo_class(selector); - } - } - - let i = block.selectors.length; - - while (i--) { - const selector = block.selectors[i]; - if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') { - if (selector.name !== 'root' && selector.name !== 'host') { - if (i === 0) code.prependRight(selector.start, attr); - } - continue; - } - - if (selector.type === 'TypeSelector' && selector.name === '*') { - code.update(selector.start, selector.end, attr); - } else { - code.appendLeft(selector.end, attr); - } - - break; - } - } - - this.blocks.forEach((block, index) => { - if (block.global) { - remove_global_pseudo_class(block.selectors[0]); - } - if (block.should_encapsulate) - encapsulate_block( - block, - index === this.blocks.length - 1 - ? attr.repeat(amount_class_specificity_to_increase + 1) - : attr - ); - }); - } - - validate(component: Component) { - let start = 0; - let end = this.blocks.length; - - for (; start < end; start += 1) { - if (!this.blocks[start].global) break; - } - - for (; end > start; end -= 1) { - if (!this.blocks[end - 1].global) break; - } - - for (let i = start; i < end; i += 1) { - if (this.blocks[i].global) { - return component.error(this.blocks[i].selectors[0], compiler_errors.css_invalid_global); - } - } - - this.validate_global_with_multiple_selectors(component); - this.validate_global_compound_selector(component); - this.validate_invalid_combinator_without_selector(component); - } - - validate_global_with_multiple_selectors(component: Component) { - if (this.blocks.length === 1 && this.blocks[0].selectors.length === 1) { - // standalone :global() with multiple selectors is OK - return; - } - - for (const block of this.blocks) { - for (const selector of block.selectors) { - if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - if (regex_is_single_css_selector.test(selector.children[0].value)) { - component.error(selector, compiler_errors.css_invalid_global_selector); - } - } - } - } - } - validate_invalid_combinator_without_selector(component: Component) { - for (let i = 0; i < this.blocks.length; i++) { - const block = this.blocks[i]; - if (block.combinator && block.selectors.length === 0) { - component.error( - this.node, - compiler_errors.css_invalid_selector( - component.source.slice(this.node.start, this.node.end) - ) - ); - } - if (!block.combinator && block.selectors.length === 0) { - component.error( - this.node, - compiler_errors.css_invalid_selector( - component.source.slice(this.node.start, this.node.end) - ) - ); - } - } - } - - validate_global_compound_selector(component: Component) { - for (const block of this.blocks) { - for (let index = 0; index < block.selectors.length; index++) { - const selector = block.selectors[index]; - if ( - selector.type === 'PseudoClassSelector' && - selector.name === 'global' && - index !== 0 && - selector.children && - selector.children.length > 0 && - !/[.:#\s]/.test(selector.children[0].value[0]) - ) { - component.error(selector, compiler_errors.css_invalid_global_selector_position); - } - } - } - } - - get_amount_class_specificity_increased() { - let count = 0; - for (const block of this.blocks) { - if (block.should_encapsulate) { - count++; - } - } - return count; - } -} -function apply_selector( - blocks: Block[], - node: Element, - to_encapsulate: Array<{ node: Element; block: Block }> -): boolean { - const block = blocks.pop(); - if (!block) return false; - - if (!node) { - return ( - (block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0) - ); - } - - switch (block_might_apply_to_node(block, node)) { - case BlockAppliesToNode.NotPossible: - return false; - - case BlockAppliesToNode.UnknownSelectorType: - // bail. TODO figure out what these could be - to_encapsulate.push({ node, block }); - return true; - } - - if (block.combinator) { - if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') { - for (const ancestor_block of blocks) { - if (ancestor_block.global) { - continue; - } - - if (ancestor_block.host) { - to_encapsulate.push({ node, block }); - return true; - } - - let parent = node; - while ((parent = get_element_parent(parent))) { - if ( - block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible - ) { - to_encapsulate.push({ node: parent, block: ancestor_block }); - } - } - - if (to_encapsulate.length) { - to_encapsulate.push({ node, block }); - return true; - } - } - - if (blocks.every((block) => block.global)) { - to_encapsulate.push({ node, block }); - return true; - } - - return false; - } else if (block.combinator.name === '>') { - const has_global_parent = blocks.every((block) => block.global); - if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) { - to_encapsulate.push({ node, block }); - return true; - } - - return false; - } else if (block.combinator.name === '+' || block.combinator.name === '~') { - const siblings = get_possible_element_siblings(node, block.combinator.name === '+'); - let has_match = false; - - // NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the - // css-tree limitation that does not parse the inner selector of :global - // so unless we are sure there will be no sibling to match, we will consider it as matched - const has_global = blocks.some((block) => block.global); - if (has_global) { - if (siblings.size === 0 && get_element_parent(node) !== null) { - return false; - } - to_encapsulate.push({ node, block }); - return true; - } - - for (const possible_sibling of siblings.keys()) { - if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) { - to_encapsulate.push({ node, block }); - has_match = true; - } - } - return has_match; - } - - // TODO other combinators - to_encapsulate.push({ node, block }); - return true; - } - - to_encapsulate.push({ node, block }); - return true; + /** @type {import('./private.js').CssNode} */ + node; + + /** @type {import('./Stylesheet.js').default} */ + stylesheet; + + /** @type {Block[]} */ + blocks; + + /** @type {Block[]} */ + local_blocks; + + /** @type {boolean} */ + used; + + /** + * @param {import('./private.js').CssNode} node + * @param {import('./Stylesheet.js').default} stylesheet + */ + constructor(node, stylesheet) { + this.node = node; + this.stylesheet = stylesheet; + this.blocks = group_selectors(node); + // take trailing :global(...) selectors out of consideration + let i = this.blocks.length; + while (i > 0) { + if (!this.blocks[i - 1].global) + break; + i -= 1; + } + this.local_blocks = this.blocks.slice(0, i); + const host_only = this.blocks.length === 1 && this.blocks[0].host; + const root_only = this.blocks.length === 1 && this.blocks[0].root; + this.used = this.local_blocks.length === 0 || host_only || root_only; + } + + /** @param {import('../nodes/Element.js').default} node */ + apply(node) { + + /** @type {Array<{ node: import('../nodes/Element.js').default; block: Block }>} */ + const to_encapsulate = []; + apply_selector(this.local_blocks.slice(), node, to_encapsulate); + if (to_encapsulate.length > 0) { + to_encapsulate.forEach(({ node, block }) => { + this.stylesheet.nodes_with_css_class.add(node); + block.should_encapsulate = true; + }); + this.used = true; + } + } + + /** @param {import('magic-string').default} code */ + minify(code) { + + /** @type {number} */ + let c = null; + this.blocks.forEach((block, i) => { + if (i > 0) { + if (block.start - c > 1) { + code.update(c, block.start, block.combinator.name || ' '); + } + } + c = block.end; + }); + } + + /** + * @param {import('magic-string').default} code + * @param {string} attr + * @param {number} max_amount_class_specificity_increased + */ + transform(code, attr, max_amount_class_specificity_increased) { + const amount_class_specificity_to_increase = max_amount_class_specificity_increased - + this.blocks.filter((block) => block.should_encapsulate).length; + + /** @param {import('./private.js').CssNode} selector */ + function remove_global_pseudo_class(selector) { + const first = selector.children[0]; + const last = selector.children[selector.children.length - 1]; + code.remove(selector.start, first.start).remove(last.end, selector.end); + } + + /** + * @param {Block} block + * @param {string} attr + */ + function encapsulate_block(block, attr) { + for (const selector of block.selectors) { + if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { + remove_global_pseudo_class(selector); + } + } + let i = block.selectors.length; + while (i--) { + const selector = block.selectors[i]; + if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') { + if (selector.name !== 'root' && selector.name !== 'host') { + if (i === 0) + code.prependRight(selector.start, attr); + } + continue; + } + if (selector.type === 'TypeSelector' && selector.name === '*') { + code.update(selector.start, selector.end, attr); + } + else { + code.appendLeft(selector.end, attr); + } + break; + } + } + this.blocks.forEach((block, index) => { + if (block.global) { + remove_global_pseudo_class(block.selectors[0]); + } + if (block.should_encapsulate) + encapsulate_block(block, index === this.blocks.length - 1 + ? attr.repeat(amount_class_specificity_to_increase + 1) + : attr); + }); + } + + /** @param {import('../Component.js').default} component */ + validate(component) { + let start = 0; + let end = this.blocks.length; + for (; start < end; start += 1) { + if (!this.blocks[start].global) + break; + } + for (; end > start; end -= 1) { + if (!this.blocks[end - 1].global) + break; + } + for (let i = start; i < end; i += 1) { + if (this.blocks[i].global) { + return component.error(this.blocks[i].selectors[0], compiler_errors.css_invalid_global); + } + } + this.validate_global_with_multiple_selectors(component); + this.validate_global_compound_selector(component); + this.validate_invalid_combinator_without_selector(component); + } + + /** @param {import('../Component.js').default} component */ + validate_global_with_multiple_selectors(component) { + if (this.blocks.length === 1 && this.blocks[0].selectors.length === 1) { + // standalone :global() with multiple selectors is OK + return; + } + for (const block of this.blocks) { + for (const selector of block.selectors) { + if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { + if (regex_is_single_css_selector.test(selector.children[0].value)) { + component.error(selector, compiler_errors.css_invalid_global_selector); + } + } + } + } + } + + /** @param {import('../Component.js').default} component */ + validate_invalid_combinator_without_selector(component) { + for (let i = 0; i < this.blocks.length; i++) { + const block = this.blocks[i]; + if (block.combinator && block.selectors.length === 0) { + component.error(this.node, compiler_errors.css_invalid_selector(component.source.slice(this.node.start, this.node.end))); + } + if (!block.combinator && block.selectors.length === 0) { + component.error(this.node, compiler_errors.css_invalid_selector(component.source.slice(this.node.start, this.node.end))); + } + } + } + + /** @param {import('../Component.js').default} component */ + validate_global_compound_selector(component) { + for (const block of this.blocks) { + for (let index = 0; index < block.selectors.length; index++) { + const selector = block.selectors[index]; + if (selector.type === 'PseudoClassSelector' && + selector.name === 'global' && + index !== 0 && + selector.children && + selector.children.length > 0 && + !/[.:#\s]/.test(selector.children[0].value[0])) { + component.error(selector, compiler_errors.css_invalid_global_selector_position); + } + } + } + } + get_amount_class_specificity_increased() { + let count = 0; + for (const block of this.blocks) { + if (block.should_encapsulate) { + count++; + } + } + return count; + } } +/** + * @param {Block[]} blocks + * @param {import('../nodes/Element.js').default} node + * @param {Array<{ node: import('../nodes/Element.js').default; block: Block }>} to_encapsulate + * @returns {boolean} + */ +function apply_selector(blocks, node, to_encapsulate) { + const block = blocks.pop(); + if (!block) + return false; + if (!node) { + return ((block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0)); + } + switch (block_might_apply_to_node(block, node)) { + case BlockAppliesToNode.NotPossible: + return false; + case BlockAppliesToNode.UnknownSelectorType: + // bail. TODO figure out what these could be + to_encapsulate.push({ node, block }); + return true; + } + if (block.combinator) { + if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') { + for (const ancestor_block of blocks) { + if (ancestor_block.global) { + continue; + } + if (ancestor_block.host) { + to_encapsulate.push({ node, block }); + return true; + } + let parent = node; + while ((parent = get_element_parent(parent))) { + if (block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible) { + to_encapsulate.push({ node: parent, block: ancestor_block }); + } + } + if (to_encapsulate.length) { + to_encapsulate.push({ node, block }); + return true; + } + } + if (blocks.every((block) => block.global)) { + to_encapsulate.push({ node, block }); + return true; + } + return false; + } + else if (block.combinator.name === '>') { + const has_global_parent = blocks.every((block) => block.global); + if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) { + to_encapsulate.push({ node, block }); + return true; + } + return false; + } + else if (block.combinator.name === '+' || block.combinator.name === '~') { + const siblings = get_possible_element_siblings(node, block.combinator.name === '+'); + let has_match = false; + // NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the + // css-tree limitation that does not parse the inner selector of :global + // so unless we are sure there will be no sibling to match, we will consider it as matched + const has_global = blocks.some((block) => block.global); + if (has_global) { + if (siblings.size === 0 && get_element_parent(node) !== null) { + return false; + } + to_encapsulate.push({ node, block }); + return true; + } + for (const possible_sibling of siblings.keys()) { + if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) { + to_encapsulate.push({ node, block }); + has_match = true; + } + } + return has_match; + } + // TODO other combinators + to_encapsulate.push({ node, block }); + return true; + } + to_encapsulate.push({ node, block }); + return true; +} const regex_backslash_and_following_character = /\\(.)/g; -function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToNode { - let i = block.selectors.length; - - while (i--) { - const selector = block.selectors[i]; - const name = - typeof selector.name === 'string' && - selector.name.replace(regex_backslash_and_following_character, '$1'); - - if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) { - return BlockAppliesToNode.NotPossible; - } - - if ( - block.selectors.length === 1 && - selector.type === 'PseudoClassSelector' && - name === 'global' - ) { - return BlockAppliesToNode.NotPossible; - } - - if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') { - continue; - } - - if (selector.type === 'ClassSelector') { - if ( - !attribute_matches(node, 'class', name, '~=', false) && - !node.classes.some((c) => c.name === name) - ) - return BlockAppliesToNode.NotPossible; - } else if (selector.type === 'IdSelector') { - if (!attribute_matches(node, 'id', name, '=', false)) return BlockAppliesToNode.NotPossible; - } else if (selector.type === 'AttributeSelector') { - if ( - !( - whitelist_attribute_selector.has(node.name.toLowerCase()) && - whitelist_attribute_selector - .get(node.name.toLowerCase()) - .has(selector.name.name.toLowerCase()) - ) && - !attribute_matches( - node, - selector.name.name, - selector.value && unquote(selector.value), - selector.matcher, - selector.flags - ) - ) { - return BlockAppliesToNode.NotPossible; - } - } else if (selector.type === 'TypeSelector') { - if ( - node.name.toLowerCase() !== name.toLowerCase() && - name !== '*' && - !node.is_dynamic_element - ) - return BlockAppliesToNode.NotPossible; - } else { - return BlockAppliesToNode.UnknownSelectorType; - } - } - - return BlockAppliesToNode.Possible; +/** + * @param {Block} block + * @param {import('../nodes/Element.js').default} node + * @returns {BlockAppliesToNode} + */ +function block_might_apply_to_node(block, node) { + let i = block.selectors.length; + while (i--) { + const selector = block.selectors[i]; + const name = typeof selector.name === 'string' && + selector.name.replace(regex_backslash_and_following_character, '$1'); + if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) { + return BlockAppliesToNode.NotPossible; + } + if (block.selectors.length === 1 && + selector.type === 'PseudoClassSelector' && + name === 'global') { + return BlockAppliesToNode.NotPossible; + } + if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') { + continue; + } + if (selector.type === 'ClassSelector') { + if (!attribute_matches(node, 'class', name, '~=', false) && + !node.classes.some((c) => c.name === name)) + return BlockAppliesToNode.NotPossible; + } + else if (selector.type === 'IdSelector') { + if (!attribute_matches(node, 'id', name, '=', false)) + return BlockAppliesToNode.NotPossible; + } + else if (selector.type === 'AttributeSelector') { + if (!(whitelist_attribute_selector.has(node.name.toLowerCase()) && + whitelist_attribute_selector + .get(node.name.toLowerCase()) + .has(selector.name.name.toLowerCase())) && + !attribute_matches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) { + return BlockAppliesToNode.NotPossible; + } + } + else if (selector.type === 'TypeSelector') { + if (node.name.toLowerCase() !== name.toLowerCase() && + name !== '*' && + !node.is_dynamic_element) + return BlockAppliesToNode.NotPossible; + } + else { + return BlockAppliesToNode.UnknownSelectorType; + } + } + return BlockAppliesToNode.Possible; } +/** + * @param {any} operator + * @param {any} expected_value + * @param {any} case_insensitive + * @param {any} value + */ function test_attribute(operator, expected_value, case_insensitive, value) { - if (case_insensitive) { - expected_value = expected_value.toLowerCase(); - value = value.toLowerCase(); - } - switch (operator) { - case '=': - return value === expected_value; - case '~=': - return value.split(/\s/).includes(expected_value); - case '|=': - return `${value}-`.startsWith(`${expected_value}-`); - case '^=': - return value.startsWith(expected_value); - case '$=': - return value.endsWith(expected_value); - case '*=': - return value.includes(expected_value); - default: - throw new Error("this shouldn't happen"); - } + if (case_insensitive) { + expected_value = expected_value.toLowerCase(); + value = value.toLowerCase(); + } + switch (operator) { + case '=': + return value === expected_value; + case '~=': + return value.split(/\s/).includes(expected_value); + case '|=': + return `${value}-`.startsWith(`${expected_value}-`); + case '^=': + return value.startsWith(expected_value); + case '$=': + return value.endsWith(expected_value); + case '*=': + return value.includes(expected_value); + default: + throw new Error("this shouldn't happen"); + } } -function attribute_matches( - node: CssNode, - name: string, - expected_value: string, - operator: string, - case_insensitive: boolean -) { - const spread = node.attributes.find((attr) => attr.type === 'Spread'); - if (spread) return true; - - if (node.bindings.some((binding: CssNode) => binding.name === name)) return true; - - const attr = node.attributes.find((attr: CssNode) => attr.name === name); - if (!attr) return false; - if (attr.is_true) return operator === null; - if (expected_value == null) return true; - - if (attr.chunks.length === 1) { - const value = attr.chunks[0]; - if (!value) return false; - if (value.type === 'Text') - return test_attribute(operator, expected_value, case_insensitive, value.data); - } - - const possible_values = new Set(); - - let prev_values = []; - for (const chunk of attr.chunks) { - const current_possible_values = new Set(); - if (chunk.type === 'Text') { - current_possible_values.add(chunk.data); - } else { - gather_possible_values(chunk.node, current_possible_values); - } - - // impossible to find out all combinations - if (current_possible_values.has(UNKNOWN)) return true; - - if (prev_values.length > 0) { - const start_with_space = []; - const remaining = []; - current_possible_values.forEach((current_possible_value: string) => { - if (regex_starts_with_whitespace.test(current_possible_value)) { - start_with_space.push(current_possible_value); - } else { - remaining.push(current_possible_value); - } - }); - - if (remaining.length > 0) { - if (start_with_space.length > 0) { - prev_values.forEach((prev_value) => possible_values.add(prev_value)); - } - - const combined = []; - prev_values.forEach((prev_value: string) => { - remaining.forEach((value: string) => { - combined.push(prev_value + value); - }); - }); - prev_values = combined; - - start_with_space.forEach((value: string) => { - if (regex_ends_with_whitespace.test(value)) { - possible_values.add(value); - } else { - prev_values.push(value); - } - }); - continue; - } else { - prev_values.forEach((prev_value) => possible_values.add(prev_value)); - prev_values = []; - } - } - - current_possible_values.forEach((current_possible_value: string) => { - if (regex_ends_with_whitespace.test(current_possible_value)) { - possible_values.add(current_possible_value); - } else { - prev_values.push(current_possible_value); - } - }); - if (prev_values.length < current_possible_values.size) { - prev_values.push(' '); - } - - if (prev_values.length > 20) { - // might grow exponentially, bail out - return true; - } - } - prev_values.forEach((prev_value) => possible_values.add(prev_value)); - - if (possible_values.has(UNKNOWN)) return true; - - for (const value of possible_values) { - if (test_attribute(operator, expected_value, case_insensitive, value)) return true; - } - - return false; +/** + * @param {import('./private.js').CssNode} node + * @param {string} name + * @param {string} expected_value + * @param {string} operator + * @param {boolean} case_insensitive + */ +function attribute_matches(node, name, expected_value, operator, case_insensitive) { + const spread = node.attributes.find((attr) => attr.type === 'Spread'); + if (spread) + return true; + if (node.bindings.some((binding) => binding.name === name)) + return true; + const attr = node.attributes.find((attr) => attr.name === name); + if (!attr) + return false; + if (attr.is_true) + return operator === null; + if (expected_value == null) + return true; + if (attr.chunks.length === 1) { + const value = attr.chunks[0]; + if (!value) + return false; + if (value.type === 'Text') + return test_attribute(operator, expected_value, case_insensitive, value.data); + } + const possible_values = new Set(); + let prev_values = []; + for (const chunk of attr.chunks) { + const current_possible_values = new Set(); + if (chunk.type === 'Text') { + current_possible_values.add(chunk.data); + } + else { + gather_possible_values(chunk.node, current_possible_values); + } + // impossible to find out all combinations + if (current_possible_values.has(UNKNOWN)) + return true; + if (prev_values.length > 0) { + const start_with_space = []; + const remaining = []; + current_possible_values.forEach((current_possible_value) => { + if (regex_starts_with_whitespace.test(current_possible_value)) { + start_with_space.push(current_possible_value); + } + else { + remaining.push(current_possible_value); + } + }); + if (remaining.length > 0) { + if (start_with_space.length > 0) { + prev_values.forEach((prev_value) => possible_values.add(prev_value)); + } + const combined = []; + prev_values.forEach((prev_value) => { + remaining.forEach((value) => { + combined.push(prev_value + value); + }); + }); + prev_values = combined; + start_with_space.forEach((value) => { + if (regex_ends_with_whitespace.test(value)) { + possible_values.add(value); + } + else { + prev_values.push(value); + } + }); + continue; + } + else { + prev_values.forEach((prev_value) => possible_values.add(prev_value)); + prev_values = []; + } + } + current_possible_values.forEach((current_possible_value) => { + if (regex_ends_with_whitespace.test(current_possible_value)) { + possible_values.add(current_possible_value); + } + else { + prev_values.push(current_possible_value); + } + }); + if (prev_values.length < current_possible_values.size) { + prev_values.push(' '); + } + if (prev_values.length > 20) { + // might grow exponentially, bail out + return true; + } + } + prev_values.forEach((prev_value) => possible_values.add(prev_value)); + if (possible_values.has(UNKNOWN)) + return true; + for (const value of possible_values) { + if (test_attribute(operator, expected_value, case_insensitive, value)) + return true; + } + return false; } -function unquote(value: CssNode) { - if (value.type === 'Identifier') return value.name; - const str = value.value; - if ((str[0] === str[str.length - 1] && str[0] === "'") || str[0] === '"') { - return str.slice(1, str.length - 1); - } - return str; +/** @param {import('./private.js').CssNode} value */ +function unquote(value) { + if (value.type === 'Identifier') + return value.name; + const str = value.value; + if ((str[0] === str[str.length - 1] && str[0] === "'") || str[0] === '"') { + return str.slice(1, str.length - 1); + } + return str; } -function get_element_parent(node: Element): Element | null { - let parent: INode = node; - while ((parent = parent.parent) && parent.type !== 'Element'); - return parent as Element | null; -} +/** + * @param {import('../nodes/Element.js').default} node + * @returns {any} + */ +function get_element_parent(node) { + /** @type {import('../nodes/interfaces.js').INode} */ + let parent = node; + while ((parent = parent.parent) && parent.type !== 'Element') + ; + return /** @type {import('../nodes/Element.js').default | null} */ (parent); +} /** * Finds the given node's previous sibling in the DOM * @@ -550,264 +530,306 @@ function get_element_parent(node: Element): Element | null { * is considered to look like: *

Heading 1

*

Heading 2

+ * @param {import('../nodes/interfaces.js').INode} node + * @returns {import('../nodes/interfaces.js').INode} */ -function find_previous_sibling(node: INode): INode { - let current_node: INode = node; - do { - if (current_node.type === 'Slot') { - const slot_children = current_node.children; - if (slot_children.length > 0) { - current_node = slot_children.slice(-1)[0]; // go to its last child first - continue; - } - } - - while (!current_node.prev && current_node.parent && current_node.parent.type === 'Slot') { - current_node = current_node.parent; - } - current_node = current_node.prev; - } while (current_node && current_node.type === 'Slot'); - - return current_node; +function find_previous_sibling(node) { + + /** @type {import('../nodes/interfaces.js').INode} */ + let current_node = node; + do { + if (current_node.type === 'Slot') { + const slot_children = current_node.children; + if (slot_children.length > 0) { + current_node = slot_children.slice(-1)[0]; // go to its last child first + continue; + } + } + while (!current_node.prev && current_node.parent && current_node.parent.type === 'Slot') { + current_node = current_node.parent; + } + current_node = current_node.prev; + } while (current_node && current_node.type === 'Slot'); + return current_node; } -function get_possible_element_siblings( - node: INode, - adjacent_only: boolean -): Map { - const result: Map = new Map(); - let prev: INode = node; - while ((prev = find_previous_sibling(prev))) { - if (prev.type === 'Element') { - if ( - !prev.attributes.find( - (attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot' - ) - ) { - result.set(prev, NodeExist.Definitely); - } - - if (adjacent_only) { - break; - } - } else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') { - const possible_last_child = get_possible_last_child(prev, adjacent_only); - - add_to_map(possible_last_child, result); - if (adjacent_only && has_definite_elements(possible_last_child)) { - return result; - } - } - } - - if (!prev || !adjacent_only) { - let parent: INode = node; - let skip_each_for_last_child = node.type === 'ElseBlock'; - while ( - (parent = parent.parent) && - (parent.type === 'EachBlock' || - parent.type === 'IfBlock' || - parent.type === 'ElseBlock' || - parent.type === 'AwaitBlock') - ) { - const possible_siblings = get_possible_element_siblings(parent, adjacent_only); - add_to_map(possible_siblings, result); - - if (parent.type === 'EachBlock') { - // first child of each block can select the last child of each block as previous sibling - if (skip_each_for_last_child) { - skip_each_for_last_child = false; - } else { - add_to_map(get_possible_last_child(parent, adjacent_only), result); - } - } else if (parent.type === 'ElseBlock') { - skip_each_for_last_child = true; - parent = parent.parent; - } - - if (adjacent_only && has_definite_elements(possible_siblings)) { - break; - } - } - } - - return result; +/** + * @param {import('../nodes/interfaces.js').INode} node + * @param {boolean} adjacent_only + * @returns {Map} + */ +function get_possible_element_siblings(node, adjacent_only) { + + /** @type {Map} */ + const result = new Map(); + + /** @type {import('../nodes/interfaces.js').INode} */ + let prev = node; + while ((prev = find_previous_sibling(prev))) { + if (prev.type === 'Element') { + if (!prev.attributes.find((attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot')) { + result.set(prev, NodeExist.Definitely); + } + if (adjacent_only) { + break; + } + } + else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') { + const possible_last_child = get_possible_last_child(prev, adjacent_only); + add_to_map(possible_last_child, result); + if (adjacent_only && has_definite_elements(possible_last_child)) { + return result; + } + } + } + if (!prev || !adjacent_only) { + + /** @type {import('../nodes/interfaces.js').INode} */ + let parent = node; + let skip_each_for_last_child = node.type === 'ElseBlock'; + while ((parent = parent.parent) && + (parent.type === 'EachBlock' || + parent.type === 'IfBlock' || + parent.type === 'ElseBlock' || + parent.type === 'AwaitBlock')) { + const possible_siblings = get_possible_element_siblings(parent, adjacent_only); + add_to_map(possible_siblings, result); + if (parent.type === 'EachBlock') { + // first child of each block can select the last child of each block as previous sibling + if (skip_each_for_last_child) { + skip_each_for_last_child = false; + } + else { + add_to_map(get_possible_last_child(parent, adjacent_only), result); + } + } + else if (parent.type === 'ElseBlock') { + skip_each_for_last_child = true; + parent = parent.parent; + } + if (adjacent_only && has_definite_elements(possible_siblings)) { + break; + } + } + } + return result; } -function get_possible_last_child( - block: EachBlock | IfBlock | AwaitBlock, - adjacent_only: boolean -): Map { - const result: Map = new Map(); - - if (block.type === 'EachBlock') { - const each_result: Map = loop_child(block.children, adjacent_only); - const else_result: Map = block.else - ? loop_child(block.else.children, adjacent_only) - : new Map(); - - const not_exhaustive = !has_definite_elements(else_result); - - if (not_exhaustive) { - mark_as_probably(each_result); - mark_as_probably(else_result); - } - add_to_map(each_result, result); - add_to_map(else_result, result); - } else if (block.type === 'IfBlock') { - const if_result: Map = loop_child(block.children, adjacent_only); - const else_result: Map = block.else - ? loop_child(block.else.children, adjacent_only) - : new Map(); - - const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result); - - if (not_exhaustive) { - mark_as_probably(if_result); - mark_as_probably(else_result); - } - - add_to_map(if_result, result); - add_to_map(else_result, result); - } else if (block.type === 'AwaitBlock') { - const pending_result: Map = block.pending - ? loop_child(block.pending.children, adjacent_only) - : new Map(); - const then_result: Map = block.then - ? loop_child(block.then.children, adjacent_only) - : new Map(); - const catch_result: Map = block.catch - ? loop_child(block.catch.children, adjacent_only) - : new Map(); - - const not_exhaustive = - !has_definite_elements(pending_result) || - !has_definite_elements(then_result) || - !has_definite_elements(catch_result); - - if (not_exhaustive) { - mark_as_probably(pending_result); - mark_as_probably(then_result); - mark_as_probably(catch_result); - } - - add_to_map(pending_result, result); - add_to_map(then_result, result); - add_to_map(catch_result, result); - } - - return result; +/** + * @param {import('../nodes/EachBlock.js').default | import('../nodes/IfBlock.js').default | import('../nodes/AwaitBlock.js').default} block + * @param {boolean} adjacent_only + * @returns {Map} + */ +function get_possible_last_child(block, adjacent_only) { + + /** @type {Map} */ + const result = new Map(); + if (block.type === 'EachBlock') { + + /** @type {Map} */ + const each_result = loop_child(block.children, adjacent_only); + + /** @type {Map} */ + const else_result = block.else + ? loop_child(block.else.children, adjacent_only) + : new Map(); + const not_exhaustive = !has_definite_elements(else_result); + if (not_exhaustive) { + mark_as_probably(each_result); + mark_as_probably(else_result); + } + add_to_map(each_result, result); + add_to_map(else_result, result); + } + else if (block.type === 'IfBlock') { + + /** @type {Map} */ + const if_result = loop_child(block.children, adjacent_only); + + /** @type {Map} */ + const else_result = block.else + ? loop_child(block.else.children, adjacent_only) + : new Map(); + const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result); + if (not_exhaustive) { + mark_as_probably(if_result); + mark_as_probably(else_result); + } + add_to_map(if_result, result); + add_to_map(else_result, result); + } + else if (block.type === 'AwaitBlock') { + + /** @type {Map} */ + const pending_result = block.pending + ? loop_child(block.pending.children, adjacent_only) + : new Map(); + + /** @type {Map} */ + const then_result = block.then + ? loop_child(block.then.children, adjacent_only) + : new Map(); + + /** @type {Map} */ + const catch_result = block.catch + ? loop_child(block.catch.children, adjacent_only) + : new Map(); + const not_exhaustive = !has_definite_elements(pending_result) || + !has_definite_elements(then_result) || + !has_definite_elements(catch_result); + if (not_exhaustive) { + mark_as_probably(pending_result); + mark_as_probably(then_result); + mark_as_probably(catch_result); + } + add_to_map(pending_result, result); + add_to_map(then_result, result); + add_to_map(catch_result, result); + } + return result; +} + +/** + * @param {Map} result + * @returns {boolean} + */ +function has_definite_elements(result) { + if (result.size === 0) + return false; + for (const exist of result.values()) { + if (exist === NodeExist.Definitely) { + return true; + } + } + return false; } -function has_definite_elements(result: Map): boolean { - if (result.size === 0) return false; - for (const exist of result.values()) { - if (exist === NodeExist.Definitely) { - return true; - } - } - return false; +/** + * @param {Map} from + * @param {Map} to + */ +function add_to_map(from, to) { + from.forEach((exist, element) => { + to.set(element, higher_existence(exist, to.get(element))); + }); } -function add_to_map(from: Map, to: Map) { - from.forEach((exist, element) => { - to.set(element, higher_existence(exist, to.get(element))); - }); +/** + * @param {NodeExist | null} exist1 + * @param {NodeExist | null} exist2 + * @returns {NodeExist} + */ +function higher_existence(exist1, exist2) { + if (exist1 === undefined || exist2 === undefined) + return exist1 || exist2; + return exist1 > exist2 ? exist1 : exist2; } -function higher_existence(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist { - if (exist1 === undefined || exist2 === undefined) return exist1 || exist2; - return exist1 > exist2 ? exist1 : exist2; +/** @param {Map} result */ +function mark_as_probably(result) { + for (const key of result.keys()) { + result.set(key, NodeExist.Probably); + } } -function mark_as_probably(result: Map) { - for (const key of result.keys()) { - result.set(key, NodeExist.Probably); - } +/** + * @param {import('../nodes/interfaces.js').INode[]} children + * @param {boolean} adjacent_only + */ +function loop_child(children, adjacent_only) { + + /** @type {Map} */ + const result = new Map(); + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + if (child.type === 'Element') { + result.set(child, NodeExist.Definitely); + if (adjacent_only) { + break; + } + } + else if (child.type === 'EachBlock' || + child.type === 'IfBlock' || + child.type === 'AwaitBlock') { + const child_result = get_possible_last_child(child, adjacent_only); + add_to_map(child_result, result); + if (adjacent_only && has_definite_elements(child_result)) { + break; + } + } + } + return result; } +class Block { -function loop_child(children: INode[], adjacent_only: boolean) { - const result: Map = new Map(); - for (let i = children.length - 1; i >= 0; i--) { - const child = children[i]; - if (child.type === 'Element') { - result.set(child, NodeExist.Definitely); - if (adjacent_only) { - break; - } - } else if ( - child.type === 'EachBlock' || - child.type === 'IfBlock' || - child.type === 'AwaitBlock' - ) { - const child_result = get_possible_last_child(child, adjacent_only); - add_to_map(child_result, result); - if (adjacent_only && has_definite_elements(child_result)) { - break; - } - } - } - return result; + /** @type {boolean} */ + host; + + /** @type {boolean} */ + root; + + /** @type {import('./private.js').CssNode} */ + combinator; + + /** @type {import('./private.js').CssNode[]} */ + selectors; + + /** @type {number} */ + start; + + /** @type {number} */ + end; + + /** @type {boolean} */ + should_encapsulate; + + /** @param {import('./private.js').CssNode} combinator */ + constructor(combinator) { + this.combinator = combinator; + this.host = false; + this.root = false; + this.selectors = []; + this.start = null; + this.end = null; + this.should_encapsulate = false; + } + + /** @param {import('./private.js').CssNode} selector */ + add(selector) { + if (this.selectors.length === 0) { + this.start = selector.start; + this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host'; + } + this.root = this.root || (selector.type === 'PseudoClassSelector' && selector.name === 'root'); + this.selectors.push(selector); + this.end = selector.end; + } + get global() { + return (this.selectors.length >= 1 && + this.selectors[0].type === 'PseudoClassSelector' && + this.selectors[0].name === 'global' && + this.selectors.every((selector) => selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector')); + } } -class Block { - host: boolean; - root: boolean; - combinator: CssNode; - selectors: CssNode[]; - start: number; - end: number; - should_encapsulate: boolean; - - constructor(combinator: CssNode) { - this.combinator = combinator; - this.host = false; - this.root = false; - this.selectors = []; - - this.start = null; - this.end = null; - - this.should_encapsulate = false; - } - - add(selector: CssNode) { - if (this.selectors.length === 0) { - this.start = selector.start; - this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host'; - } - this.root = this.root || (selector.type === 'PseudoClassSelector' && selector.name === 'root'); - - this.selectors.push(selector); - this.end = selector.end; - } - - get global() { - return ( - this.selectors.length >= 1 && - this.selectors[0].type === 'PseudoClassSelector' && - this.selectors[0].name === 'global' && - this.selectors.every( - (selector) => - selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector' - ) - ); - } +/** @param {import('./private.js').CssNode} selector */ +function group_selectors(selector) { + + /** @type {Block} */ + let block = new Block(null); + const blocks = [block]; + selector.children.forEach((child) => { + if (child.type === 'WhiteSpace' || child.type === 'Combinator') { + block = new Block(child); + blocks.push(block); + } + else { + block.add(child); + } + }); + return blocks; } -function group_selectors(selector: CssNode) { - let block: Block = new Block(null); - const blocks = [block]; - selector.children.forEach((child: CssNode) => { - if (child.type === 'WhiteSpace' || child.type === 'Combinator') { - block = new Block(child); - blocks.push(block); - } else { - block.add(child); - } - }); - return blocks; -} diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index d458a35d49..6dc4df9548 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -4,7 +4,7 @@ import Selector from './Selector'; import Element from '../nodes/Element'; import { Ast, CssHashGetter } from '../../interfaces'; import Component from '../Component'; -import { CssNode } from './interfaces'; +import { CssNode } from './private'; import hash from '../utils/hash'; import compiler_warnings from '../compiler_warnings'; import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore'; diff --git a/src/compiler/compile/css/gather_possible_values.ts b/src/compiler/compile/css/gather_possible_values.js similarity index 66% rename from src/compiler/compile/css/gather_possible_values.ts rename to src/compiler/compile/css/gather_possible_values.js index eff39bc720..faae055ddd 100644 --- a/src/compiler/compile/css/gather_possible_values.ts +++ b/src/compiler/compile/css/gather_possible_values.js @@ -1,8 +1,10 @@ -import { Node } from 'estree'; - export const UNKNOWN = {}; -export function gather_possible_values(node: Node, set: Set) { +/** + * @param {import("estree").Node} node + * @param {Set} set + */ +export function gather_possible_values(node, set) { if (node.type === 'Literal') { set.add(node.value); } else if (node.type === 'ConditionalExpression') { diff --git a/src/compiler/compile/css/interfaces.ts b/src/compiler/compile/css/private.d.ts similarity index 100% rename from src/compiler/compile/css/interfaces.ts rename to src/compiler/compile/css/private.d.ts diff --git a/src/compiler/compile/render_dom/Block.js b/src/compiler/compile/render_dom/Block.js index f325a131a2..92867e8b6a 100644 --- a/src/compiler/compile/render_dom/Block.js +++ b/src/compiler/compile/render_dom/Block.js @@ -16,7 +16,7 @@ export default class Block { * @property {Block} [parent] * @property {import('estree').Identifier} name * @property {string} type - * @property {import('./Renderer.js').default} renderer + * @property {import('./Renderer.js').default} [renderer] * @property {string} [comment] * @property {import('estree').Identifier} [key] * @property {Map} [bindings] diff --git a/src/compiler/compile/render_dom/index.js b/src/compiler/compile/render_dom/index.js index e6faa458ec..7e640ac64a 100644 --- a/src/compiler/compile/render_dom/index.js +++ b/src/compiler/compile/render_dom/index.js @@ -11,13 +11,14 @@ import { push_array } from '../../utils/push_array.js'; /** * @param {import('../Component.js').default} component * @param {import('../../interfaces.js').CompileOptions} options - * @returns {{ js: import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').Node[]; css: import('../../interfaces.js').CssResult; }} + * @returns {{ js: import('estree').Node[]; css: import('../../interfaces.js').CssResult; }} */ export default function dom(component, options) { const { name } = component; const renderer = new Renderer(component, options); const { block } = renderer; block.has_outro_method = true; + /** @type {import('estree').Node[][]} */ const body = []; if (renderer.file_var) { const file = component.file ? x`"${component.file}"` : x`undefined`; @@ -537,7 +538,7 @@ export default function dom(component, options) { if (renderer.context_overflow) { dirty = x`[]`; for (let i = 0; i < renderer.context.length; i += 31) { - dirty.elements.push(x`-1`); + /** @type {any} */ (dirty).elements.push(x`-1`); } } const superclass = { @@ -570,7 +571,7 @@ export default function dom(component, options) { `[0] ); push_array(declaration.body.body, accessors); - body.push(declaration); + body.push(/** @type {any} */ (declaration)); if (options.customElement) { const props_str = writable_props.reduce((def, prop) => { def[prop.export_name] = diff --git a/src/compiler/compile/render_dom/invalidate.js b/src/compiler/compile/render_dom/invalidate.js index 00724157fe..69934a11e0 100644 --- a/src/compiler/compile/render_dom/invalidate.js +++ b/src/compiler/compile/render_dom/invalidate.js @@ -7,6 +7,7 @@ import { x } from 'code-red'; * @param {import('estree').Node} node * @param {Set} names * @param {boolean} main_execution_context + * @returns {any} */ export function invalidate(renderer, scope, node, names, main_execution_context = false) { const { component } = renderer; @@ -59,6 +60,7 @@ export function invalidate(renderer, scope, node, names, main_execution_context 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 = @@ -88,8 +90,9 @@ export function invalidate(renderer, scope, node, names, main_execution_context /** * @param {import('./Renderer.js').default} renderer * @param {string} name - * @param {unknown} [value] + * @param {any} [value] * @param {boolean} main_execution_context + * @returns {import('estree').Node} */ export function renderer_invalidate(renderer, name, value, main_execution_context = false) { const variable = renderer.component.var_lookup.get(name); diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.js b/src/compiler/compile/render_dom/wrappers/EachBlock.js index 8701dcec2a..a73e583e05 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.js +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.js @@ -531,7 +531,7 @@ export default class EachBlockWrapper extends Wrapper { * initial_mount_node: import('estree').Identifier; * update_anchor_node: import('estree').Identifier; * update_mount_node: import('estree').Identifier; - * }} + * }} params */ render_unkeyed({ block, diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.js b/src/compiler/compile/render_dom/wrappers/IfBlock.js index 897cc332af..28943e1285 100644 --- a/src/compiler/compile/render_dom/wrappers/IfBlock.js +++ b/src/compiler/compile/render_dom/wrappers/IfBlock.js @@ -422,7 +422,7 @@ export default class IfBlockWrapper extends Wrapper { * @param {import('estree').Identifier} parent_node * @param {import('estree').Identifier} _parent_nodes * @param {boolean} dynamic - * @param {any} + * @param {any} opts * @param {DetachingOrNull} detaching */ render_compound_with_outros( @@ -609,7 +609,7 @@ export default class IfBlockWrapper extends Wrapper { * @param {import('estree').Identifier} parent_node * @param {import('estree').Identifier} _parent_nodes * @param {boolean} dynamic - * @param {any} + * @param {any} opts * @param {DetachingOrNull} detaching */ render_simple(